APK 署名スキーム v2

APK 署名スキーム v2 は、APK の保護部分への変更を検出することで、検証速度を高めて整合性に関する保証を強化するファイル全体の署名スキームです。

APK 署名スキーム v2 で署名すると、APK ファイル内で ZIP ファイルのセントラル ディレクトリ セクションの直前に APK 署名ブロックが挿入されます。APK 署名ブロック内では、v2 署名と署名者の識別情報が APK 署名スキーム v2 ブロックに保存されます。

署名前の APK と署名後の APK

図 1. 署名前の APK と署名後の APK

APK 署名スキーム v2 は、Android 7.0(Nougat)で導入されました。APK を Android 6.0(Marshmallow)以前のデバイスにインストールできるようにするには、v2 スキームで署名を行う前に、JAR 署名を使用して APK に署名する必要があります。

APK 署名ブロック

v2 以降の APK 署名は、v1 APK 形式との下位互換性を維持するため、APK 署名ブロック(APK 署名スキーム v2 をサポートするために導入された新しいコンテナ)内に格納されます。APK ファイルでは、APK 署名ブロックはファイルの最後にある ZIP セントラル ディレクトリの直前にあります。

ブロックには、APK でブロックを迅速に検索できるように、ラップされた ID と値のペアが含まれています。APK の v2 署名は、ID 0x7109871a という ID と値のペアとして格納されます。

形式

APK 署名ブロックの形式は次のとおりです(数値フィールドはすべてリトルエンディアン)。

  • size of block バイト単位(このフィールドを除く)(uint64)
  • uint64 の長さプレフィックス付き ID と値のペアのシーケンス:
    • ID(uint32)
    • value(可変長: ペアの長さ - 4 バイト)
  • size of block バイト単位 - 最初のフィールドと同様(uint64)
  • magic「APK Sig Block 42」(16 バイト)

APK は、最初に ZIP セントラル ディレクトリの始点を検出して解析を行います(ファイルの末尾で ZIP セントラル ディレクトリの末尾レコードを検出して、レコードからセントラル ディレクトリの始点オフセットを読み取ります)。magic の値によって、セントラル ディレクトリの前に APK 署名ブロックが適切に配置されるようになります。また、size of block の値により、ファイル内でブロックの始点を迅速に検出します。

ブロックの検出時、ID が不明な ID と値のペアは無視されます。

APK 署名スキーム v2 ブロック

APK は、署名鍵で表される 1 人以上の署名者 / ID によって署名されています。この情報は、APK 署名スキーム v2 ブロックとして格納されます。署名者ごとに、次の情報が格納されています。

  • (署名アルゴリズム、ダイジェスト、署名)タプル。ダイジェストは、APK のコンテンツに対する整合性チェックから署名の検証を分離するために保存されます。
  • 署名者の ID を表す X.509 証明書チェーン。
  • key-value ペアとして追加の属性。

提供されたリストにあるサポート対象の署名を使用して、署名者ごとに APK の検証が行われます。署名アルゴリズムが不明な署名は無視されます。サポート対象の署名が複数検出された場合に使用する署名の選択方法は、各実装に応じて異なります。これにより、将来にわたってより強力な署名方法を下位互換性のある方法で導入できます。そのためここでは、最も強力な署名の検証を行うことが推奨されます。

形式

APK 署名スキーム v2 ブロックは、0x7109871a という ID で APK 署名ブロック内に格納されます。

APK 署名スキーム v2 ブロックの形式は次のとおりです(数値はすべてリトルエンディアンで、長さプレフィックス付きフィールドはすべて uint32 を使用します)。

  • 長さプレフィックス付き signer の長さプレフィックス付きシーケンス:
    • 長さプレフィックス付き signed data:
      • 長さプレフィックス付き digests の長さプレフィックス付きシーケンス:
      • X.509 certificates の長さプレフィックス付きシーケンス:
        • 長さプレフィックス付き X.509 certificate(ASN.1 DER 形式)
      • 長さプレフィックス付き additional attributes の長さプレフィックス付きシーケンス:
        • ID(uint32)
        • value(可変長: 追加属性の長さ - 4 バイト)
    • 長さプレフィックス付き signatures の長さプレフィックス付きシーケンス:
      • signature algorithm ID(uint32)
      • signed data に関する長さプレフィックス付き signature
    • 長さプレフィックス付き public key(SubjectPublicKeyInfo、ASN.1 DER 形式)

署名アルゴリズム ID

  • 0x0101 - SHA2-256 ダイジェスト、SHA2-256 MGF1、32 バイトのソルト(トレーラー: 0xbc)付きの RSASSA-PSS
  • 0x0102 - SHA2-512 ダイジェスト、SHA2-512 MGF1、64 バイトのソルト(トレーラー: 0xbc)付きの RSASSA-PSS
  • 0x0103 - SHA2-256 ダイジェスト付きの RSASSA-PKCS1-v1_5これは、決定性署名を必要とするビルドシステム用です。
  • 0x0104 - SHA2-512 ダイジェスト付きの RSASSA-PKCS1-v1_5これは、決定性署名を必要とするビルドシステム用です。
  • 0x0201 - SHA2-256 ダイジェスト付きの ECDSA
  • 0x0202 - SHA2-512 ダイジェスト付きの ECDSA
  • 0x0301 - SHA2-256 ダイジェスト付きの DSA

上記のアルゴリズムは、すべて Android プラットフォームでサポートされています。 署名ツールは、アルゴリズムのサブセットをサポートします。

サポートされている鍵サイズと EC カーブ:

  • RSA: 1024、2048、4096、8192、16384
  • EC: NIST P-256、P-384、P-521
  • DSA: 1024、2048、3072

整合性保護されたコンテンツ

APK コンテンツを保護するために、APK は次の 4 つのセクションで構成されています。

  1. ZIP エントリのコンテンツ(オフセット 0 から APK 署名ブロックの始点まで)
  2. APK 署名ブロック
  3. ZIP セントラル ディレクトリ
  4. ZIP セントラル ディレクトリの末尾

署名後の APK セクション

図 2. 署名後の APK セクション

APK 署名スキーム v2 は、セクション 1、3、4、およびセクション 2 に含まれる APK 署名スキーム v2 ブロックの signed data ブロックの整合性を保護します。

セクション 1、3、および 4 の整合性は、signed data ブロックに格納されたコンテンツの 1 つあるいは複数のダイジェストによって保護され、ダイジェストは 1 つあるいは複数の署名によって保護されます。

セクション 1、3、および 4 のダイジェストは、2 レベルのマークルツリーと同様に、次のように算出されます。 各セクションは、連続する 1 MB(220 バイト)のチャンクに分割されます。各セクションの最後のチャンクは短くなることがあります。各チャンクのダイジェストは、バイト 0xa5 の連結、チャンクのバイト単位の長さ(リトルエンディアン uint32)、およびチャンクのコンテンツに対して算出されます。最上位のダイジェストは、バイト 0x5a の連結、チャンクの数(リトルエンディアン uint32)、およびチャンクが APK に表示される順序でのダイジェストの連結に対して算出されます。ダイジェストはチャンク形式で算出され、並列化によって演算が高速化されます。

APK ダイジェスト

図 3. APK ダイジェスト

セクション 4(ZIP セントラル ディレクトリの末尾)の保護は、ZIP セントラル ディレクトリのオフセットを含むセクションによって複雑化されます。新しい署名が追加されるなどして APK 署名ブロックのサイズが変更されると、オフセットが変更されます。したがって、ZIP セントラル ディレクトリの末尾でダイジェストを算出する場合、ZIP セントラル ディレクトリのオフセットを含むフィールドは、APK 署名ブロックのオフセットを含むものとして扱われる必要があります。

ロールバック保護

v2 署名 APK の検証をサポートする Android プラットフォームで、攻撃者が v1 署名 APK として v2 署名 APK を検証しようとする可能性があります。このような攻撃を回避するには、v1 署名でもある v2 で署名された APK の META-INF/*.SF ファイルのメイン セクションに X-Android-APK-Signed 属性を含める必要があります。属性の値は、コンマで区切られた APK 署名スキーム ID のセットです(このスキームの ID は 2 です)。v1 署名を検証する場合、このセット(v2 スキームなど)から検証者が優先する APK 署名スキームの署名を持たない APK を拒否するには、APK 検証ツールが必要です。この保護は、META-INF/*.SF ファイルが v1 署名によって保護されているという事実を前提としています。JAR 署名付き APK 検証のセクションを参照してください。

攻撃者は、APK 署名スキーム v2 ブロックからより強力な署名を削除しようとする可能性があります。この攻撃を回避するために、APK が署名されていた署名アルゴリズム ID のリストは、各署名によって保護される signed data ブロックに格納されます。

検証

Android 7.0 以降では、APK 署名スキーム v2 以降または JAR 署名(v1 スキーム)に従って APK が検証されます。古いプラットフォームでは、v2 署名が無視され、v1 署名のみの検証が行われます。

APK 署名検証プロセス

図 4. APK 署名検証プロセス(赤が新しいステップ)

APK 署名スキーム v2 の検証

  1. APK 署名ブロックを検出し、次のことを検証します。
    1. APK 署名ブロックの 2 つのサイズ フィールドに同じ値が含まれる。
    2. ZIP セントラル ディレクトリの直後に ZIP セントラル ディレクトリの末尾レコードがある。
    3. ZIP セントラル ディレクトリの末尾の後に追加のデータがない。
  2. APK 署名ブロック内の最初の APK 署名スキーム v2 ブロックを検出します。v2 ブロックがある場合は、ステップ 3 に進みます。v2 ブロックがない場合は、v1 スキームを使用した APK の検証に戻ります。
  3. APK 署名スキーム v2 ブロックの各 signer に関しては、次を行います。
    1. signatures の中からサポート対象となる最も強力な signature algorithm ID を選択する。強度の順序は、各実装 / プラットフォームのバージョンに応じて異なります。
    2. public key を使用して、signed data に対する signatures から、対応する signature を検証する(これで signed data の解析が安全に行えるようになります)。
    3. digestssignatures の署名アルゴリズム ID の順序リストが同一であることを検証する(署名の削除 / 追加を防ぐため)。
    4. 署名アルゴリズムで使用されるアルゴリズムと同一のダイジェスト アルゴリズムを使用して、APK コンテンツのダイジェストを算出する。
    5. 算出されたダイジェストが digests に対応する digest と同一であることを検証する。
    6. certificates の最初の certificate の SubjectPublicKeyInfo が public key と同一であることを検証する。
  4. 少なくとも 1 人の signer が検出され、それぞれの signer に対してステップ 3 が成功した場合、検証は正常に完了します。

注意: ステップ 3 または 4 でエラーが発生した場合は、v1 スキームを使用した APK の検証を行わないでください。

JAR 署名 APK 検証(v1 スキーム)

JAR 署名 APK は標準の署名付き JAR で、META-INF/MANIFEST.MF にリストされたエントリが正しく含まれている必要があり、すべてのエントリが同一の署名者のセットで署名される必要があります。この APK の整合性は次のように検証されます。

  1. 各署名者が、META-INF/<signer>.SF および META-INF/<signer>.(RSA|DSA|EC) JAR エントリで表される。
  2. <signer>.(RSA|DSA|EC) が SignedData 構造を持つ PKCS #7 CMS ContentInfo で、署名は <signer>.SF ファイルで検証される。
  3. <signer>.SF ファイルに、META-INF/MANIFEST.MF のファイル全体のダイジェストおよび META-INF/MANIFEST.MF の各セクションのダイジェストが含まれる。MANIFEST.MF のファイル全体のダイジェストが検証され、この検証で不合格だった場合、代わりに各 MANIFEST.MF セクションのダイジェストが検証される。
  4. META-INF/MANIFEST.MF には、整合性が保護された JAR エントリごとに、エントリの非圧縮コンテンツのダイジェストを含んだ対応する名前を持つセクションが含まれる。こうしたダイジェストがすべて検証される。
  5. MANIFEST.MF にリストされていない JAR エントリが APK に含まれており、それが JAR 署名の一部ではない場合、APK 検証は失敗する。

したがって、保護チェーンは <signer>.(RSA|DSA|EC) -> <signer>.SF -> MANIFEST.MF -> 各整合性保護 JAR エントリのコンテンツのようになります。