diff options
Diffstat (limited to 'src/devices/tech/ota/sign_builds.jd')
-rwxr-xr-x | src/devices/tech/ota/sign_builds.jd | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/src/devices/tech/ota/sign_builds.jd b/src/devices/tech/ota/sign_builds.jd new file mode 100755 index 00000000..e9869205 --- /dev/null +++ b/src/devices/tech/ota/sign_builds.jd @@ -0,0 +1,269 @@ +page.title=Signing Builds for Release +@jd:body + +<!-- + Copyright 2015 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<div id="qv-wrapper"> + <div id="qv"> + <h2>In this document</h2> + <ol id="auto-toc"> + </ol> + </div> +</div> + +<p>Android uses cryptographic signatures in two places:</p> +<ol> +<li>Each .apk file must be signed. Android's Package Manager uses an .apk +signature in two ways:<ul> +<li>When an application is replaced, it must be signed by the same key as the +old application in order to get access to the old application's data.</li> +<li>If two or more applications want to share a user ID (so they can share +data, etc.), they must be signed with the same key.</ul></li> +<li>OTA update packages must be signed with one of the keys expected by the +system or the installation process will reject them.</ul></li> +</ol> +<h2 id="certificates-keys">Certificates and keys</h2> +<p>Each key comes in two files: the <i>certificate</i>, which has the +extension .x509.pem, and the <i>private key</i>, which has the extension .pk8. +The private key should be kept secret and is needed to sign a package. The key +may itself be protected by a password—a reasonable strategy is to store your +keys in source control along with the code—but keep them protected by a +password known only to the people who make final releases. The certificate, in +contrast, contains only the public half of the key, so it can be distributed +widely. It is used to verify a package has been signed by the corresponding +private key.</p> +<p>The standard Android build uses four keys, all of which reside in <code> +build/target/product/security</code>:</p> + +<dl> +<dt>testkey</dt> +<dd>Generic default key for packages that do not otherwise specify a key.</dd> +<dt>platform</dt> +<dd>Test key for packages that are part of the core platform.</dd> +<dt>shared</dt> +<dd>Test key for things that are shared in the home/contacts process.</dd> +<dt>media</dt> +<dd>Test key for packages that are part of the media/download system.</dd></dl> + +<p>Individual packages specify one of these keys by setting LOCAL_CERTIFICATE +in their Android.mk file. (testkey is used if this variable is not set.) You +can also specify an entirely different key by pathname, e.g.:</p> + +<p><code>device/yoyodyne/apps/SpecialApp/Android.mk</code></p> +<pre> + [...] + +LOCAL_CERTIFICATE := device/yoyodyne/security/special +</pre> + +<p>Now the build uses the <code>device/yoyodyne/security/special.{x509.pem,pk8} +</code> key to sign SpecialApp.apk. The build can use only private keys that +are <i>not </i>password protected.</p> + +<h2>Generating keys</h2> +<p>Android uses 2048-bit RSA keys with public exponent 3. You can generate +certificate/private key pairs using the openssl tool from +<a href="http://www.openssl.org/">openssl.org</a>:</p> + +<pre> +# generate RSA key +% <b>openssl genrsa -3 -out temp.pem 2048</b> +Generating RSA private key, 2048 bit long modulus +....+++ +.....................+++ +e is 3 (0x3) + +# create a certificate with the public part of the key +% <b>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'</b> + +# create a PKCS#8-formatted version of the private key +% <b>openssl pkcs8 -in temp.pem -topk8 -outform DER -out releasekey.pk8 -nocrypt</b> + +# securely delete the temp.pem file +% <b>shred --remove temp.pem</b> +</pre> + +<p>The openssl pkcs8 command given above creates a .pk8 file with <i>no</i> +password, suitable for use with the build system. To create a .pk8 secured +with a password (which you should do for all actual release keys), replace the +<code>-nocrypt</code> argument with <code>-passout stdin</code>; then openssl +will encrypt the private key with a password read from standard input. No +prompt is printed, so if stdin is the terminal the program will appear to hang +when it's really just waiting for you to enter a password. Other values can be +used for the-passout argument to read the password from other locations; for +details, see the +<a href="http://www.openssl.org/docs/apps/openssl.html#PASS_PHRASE_ARGUMENTS"> +openssl documentation</a>.</p> +<p>The temp.pem intermediate file contains the private key without any kind of +password protection, so dispose of it thoughtfully when generating release +keys. In particular, the GNUshred utility may not be effective on network or +journaled filesystems. You can use a working directory located in a RAM disk +(such as a tmpfs partition) when generating keys to ensure the intermediates +are not inadvertently exposed.</p> + +<h2 id="sign-apps-for-release">Signing apps for release</h2> +<p>The first step in preparing a build for release is to sign all the .apk +files in it, replacing the test keys used by the build system. This is done +with the <code>sign_target_files_apks</code> script. It takes a target-files +.zip as input and produces a new target-files .zip in which all the .apks have +been signed with new keys.</p> +<p>When you run this script, you must specify on the command line a +replacement key for each key used in the build. The <code>-k <i>src_key</i>=<i> +dest_key</i></code> flag specifies key replacements one at a time. The flag +<code>-d <i>dir</i></code> lets you specify a directory with four keys to +replace all those in <code>build/target/product/security</code>; it is +equivalent to using <code>-k</code> four times to specify the mappings:</p> + +<pre> +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 +</pre> + +<p>For the hypothetical tardis product, you need five password-protected keys: +four to replace the four in <code>build/target/product/security</code>, and +one to replace the additional <code>keydevice/yoyodyne/security/special</code> +required by SpecialApp in the example above. If the keys were in the following +files:</p> + +<pre> +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/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 +</pre> + +<p>Then you would sign all the apps like this:</p> + +<pre> +% <b>./build/tools/releasetools/sign_target_files_apks \ + -d vendor/yoyodyne/security/tardis \ + -k vendor/yoyodyne/special=vendor/yoyodyne/special-release \ + -o \ </b># explained in the next section<b> + tardis-target_files.zip signed-tardis-target_files.zip</b> +Enter password for vendor/yoyodyne/security/special-release 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: 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) +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. +</pre> + +<p>After prompting the user for passwords for all password-protected keys, the +script re-signs all the .apk files in the input target .zip with the release +keys. Before running the command, you can also set the ANDROID_PW_FILE +environment variable to a temporary filename; the script then invokes your +editor to allow you to enter passwords for all keys (this may be a more +convenient way to enter passwords).<p> +<p><code>sign_target_files_apks</code> also rewrites the build description and +fingerprint in the build properties files to reflect the fact that this is a +signed build. The <code>-t</code> flag can control what edits are made to the +fingerprint. Run the script with <code>-h</code> to see documentation on all +flags.</p> + +<h2 id="sign-ota-packages">Signing OTA packages</h2> +<p>You need the following components to sign OTA packages:</p> +<ol> +<li>Certificates of the keys you want this build to accept.</li> +<li>Sign the newly-created package with the private key (must correspond to +the certificate embedded in the current build of any device to which you want +to send this package).</li> +</ol> +<p>To achieve these components:</p> +<ul> +<li>The target-files .zip produced by the build sets the OTA certificate to +the certificate of the test key. Passing the <code>-o</code> flag to <code> +sign_target_files_apks</code> replaces this key with the release key from your +build.</li> +<li>To sign the OTA update package, use the <code>-k</code> option when +generating it to specify the key. You should give <code>ota_from_target_files +</code> the <i>signed</i> version of the target-files .zip as well: +<pre> +% <b>./build/tools/releasetools/ota_from_target_files \ + -k vendor/yoyodyne/security/tardis/releasekey \ + signed-tardis-target_files.zip \ + signed-ota_update.zip</b> +unzipping target target-files... +(using device-specific extensions from target_files) +Enter password for vendor/yoyodyne/security/tardis/releasekey key> +done.</pre></li></ul> + +<h3 id="signatures-sideloading">Signatures and sideloading</h3> +<p>Sideloading does not bypass recovery's normal package signature +verification mechanism—before installing a package, recovery will verify that +it is signed with one of the private keys matching the public keys stored in +the recovery partition, just as it would for a package delivered over-the-air. +</p> +<p>Update packages received from the main system are typically verified twice: +once by the main system, using the <code><a href="http://developer.android.com/ +reference/android/os/RecoverySystem.html#verifyPackage">RecoverySystem. +verifyPackage()</a></code> method in the android API, and then again by +recovery. The RecoverySystem API checks the signature against public keys +stored in the main system, in the file <code>/system/etc/security/otacerts.zip +</code> (by default). Recovery checks the signature against public keys stored +in the recovery partition RAM disk, in the file <code>/res/keys</code>.</p> +<p>Normally these two locations store the same set of keys. By adding a key to +<i>just</i> the recovery set of keys, it's possible to sign packages that can +be installed only via sideloading (assuming the main system's update download +mechanism is correctly doing verification against otacerts.zip). You can +specify extra keys to be included only in recovery by setting the +PRODUCT_EXTRA_RECOVERY_KEYS variable in your product definition:</p> + +<p><code>vendor/yoyodyne/tardis/products/tardis.mk</code></p> +<pre> + [...] + +PRODUCT_EXTRA_RECOVERY_KEYS := vendor/yoyodyne/security/tardis/sideload +</pre> + +<p>This includes the public key <code>vendor/yoyodyne/security/tardis/sideload. +x509.pem</code> in the recovery keys file so it can install packages signed +with it. The extra key is <i>not</i> included in otacerts.zip though, so +systems that correctly verify downloaded packages do not invoke recovery for +packages signed with this key.</p>
\ No newline at end of file |