web-dev-qa-db-ja.com

AWSでJavaから署名を生成する方法

RESTクライアントからAPIエンドポイントを呼び出すと、署名に関するエラーが発生しました。

リクエスト:

Hosthttps://xxx.execute-api.ap-southeast-1.amazonaws.com/latest/api/name

Authorization:AWS4-HMAC-SHA256 Credential = {AWSKEY}/20160314/ap-southeast-1/execute-api/aws4_request、SignedHeaders = Host; range; x-amz-date、Signature = {signature}

X-Amz-Date:20160314T102915Z

応答:

{
"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' "
}

Javaコードから、署名を生成する方法のAWSリファレンスに従いました。

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

署名を生成しているときに何が問題だったかわかりますか?

署名の生成方法の参照: http://docs.aws.Amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-Java

11
Tun Lin Aung

Aws-Java-sdk-coreのクラスを使用できます: https://github.com/aws/aws-sdk-Java/tree/master/aws-Java-sdk-core

より具体的には、Request、Aws4Signer、およびその他のいくつかのもの:

//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>());

よりすっきりとしたデザインが必要な場合は、Decoratorパターンを使用していくつかのエレガントなクラスを作成し、上記の混乱を隠すことができます。その例: http://www.amihaiemil.com/2017/02/18/decorators-with-tunnels.html

13
amihaiemil

上記のコード例から、正規のリクエストを作成しておらず、署名された文字列にそれを含めていないようです http://docs.aws.Amazon.com/general/latest/gr/sigv4- create-canonical-request.html

これを自分で実装する代わりに、サードパーティのライブラリを使用することを検討しました。

aws-v4-signer-Java は、軽量で依存関係がなく、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();

免責事項:私は図書館の著者です。

3
lucasweb

これは100%を使用して可能ですJava追加の依存関係のないライブラリ、ここで生成された クエリパラメータ を使用するだけです:

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

最も簡単な方法は、AmazonのSDKのメソッドとhttp-clientを使用することです。以下の3つのステップに従います。

ステップ1:基本的なAWS認証情報を作成します。

 BasicAWSCredentials awsCreds = new BasicAWSCredentials(ACCESS_KEY,AWS_DATASHOP_SECRET_KEY);

ステップ2:signableRequestを作成します。

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

ステップ3:AmazonHttpClientを使用してリクエストを実行します。

new AmazonHttpClient(new ClientConfiguration())
                    .requestExecutionBuilder()
                    .executionContext(new ExecutionContext(true))
                    .request(signableRequest)
                    .errorResponseHandler((new SimpleAwsErrorHandler()))
                    .execute(new MyResponseHandler());

HttpResponseHandlerSimpleAwsErrorHandlerには必ずMyResponseHandlerを実装してください

通常のhttpクライアントを使用する場合は、正規リクエストを作成し、ほとんどの場合一致しない署名を計算する必要があります

0
Prakash Palnati

署名プロセスは長く、エラーが発生しやすいので、ここにいくつかのヒントがあります

0
KryptonJ