Lorsque j'appelle des points de terminaison API à partir du client REST, j'ai eu une erreur en ce qui concerne la signature.
Demande:
Hôte : https://xxx.execute-api.ap-southeast-1.amazonaws.com/latest/api/name
Autorisation : AWS4-HMAC-SHA256 Credential =
{AWSKEY}
/20160314/ap-sud-est-1/execute-api/aws4_request, SignedHeaders = Host; range; x-amz-date, Signature ={signature}
Date X-Amz : 20160314T102915Z
Réponse:
{
"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' "
}
À partir de Java code, j'ai suivi la référence AWS sur la façon de générer une signature.
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;
}
Puis-je savoir ce que je me suis trompé lors de la génération de Signature?
Référence comment générer la signature: http://docs.aws.Amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-Java
Vous pouvez utiliser des classes de aws-Java-sdk-core: https://github.com/aws/aws-sdk-Java/tree/master/aws-Java-sdk-core
Plus précisément, Request, Aws4Signer et quelques autres:
//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>());
Si vous voulez un design plus propre, vous pouvez utiliser le motif Décorateur pour composer des classes élégantes et masquer le désordre ci-dessus. Un exemple pour cela ici: http://www.amihaiemil.com/2017/02/18/decorators-with-tunnels.html
D'après l'exemple de code ci-dessus, il semble que vous ne créez pas de demande canonique et que vous l'incluez dans la chaîne qui est signée conformément à http://docs.aws.Amazon.com/general/latest/gr/sigv4- create-canonical-request.html
Au lieu de l'implémenter vous-même, vous avez envisagé d'utiliser une bibliothèque tierce.
aws-v4-signer-Java est une bibliothèque légère sans dépendance qui facilite la génération de signatures 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();
Avertissement: je suis l'auteur des bibliothèques.
Ceci est possible en utilisant 100% Java sans dépendances supplémentaires, utilisez simplement les paramètres de requête générés ici:
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);
}
...
Le moyen le plus simple consiste à utiliser les méthodes et le client http du SDK d'Amazon. Je suis les 3 étapes ci-dessous.
Étape 1: créer des informations d'identification AWS de base:
BasicAWSCredentials awsCreds = new BasicAWSCredentials(ACCESS_KEY,AWS_DATASHOP_SECRET_KEY);
Étape 2: créer une demande signable:
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);
Étape 3: exécuter la demande à l'aide d'AmazonHttpClient:
new AmazonHttpClient(new ClientConfiguration())
.requestExecutionBuilder()
.executionContext(new ExecutionContext(true))
.request(signableRequest)
.errorResponseHandler((new SimpleAwsErrorHandler()))
.execute(new MyResponseHandler());
Assurez-vous d'implémenter HttpResponseHandler
pour SimpleAwsErrorHandler
et MyResponseHandler
Si vous souhaitez utiliser des clients http normaux, vous devrez créer une demande canonique et calculer la signature qui, le plus souvent, ne correspond pas .
Le processus de signature est long et sujet aux erreurs, voici quelques conseils