Quantcast
Channel: Amazon S3 - Specifying SSE-S3 with pre-signed URL's usingAPEX - Salesforce Stack Exchange
Viewing all articles
Browse latest Browse all 2

Amazon S3 - Specifying SSE-S3 with pre-signed URL's usingAPEX

$
0
0

Has anybody worked with creating pre-signed urls for Amazon S3 within APEX along with specifying server side encryption?

Currently I am generating pre-signed urls for uploading and downloading files to the S3 bucket. These urls are consumed by an angular front end and this is working fine.

However we need to ask S3 to encrypt the files.I thought this was pretty simple and involved setting a header in the request. But I am unable to do this. I am getting the dreaded error stating the signature does not match the calculated signature. The following documentation states that SSE-S3 does not work with pre-sgned urls.

http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingServerSideEncryption.html

“You can't enforce whether or not objects are encrypted with SSE-S3 when they are uploaded using pre-signed URLs. “

There is another option of doing using SSE-C. But adding those headers in the signature causes the signature to be incorrect. These are the headers:

x-amz-server-side​-encryption​-customer-algorithm

x-amz-server-side​-encryption​-customer-key

x-amz-server-side​-encryption​-customer-key-MD5

Relevant original code:Setting headers:

Map<string, string> defaults = new Map<string, string>();            defaults.put('X-Amz-Algorithm', S3Connection.AWS_HEADER_ENCRYPTION_SCHEME);            defaults.put('X-Amz-Credential', this.accessKey +'/'+ this.dateStampYYYYMMDD +'/'+ this.awsRegion +'/s3/aws4_request');            defaults.put('X-Amz-Date', this.dateStampISO); // convert to ISO 8601 format            defaults.put('X-Amz-Expires', this.expiresSeconds);            defaults.put('X-Amz-SignedHeaders', 'host');

Calculating S3 canonical string and signature.

public string getRequestString() {

    // ==== CONSTRUCT THE REQUEST STRING FROM THE DATA ====    // Now construct the canonical request string from these elements.    string canonicalRequestString = this.httpVerb +'\n'+'/'+ bucketName +'/'+ this.canonicalUri +'\n';    // Add the query string    // we assume the elements remain in alphabetical order. TODO: sort just to be sure.    for (string key : this.canonicalQueryMap.keySet()) {        canonicalRequestString += awsUriEncode(key) +'='+ awsUriEncode(this.canonicalQueryMap.get(key)) +'&';    }    for (string key : this.additionalCanonicalHeaders.keySet()) {        canonicalRequestString += awsUriEncode(key) +'='+ awsUriEncode(this.additionalCanonicalHeaders.get(key)) +'&';    }    // replace the last & with a \n    canonicalRequestString = canonicalRequestString.left(canonicalRequestString.length() - 1) +

'\n';

    // Add the canonical headers with a linefeed at the end of each    for (string key : this.canonicalHeaders.keySet()) {        if(this.canonicalHeaders.get(key) != null) {            canonicalRequestString += key.toLowerCase() +':'+ this.canonicalHeaders.get(key).trim() +'\n';        }    }    // add an additional linefeed    canonicalRequestString += '\n';    // Add signed headers    // we assume the elements remain in alphabetical order.    // TODO: sort just to be sure and check that the map isn't empty.    integer keyNum = 0;    for (string key : this.signedHeaders) {        keyNum ++;        if(keyNum > 1) {            canonicalRequestString += ';';        }        canonicalRequestString += key.toLowerCase();    }    // add an additional linefeed    canonicalRequestString += '\n';    // Add the final line which specifies that we aren't sure of the file size.    canonicalRequestString += 'UNSIGNED-PAYLOAD';    return canonicalRequestString;}public string getStringToSign() {    // ==== CONSTRUCT THE STRING TO SIGN ====    string stringToSign = S3Connection.AWS_HEADER_ENCRYPTION_SCHEME +'\n'+ this.dateStampISO +'\n'+ this.dateStampYYYYMMDD +'/'+ this.awsRegion +'/s3/aws4_request'+'\n'+ EncodingUtil.convertToHex(Crypto.generateDigest(S3Connection.AWS_ENCRYPTION_SCHEME,

blob.valueOf(this.getRequestString()))); return stringToSign; }

public blob getSigningKey() {    // ==== GENERATE THE SIGNING KEY ====    Blob dateKey = Crypto.generateMac('hmacSHA256', Blob.valueOf(this.dateStampYYYYMMDD), Blob.valueOf('AWS4'+

this.accessSecret)); Blob dateRegionKey = Crypto.generateMac('hmacSHA256', Blob.valueOf(this.awsRegion), dateKey); Blob dateRegionServiceKey = Crypto.generateMac('hmacSHA256', blob.valueOf(this.awsServiceName), dateRegionKey); Blob signingKey = Crypto.generateMac('hmacSHA256', blob.valueOf('aws4_request'), dateRegionServiceKey);

    return signingKey;}public string getSignature() {    // ==== GENERATE THE SIGNATURE ====    return this.generateSignature(this.getStringToSign(), this.getSigningKey());}public string generateSignature(string stringToSign, blob signingKey) {    blob signatureBlob = Crypto.generateMac('hmacSHA256', blob.valueOf(stringToSign), signingKey);    return EncodingUtil.convertToHex(signatureBlob);}

I assume server side encryption is a pretty general requirement. How do we achieve it in APEX with pre-signed urls? Thanks!


Viewing all articles
Browse latest Browse all 2

Latest Images

Trending Articles





Latest Images