プログラムからmBaaSを操作する際にはAPIを利用しますが、その時に肝になるのが署名文字列の作成です。パスなどをつなげて、SHA256でハッシュ値を作ったりと非常に面倒な処理が必要です。
アプリ側であればSDKを使ってもらえれば問題ありません。問題はサーバサイドで、オフィシャルではNode.js、非公式ではRubyとPHPくらいしかライブラリがありません。
そこで今回は一番面倒と思われる署名作成処理について、他の言語での実装方法について解説します。今回はJava言語です。JavaはAndroid向けにSDKを提供していますが、これはAndroidと密結合になっており、サーバサイドなどでは利用できません。そこで純粋なJavaで署名処理を実装し直してみます。
必要な引数について
署名を作成する際には以下の情報が必要です。
変数 | 名前 | 説明 |
---|---|---|
method | HTTPメソッド | GETやPOSTといった文字列です |
fqdn | FQDN | 通常はmbaas.api.nifcloud.com、スクリプトの場合はscript.mbaas.api.nifcloud.comになります |
path | リクエストするAPIのパス | /2013-09-01/classes/TestClassなどです |
queries | クエリストリング | クエリストリングです。今回はマップで作成しています |
applicationKey | アプリケーションキー | 管理画面で取得できるアプリケーションキーです |
signatureMethod | シグネチャメソッド | HmacSHA256という文字列固定です |
signatureVersion | シグネチャバージョン | 2という文字列固定です |
timestamp | タイムスタンプ | APIリクエスト時の時間です |
clientKey | クライアントキー | 管理画面で取得できるクライアントキーです |
生成前の変数について
まず上記の情報を揃えます。各値は検証のため、REST API リファレンス : シグネチャの生成方法 | ニフクラ mobile backendに合わせています。
Signature
オブジェクトの sign メソッドで署名文字列を返します。
String method = "GET"; String path = "/2013-09-01/classes/TestClass"; String applicationKey = "d28...fc0"; Timestamp ts = new Timestamp(System.currentTimeMillis()); // Map<String,String> query = new TreeMap<>(); JSONObject queries = new JSONObject(); JSONObject where = new JSONObject(); where.put("testKey", "testValue"); queries.put("where", where); String clientKey = "339...9b8"; Signature s = new Signature(); String signature = s.sign(method, path, applicationKey, ts, queries, clientKey);
メソッド、FQDN、パスを配列に入れる
最終的に改行繋ぎの文字列を作るために、まずは配列に入れておきます。そして最後にqueryStringメソッドでクエリなどの文字列を連結します。
public static final String FQDN = "mbaas.api.nifcloud.com"; public static String sign(String method, String path, String applicationKey, Timestamp time, JSONObject queries, String clientKey) { String result = null; try { String str = ""; String[] ary = new String[4]; ary[0] = method; ary[1] = FQDN; ary[2] = path; ary[3] = queryString(applicationKey, time, queries); } catch (NoSuchAlgorithmException e) { result = "アルゴリズムがありません"; } catch (InvalidKeyException e) { result = "キーが不正です"; } return result; }
署名用文字列の生成
queryStringメソッドについてです。タイムスタンプをISO8601形式に変換し、各値をTreeMapオブジェクトに追加していきます。TreeMapオブジェクトは自動的にソートを行ってくれるので便利です。
private static String queryString(String applicationKey, Timestamp time, JSONObject queries) { String result = null; try { DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSS'Z'"); df.setTimeZone(new SimpleTimeZone(0, "GMT")); String timestamp = df.format(time); Map<String,String> map = new TreeMap<>(); map.put(SIGNATURE_METHOD_NAME, SIGNATURE_METHOD_VALUE); map.put(SIGNATURE_VERSION_NAME, SIGNATURE_VERSION_VALUE); map.put(NCMB_APPLICATION_KEY_NAME, applicationKey); map.put(NCMB_APPLICATION_TIMESTAMP_NAME, timestamp); // クエリはJSONから取り出して、エンコードする Iterator<String> iterator = queries.keys(); while (iterator.hasNext()) { String key = iterator.next(); map.put(key, URLEncoder.encode(queries.getJSONObject(key).toString(), "UTF-8")); } // TreeMapのキーと値をイコールでつないでいく ArrayList<String> ary = new ArrayList<>(); for (Map.Entry<String, String> entry : map.entrySet()) { ary.add(entry.getKey() + "=" + entry.getValue()); } // 最後に値を&でつないで完了 result = String.join("&", ary); } catch (UnsupportedEncodingException e) { result = "サポートされていないエンコードです"; } return result; }
ここまでの処理で署名用文字列が完成します。
署名を生成する
署名を生成するのはHMAC SHA256でハッシュを生成し、それをBASE64でエンコードするだけです。処理としては以下のようになります。
SecretKeySpec signingKey = new SecretKeySpec(clientKey.getBytes(StandardCharsets.UTF_8), SIGNATURE_METHOD_VALUE); Mac mac = Mac.getInstance(SIGNATURE_METHOD_VALUE); mac.init(signingKey); byte[] rawHmac = mac.doFinal(String.join("\n", ary).getBytes(StandardCharsets.UTF_8)); result = Base64.getEncoder().encodeToString(rawHmac);
インポートするライブラリ
必要なライブラリは以下の通りです。
import java.util.Base64; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.security.NoSuchAlgorithmException; import java.security.InvalidKeyException; import java.io.UnsupportedEncodingException; import java.util.Map; import java.util.HashMap; import java.util.TreeMap; import java.util.ArrayList; import java.sql.Timestamp; import java.net.URLEncoder; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.SimpleTimeZone; import org.json.JSONObject; import java.util.Iterator;
まとめ
この処理で署名生成処理が完了します。署名さえ作れればmBaaSとサーバサイドで連携するのは難しくありません。ぜひ試してみてください。