FJCT_ニフクラ mobile backend(mBaaS)お役立ちブログ

スマホアプリ開発にニフクラ mobile backend(mBaaS)。アプリ開発に役立つ情報をおとどけ!

様々な言語からmBaaSを扱うために署名作成アルゴリズムを考える【Java編】

f:id:mbaasdevrel:20190318155501p:plain

プログラムから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とサーバサイドで連携するのは難しくありません。ぜひ試してみてください。

中津川 篤司

中津川 篤司

NCMBエヴァンジェリスト。プログラマ、エンジニアとしていくつかの企業で働き、28歳のときに独立。 2004年、まだ情報が少なかったオープンソースソフトの技術ブログ「MOONGIFT」を開設し、毎日情報を発信している。2013年に法人化、ビジネスとエンジニアを結ぶDXエージェンシー「DevRel」活動をスタート。