アプリケーション(.apk)の自己署名を検証する


Androidアプリケーションを配布する場合は何らかの形で署名されており、デバッグ版であればDEBUG KEYが、リリース版であればRELEASE KEYが利用されます。これら署名に基づいて、AndroidのOS側でインストールや更新確認が行われます。

今回は、Androidアプリケーションの自己署名をアプリ自身で検証(Verify)する方法を紹介します。
以下に紹介する内容は仕組みは簡単で完全ではありませんが、改ざん、再署名による二次配布対策として、カジュアルなアプリケーション改ざんには非常に有用な手段です。

署名の検証以外にも意図しない使い方を防ぐ方法はいくつかあり、パーミッションの変更検出などがあります。

  • Androidアプリケーションのパーミッション改竄を検知するスニペット via @sys1yagi
  • パーミッションに関しては、sys1yagiさんのサイトが詳しいのでそちらを参考にしてください。

    サンプルコードは続きからどうぞ。

    ■src/CheckSignatureActivity.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    public class CheckSignatureActivity extends Activity {
     
        // DEBUG KEYとRELEASE KEYが異なることに注意
        static final String KEY = "デバッグキーを指定";
     
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
     
            PackageManager pm = getPackageManager();
            try {
                PackageInfo packageInfo = pm.getPackageInfo(getPackageName(),
                        PackageManager.GET_SIGNATURES);
     
                // 通常[0]のみ
                for (int i = 0; i < packageInfo.signatures.length; i++) {
     
                    Signature signature = packageInfo.signatures[i];
                    Log.d("SIGNATURE",
                            "Signature: " + signature.toCharsString());
     
                    if (KEY.equals(signature.toCharsString())) {
                        Log.d("SIGNATURE", "キーが一致");
                    } else {
                        Log.d("SIGNATURE", "キーが不一致");
                    }
                }
            } catch (NameNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    少し長いですが、わかりやすさを優先して全体を掲載しました。

    4行目:検証するためのDEBUG KEYもしくはRELEASE KEYを登録してください。一度、サンプルアプリを実行させ、出力値をLogCatからコピー&ペーストすると良いでしょう。
    11行目:PackageManagerを取得します
    13,14行目:署名(シグネチャ)を取得するためPackageManager.GET_SIGNATURESを指定します
    21行目:SignatureクラスのtoCharsStringメソッドで署名を文字列として取得できます
    23行目:アプリケーションに組み込んだ自己署名と比較して再署名されていないか(=改変されていないか)を確認します

    このようにすると署名の検証ができますが、アプリケーションの改ざんに対して万全というわけではありません。
    サンプルでは”static final String KEY”などStringとしてアプリケーションにそのまま埋め込んでいますが、
    署名のバイナリ値を鍵として検証コード自体を改変して検証をスキップされる可能性もあります(23行目のif文を無効化されても同様です)。
    つまり、本格的にターゲットとして狙われると十分でないことがわかります。

    改ざん防止はいたちごっこ的なところがあるのでバランスの問題といえるでしょう。再署名による2次配布など安易な改ざんは防げることは確かですので、アプリケーションにどこまでの防御策が必要かを考えた上で利用可否を決定すべきでしょう。

    以上、おつかれさまでした。

    One Comment