云:AWS
SDK版本:java-aws-sdk-1.10.49
。
我想为我要上传的对象发展对象锁定。
下面是我的代码,它正在生成一个aws
签名4标头,但仍然无法解决问题。
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
import java.util.TreeMap;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class AWSV4Auth {
private AWSV4Auth() {
}
public static class Builder {
private String accessKeyID;
private String secretAccessKey;
private String regionName;
private String serviceName;
private String httpMethodName;
private String canonicalURI;
private TreeMap<String, String> queryParametes;
private TreeMap<String, String> awsHeaders;
private String payload;
private boolean debug = false;
public Builder(String accessKeyID, String secretAccessKey) {
this.accessKeyID = accessKeyID;
this.secretAccessKey = secretAccessKey;
}
public Builder regionName(String regionName) {
this.regionName = regionName;
return this;
}
public Builder serviceName(String serviceName) {
this.serviceName = serviceName;
return this;
}
public Builder httpMethodName(String httpMethodName) {
this.httpMethodName = httpMethodName;
return this;
}
public Builder canonicalURI(String canonicalURI) {
this.canonicalURI = canonicalURI;
return this;
}
public Builder queryParametes(TreeMap<String, String> queryParametes) {
this.queryParametes = queryParametes;
return this;
}
public Builder awsHeaders(TreeMap<String, String> awsHeaders) {
this.awsHeaders = awsHeaders;
return this;
}
public Builder payload(String payload) {
this.payload = payload;
return this;
}
public Builder debug() {
this.debug = true;
return this;
}
public AWSV4Auth build() {
return new AWSV4Auth(this);
}
}
private String accessKeyID;
private String secretAccessKey;
private String regionName;
private String serviceName;
private String httpMethodName;
private String canonicalURI;
private TreeMap<String, String> queryParametes;
private TreeMap<String, String> awsHeaders;
private String payload;
private boolean debug = false;
/* Other variables */
private final String HMACAlgorithm = "AWS4-HMAC-SHA256";
private final String aws4Request = "aws4_request";
private String strSignedHeader;
private String xAmzDate;
private String currentDate;
private AWSV4Auth(Builder builder) {
accessKeyID = builder.accessKeyID;
secretAccessKey = builder.secretAccessKey;
regionName = builder.regionName;
serviceName = builder.serviceName;
httpMethodName = builder.httpMethodName;
canonicalURI = builder.canonicalURI;
queryParametes = builder.queryParametes;
awsHeaders = builder.awsHeaders;
payload = builder.payload;
debug = builder.debug;
/* Get current timestamp value.(UTC) */
xAmzDate = getTimeStamp();
currentDate = getDate();
}
/**
* Task 1: Create a Canonical Request for Signature Version 4.
*
* @return
*/
private String prepareCanonicalRequest() {
StringBuilder canonicalURL = new StringBuilder("");
/* Step 1.1 Start with the HTTP request method (GET, PUT, POST, etc.), followed by a newline character. */
canonicalURL.append(httpMethodName).append("\n");
/* Step 1.2 Add the canonical URI parameter, followed by a newline character. */
canonicalURI = canonicalURI == null || canonicalURI.trim().isEmpty() ? "/" : canonicalURI;
canonicalURL.append(canonicalURI).append("\n");
/* Step 1.3 Add the canonical query string, followed by a newline character. */
StringBuilder queryString = new StringBuilder("");
if (queryParametes != null && !queryParametes.isEmpty()) {
for (Map.Entry<String, String> entrySet : queryParametes.entrySet()) {
String key = entrySet.getKey();
String value = entrySet.getValue();
queryString.append(key).append("=").append(encodeParameter(value)).append("&");
}
/* @co-author https://github.com/dotkebi @git #1 @date 16th March, 2017 */
queryString.deleteCharAt(queryString.lastIndexOf("&"));
queryString.append("\n");
} else {
queryString.append("\n");
}
canonicalURL.append(queryString);
/* Step 1.4 Add the canonical headers, followed by a newline character. */
StringBuilder signedHeaders = new StringBuilder("");
if (awsHeaders != null && !awsHeaders.isEmpty()) {
for (Map.Entry<String, String> entrySet : awsHeaders.entrySet()) {
String key = entrySet.getKey();
String value = entrySet.getValue();
signedHeaders.append(key).append(";");
canonicalURL.append(key).append(":").append(value).append("\n");
}
/* Note: Each individual header is followed by a newline character, meaning the complete list ends with a newline character. */
canonicalURL.append("\n");
} else {
canonicalURL.append("\n");
}
/* Step 1.5 Add the signed headers, followed by a newline character. */
strSignedHeader = signedHeaders.substring(0, signedHeaders.length() - 1); // Remove last ";"
canonicalURL.append(strSignedHeader).append("\n");
/* Step 1.6 Use a hash (digest) function like SHA256 to create a hashed value from the payload in the body of the HTTP or HTTPS. */
payload = payload == null ? "" : payload;
canonicalURL.append(generateHex(payload));
if (debug) {
System.out.println("##Canonical Request:\n" + canonicalURL.toString());
}
return canonicalURL.toString();
}
/**
* Task 2: Create a String to Sign for Signature Version 4.
*
* @param canonicalURL
* @return
*/
private String prepareStringToSign(String canonicalURL) {
String stringToSign = "";
/* Step 2.1 Start with the algorithm designation, followed by a newline character. */
stringToSign = HMACAlgorithm + "\n";
/* Step 2.2 Append the request date value, followed by a newline character. */
stringToSign += xAmzDate + "\n";
/* Step 2.3 Append the credential scope value, followed by a newline character. */
stringToSign += currentDate + "/" + regionName + "/" + serviceName + "/" + aws4Request + "\n";
/* Step 2.4 Append the hash of the canonical request that you created in Task 1: Create a Canonical Request for Signature Version 4. */
stringToSign += generateHex(canonicalURL);
if (debug) {
System.out.println("##String to sign:\n" + stringToSign);
}
return stringToSign;
}
/**
* Task 3: Calculate the AWS Signature Version 4.
*
* @param stringToSign
* @return
*/
private String calculateSignature(String stringToSign) {
try {
/* Step 3.1 Derive your signing key */
byte[] signatureKey = getSignatureKey(secretAccessKey, currentDate, regionName, serviceName);
/* Step 3.2 Calculate the signature. */
byte[] signature = HmacSHA256(signatureKey, stringToSign);
/* Step 3.2.1 Encode signature (byte[]) to Hex */
String strHexSignature = bytesToHex(signature);
return strHexSignature;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
/**
* Task 4: Add the Signing Information to the Request. We'll return Map of
* all headers put this headers in your request.
*
* @return
*/
public Map<String, String> getHeaders() {
awsHeaders.put("x-amz-date", xAmzDate);
/* Execute Task 1: Create a Canonical Request for Signature Version 4. */
String canonicalURL = prepareCanonicalRequest();
/* Execute Task 2: Create a String to Sign for Signature Version 4. */
String stringToSign = prepareStringToSign(canonicalURL);
/* Execute Task 3: Calculate the AWS Signature Version 4. */
String signature = calculateSignature(stringToSign);
if (signature != null) {
Map<String, String> header = new HashMap<String, String>(0);
header.put("x-amz-date", xAmzDate);
header.put("Authorization", buildAuthorizationString(signature));
if (debug) {
System.out.println("##Signature:\n" + signature);
System.out.println("##Header:");
for (Map.Entry<String, String> entrySet : header.entrySet()) {
System.out.println(entrySet.getKey() + " = " + entrySet.getValue());
}
System.out.println("================================");
}
return header;
} else {
if (debug) {
System.out.println("##Signature:\n" + signature);
}
return null;
}
}
/**
* Build string for Authorization header.
*
* @param strSignature
* @return
*/
private String buildAuthorizationString(String strSignature) {
return HMACAlgorithm + " "
+ "Credential=" + accessKeyID + "/" + getDate() + "/" + regionName + "/" + serviceName + "/" + aws4Request + ","
+ "SignedHeaders=" + strSignedHeader + ","
+ "Signature=" + strSignature;
}
/**
* Generate Hex code of String.
*
* @param data
* @return
*/
private String generateHex(String data) {
MessageDigest messageDigest;
try {
messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(data.getBytes("UTF-8"));
byte[] digest = messageDigest.digest();
return String.format("%064x", new java.math.BigInteger(1, digest));
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
/**
* Apply HmacSHA256 on data using given key.
*
* @param data
* @param key
* @return
* @throws Exception
* @reference:
* http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-java
*/
private byte[] HmacSHA256(byte[] key, String data) throws Exception {
String algorithm = "HmacSHA256";
Mac mac = Mac.getInstance(algorithm);
mac.init(new SecretKeySpec(key, algorithm));
return mac.doFinal(data.getBytes("UTF8"));
}
/**
* Generate AWS signature key.
*
* @param key
* @param date
* @param regionName
* @param serviceName
* @return
* @throws Exception
* @reference
* http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-java
*/
private byte[] getSignatureKey(String key, String date, String regionName, String serviceName) throws Exception {
byte[] kSecret = ("AWS4" + key).getBytes("UTF8");
byte[] kDate = HmacSHA256(kSecret, date);
byte[] kRegion = HmacSHA256(kDate, regionName);
byte[] kService = HmacSHA256(kRegion, serviceName);
byte[] kSigning = HmacSHA256(kService, aws4Request);
return kSigning;
}
final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
/**
* Convert byte array to Hex
*
* @param bytes
* @return
*/
private String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars).toLowerCase();
}
/**
* Get timestamp. yyyyMMdd'T'HHmmss'Z'
*
* @return
*/
private String getTimeStamp() {
DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));//server timezone
return dateFormat.format(new Date());
}
/**
* Get date. yyyyMMdd
*
* @return
*/
private String getDate() {
DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));//server timezone
return dateFormat.format(new Date());
}
private String encodeParameter(String param){
try {
return URLEncoder.encode(param, "UTF-8");
} catch (Exception e) {
return URLEncoder.encode(param);
}
}
}
现在包含我的授权标头的请求如下
AWS4-HMAC-SHA256凭证= xxxx / xx / us-east-1 / s3 / aws4_request,SignedHeaders = content-type; x-amz-date; x-amz-object-lock-legal-hold; x- amz对象锁定模式; x-amz对象锁定保留直到日期,Signature = xxx
但是下面的异常仍然到来,请帮助
com.amazonaws.services.s3.model.AmazonS3Exception: Put Object requests with Object Lock parameters require AWS Signature Version 4 (Service: Amazon S3; Status Code: 400; Error Code: InvalidArgument; Request ID: 30E27045CC75BD10), S3 Extended Request ID: sTQY2dy9o21y8iO0eeD/33V9DNjoLEwpElgyQi3S3FlC5P38DWRudzV1tErpON14pCAqUtJVHjM=
at com.amazonaws.http.AmazonHttpClient.handleErrorResponse(AmazonHttpClient.java:1307)
at com.amazonaws.http.AmazonHttpClient.executeOneRequest(AmazonHttpClient.java:894)
at com.amazonaws.http.AmazonHttpClient.executeHelper(AmazonHttpClient.java:597)
at com.amazonaws.http.AmazonHttpClient.doExecute(AmazonHttpClient.java:363)
at com.amazonaws.http.AmazonHttpClient.executeWithTimer(AmazonHttpClient.java:329)
at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:308)
at com.amazonaws.services.s3.AmazonS3Client.invoke(AmazonS3Cli
ent.java:3659)
答案 0 :(得分:0)
我建议您不要自己编写签名生成的代码,而应使用适用于Java的AWS开发工具包提供的实现。
此答案向您展示了如何进行How to generate Signature in AWS from Java