APK 署名スキーム v2 は、APK の保護部分への変更を検出することで、検証速度を高めて整合性に関する保証を強化するファイル全体の署名スキームです。
APK 署名スキーム v2 で署名すると、APK ファイル内で ZIP ファイルのセントラル ディレクトリ セクションの直前に APK 署名ブロックが挿入されます。APK 署名ブロック内では、v2 署名と署名者の識別情報が APK 署名スキーム v2 ブロックに保存されます。
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
の長さプレフィックス付きシーケンス:signature algorithm ID
(uint32)- (長さプレフィックス付き)
digest
- 整合性保護されたコンテンツを参照
- X.509
certificates
の長さプレフィックス付きシーケンス:- 長さプレフィックス付き X.509
certificate
(ASN.1 DER 形式)
- 長さプレフィックス付き X.509
- 長さプレフィックス付き
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 つのセクションで構成されています。
- ZIP エントリのコンテンツ(オフセット 0 から APK 署名ブロックの始点まで)
- APK 署名ブロック
- ZIP セントラル ディレクトリ
- ZIP セントラル ディレクトリの末尾
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 に表示される順序でのダイジェストの連結に対して算出されます。ダイジェストはチャンク形式で算出され、並列化によって演算が高速化されます。
セクション 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 署名スキーム v2 の検証
- APK 署名ブロックを検出し、次のことを検証します。
- APK 署名ブロックの 2 つのサイズ フィールドに同じ値が含まれる。
- ZIP セントラル ディレクトリの直後に ZIP セントラル ディレクトリの末尾レコードがある。
- ZIP セントラル ディレクトリの末尾の後に追加のデータがない。
- APK 署名ブロック内の最初の APK 署名スキーム v2 ブロックを検出します。 v2 ブロックがある場合は、ステップ 3 に進みます。v2 ブロックがない場合は、v1 スキームを使用した APK の検証に戻ります。
- APK 署名スキーム v2 ブロックの各
signer
に関しては、次を行います。signatures
の中からサポート対象となる最も強力なsignature algorithm ID
を選択する。強度の順序は、各実装 / プラットフォームのバージョンに応じて異なります。public key
を使用して、signed data
に対するsignatures
から、対応するsignature
を検証する(これでsigned data
の解析が安全に行えるようになります)。digests
とsignatures
の署名アルゴリズム ID の順序リストが同一であることを検証する(署名の削除 / 追加を防ぐため)。- 署名アルゴリズムで使用されるアルゴリズムと同一のダイジェスト アルゴリズムを使用して、APK コンテンツのダイジェストを算出する。
- 算出されたダイジェストが
digests
に対応するdigest
と同一であることを検証する。 certificates
の最初のcertificate
の SubjectPublicKeyInfo がpublic key
と同一であることを検証する。
- 少なくとも 1 人の
signer
が検出され、それぞれのsigner
に対してステップ 3 が成功した場合、検証は正常に完了します。
注意: ステップ 3 または 4 でエラーが発生した場合は、v1 スキームを使用した APK の検証を行わないでください。
JAR 署名 APK 検証(v1 スキーム)
JAR 署名 APK は標準の署名付き JAR で、META-INF/MANIFEST.MF にリストされたエントリが正しく含まれている必要があり、すべてのエントリが同一の署名者のセットで署名される必要があります。この APK の整合性は次のように検証されます。
- 各署名者が、META-INF/<signer>.SF および META-INF/<signer>.(RSA|DSA|EC) JAR エントリで表される。
- <signer>.(RSA|DSA|EC) が SignedData 構造を持つ PKCS #7 CMS ContentInfo で、署名は <signer>.SF ファイルで検証される。
- <signer>.SF ファイルに、META-INF/MANIFEST.MF のファイル全体のダイジェストおよび META-INF/MANIFEST.MF の各セクションのダイジェストが含まれる。MANIFEST.MF のファイル全体のダイジェストが検証され、この検証で不合格だった場合、代わりに各 MANIFEST.MF セクションのダイジェストが検証される。
- META-INF/MANIFEST.MF には、整合性が保護された JAR エントリごとに、エントリの非圧縮コンテンツのダイジェストを含んだ対応する名前を持つセクションが含まれる。こうしたダイジェストがすべて検証される。
- MANIFEST.MF にリストされていない JAR エントリが APK に含まれており、それが JAR 署名の一部ではない場合、APK 検証は失敗する。
したがって、保護チェーンは <signer>.(RSA|DSA|EC) -> <signer>.SF -> MANIFEST.MF -> 各整合性保護 JAR エントリのコンテンツのようになります。