0

I've read several tutorials about how to upload files to Amazon S3 from Salesforce using Apex (like this tutorial), but none of them are working for me. Also I've read many questions in this page but I haven't found a solution.

Every time I send a PUT request to upload a file in S3, I'm getting an error message saying 'The authorization mechanism you have provided is not supported. Please use AWS4-HMAC-SHA256' so I investigated how to solve it, and I found that using named credentials in SF I could provide a valid credential, so I followed this documentation about how to create a Named Credential for AWS Signature Version 4.

However, my problem is that, even thought I configured correctly the named credential with my key and secret for S3 (I checked it twice) I'm always getting an error in the response saying Access Denied and I'm unable to upload the file to S3

Here is the code I'm using. I create first a dummy xml file and then I try to upload it to S3.

public static void uploadToAmazonS3() {

        Xmlstreamwriter xmlW = new Xmlstreamwriter();
        xmlW.writeStartDocument('utf-8','1.0');
        xmlW.writeStartElement(null,'SERVICE', null);
        xmlW.writeAttribute(null,null,'version','1.0');
        xmlW.writeStartElement(null,'CLIENT',null);
        xmlW.writeAttribute(null,null,'email','xx.xx@xx.xx');
        xmlW.writeStartElement(null,'BASIC_FIELDS',null);
        xmlW.writeAttribute(null,null,'id','1');

        xmlW.writeStartElement(null,'ADRESS_LINE_1',null);
        xmlW.writeAttribute(null,null,'id','2');
        xmlW.writeCharacters('4 rue de rocher');
        xmlW.writeEndElement(); //Close ADRESS_LINE_1

        xmlW.writeStartElement(null,'CITY',null);
        xmlW.writeAttribute(null,null,'id','3');
        xmlW.writeCharacters('Paris');
        xmlW.writeEndElement(); //Close CITY

        xmlW.writeEndElement(); //Close BASIC_FIELDS
        xmlW.writeEndElement(); //Close CLIENT
        xmlW.writeEndElement(); //Close SERVICE
        xmlW.writeEndDocument(); //Close XML document

        String xmlStringxmlRes = xmlW.getXmlString();
        xmlW.close();
        Blob xmlContentBlob = Blob.valueOf(xmlStringxmlRes);

        String attachmentBody = EncodingUtil.base64Encode(xmlContentBlob);
        String formattedDateString = Datetime.now().formatGMT('EEE, dd MMM yyyy HH:mm:ss z');
        String key = 'xxxxx'; //AWS key
        String secret = 'xxxxxxx'; //AWS Secret key
        String bucketname = 'emdev-test1'; //AWS bucket name
        String host = 's3.us-east-2.amazonaws.com';
        String method = 'PUT';
        String filename = 'Upload-Test-1';

        HttpRequest req = new HttpRequest();
        req.setMethod(method);
        req.setEndpoint('callout:Test1Amazons3' + '/' + filename); //virtual hosted-style request
        //req.setEndpoint('https://' + bucketname + '.' + host + '/' + filename);
        req.setHeader('Host', bucketname + '.' + host);
        req.setHeader('Content-Length', String.valueOf(attachmentBody.length()));
        req.setHeader('Content-Encoding', 'UTF-8');
        req.setHeader('Content-type', 'application/xml');
        req.setHeader('Connection', 'keep-alive');
        req.setHeader('Date', formattedDateString);
        req.setHeader('ACL', 'public-read-write');
        Blob xmlBlob = EncodingUtil.base64Decode(attachmentBody);
        req.setBodyAsBlob(xmlBlob);

        String stringToSign = 'PUT\n\n' + 'application/xml' + '\n' + formattedDateString + '\n' + '/' + bucketname + '/' + filename;
        Blob mac = Crypto.generateMac('HMACSHA1', blob.valueof(stringToSign), blob.valueof(secret)); //HMACSHA256
        String signedKey  = EncodingUtil.base64Encode(mac);
        String authHeader = 'AWS' + ' ' + key + ':' + signedKey;

        req.setHeader('Authorization', authHeader);
        Http http = new Http();
        HTTPResponse res = http.send(req);

        System.debug('>>>> The response');
        System.debug(res);
        System.debug('>>>> The body');
        System.debug(res.getBody());
        System.debug('>>>> Status code');
        System.debug(res.getStatusCode());
        System.debug('>>>> Finished method');

    }

And I always get "Access Denied". Any help would be really appreciated, as I've spent so many hours trying to find a solution. Thankyou very much for your time

PD: I've also read this other question here but this question is different from mine since the error is different than mine,my error is "Access denied" when I'm using naming credentials with Amazon S3. However I've tried this solution also and it didn't work. Thanks.

2

1 Answer 1

1

I finally could solve my problem, it turns out that the credentials that I was using was wrong, so I had to generate new ones and once that I introduced the new credentials in my code it started working well, so the first thing I would suggest if anyone is facing similar issues is

  1. Login to Amazon S3 and go to the following link to generate new credentials (https://console.aws.amazon.com/iam/home?#/security_credentials)

  2. Create a named credential for S3 in Salesforce puting the key and secret you created in the previous step, like is shown in this image

Named Credentials for S3

  1. Do not pass any authentication headers to the HTTP request, as this will be passed by the named credential we created in step 2

  2. Adjust the following code for sending your files to S3. Put your key and secret on it, as well as the bucket name, the host, and the name of the file that you want to create with the file extension (i. eg. 'MyFile.txt'). Otherwise it won't have extension when you look at it in S3. For the fileType variable, take a look of the available file extensions that are here (in my case is text/xml)

    Xmlstreamwriter xmlW = new Xmlstreamwriter(); xmlW.writeStartDocument('utf-8','1.0'); xmlW.writeStartElement(null,'SERVICE', null); xmlW.writeAttribute(null,null,'version','1.0'); xmlW.writeStartElement(null,'CLIENT',null); xmlW.writeAttribute(null,null,'email','xx.xx@xx.xx'); xmlW.writeStartElement(null,'BASIC_FIELDS',null); xmlW.writeAttribute(null,null,'id','1');

     xmlW.writeStartElement(null,'ADRESS_LINE_1',null);
     xmlW.writeAttribute(null,null,'id','2');
     xmlW.writeCharacters('4 rue de rocher');
     xmlW.writeEndElement();
    
     xmlW.writeStartElement(null,'CITY',null);
     xmlW.writeAttribute(null,null,'id','3');
     xmlW.writeCharacters('Paris');
     xmlW.writeEndElement();
    
     xmlW.writeEndElement();
     xmlW.writeEndElement();
     xmlW.writeEndElement();
     xmlW.writeEndDocument();
    
     String xmlStringxmlRes = xmlW.getXmlString();
     xmlW.close();
     Blob xmlContentBlob = Blob.valueOf(xmlStringxmlRes);
    
     String attachmentBody = EncodingUtil.base64Encode(xmlContentBlob);
     String formattedDateString = Datetime.now().formatGMT('EEE, dd MMM yyyy HH:mm:ss z');
     String key = 'xxxxxxxxx'; 
     String secret = 'yyyyyyyyyyyyyyyyyyyyy'; 
     String bucketname = 'emdev-test1';
     String host = 's3.us-east-2.amazonaws.com';
     String method = 'PUT';
     String filename = 'Upload-Test-3.xml';
     String fileType = 'text/xml';
    
     HttpRequest req = new HttpRequest();
     req.setMethod(method);
     req.setEndpoint('callout:Test1Amazons3' + '/' + filename);
     req.setHeader('Host', bucketname + '.' + host);
     req.setHeader('Content-Length', String.valueOf(attachmentBody.length()));
     req.setHeader('Content-Encoding', 'UTF-8');
     req.setHeader('Content-type', fileType);
     req.setHeader('Connection', 'keep-alive');
     req.setHeader('Date', formattedDateString);
     req.setHeader('ACL', 'public-read-write');
     req.setBodyAsBlob(EncodingUtil.base64Decode(attachmentBody));
    
     Http http = new Http();
     HTTPResponse res = http.send(req);
    
     System.debug('>>>> The response');
     System.debug(res);
     System.debug('>>>> The body');
     System.debug(res.getBody());
     System.debug('>>>> Status code');
     System.debug(res.getStatusCode());
     System.debug('>>>> Finished method');
    

That's it, hopefully this will help some one else in the future with this problem. Regards! :)

Not the answer you're looking for? Browse other questions tagged or ask your own question.