Created
December 12, 2025 07:46
-
-
Save weiserman/13af57b48616ae114cee3ecf434dc531 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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