web-dev-qa-db-fra.com

Comment générer une signature dans AWS à partir de Java

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

11
Tun Lin Aung

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

13
amihaiemil

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.

3
lucasweb

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);
}

...
1
OscarG

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 .

0
Prakash Palnati

Le processus de signature est long et sujet aux erreurs, voici quelques conseils

0
KryptonJ