如何在 AWS 中从 Java 生成签名

Posted

技术标签:

【中文标题】如何在 AWS 中从 Java 生成签名【英文标题】:How to generate Signature in AWS from Java 【发布时间】:2016-06-29 09:30:56 【问题描述】:

当我从 REST 客户端调用 API 端点时,我遇到了与签名有关的错误。

请求:

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

授权:AWS4-HMAC-SHA256 Credential=AWSKEY/20160314/ap-southeast-1/execute-api/aws4_request,SignedHeaders=host;range;x-amz-date,Signature =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

【讨论】:

令人惊讶的是,它不适用于其他 HTTP 客户端。同一组标头生成“我们计算的请求签名与您提供的签名不匹配。”错误。AmazonHttp 客户端运行顺利。 什么是AwsCredentialsFromSystem?我在 AWS Java API 中找不到对该类的引用。 AwsCredentialsFromSystem 是我的类,它实现了接口 AwsCredentials(来自 SDK)。它只是一个从系统属性中读取所需凭据的类:github.com/opencharles/charles-rest/blob/…【参考方案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();

免责声明:我是图书馆的作者。

【讨论】:

你好 Lucas,我​​很好奇你是否测试了这个库,用于为前端客户端提供签名以直接上传到 S3 的后端服务器。是否需要计算内容客户端的 sha256,然后将其作为签名计算的一部分提交给服务器? @CharneyKaye 你找到答案了吗我也在找呢 @lucasweb 不是硬编码标题对,我们是否有任何规定可以传递 Map/List ?如果是这样,那么通过方法参数一次动态接收所有标头并生成签名对我很有用 SQS 是否可用 我可以使用 aws-v4-signer 为 s3 表单发布请求生成签名 (x-amz-signature) 吗?我相信在这种情况下不需要标头,并且签名是由策略和秘密组成的?【参考方案3】:

这可以使用 100% java 库而无需额外依赖,只需使用此处生成的query parameters:

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


...

【讨论】:

谢谢,正在尝试为 Azure 生成密钥,因此不想使用 Amazon SDK。这个纯 Java 解决方案成功了 您不需要在signString 中添加host 吗?【参考方案4】:

签名过程冗长且容易出错,这里有一些提示

确保你的access key和secret是正确的,首先尝试使用Postman测试请求,简单快速,见Use Postman to Call a REST API 确保使用 UTC 时间 签名过程同时使用时间戳 (YYYYMMDD'T'HHMMSS'Z') 和日期时间 (YYYYMMDD),因此请仔细检查您的实现 使用任何在线哈希工具来验证您的哈希算法是否符合预期 仔细阅读python实现,见Examples of the Complete Version 4 Signing Process (Python) 在 Github 上查看我完整的 java 实现 - A Java(SpringBoot) template for Java and AWS SageMaker DeepAR model endpoint invocation integration

【讨论】:

【参考方案5】:

您可以调查 AWS 网站共享的代码示例。我使用了一些 util 类和一些我需要的 java 类。所以你不必使用所有的类和其他东西。我留下了下面的链接。

AWS Java Samples in doc of Amazon

【讨论】:

以上是关于如何在 AWS 中从 Java 生成签名的主要内容,如果未能解决你的问题,请参考以下文章

Sketch JS,如何在音频标签上使用 AWS 预签名 URL?

如何配置 AWS 用户 cognito 身份验证流程以在 Java sdk 后端生成身份令牌、访问令牌?

如何在 aws 中从我的 mac 设置和登录 ***?

如何在 local-exec 配置程序中从 terraform 继承 aws 凭据

如何在我的 Spring Boot 应用程序中从 AWS 访问环境变量

在没有 Bouncy Castle 的情况下,如何在 java 中从私有(ecdsa)生成公钥?