How to generate a signature in AWS with Java

When I call the API endpoints from the REST client, I got an error regarding the Signature.

Request:

Host : https://xxx.execute-api.ap-southeast-1.amazonaws.com/latest/api/name

Authorization : AWS4-HMAC-SHA256 Credential = {AWSKEY} / 20160314 / ap-southeast-1 / execute-api / aws4_request, SignedHeaders = host; range; x-amz-date, Signature = {signature}

X-Amz-Date : 20160314T102915Z

Answer:

 { "message": "The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details. The Canonical String for this request should have been 'xxx' " } 

From the Java code, I followed the AWS link on how to generate a signature.

  String secretKey = "{mysecretkey}"; String dateStamp = "20160314"; String regionName = "ap-southeast-1"; String serviceName = "execute-api"; byte[] signature = getSignatureKey(secretKey, dateStamp, regionName, serviceName); System.out.println("Signature : " + Hex.encodeHexString(signature)); static 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")); } static 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; } 

Can I find out that I was mistaken in creating the Signature?

Link how to generate a signature: http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-java

+9
source share
4 answers

You can use the classes from aws-java-sdk-core: https://github.com/aws/aws-sdk-java/tree/master/aws-java-sdk-core

In particular, Request, Aws4Signer and several others:

 //Instantiate the request Request<Void> request = new DefaultRequest<Void>("es"); //Request to ElasticSearch request.setHttpMethod(HttpMethodName.GET); request.setEndpoint(URI.create("http://...")); //Sign it... AWS4Signer signer = new AWS4Signer(); signer.setRegionName("..."); signer.setServiceName(request.getServiceName()); signer.sign(request, new AwsCredentialsFromSystem()); //Execute it and get the response... Response<String> rsp = new AmazonHttpClient(new ClientConfiguration()) .requestExecutionBuilder() .executionContext(new ExecutionContext(true)) .request(request) .errorResponseHandler(new SimpleAwsErrorHandler()) .execute(new SimpleResponseHandler<String>()); 

If you want a cleaner design, you can use the Decorator template to compose some elegant classes and hide the clutter described above. An example for this is here: http://www.amihaiemil.com/2017/02/18/decorators-with-tunnels.html

+11
source

From the above code example, it looks like you are not creating a canonical request and not including it in a line that is signed as http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html

Instead of implementing this yourself, you looked at using a third-party library.

aws-v4-signer-java is a lightweight zero-dependency library that makes it easy to create AWS V4 signatures.

 String contentSha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; HttpRequest request = new HttpRequest("GET", new URI("https://examplebucket.s3.amazonaws.com?max-keys=2&prefix=J")); String signature = Signer.builder() .awsCredentials(new AwsCredentials(ACCESS_KEY, SECRET_KEY)) .header("Host", "examplebucket.s3.amazonaws.com") .header("x-amz-date", "20130524T000000Z") .header("x-amz-content-sha256", contentSha256) .buildS3(request, contentSha256) .getSignature(); 

Disclaimer: I am the author of the libraries.

+2
source

This is possible when using 100% Java libraries without additional dependencies, just use the query parameters generated here:

 import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SignatureException; import java.util.Formatter; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.util.Base64; ... private static final String ACCESS_KEY = "..."; private static final String SECRET_KEY = "..."; private static final int expiresTime = 1 * 24 * 60 * 60; private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1"; public void sign(String protocol, String bucketName, String contentPath) throws Exception { Calendar cal = Calendar.getInstance(); cal.add(Calendar.HOUR_OF_DAY, 24); String host = bucketName + ".s3-us-west-2.amazonaws.com"; long expireTime = cal.getTimeInMillis() / 1000; String signString = "GET\n" + "\n" + "\n" + expireTime + "\n" + "/" + bucketName + contentPath; SecretKeySpec signingKey = new SecretKeySpec(SECRET_KEY.getBytes(), HMAC_SHA1_ALGORITHM); Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM); mac.init(signingKey); String signature = URLEncoder.encode(new String(Base64.getEncoder().encode(mac.doFinal(signString.getBytes())))); System.out.println(signature); String fullPayload = "?AWSAccessKeyId=" + ACCESS_KEY + "&Expires=" + expireTime + "&Signature=" + signature; System.out.println(protocol + "://" + host + contentPath + fullPayload); } ... 
0
source

The signing process is lengthy and error prone, here are some tips

0
source

All Articles