Translate

2013年3月9日土曜日

Google Web Toolkitクライアント側でHmacSHA1変換する

CloudStack APIを呼び出すURLを生成する
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 件のコメント:

既存アプリケーションをK8s上でコンテナ化して動かす場合の設計注意事項メモ

既存アプリをK8sなどのコンテナにして動かすには、どこを注意すればいいか..ちょっと調べたときの注意事項をメモにした。   1. The Twelve Factors (日本語訳からの転記) コードベース   バージョン管理されている1つのコードベースと複数のデプロイ 依存関係 ...