如何从Java在AWS中生成签名

如何从Java在AWS中生成签名,java,rest,amazon-web-services,signature,Java,Rest,Amazon Web Services,Signature,当我从REST客户机调用API端点时,由于涉及签名而导致错误 String secretKey = "{mysecretkey}"; String dateStamp = "20160314"; String regionName = "ap-southeast-1"; String serviceName = "execute-api"; byte[] signature = getSignatureKey(secretKey, dateStamp,

当我从REST客户机调用API端点时,由于涉及签名而导致错误

    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;
    }
请求:

主机

授权:AWS4-HMAC-SHA256凭证=
{AWSKEY}
/20160314/ap-Southest-1/执行api/AWS4_请求,签名头=主机;范围x-amz-date,签名=
{Signature}

X-Amz-Date: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;
    }
我可以知道我在生成签名时出错了吗


参考如何生成签名:

从上面的代码示例来看,您似乎没有创建规范请求,并将其包含在根据进行签名的字符串中

您没有亲自实现这一点,而是考虑使用第三方库

是一个轻量级的零依赖性库,可轻松生成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();

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

您可以使用aws java sdk核心中的类:

更具体地说,是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>());
//实例化请求
请求=新的默认请求(“es”)//对ElasticSearch的请求
request.setHttpMethod(HttpMethodName.GET);
request.setEndpoint(URI.create(“http://..."));
//签名。。。
AWS4Signer signer=新的AWS4Signer();
signer.setRegionName(“…”);
signer.setServiceName(request.getServiceName());
signer.sign(请求,新的AwsCredentialsFromSystem());
//执行它并获得响应。。。
响应rsp=new AmazonHttpClient(new ClientConfiguration())
.requestExecutionBuilder()
.executionContext(新executionContext(true))
.请求(请求)
.errorResponseHandler(新的SimpleAWErrorHandler())
.execute(新的SimpleResponseHandler());

如果您想要一个更干净的设计,您可以使用Decorator模式组合一些优雅的类并隐藏上面的混乱。这里有一个例子:

这可以使用100%的java库而不需要额外的依赖项,只需使用此处生成的:

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

...

签名过程冗长且容易出错,以下是一些提示

  • 确保您的访问密钥和密码正确无误,首先尝试使用Postman来测试请求,这非常简单快速,请参阅
  • 确保使用UTC时间
  • 签名过程同时使用时间戳(YYYYMMDD'T'HHMMSS'Z')和日期时间(yyyyymmdd),因此请仔细检查您的实现
  • 使用任何在线哈希工具验证哈希算法是否按预期运行
  • 仔细阅读python实现,请参见
  • 请参阅我在Github上的完整java实现-

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


Hiya Lucas,我很好奇您是否已经测试过这个库的后端服务器,为前端客户端提供签名,以便直接上传到S3。是否需要计算客户端内容的sha256,然后将其提交给服务器作为签名计算的一部分?@CharneyKaye您找到我也在寻找的答案了吗it@lucasweb我们是否有传递映射/列表的规定,而不是硬编码头对?如果是这样的话,那么通过方法参数一次动态接收所有头并生成签名对我来说是很有用的。这对SQSDid可用吗?你找到了解决方案吗?你是如何解决这个问题的?我得到了sameHi,请您也添加解决方案,您如何解决这个问题?谢谢,is正在尝试为Azure生成密钥,因此不想使用Amazon SDK。这个纯Java解决方案解决了您不需要在
signString
中添加
host
的难题,令人惊讶的是,它不适用于其他HTTP客户端。同一组头生成“我们计算的请求签名与您提供的签名不匹配。”错误。AmazonHttp客户端运行顺利。什么是
AwsCredentialsFromSystem
?我找不到该类的引用AWS Java API.AwsCredentialsFromSystem是我的类,它实现了接口AwsCredentials(来自SDK)。它只是一个从系统属性读取所需凭据的类: