生成 AWS 签名 v4 签名以上传到 s3(从 v2 迁移)



我目前有一个工作实现,其工作原理如下:

UI 选择一个文件 =>单击上传 =>调用我的后端 API 请求签名,因为我不想暴露我的访问权限 + 密钥 =>返回签名 + 策略 =>上传到 s3。

这对于 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文档,但我认为我有点误解了有效负载。我真的需要让我的 UI 在整个文件的 sha256 哈希中传递吗?因为这似乎有点痛苦,特别是因为我的文件可以> 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?

任何帮助将不胜感激,因为我已经把头垂在这种方式上太久了,我宁愿不要大修太多。

您可以使用标准开发工具包方法将文件上传到 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();
}

最新更新