Webアプリツールを作っていた。
GWTクライアントから非同期呼び出しして
サーバ上でJavaで処理させれば問題ないなとおもい
さくっとつくったのだけど、
どこの誰とも分からない人間に
自分のAPIキーや秘密キーを送ることはできない
人もいるだろうなあと思い
完全クライアント処理できるように
実装しようと修正しだしたのだけど..
Base64、URLエンコードなどは
クライアントのJava実装→JavaScript変換
で実装できた。
しかし..
HmacSHA1変換がどうしてもGWTのクライアント側で出来ない..
具体的にかくと、
通常のJavaSEが使える環境で
変数messageを秘密キー変数secretPassphraseで
HmacSHA1変換する場合は、
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
...
SecretKeySpec secretKeySpec =
new SecretKeySpec(secretPassphrase.getBytes(), "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(secretKeySpec);
byte[] hash = mac.doFinal(message.getBytes());
といった実装していた部分を
clientパッケージ以下では動作しない。
結局以下のオープンソースJavaScript関数を
HTMLでロードさせてJSNI(JavaScript Native Interface)で
呼び出す方法を使った。
CryptJS
http://code.google.com/p/crypto-js/
通常のJavaScriptでの使い方は
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/hmac-md5.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/hmac-sha1.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/hmac-sha256.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/hmac-sha512.js"></script>
<script>
var hash = CryptoJS.HmacMD5("Message", "Secret Passphrase");
var hash = CryptoJS.HmacSHA1("Message", "Secret Passphrase");
var hash = CryptoJS.HmacSHA256("Message", "Secret Passphrase");
var hash = CryptoJS.HmacSHA512("Message", "Secret Passphrase");
</script>
である。
なのでGWTを使用するHTMLファイル上に
上記のscriptタグ部分を定義しておく。
変数hashはバイト配列である。
JSNIでは呼び出す側の戻り値が特殊な場合は
JavaScriptObjectに受けないといけない。
なので
GWTクライアント側では
- private static native JavaScriptObject _hmac_sha1(String org, String secretKey) /*-{
- return $wnd.CryptoJS.HmacSHA1(org, secretKey);
- }-*/;
と受けるしかない。
これをbyte[]に戻すためにイロイロ試したが
一旦Stringで受けて出てきた文字列を
先頭から2文字づつ切り出し
符号つき16進数をそれぞれバイト化して配列にすると
サーバ側で処理した文字列と同じ結果になった。
private static byte[] HmacSHA1(
String message, String secretPassphrase){
String org =
UrlLocalGenerator._hmac_sha1(
message, secretPassphrase).toString();
int length = org.length()/2;
byte[] ret = new byte[length];
for(int cnt=0;cnt<length;cnt++){
String tgt = "0x" + org.substring(0, 2);
org = org.substring(2);
ret[cnt] = (byte)Integer.decode(tgt).intValue();
}
return ret;
}
というなんともかっこわるい実装になってしまった..
0 件のコメント:
コメントを投稿