如何从 Java 在 AWS 中生成签名
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/35985931/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to generate Signature in AWS from Java
提问by Tun Lin Aung
When I invoke API endpoints from REST client, I got error by concerning with Signature.
当我从 REST 客户端调用 API 端点时,我因涉及签名而出错。
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
主机: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
Response:
回复:
{
"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 Java code, I followed AWS reference of how to generate Signature.
从 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;
}
May I know what I was wrong while generating Signature?
我可以知道我在生成签名时做错了什么吗?
Reference how to generate Signature : http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-java
参考如何生成签名:http: //docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-java
回答by lucasweb
From the code example above it looks like you are not creating a canonical request and including it in the string that gets signed as per http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
从上面的代码示例中,您似乎没有创建规范请求并将其包含在按照http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-签名的字符串中请求.html
Instead of implementing this yourself have you looked at using a third-party library.
您没有自己实现这一点,而是考虑使用第三方库。
aws-v4-signer-javais a lightweight, zero-dependency library that makes it easy to generate AWS V4 signatures.
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();
Disclaimer: I'm the libraries author.
免责声明:我是图书馆的作者。
回答by amihaiemil
You can use classes from aws-java-sdk-core: https://github.com/aws/aws-sdk-java/tree/master/aws-java-sdk-core
您可以使用 aws-java-sdk-core 中的类:https: //github.com/aws/aws-sdk-java/tree/master/aws-java-sdk-core
More specifically, Request, Aws4Signer and a few other ones:
更具体地说,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>());
If you want a cleaner design, you can use the Decorator pattern to compose some elegant classes and hide the above mess. An example for that here: http://www.amihaiemil.com/2017/02/18/decorators-with-tunnels.html
如果你想要一个更简洁的设计,你可以使用装饰器模式来组合一些优雅的类并隐藏上面的混乱。这里的一个例子:http: //www.amihaiemil.com/2017/02/18/decorators-with-tunnels.html
回答by OscarG
This is possible using 100% java libraries without additional dependencies, just use the query parametersgenerated here:
这可以使用 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);
}
...
回答by KryptonJ
The signing process is lengthy and error-prone, here are some tips
签名过程漫长且容易出错,这里有一些提示
- Make sure your access key and secret is correct, try to use Postman to test the request at first, it's easy and fast, see Use Postman to Call a REST API
- Make sure you use UTC time
- The signing process uses both timestamp(YYYYMMDD'T'HHMMSS'Z') and datetime(YYYYMMDD), so double check your implementation for that
- Use any online hash tool to verify your hash algorithm behaves as expected
- Read the python implementation carefully, see Examples of the Complete Version 4 Signing Process (Python)
- See my fully working java implementation on Github - A Java(SpringBoot) template for Java and AWS SageMaker DeepAR model endpoint invocation integration
- 确保您的访问密钥和密码正确,首先尝试使用 Postman 测试请求,简单快捷,请参阅使用 Postman 调用 REST API
- 确保您使用 UTC 时间
- 签名过程同时使用时间戳(YYYYMMDD'T'HHMMSS'Z')和日期时间(YYYYMMDD),因此请仔细检查您的实现
- 使用任何在线哈希工具来验证您的哈希算法是否按预期运行
- 仔细阅读python实现,见完整版本4签名过程示例(Python)
- 请参阅我在 Github 上完全有效的 Java 实现 -用于 Java 和 AWS SageMaker DeepAR 模型端点调用集成的 Java(SpringBoot) 模板
回答by Prakash Palnati
The easiest way is to use methods and http-client from Amazon's SDK. I follow the below 3 steps.
最简单的方法是使用 Amazon 的 SDK 中的方法和 http-client。我遵循以下 3 个步骤。
Step1: Create basic AWS credentials:
步骤 1:创建基本的 AWS 凭证:
BasicAWSCredentials awsCreds = new BasicAWSCredentials(ACCESS_KEY,AWS_DATASHOP_SECRET_KEY);
Step2: Create signableRequest:
Step2:创建signableRequest:
DefaultRequest<?> signableRequest = new DefaultRequest<>("aws-service-name");
signableRequest.setHttpMethod(HttpMethodName.GET);
signableRequest.setResourcePath("fooo");
signableRequest.setEndpoint(URI.create(baar));
signableRequest.addParameter("execution_id", executionId);
signableRequest.addHeader("Content-Type", "application/json");
signer.sign(signableRequest, awsCreds);
Step3: Execute request using AmazonHttpClient:
步骤 3:使用 AmazonHttpClient 执行请求:
new AmazonHttpClient(new ClientConfiguration())
.requestExecutionBuilder()
.executionContext(new ExecutionContext(true))
.request(signableRequest)
.errorResponseHandler((new SimpleAwsErrorHandler()))
.execute(new MyResponseHandler());
Make sure to implement HttpResponseHandler
for SimpleAwsErrorHandler
and MyResponseHandler
确保HttpResponseHandler
为SimpleAwsErrorHandler
和MyResponseHandler
If you want to use normal http clients, you would have to create a canonical request and calculate signature which most often doesn't match.
如果要使用普通的 http 客户端,则必须创建规范请求并计算通常不匹配的签名。