SignatureDoesNotMatch

Amazon Web Services(AWS)の「Product Advertising API Signed Requests Sample Code」を利用して、リクエストを署名付きにする際に、ハマった。。。

原因は、apache commons-codecバージョンが異なったこと。本記事を作成時点での最新版は1.4であるが、AWSで署名付きのリクエスト(URL)を作成する際は、1.3を使用しなければ動作しない。

AWSのAPI解説ページのコメント欄に注意書きがあった。コメントは見ていなかったよ。。。

下記に問題とエラーメッセージ、振り返りなどをメモしておく。

【問題】
・署名付きリクエストを作成するヘルパークラスを実装し、署名付きURLを作成したが、署名が正しくないと言われる。

AWSからのレスポンス】

<?xml version="1.0"?>
<ItemSearchErrorResponse xmlns="http://ecs.amazonaws.com/doc/2010-11-01/">
<Error>
 <Code>SignatureDoesNotMatch</Code>
 <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.</Message>
</Error>
<RequestID>a734c827-efe9-46bf-97fe-58a2d57c7110</RequestID>
</ItemSearchErrorResponse>

【解決までの道のり】
AWSのレスポンスに、「Check your AWS Secret Access Key and signing method」とあるので、まずは、AWS Secret Access Keyを確認。問題なかった。
signing methodは、AWSが提供しているものだから、確認しなかった。

AmazonSigned Requests Helperページで作成した署名付きのURLと、私のアプリで作成した署名付きURLを比較。違いが見られない。。。

③基本にもどり、マニュアルを見直す。コメント欄に次の記述を発見。かなり落ちこんだ。。。

Use Commons Codec Version 1.3 (commons-codec-1.3.jar) and the requests are signed properly.
The 1.4 Version of the Commons Codec does not sign the requests properly with the code as is.

【振り返り】
・マニュアルはしっかり読みましょう。
・Commons Codecはどこで使用しているのか。
Base64エンコーディングする際に、使用する。下記は、署名付きリクエストを作成するメソッドで、「Base64 encoder = new Base64();」のBase64クラスがCommons Codecライブラリ提供メソッドである。

private String hmac(String stringToSign) {
		String signature = null;
		byte[] data;
		byte[] rawHmac;
		try {
			data = stringToSign.getBytes(UTF8_CHARSET);
			rawHmac = mac.doFinal(data);
			Base64 encoder = new Base64();
			signature = new String(encoder.encode(rawHmac));
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException(UTF8_CHARSET + " is unsupported!", e);
		}
		return signature;
	}

commons-codec.1.3.jarとcommons-codec.1.4.jarとで、Base64の仕様の相違を調べておくべきだな。。。