リリース用のビルドへの署名

Android OS イメージでは、次の 2 つの場所で暗号署名が使用されます。

  1. イメージ内の .apk ファイルそれぞれに署名する必要があります。Android のパッケージ マネージャーは、次の 2 つの方法で .apk 署名を行います。
    • アプリを置き換える場合、古いアプリのデータにアクセスするために古いアプリと同じ鍵で署名しなければなりません。これは、.apk を上書きしてユーザーアプリを更新する場合にも、/data にインストールされる新しいバージョンでシステムアプリをオーバーライドする場合にも当てはまります。
    • 2 つ以上のアプリで(データを共有するなどの目的で)ユーザー ID を共有する必要がある場合は、同じキーで署名する必要があります。
  2. OTA アップデート パッケージは、システムが想定している鍵で署名する必要があります。そうしないとインストール処理で拒否されます。

リリースキー

Android ツリーは build/target/product/securityテストキーを含みます。make を使用して Android OS イメージをビルドすると、テストキーを使用してすべての .apk ファイルに署名します。テストキーは一般に知られているため、だれでも .apk ファイルに同じキーで署名できることになり、そのままでは OS イメージに組み込まれているシステムアプリの置き換えやハイジャックにつながる可能性があります。そのため公開またはデプロイする Android OS イメージには、自分だけがアクセスできる特別なリリースキーのセットを使用して署名する必要があります。

一意のリリースキーのセットを独自に生成するには、Android ツリーのルートから次のコマンドを実行します。

subject='/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com'
mkdir ~/.android-certs
for x in releasekey platform shared media networkstack; do \
    ./development/tools/make_key ~/.android-certs/$x "$subject"; \
  done

$subject は、組織の情報を反映する値に変更します。任意のディレクトリを使用できますが、バックアップされていて安全な場所かどうかを確認してから選択してください。ベンダーによっては、秘密鍵を強力なパスフレーズで暗号化し、その暗号化された鍵をソース管理に格納したり、エアギャップ コンピュータなど別の場所にリリースキーをそのまま保存したりしています。

リリース イメージを生成するには、次のコマンドを使用します。

make dist
sign_target_files_apks \
-o \    # explained in the next section
--default_key_mappings ~/.android-certs out/dist/*-target_files-*.zip \
signed-target_files.zip

sign_target_files_apks スクリプトは、ターゲット ファイルの .zip を入力として、すべての .apk ファイルが新しい鍵で署名された新規のターゲット ファイル .zip を生成します。新たに署名されたイメージは、signed-target_files.zipIMAGES/ 内にあります。

OTA パッケージへの書名

署名付きターゲット ファイル zip は、次の手順により署名付き OTA アップデート zip に変換できます。
ota_from_target_files \
-k  (--package_key) 
signed-target_files.zip \
signed-ota_update.zip

署名とサイドローディング

サイドローディングはリカバリのパッケージに対する通常の署名検証メカニズムをバイパスしません。無線(OTA)で送信されたパッケージと同様、リカバリはパッケージをインストールする前に、リカバリ パーティションに格納された公開鍵に対応する秘密鍵で署名されていることを確認します。

メインシステムから受け取ったアップデート パッケージに対しては、2 回検証が行われます。1 回目はメインシステムによる検証で、Android API の RecoverySystem.verifyPackage() メソッドを使用して検証します。その後もう一度リカバリにより検証されます。RecoverySystem API は、メインシステムの(デフォルトでは)/system/etc/security/otacerts.zip ファイル内に格納されている公開鍵と署名を照らし合わせます。リカバリは、リカバリ パーティションの RAM ディスクの /res/keys ファイル内に保存されている公開鍵と署名を照らし合わせます。

デフォルトでは、ビルド時に作成されたターゲット ファイル .zip によりテストキーに対応する OTA 証明書が設定されます。リリース イメージでは、アップデート パッケージの真正性をデバイスが確認できるように、別の証明書を使用する必要があります。これまで説明したように、sign_target_files_apks-o フラグを渡すと、テストキー証明書は証明書ディレクトリのリリースキー証明書に置き換えられます。

通常、システム イメージとリカバリ イメージが格納するのは同じ OTA 公開鍵のセットです。リカバリのキーセットだけにキーを追加して、サイドローディングのみでインストールできるパッケージに署名することができます(その場合、メインシステムのアップデート ダウンロード メカニズムで otacerts.zip との署名検証が正しく行われていることが前提条件となります)。リカバリにのみ追加するキーを指定するには、プロダクト定義で PRODUCT_EXTRA_RECOVERY_KEYS 変数を設定します。

vendor/yoyodyne/tardis/products/tardis.mk
 [...]

PRODUCT_EXTRA_RECOVERY_KEYS := vendor/yoyodyne/security/tardis/sideload

これによりリカバリキーのファイルに公開鍵 vendor/yoyodyne/security/tardis/sideload.x509.pem が含まれ、この鍵で署名されたパッケージのインストールが可能になります。追加のキーは otacerts.zip に含まれていないため、ダウンロードしたパッケージが正しく検証されると、システムはこのキーで署名されたパッケージのリカバリを呼び出しません。

証明書と秘密鍵

各鍵には 2 つのファイルがあり、証明書(拡張子は .x509.pem)と秘密鍵(拡張子は .pk8)です。 秘密鍵は秘密に保管してください。パッケージに署名する際に必要になります。鍵自体はパスワードで保護できます。一方、証明書には鍵の公開部分のみが含まれているため、広く配布できます。パッケージが対応する秘密鍵で署名されていることを検証するために使用されます。

標準的な Android ビルドでは、5 つのキーが使用され、このキーはすべて build/target/product/security 内にあります。

testkey
キーが指定されていないパッケージにデフォルトで設定される汎用キー。
platform
コア プラットフォームの一部であるパッケージのテストキー。
shared
ホームプロセスまたは連絡先プロセスで共有されるもののテストキー。
media
メディア システムまたはダウンロード システムの一部であるパッケージのテストキー。
networkstack
ネットワーク システムの一部であるパッケージのテストキー。networkstack 鍵は、モジュラー システム コンポーネントとして設計されたバイナリに署名する際に使用されます。モジュールのアップデートが個別にビルドされ、デバイス イメージにビルド済みとして統合されている場合、Android ソースツリーに networkstack 鍵を生成する必要はありません。

Android.mk ファイルで LOCAL_CERTIFICATE を設定して、個々のパッケージにこれらのキーのいずれか 1 つを指定します。この変数が設定されていない場合は、testkey が使用されます。次のようにまったく別のキーをパス名で指定することもできます。

device/yoyodyne/apps/SpecialApp/Android.mk
 [...]

LOCAL_CERTIFICATE := device/yoyodyne/security/special

このビルドでは、device/yoyodyne/security/special.{x509.pem,pk8} キーを使用して SpecialApp.apk に署名します。このビルドで使用できるのは、パスワードで保護されていない秘密鍵だけです。

高度な署名オプション

APK 署名鍵の置換

署名スクリプト sign_target_files_apks は、ビルド用に生成されたターゲット ファイルに対して処理を実行します。ビルド時に使用される証明書と秘密鍵のすべての情報が、ターゲット ファイルに含まれます。署名スクリプトを実行してリリースの署名を行う際に、キー名または APK 名に基づいて署名鍵を置き換えることができます。

キー名に基づいて鍵の置換を指定するには、次のように --key_mapping--default_key_mappings フラグを使用します。

  • --key_mapping src_key=dest_key フラグは、一度に 1 つのキーの置換を指定します。
  • --default_key_mappings dir フラグは 5 つのキーを含むディレクトリを指定して、build/target/product/security にあるすべてのキーを置き換えます。これは --key_mapping を 5 回使用してマッピングを指定するのと同じです。
build/target/product/security/testkey      = dir/releasekey
build/target/product/security/platform     = dir/platform
build/target/product/security/shared       = dir/shared
build/target/product/security/media        = dir/media
build/target/product/security/networkstack = dir/networkstack

APK 名に基づいて署名鍵の置換を指定するには、--extra_apks apk_name1,apk_name2,...=key フラグを使用します。key が空の場合、スクリプトは指定された APK を事前署名済みとして処理します。

仮の tardis プロダクトでは、パスワードで保護された 6 つのキーが必要です。5 つは build/target/product/security の 5 つを置き換え、1 つは(上記の例で SpecialApp が要求する)追加のキー device/yoyodyne/security/special を置換します。キーが次のファイルにある場合、

vendor/yoyodyne/security/tardis/releasekey.x509.pem
vendor/yoyodyne/security/tardis/releasekey.pk8
vendor/yoyodyne/security/tardis/platform.x509.pem
vendor/yoyodyne/security/tardis/platform.pk8
vendor/yoyodyne/security/tardis/shared.x509.pem
vendor/yoyodyne/security/tardis/shared.pk8
vendor/yoyodyne/security/tardis/media.x509.pem
vendor/yoyodyne/security/tardis/media.pk8
vendor/yoyodyne/security/tardis/networkstack.x509.pem
vendor/yoyodyne/security/tardis/networkstack.pk8
vendor/yoyodyne/security/special.x509.pem
vendor/yoyodyne/security/special.pk8           # NOT password protected
vendor/yoyodyne/security/special-release.x509.pem
vendor/yoyodyne/security/special-release.pk8   # password protected

この場合、すべてのアプリに次のように署名します。

./build/make/tools/releasetools/sign_target_files_apks \
    --default_key_mappings vendor/yoyodyne/security/tardis \
    --key_mapping vendor/yoyodyne/security/special=vendor/yoyodyne/security/special-release \
    --extra_apks PresignedApp= \
    -o tardis-target_files.zip \
    signed-tardis-target_files.zip

これにより、次のようになります。

Enter password for vendor/yoyodyne/security/special-release key>
Enter password for vendor/yoyodyne/security/tardis/networkstack key>
Enter password for vendor/yoyodyne/security/tardis/media key>
Enter password for vendor/yoyodyne/security/tardis/platform key>
Enter password for vendor/yoyodyne/security/tardis/releasekey key>
Enter password for vendor/yoyodyne/security/tardis/shared key>
    signing: Phone.apk (vendor/yoyodyne/security/tardis/platform)
    signing: Camera.apk (vendor/yoyodyne/security/tardis/media)
    signing: NetworkStack.apk (vendor/yoyodyne/security/tardis/networkstack)
    signing: Special.apk (vendor/yoyodyne/security/special-release)
    signing: Email.apk (vendor/yoyodyne/security/tardis/releasekey)
        [...]
    signing: ContactsProvider.apk (vendor/yoyodyne/security/tardis/shared)
    signing: Launcher.apk (vendor/yoyodyne/security/tardis/shared)
NOT signing: PresignedApp.apk
        (skipped due to special cert string)
rewriting SYSTEM/build.prop:
  replace:  ro.build.description=tardis-user Eclair ERC91 15449 test-keys
     with:  ro.build.description=tardis-user Eclair ERC91 15449 release-keys
  replace: ro.build.fingerprint=generic/tardis/tardis/tardis:Eclair/ERC91/15449:user/test-keys
     with: ro.build.fingerprint=generic/tardis/tardis/tardis:Eclair/ERC91/15449:user/release-keys
    signing: framework-res.apk (vendor/yoyodyne/security/tardis/platform)
rewriting RECOVERY/RAMDISK/default.prop:
  replace:  ro.build.description=tardis-user Eclair ERC91 15449 test-keys
     with:  ro.build.description=tardis-user Eclair ERC91 15449 release-keys
  replace: ro.build.fingerprint=generic/tardis/tardis/tardis:Eclair/ERC91/15449:user/test-keys
     with: ro.build.fingerprint=generic/tardis/tardis/tardis:Eclair/ERC91/15449:user/release-keys
using:
    vendor/yoyodyne/security/tardis/releasekey.x509.pem
for OTA package verification
done.

このスクリプトは、パスワード保護された鍵のパスワードをすべて入力するようユーザーに求めた後、入力ターゲット ファイル .zip 内のすべての APK ファイルにリリースキーを使用して再署名します。コマンドを実行する前に ANDROID_PW_FILE 環境変数を一時ファイル名に設定しておくと、スクリプトの実行中にエディタが起動し、そこにすべてのキーのパスワードを入力できます(パスワードの入力にはこの方法のほうが便利でしょう)。

APEX 署名鍵の置換

Android 10 では、低レベルのシステム モジュールをインストールするための APEX ファイル形式が導入されています。APEX 署名で説明したように、各 APEX ファイルは 2 つのキーで署名されます。1 つは APEX 内のミニ ファイル システムのイメージ用、もう 1 つは APEX 全体用です。

リリース用に署名する場合、APEX ファイルの 2 つの署名鍵をリリースキーに置き換えます。ファイル システムのペイロードキーは、--extra_apex_payload フラグを使用して指定され、APEX ファイル署名鍵全体は --extra_apks フラグで指定されます。

架空の tardis プロダクトでは、com.android.conscrypt.apexcom.android.media.apexcom.android.runtime.release.apex の APEX ファイルのキーが次のように構成されているとします。

name="com.android.conscrypt.apex" public_key="PRESIGNED" private_key="PRESIGNED" container_certificate="PRESIGNED" container_private_key="PRESIGNED"
name="com.android.media.apex" public_key="PRESIGNED" private_key="PRESIGNED" container_certificate="PRESIGNED" container_private_key="PRESIGNED"
name="com.android.runtime.release.apex" public_key="vendor/yoyodyne/security/testkeys/com.android.runtime.avbpubkey" private_key="vendor/yoyodyne/security/testkeys/com.android.runtime.pem" container_certificate="vendor/yoyodyne/security/testkeys/com.google.android.runtime.release_container.x509.pem" container_private_key="vendor/yoyodyne/security/testkeys/com.google.android.runtime.release_container.pk8"

また、リリースキーを含む次のファイルがあるとします。

vendor/yoyodyne/security/runtime_apex_container.x509.pem
vendor/yoyodyne/security/runtime_apex_container.pk8
vendor/yoyodyne/security/runtime_apex_payload.pem

次のコマンドは、リリース署名中に com.android.runtime.release.apexcom.android.tzdata.apex の署名鍵をオーバーライドします。詳しく説明すると、com.android.runtime.release.apex は指定された 2 つのリリースキー(APEX ファイルを runtime_apex_container で、ファイル イメージのペイロードを runtime_apex_payload で)で署名され、com.android.tzdata.apex は事前署名済みとして処理されます。その他の APEX ファイルはすべて、ターゲット ファイルのデフォルト構成に従って処理されます。

./build/make/tools/releasetools/sign_target_files_apks \
    --default_key_mappings   vendor/yoyodyne/security/tardis \
    --extra_apks             com.android.runtime.release.apex=vendor/yoyodyne/security/runtime_apex_container \
    --extra_apex_payload_key com.android.runtime.release.apex=vendor/yoyodyne/security/runtime_apex_payload.pem \
    --extra_apks             com.android.media.apex= \
    --extra_apex_payload_key com.android.media.apex= \
    -o tardis-target_files.zip \
    signed-tardis-target_files.zip

上記のコマンドを実行すると、次のログが生成されます。

        [...]
    signing: com.android.runtime.release.apex                  container (vendor/yoyodyne/security/runtime_apex_container)
           : com.android.runtime.release.apex                  payload   (vendor/yoyodyne/security/runtime_apex_payload.pem)
NOT signing: com.android.conscrypt.apex
        (skipped due to special cert string)
NOT signing: com.android.media.apex
        (skipped due to special cert string)
        [...]

その他のオプション

sign_target_files_apks 署名スクリプトは、署名付きビルドであることを示すためにビルド プロパティ ファイルのビルドの説明とフィンガープリントを書き換えます。フィンガープリントに加えられる編集は、--tag_changes フラグで制御できます。-h を指定してスクリプトを実行すると、すべてのフラグの説明が表示されます。

鍵の手動生成

Android は 2,048 ビット RSA キーと公開指数 3 を使用します。openssl.org の openssl ツールを使用して、証明書と秘密鍵のペアを生成できます。

# generate RSA key
openssl genrsa -3 -out temp.pem 2048
Generating RSA private key, 2048 bit long modulus
....+++
.....................+++
e is 3 (0x3)

# create a certificate with the public part of the key
openssl req -new -x509 -key temp.pem -out releasekey.x509.pem -days 10000 -subj '/C=US/ST=California/L=San Narciso/O=Yoyodyne, Inc./OU=Yoyodyne Mobility/CN=Yoyodyne/emailAddress=yoyodyne@example.com'

# create a PKCS#8-formatted version of the private key
openssl pkcs8 -in temp.pem -topk8 -outform DER -out releasekey.pk8 -nocrypt

# securely delete the temp.pem file
shred --remove temp.pem

上記の openssl pkcs8 コマンドは、パスワードのない .pk8 ファイルを作成します。これはビルドシステムでの使用に適しています。実際のリリースキーで署名する場合は、パスワードで保護された .pk8 を作成することをおすすめします。それには、-nocrypt 引数を -passout stdin で置き換えてください。openssl は標準入力からパスワードを読み取り、それを使用して秘密鍵を暗号化します。プロンプトは表示されないので、stdin がターミナルの場合はプログラムがただパスワードの入力を待つことになり、ハングしたように見えます。passout 引数に他の値を渡して、別の場所からパスワードを読み取らせることができます。詳細については、openssl のドキュメントをご覧ください。

temp.pem 中間ファイルにはパスワード保護のない秘密鍵が含まれているため、リリースキーを生成する際には気を付けて破棄してください。特に、GNUshred ユーティリティはネットワークやジャーナリングされたファイル システムでは有効でない場合があります。中間ディスクが誤って公開されないように、鍵を生成する際に RAM ディスク内の作業ディレクトリ(tmpfs パーティションなど)を使用できます。

イメージ ファイルの作成

signed-target_files.zip を作成したら、デバイスに配置できるようにイメージを作成する必要があります。署名付きイメージをターゲット ファイルから作成するには、Android ツリーのルートから次のコマンドを実行します。

img_from_target_files signed-target_files.zip signed-img.zip
生成されるファイル signed-img.zip にはすべての .img ファイルが含まれます。イメージをデバイスに読み込むには、次のように fastboot を使用します。
fastboot update signed-img.zip