Skip to content

Instantly share code, notes, and snippets.

@weiserman
Created December 12, 2025 07:46
Show Gist options
  • Select an option

  • Save weiserman/13af57b48616ae114cee3ecf434dc531 to your computer and use it in GitHub Desktop.

Select an option

Save weiserman/13af57b48616ae114cee3ecf434dc531 to your computer and use it in GitHub Desktop.
import com.sap.gateway.ip.core.customdev.util.Message;
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import java.text.SimpleDateFormat
import java.util.TimeZone;
import org.apache.commons.codec.digest.DigestUtils;
import com.sap.it.api.ITApiFactory;
import com.sap.it.api.securestore.SecureStoreService
import groovy.json.JsonBuilder
import groovy.json.JsonSlurper
def Message processData(Message message) {
//************* Read and Parse JSON from message body *************
def body = message.getBody(String)
def jsonSlurper = new JsonSlurper()
def jsonInput = jsonSlurper.parseText(body)
//************* Extract values from JSON *************
def arnInput = jsonInput.arn
def region = jsonInput.region ?: ""
def expires = jsonInput.expiresIn ?: "3600"
//************* DEBUG: Log the input *************
def messageLog = messageLogFactory.getMessageLog(message);
if(messageLog != null){
messageLog.addAttachmentAsString("Input JSON", body, "application/json");
messageLog.addAttachmentAsString("Input ARN", arnInput, "text/plain");
}
//************* Parse ARN *************
def bucket = ""
def objectKey = ""
if (arnInput.startsWith("arn:aws:s3")) {
def arnWithoutPrefix = arnInput.substring("arn:aws:s3:".length())
def tripleColonIndex = arnWithoutPrefix.indexOf(":::")
if (tripleColonIndex > 0) {
if (region == "") {
region = arnWithoutPrefix.substring(0, tripleColonIndex)
}
def bucketAndKey = arnWithoutPrefix.substring(tripleColonIndex + 3)
def firstSlashIndex = bucketAndKey.indexOf("/")
if (firstSlashIndex > 0) {
bucket = bucketAndKey.substring(0, firstSlashIndex)
objectKey = bucketAndKey.substring(firstSlashIndex + 1)
} else {
bucket = bucketAndKey
objectKey = ""
}
} else {
def bucketAndKey = arnWithoutPrefix.replace("::", "")
def firstSlashIndex = bucketAndKey.indexOf("/")
if (firstSlashIndex > 0) {
bucket = bucketAndKey.substring(0, firstSlashIndex)
objectKey = bucketAndKey.substring(firstSlashIndex + 1)
} else {
bucket = bucketAndKey
objectKey = ""
}
}
}
if (region == null || region.isEmpty()) {
region = "us-west-2"
}
//************* DEBUG: Log parsed values *************
if(messageLog != null){
def debugInfo = "Bucket: ${bucket}\nObject Key: ${objectKey}\nRegion: ${region}\nExpires: ${expires}"
messageLog.addAttachmentAsString("Parsed Values", debugInfo, "text/plain");
}
//************* Secure Store Aliases *************
def apiacesskey_alias = jsonInput.accessKeyAlias ?: "AWS_IAM_ACCESS_KEY"
def apisecretkey_alias = jsonInput.secretKeyAlias ?: "AWS_IAM_SECRET_KEY"
//************* Access Secure Parameters *************
def secureStorageService = ITApiFactory.getService(SecureStoreService.class, null);
def secureParameterAcessKey = secureStorageService.getUserCredential(apiacesskey_alias);
def secureParameterSecretKey = secureStorageService.getUserCredential(apisecretkey_alias);
def access_key = secureParameterAcessKey.getPassword().toString();
def secret_key = secureParameterSecretKey.getPassword().toString();
//************* Initiating variables *************
String method = "GET";
String service = "s3";
String host = bucket + ".s3." + region + ".amazonaws.com";
// ************ Create a timestamp *************
def now = new Date()
def amzFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'")
amzFormat.setTimeZone(TimeZone.getTimeZone("UTC"))
def stampFormat = new SimpleDateFormat("yyyyMMdd")
stampFormat.setTimeZone(TimeZone.getTimeZone("UTC"))
def amzDate = amzFormat.format(now)
def date_stamp = stampFormat.format(now)
//************* URI Encoding *************
String canonical_uri = "/" + uriEncode(objectKey, true)
String objectKeyUrlEncoded = uriEncode(objectKey, false)
//************* Build Canonical Query String *************
String canonical_querystring = "X-Amz-Algorithm=AWS4-HMAC-SHA256" +
"&X-Amz-Credential=" + uriEncode(access_key, false) + "%2F" + date_stamp + "%2F" + region + "%2Fs3%2Faws4_request" +
"&X-Amz-Date=" + amzDate +
"&X-Amz-Expires=" + expires +
"&X-Amz-SignedHeaders=host"
//************* Canonical Headers *************
String canonical_headers = "host:" + host + "\n"
String signed_headers = "host"
String payload_hash = "UNSIGNED-PAYLOAD"
//************* Build Canonical Request *************
String canonical_request = method + "\n" +
canonical_uri + "\n" +
canonical_querystring + "\n" +
canonical_headers + "\n" +
signed_headers + "\n" +
payload_hash
//************* String to Sign *************
String algorithm = "AWS4-HMAC-SHA256";
String credential_scope = date_stamp + "/" + region + "/" + service + "/" + "aws4_request";
String string_to_sign = algorithm + "\n" +
amzDate + "\n" +
credential_scope + "\n" +
DigestUtils.sha256Hex(canonical_request);
//************* Generate Signing Key *************
byte[] signing_key = getSignatureKey(secret_key, date_stamp, region, service);
//************* Generate Signature *************
byte[] signature = HmacSHA256(string_to_sign, signing_key);
String strHexSignature = bytesToHex(signature);
//************* Build Final Presigned URL *************
String urlfile = "https://" + host + "/" + objectKeyUrlEncoded;
String urlfileSigned = urlfile + "?" + canonical_querystring + "&X-Amz-Signature=" + strHexSignature;
//************* Create JSON response *************
def response = [
presignedUrl: urlfileSigned,
bucket: bucket,
objectKey: objectKey,
region: region,
expiresIn: expires,
generatedAt: amzDate
]
def jsonBuilder = new JsonBuilder(response)
message.setProperty("presignedUrl", urlfileSigned)
message.setProperty("bucket", bucket)
message.setProperty("objectKey", objectKey)
message.setBody(jsonBuilder.toPrettyString())
return message;
}
//************* URI Encode function for AWS SigV4 *************
String uriEncode(String input, boolean encodeSlash) {
StringBuilder result = new StringBuilder();
for (byte b : input.getBytes("UTF-8")) {
if ((b >= 'A' && b <= 'Z') || (b >= 'a' && b <= 'z') || (b >= '0' && b <= '9') || b == '_' || b == '-' || b == '~' || b == '.') {
result.append((char) b);
} else if (b == '/' && !encodeSlash) {
result.append('/');
} else {
result.append('%');
result.append(String.format("%02X", b & 0xFF));
}
}
return result.toString();
}
//************* Function bytes to Hex *************
String bytesToHex(byte[] bytes) {
char[] hexArray = "0123456789ABCDEF".toCharArray();
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars).toLowerCase();
}
//************* Function HmacSHA256 *************
byte[] HmacSHA256(String data, byte[] key) throws Exception {
String algorithm = "HmacSHA256";
Mac mac = Mac.getInstance(algorithm);
mac.init(new SecretKeySpec(key, algorithm));
return mac.doFinal(data.getBytes("UTF8"));
}
//************* Function getSignature *************
byte[] getSignatureKey(String key, String dateStamp, String regionName, String serviceName) throws Exception {
byte[] kSecret = ("AWS4" + key).getBytes("UTF8");
byte[] kDate = HmacSHA256(dateStamp, kSecret);
byte[] kRegion = HmacSHA256(regionName, kDate);
byte[] kService = HmacSHA256(serviceName, kRegion);
byte[] kSigning = HmacSHA256("aws4_request", kService);
return kSigning;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment