问题描述
我目前有一个可行的实现,其工作方式如下:
UI选择文件=>单击上载=>调用我的后端API以请求签名,因为我不想公开访问权限+秘钥=>返回签名+策略=>将文件上传到s3./p>
这对于v2来说很好用,很花哨.
String base64Policy = (new BASE64Encoder()).encode(policy.toString().getBytes("UTF-8")).replaceAll("\n", "").replaceAll("\r", "");
Mac hmac = Mac.getInstance("HmacSHA1");
hmac.init(new SecretKeySpec(secretKey.getBytes("UTF-8"), "HmacSHA1"));
String signature = (new BASE64Encoder()).encode(hmac.doFinal(base64Policy.getBytes("UTF-8"))).replaceAll("\n", "");
现在,我很有趣,我的新存储桶位于不支持v2的区域.
我一直在关注AWS文档,但我认为我对有效负载有些误解.我真的需要在整个文件的sha256哈希中传递我的UI吗?因为这似乎有点麻烦,尤其是因为我的文件可以大于1兆.
我尝试使用的代码:
byte[] signatureKey = getSignatureKey(secretKey, LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")), bucketRegion, "s3");
StringBuilder sb = new StringBuilder();
for (byte b : signatureKey) {
sb.append(String.format("%02X", b));
}
private 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;
}
private 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"));
}
但是当我尝试使用其余代码时,这会给出无效的签名响应.
我难以忍受,只是误会了: https://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html 吗?
任何帮助都将不胜感激,因为我一直对这种方式持怀疑态度,并且我宁愿不要大修.
您可以使用标准SDK方法将文件上传到S3,而无需生成签名,请参阅文档.但是,我认为,如果出于某种原因需要签名,则生成签名的最简单方法是使用AWS开发工具包中的方法,请参见以下扩展了AWS4Signer
的类:
public class AwsAuthUtil extends AWS4Signer {
private String serviceName;
private AWSCredentials credentials;
private String region;
public AwsAuthUtil(AWSCredentials credentials, String region, String serviceName) {
this.credentials = credentials;
this.region = region;
this.serviceName = serviceName;
}
public String getSignature(String policy, LocalDateTime dateTime) {
try {
String dateStamp = dateTime.format(ofPattern("yyyyMMdd"));
return Hex.encodeHexString(hmacSha256(newSigningKey(credentials, dateStamp, region, serviceName), policy));
} catch (Exception e) {
throw new RuntimeException("Error", e);
}
}
private byte[] hmacSha256(byte[] key, String data) throws Exception {
Mac mac = Mac.getInstance(SigningAlgorithm.HmacSHA256.name());
mac.init(new SecretKeySpec(key, SigningAlgorithm.HmacSHA256.name()));
return mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
}
}
AWS4Signer
的来源
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>1.11.213</version>
</dependency>
和AWSCredentials
可以构建为
AWSCredentials awsCredentials = new BasicAWSCredentials(s3AccessKey, s3SecretKey);
此外,在使用多部分数据时,您还应该考虑使用HTTP标头,例如,请参见以下构建 HttpEntity
public HttpEntity buildPostMultipartDataEntity(String objectKey, byte[] data, String signature, LocalDateTime dateTime) {
String dateTimeStr = dateTime.format(ofPattern("yyyyMMdd'T'HHmmss'Z'"));
String date = dateTime.format(ofPattern("yyyyMMdd"));
return MultipartEntityBuilder
.create()
.addTextBody("key", objectKey)
.addTextBody("Policy", policy)
.addTextBody("X-Amz-Signature", signature)
.addTextBody("X-Amz-Algorithm", algorithm)
.addTextBody("X-Amz-Date", dateTimeStr)
.addTextBody("X-Amz-Credential", String.format("%s/%s/%s/s3/aws4_request", accessKey, date, region))
.addBinaryBody("file", data)
.build();
}
I currently have a working implementation that works as follows:
UI select a file => click upload => call to my backend API to request a signature since I don't want to expose my access + secretkey => return the signature + policy => do an upload to s3.
This works fine and dandy for v2.
String base64Policy = (new BASE64Encoder()).encode(policy.toString().getBytes("UTF-8")).replaceAll("\n", "").replaceAll("\r", "");
Mac hmac = Mac.getInstance("HmacSHA1");
hmac.init(new SecretKeySpec(secretKey.getBytes("UTF-8"), "HmacSHA1"));
String signature = (new BASE64Encoder()).encode(hmac.doFinal(base64Policy.getBytes("UTF-8"))).replaceAll("\n", "");
Now I get to the fun bit where my new buckets are in a region where v2 isn't supported.
I was following the AWS documentation but I think I am misunderstanding the payload bit a bit. Do I really need to have my UI pass in a sha256 hash of my whole file? Since that would seem to be a bit of a pain, especially since my files can be > 1 gig.
The code I was attempting to use:
byte[] signatureKey = getSignatureKey(secretKey, LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")), bucketRegion, "s3");
StringBuilder sb = new StringBuilder();
for (byte b : signatureKey) {
sb.append(String.format("%02X", b));
}
private 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;
}
private 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"));
}
But this gives an invalid signature response when I try to use the rest of my code.
Am I derping that hard, and just misunderstanding: https://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html ?
Any help would be much appreciated since I've been hanging my head against this way too long and I'd prefer not to overhaul too much.
You can upload a file to S3 by using standard SDK methods without generating a signature, please see the documentation.But if you need a signature for some reason, I think, the simplest way to generate a signature is to use methods from AWS SDK, please see the following class which extends AWS4Signer
:
public class AwsAuthUtil extends AWS4Signer {
private String serviceName;
private AWSCredentials credentials;
private String region;
public AwsAuthUtil(AWSCredentials credentials, String region, String serviceName) {
this.credentials = credentials;
this.region = region;
this.serviceName = serviceName;
}
public String getSignature(String policy, LocalDateTime dateTime) {
try {
String dateStamp = dateTime.format(ofPattern("yyyyMMdd"));
return Hex.encodeHexString(hmacSha256(newSigningKey(credentials, dateStamp, region, serviceName), policy));
} catch (Exception e) {
throw new RuntimeException("Error", e);
}
}
private byte[] hmacSha256(byte[] key, String data) throws Exception {
Mac mac = Mac.getInstance(SigningAlgorithm.HmacSHA256.name());
mac.init(new SecretKeySpec(key, SigningAlgorithm.HmacSHA256.name()));
return mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
}
}
where AWS4Signer
is from
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>1.11.213</version>
</dependency>
and AWSCredentials
can be built as
AWSCredentials awsCredentials = new BasicAWSCredentials(s3AccessKey, s3SecretKey);
Also you should consider http headers when you use multipart data, for example, please see the following method which builds HttpEntity
public HttpEntity buildPostMultipartDataEntity(String objectKey, byte[] data, String signature, LocalDateTime dateTime) {
String dateTimeStr = dateTime.format(ofPattern("yyyyMMdd'T'HHmmss'Z'"));
String date = dateTime.format(ofPattern("yyyyMMdd"));
return MultipartEntityBuilder
.create()
.addTextBody("key", objectKey)
.addTextBody("Policy", policy)
.addTextBody("X-Amz-Signature", signature)
.addTextBody("X-Amz-Algorithm", algorithm)
.addTextBody("X-Amz-Date", dateTimeStr)
.addTextBody("X-Amz-Credential", String.format("%s/%s/%s/s3/aws4_request", accessKey, date, region))
.addBinaryBody("file", data)
.build();
}
这篇关于生成AWS Signature v4签名以上传到s3(从v2迁移)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!