如何在 AWS 中从 Java 生成签名

2022-09-03 00:50:14

当我从REST客户端调用API端点时,我遇到了错误,因为我担心签名。

请求:

主持人https://xxx.execute-api.ap-southeast-1.amazonaws.com/latest/api/name

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

X-Amz-日期: 20160314T102915Z

响应:

{
"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' "
}

从 Java 代码中,我按照 AWS 参考了如何生成签名。

    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;
    }

我可以知道我在生成签名时犯了什么错误吗?

参考如何生成签名:http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-java


答案 1

您可以使用 aws-java-sdk-core 中的类:https://github.com/aws/aws-sdk-java/tree/master/aws-java-sdk-core

更具体地说,Request,Aws4Signer和其他一些:

//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>());

如果你想要一个更干净的设计,你可以使用装饰器模式来组成一些优雅的类,并隐藏上面的混乱。这里有一个例子:http://www.amihaiemil.com/2017/02/18/decorators-with-tunnels.html


答案 2

从上面的代码示例来看,您似乎没有创建规范请求并将其包含在根据 http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html

与其自己实现它,不如考虑使用第三方库。

aws-v4-signer-java 是一个轻量级的零依赖性库,可以轻松生成 AWS V4 签名。

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();

免责声明:我是库的作者。