1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.apksig; 18 19 import static com.android.apksig.internal.apk.ApkSigningBlockUtils.getLengthPrefixedSlice; 20 21 import com.android.apksig.apk.ApkFormatException; 22 import com.android.apksig.internal.apk.ApkSigningBlockUtils; 23 import com.android.apksig.internal.apk.SignatureAlgorithm; 24 import com.android.apksig.internal.apk.v3.V3SchemeSigner; 25 import com.android.apksig.internal.apk.v3.V3SigningCertificateLineage; 26 import com.android.apksig.internal.apk.v3.V3SigningCertificateLineage.SigningCertificateNode; 27 import com.android.apksig.internal.util.AndroidSdkVersion; 28 import com.android.apksig.internal.util.Pair; 29 import com.android.apksig.internal.util.RandomAccessFileDataSink; 30 import com.android.apksig.util.DataSink; 31 import com.android.apksig.util.DataSource; 32 import com.android.apksig.util.DataSources; 33 34 import java.io.File; 35 import java.io.IOException; 36 import java.io.RandomAccessFile; 37 import java.nio.ByteBuffer; 38 import java.nio.ByteOrder; 39 import java.security.InvalidKeyException; 40 import java.security.NoSuchAlgorithmException; 41 import java.security.PrivateKey; 42 import java.security.PublicKey; 43 import java.security.SignatureException; 44 import java.security.cert.CertificateEncodingException; 45 import java.security.cert.X509Certificate; 46 import java.util.ArrayList; 47 import java.util.Arrays; 48 import java.util.Collections; 49 import java.util.List; 50 51 /** 52 * APK Signer Lineage. 53 * 54 * <p>The signer lineage contains a history of signing certificates with each ancestor attesting to 55 * the validity of its descendant. Each additional descendant represents a new identity that can be 56 * used to sign an APK, and each generation has accompanying attributes which represent how the 57 * APK would like to view the older signing certificates, specifically how they should be trusted in 58 * certain situations. 59 * 60 * <p> Its primary use is to enable APK Signing Certificate Rotation. The Android platform verifies 61 * the APK Signer Lineage, and if the current signing certificate for the APK is in the Signer 62 * Lineage, and the Lineage contains the certificate the platform associates with the APK, it will 63 * allow upgrades to the new certificate. 64 * 65 * @see <a href="https://source.android.com/security/apksigning/index.html">Application Signing</a> 66 */ 67 public class SigningCertificateLineage { 68 69 private final static int MAGIC = 0x3eff39d1; 70 71 private final static int FIRST_VERSION = 1; 72 73 private static final int CURRENT_VERSION = FIRST_VERSION; 74 75 /** accept data from already installed pkg with this cert */ 76 private static final int PAST_CERT_INSTALLED_DATA = 1; 77 78 /** accept sharedUserId with pkg with this cert */ 79 private static final int PAST_CERT_SHARED_USER_ID = 2; 80 81 /** grant SIGNATURE permissions to pkgs with this cert */ 82 private static final int PAST_CERT_PERMISSION = 4; 83 84 /** 85 * Enable updates back to this certificate. WARNING: this effectively removes any benefit of 86 * signing certificate changes, since a compromised key could retake control of an app even 87 * after change, and should only be used if there is a problem encountered when trying to ditch 88 * an older cert. 89 */ 90 private static final int PAST_CERT_ROLLBACK = 8; 91 92 /** 93 * Preserve authenticator module-based access in AccountManager gated by signing certificate. 94 */ 95 private static final int PAST_CERT_AUTH = 16; 96 97 private final int mMinSdkVersion; 98 99 /** 100 * The signing lineage is just a list of nodes, with the first being the original signing 101 * certificate and the most recent being the one with which the APK is to actually be signed. 102 */ 103 private final List<SigningCertificateNode> mSigningLineage; 104 SigningCertificateLineage(int minSdkVersion, List<SigningCertificateNode> list)105 private SigningCertificateLineage(int minSdkVersion, List<SigningCertificateNode> list) { 106 mMinSdkVersion = minSdkVersion; 107 mSigningLineage = list; 108 } 109 createSigningLineage( int minSdkVersion, SignerConfig parent, SignerCapabilities parentCapabilities, SignerConfig child, SignerCapabilities childCapabilities)110 private static SigningCertificateLineage createSigningLineage( 111 int minSdkVersion, SignerConfig parent, SignerCapabilities parentCapabilities, 112 SignerConfig child, SignerCapabilities childCapabilities) 113 throws CertificateEncodingException, InvalidKeyException, NoSuchAlgorithmException, 114 SignatureException { 115 SigningCertificateLineage signingCertificateLineage = 116 new SigningCertificateLineage(minSdkVersion, new ArrayList<>()); 117 signingCertificateLineage = 118 signingCertificateLineage.spawnFirstDescendant(parent, parentCapabilities); 119 return signingCertificateLineage.spawnDescendant(parent, child, childCapabilities); 120 } 121 readFromFile(File file)122 public static SigningCertificateLineage readFromFile(File file) 123 throws IOException { 124 if (file == null) { 125 throw new NullPointerException("file == null"); 126 } 127 RandomAccessFile inputFile = new RandomAccessFile(file, "r"); 128 return readFromDataSource(DataSources.asDataSource(inputFile)); 129 } 130 readFromDataSource(DataSource dataSource)131 public static SigningCertificateLineage readFromDataSource(DataSource dataSource) 132 throws IOException { 133 if (dataSource == null) { 134 throw new NullPointerException("dataSource == null"); 135 } 136 ByteBuffer inBuff = dataSource.getByteBuffer(0, (int) dataSource.size()); 137 inBuff.order(ByteOrder.LITTLE_ENDIAN); 138 return read(inBuff); 139 } 140 141 /** 142 * Extracts a Signing Certificate Lineage from a v3 signer proof-of-rotation attribute. 143 * 144 * <note> 145 * this may not give a complete representation of an APK's signing certificate history, 146 * since the APK may have multiple signers corresponding to different platform versions. 147 * Use <code> readFromApkFile</code> to handle this case. 148 * </note> 149 * @param attrValue 150 */ readFromV3AttributeValue(byte[] attrValue)151 public static SigningCertificateLineage readFromV3AttributeValue(byte[] attrValue) 152 throws IOException { 153 List<SigningCertificateNode> parsedLineage = 154 V3SigningCertificateLineage.readSigningCertificateLineage(ByteBuffer.wrap( 155 attrValue)); 156 int minSdkVersion = calculateMinSdkVersion(parsedLineage); 157 return new SigningCertificateLineage(minSdkVersion, parsedLineage); 158 } 159 readFromApkFile(File apkFile)160 public static SigningCertificateLineage readFromApkFile(File apkFile) { 161 throw new UnsupportedOperationException("Not yet implemented"); 162 } 163 writeToFile(File file)164 public void writeToFile(File file) throws IOException { 165 if (file == null) { 166 throw new NullPointerException("file == null"); 167 } 168 RandomAccessFile outputFile = new RandomAccessFile(file, "rw"); 169 writeToDataSink(new RandomAccessFileDataSink(outputFile)); 170 } 171 writeToDataSink(DataSink dataSink)172 public void writeToDataSink(DataSink dataSink) throws IOException { 173 if (dataSink == null) { 174 throw new NullPointerException("dataSink == null"); 175 } 176 dataSink.consume(write()); 177 } 178 179 /** 180 * Add a new signing certificate to the lineage. This effectively creates a signing certificate 181 * rotation event, forcing APKs which include this lineage to be signed by the new signer. The 182 * flags associated with the new signer are set to a default value. 183 * 184 * @param parent current signing certificate of the containing APK 185 * @param child new signing certificate which will sign the APK contents 186 */ spawnDescendant(SignerConfig parent, SignerConfig child)187 public SigningCertificateLineage spawnDescendant(SignerConfig parent, SignerConfig child) 188 throws CertificateEncodingException, InvalidKeyException, NoSuchAlgorithmException, 189 SignatureException { 190 if (parent == null || child == null) { 191 throw new NullPointerException("can't add new descendant to lineage with null inputs"); 192 } 193 SignerCapabilities signerCapabilities = new SignerCapabilities.Builder().build(); 194 return spawnDescendant(parent, child, signerCapabilities); 195 } 196 197 /** 198 * Add a new signing certificate to the lineage. This effectively creates a signing certificate 199 * rotation event, forcing APKs which include this lineage to be signed by the new signer. 200 * 201 * @param parent current signing certificate of the containing APK 202 * @param child new signing certificate which will sign the APK contents 203 * @param childCapabilities flags 204 */ spawnDescendant( SignerConfig parent, SignerConfig child, SignerCapabilities childCapabilities)205 public SigningCertificateLineage spawnDescendant( 206 SignerConfig parent, SignerConfig child, SignerCapabilities childCapabilities) 207 throws CertificateEncodingException, InvalidKeyException, 208 NoSuchAlgorithmException, SignatureException { 209 if (parent == null) { 210 throw new NullPointerException("parent == null"); 211 } 212 if (child == null) { 213 throw new NullPointerException("child == null"); 214 } 215 if (childCapabilities == null) { 216 throw new NullPointerException("childCapabilities == null"); 217 } 218 if (mSigningLineage.isEmpty()) { 219 throw new IllegalArgumentException("Cannot spawn descendant signing certificate on an" 220 + " empty SigningCertificateLineage: no parent node"); 221 } 222 223 // make sure that the parent matches our newest generation (leaf node/sink) 224 SigningCertificateNode currentGeneration = mSigningLineage.get(mSigningLineage.size() - 1); 225 if (!Arrays.equals(currentGeneration.signingCert.getEncoded(), 226 parent.getCertificate().getEncoded())) { 227 throw new IllegalArgumentException("SignerConfig Certificate containing private key" 228 + " to sign the new SigningCertificateLineage record does not match the" 229 + " existing most recent record"); 230 } 231 232 // create data to be signed, including the algorithm we're going to use 233 SignatureAlgorithm signatureAlgorithm = getSignatureAlgorithm(parent); 234 ByteBuffer prefixedSignedData = ByteBuffer.wrap( 235 V3SigningCertificateLineage.encodeSignedData( 236 child.getCertificate(), signatureAlgorithm.getId())); 237 prefixedSignedData.position(4); 238 ByteBuffer signedDataBuffer = ByteBuffer.allocate(prefixedSignedData.remaining()); 239 signedDataBuffer.put(prefixedSignedData); 240 byte[] signedData = signedDataBuffer.array(); 241 242 // create SignerConfig to do the signing 243 List<X509Certificate> certificates = new ArrayList<>(1); 244 certificates.add(parent.getCertificate()); 245 ApkSigningBlockUtils.SignerConfig newSignerConfig = 246 new ApkSigningBlockUtils.SignerConfig(); 247 newSignerConfig.privateKey = parent.getPrivateKey(); 248 newSignerConfig.certificates = certificates; 249 newSignerConfig.signatureAlgorithms = Collections.singletonList(signatureAlgorithm); 250 251 // sign it 252 List<Pair<Integer, byte[]>> signatures = 253 ApkSigningBlockUtils.generateSignaturesOverData(newSignerConfig, signedData); 254 255 // finally, add it to our lineage 256 SignatureAlgorithm sigAlgorithm = SignatureAlgorithm.findById(signatures.get(0).getFirst()); 257 byte[] signature = signatures.get(0).getSecond(); 258 currentGeneration.sigAlgorithm = sigAlgorithm; 259 SigningCertificateNode childNode = 260 new SigningCertificateNode( 261 child.getCertificate(), sigAlgorithm, null, 262 signature, childCapabilities.getFlags()); 263 List<SigningCertificateNode> lineageCopy = new ArrayList<>(mSigningLineage); 264 lineageCopy.add(childNode); 265 return new SigningCertificateLineage(mMinSdkVersion, lineageCopy); 266 } 267 268 /** 269 * The number of signing certificates in the lineage, including the current signer, which means 270 * this value can also be used to V2determine the number of signing certificate rotations by 271 * subtracting 1. 272 */ size()273 public int size() { 274 return mSigningLineage.size(); 275 } 276 getSignatureAlgorithm(SignerConfig parent)277 private SignatureAlgorithm getSignatureAlgorithm(SignerConfig parent) 278 throws InvalidKeyException { 279 PublicKey publicKey = parent.getCertificate().getPublicKey(); 280 281 // TODO switch to one signature algorithm selection, or add support for multiple algorithms 282 List<SignatureAlgorithm> algorithms = V3SchemeSigner.getSuggestedSignatureAlgorithms( 283 publicKey, mMinSdkVersion, false /* padding support */); 284 return algorithms.get(0); 285 } 286 spawnFirstDescendant( SignerConfig parent, SignerCapabilities signerCapabilities)287 private SigningCertificateLineage spawnFirstDescendant( 288 SignerConfig parent, SignerCapabilities signerCapabilities) { 289 if (!mSigningLineage.isEmpty()) { 290 throw new IllegalStateException("SigningCertificateLineage already has its first node"); 291 } 292 293 // check to make sure that the public key for the first node is acceptable for our minSdk 294 try { 295 getSignatureAlgorithm(parent); 296 } catch (InvalidKeyException e) { 297 throw new IllegalArgumentException("Algorithm associated with first signing certificate" 298 + " invalid on desired platform versions", e); 299 } 300 301 // create "fake" signed data (there will be no signature over it, since there is no parent 302 SigningCertificateNode firstNode = new SigningCertificateNode( 303 parent.getCertificate(), null, null, new byte[0], signerCapabilities.getFlags()); 304 return new SigningCertificateLineage(mMinSdkVersion, Collections.singletonList(firstNode)); 305 } 306 read(ByteBuffer inputByteBuffer)307 private static SigningCertificateLineage read(ByteBuffer inputByteBuffer) 308 throws IOException { 309 ApkSigningBlockUtils.checkByteOrderLittleEndian(inputByteBuffer); 310 if (inputByteBuffer.remaining() < 8) { 311 throw new IllegalArgumentException( 312 "Improper SigningCertificateLineage format: insufficient data for header."); 313 } 314 315 if (inputByteBuffer.getInt() != MAGIC) { 316 throw new IllegalArgumentException( 317 "Improper SigningCertificateLineage format: MAGIC header mismatch."); 318 } 319 return read(inputByteBuffer, inputByteBuffer.getInt()); 320 } 321 read(ByteBuffer inputByteBuffer, int version)322 private static SigningCertificateLineage read(ByteBuffer inputByteBuffer, int version) 323 throws IOException { 324 switch (version) { 325 case FIRST_VERSION: 326 try { 327 List<SigningCertificateNode> nodes = 328 V3SigningCertificateLineage.readSigningCertificateLineage( 329 getLengthPrefixedSlice(inputByteBuffer)); 330 int minSdkVersion = calculateMinSdkVersion(nodes); 331 return new SigningCertificateLineage(minSdkVersion, nodes); 332 } catch (ApkFormatException e) { 333 // unable to get a proper length-prefixed lineage slice 334 throw new IOException("Unable to read list of signing certificate nodes in " 335 + "SigningCertificateLineage", e); 336 } 337 default: 338 throw new IllegalArgumentException( 339 "Improper SigningCertificateLineage format: unrecognized version."); 340 } 341 } 342 calculateMinSdkVersion(List<SigningCertificateNode> nodes)343 private static int calculateMinSdkVersion(List<SigningCertificateNode> nodes) { 344 if (nodes == null) { 345 throw new IllegalArgumentException("Can't calculate minimum SDK version of null nodes"); 346 } 347 int minSdkVersion = AndroidSdkVersion.P; // lineage introduced in P 348 for (SigningCertificateNode node : nodes) { 349 if (node.sigAlgorithm != null) { 350 int nodeMinSdkVersion = node.sigAlgorithm.getMinSdkVersion(); 351 if (nodeMinSdkVersion > minSdkVersion) { 352 minSdkVersion = nodeMinSdkVersion; 353 } 354 } 355 } 356 return minSdkVersion; 357 } 358 write()359 private ByteBuffer write() { 360 byte[] encodedLineage = 361 V3SigningCertificateLineage.encodeSigningCertificateLineage(mSigningLineage); 362 int payloadSize = 4 + 4 + 4 + encodedLineage.length; 363 ByteBuffer result = ByteBuffer.allocate(payloadSize); 364 result.order(ByteOrder.LITTLE_ENDIAN); 365 result.putInt(MAGIC); 366 result.putInt(CURRENT_VERSION); 367 result.putInt(encodedLineage.length); 368 result.put(encodedLineage); 369 return result; 370 } 371 generateV3SignerAttribute()372 public byte[] generateV3SignerAttribute() { 373 // FORMAT (little endian): 374 // * length-prefixed bytes: attribute pair 375 // * uint32: ID 376 // * bytes: value - encoded V3 SigningCertificateLineage 377 byte[] encodedLineage = 378 V3SigningCertificateLineage.encodeSigningCertificateLineage(mSigningLineage); 379 int payloadSize = 4 + 4 + encodedLineage.length; 380 ByteBuffer result = ByteBuffer.allocate(payloadSize); 381 result.order(ByteOrder.LITTLE_ENDIAN); 382 result.putInt(4 + encodedLineage.length); 383 result.putInt(V3SchemeSigner.PROOF_OF_ROTATION_ATTR_ID); 384 result.put(encodedLineage); 385 return result.array(); 386 } 387 sortSignerConfigs( List<DefaultApkSignerEngine.SignerConfig> signerConfigs)388 public List<DefaultApkSignerEngine.SignerConfig> sortSignerConfigs( 389 List<DefaultApkSignerEngine.SignerConfig> signerConfigs) { 390 if (signerConfigs == null) { 391 throw new NullPointerException("signerConfigs == null"); 392 } 393 394 // not the most elegant sort, but we expect signerConfigs to be quite small (1 or 2 signers 395 // in most cases) and likely already sorted, so not worth the overhead of doing anything 396 // fancier 397 List<DefaultApkSignerEngine.SignerConfig> sortedSignerConfigs = 398 new ArrayList<>(signerConfigs.size()); 399 for (int i = 0; i < mSigningLineage.size(); i++) { 400 for (int j = 0; j < signerConfigs.size(); j++) { 401 DefaultApkSignerEngine.SignerConfig config = signerConfigs.get(j); 402 if (mSigningLineage.get(i).signingCert.equals(config.getCertificates().get(0))) { 403 sortedSignerConfigs.add(config); 404 break; 405 } 406 } 407 } 408 if (sortedSignerConfigs.size() != signerConfigs.size()) { 409 throw new IllegalArgumentException("SignerConfigs supplied which are not present in the" 410 + " SigningCertificateLineage"); 411 } 412 return sortedSignerConfigs; 413 } 414 415 // TODO add API to return all signing certificate(s) 416 417 // TODO add API to query if given signing certificate is in set of signing certificates 418 419 // TODO add API to modify flags corresponding to a given signing certificate 420 calculateDefaultFlags()421 private static int calculateDefaultFlags() { 422 return PAST_CERT_INSTALLED_DATA | PAST_CERT_PERMISSION 423 | PAST_CERT_SHARED_USER_ID | PAST_CERT_AUTH; 424 } 425 426 /** 427 * Returns a new SigingCertificateLineage which terminates at the node corresponding to the 428 * given certificate. This is useful in the event of rotating to a new signing algorithm that 429 * is only supported on some platform versions. It enables a v3 signature to be generated using 430 * this signing certificate and the shortened proof-of-rotation record from this sub lineage in 431 * conjunction with the appropriate SDK version values. 432 * 433 * @param x509Certificate the signing certificate for which to search 434 * @return A new SigningCertificateLineage if present, or null otherwise. 435 */ getSubLineage(X509Certificate x509Certificate)436 public SigningCertificateLineage getSubLineage(X509Certificate x509Certificate) { 437 if (x509Certificate == null) { 438 throw new NullPointerException("x509Certificate == null"); 439 } 440 for (int i = 0; i < mSigningLineage.size(); i++) { 441 if (mSigningLineage.get(i).signingCert.equals(x509Certificate)) { 442 return new SigningCertificateLineage( 443 mMinSdkVersion, new ArrayList<>(mSigningLineage.subList(0, i + 1))); 444 } 445 } 446 447 // looks like we didn't find the cert, 448 throw new IllegalArgumentException("Certificate not found in SigningCertificateLineage"); 449 } 450 451 /** 452 * Consolidates all of the lineages found in an APK into one lineage, which is the longest one. 453 * In so doing, it also checks that all of the smaller lineages are contained in the largest, 454 * and that they properly cover the desired platform ranges. 455 * 456 * An APK may contain multiple lineages, one for each signer, which correspond to different 457 * supported platform versions. In this event, the lineage(s) from the earlier platform 458 * version(s) need to be present in the most recent (longest) one to make sure that when a 459 * platform version changes. 460 * 461 * <note> This does not verify that the largest lineage corresponds to the most recent supported 462 * platform version. That check requires is performed during v3 verification. </note> 463 */ consolidateLineages( List<SigningCertificateLineage> lineages)464 public static SigningCertificateLineage consolidateLineages( 465 List<SigningCertificateLineage> lineages) { 466 if (lineages == null || lineages.isEmpty()) { 467 return null; 468 } 469 int largestIndex = 0; 470 int maxSize = 0; 471 472 // determine the longest chain 473 for (int i = 0; i < lineages.size(); i++) { 474 int curSize = lineages.get(i).size(); 475 if (curSize > maxSize) { 476 largestIndex = i; 477 maxSize = curSize; 478 } 479 } 480 481 List<SigningCertificateNode> largestList = lineages.get(largestIndex).mSigningLineage; 482 // make sure all other lineages fit into this one, with the same capabilities 483 for (int i = 0; i < lineages.size(); i++) { 484 if (i == largestIndex) { 485 continue; 486 } 487 List<SigningCertificateNode> underTest = lineages.get(i).mSigningLineage; 488 if (!underTest.equals(largestList.subList(0, underTest.size()))) { 489 throw new IllegalArgumentException("Inconsistent SigningCertificateLineages. " 490 + "Not all lineages are subsets of each other."); 491 } 492 } 493 494 // if we've made it this far, they all check out, so just return the largest 495 return lineages.get(largestIndex); 496 } 497 498 /** 499 * Representation of the capabilities the APK would like to grant to its old signing 500 * certificates. The {@code SigningCertificateLineage} provides two conceptual data structures. 501 * 1) proof of rotation - Evidence that other parties can trust an APK's current signing 502 * certificate if they trust an older one in this lineage 503 * 2) self-trust - certain capabilities may have been granted by an APK to other parties based 504 * on its own signing certificate. When it changes its signing certificate it may want to 505 * allow the other parties to retain those capabilities. 506 * {@code SignerCapabilties} provides a representation of the second structure. 507 * 508 * <p>Use {@link Builder} to obtain configuration instances. 509 */ 510 public static class SignerCapabilities { 511 private final int mFlags; 512 SignerCapabilities(int flags)513 private SignerCapabilities(int flags) { 514 mFlags = flags; 515 } 516 getFlags()517 private int getFlags() { 518 return mFlags; 519 } 520 521 /** 522 * Builder of {@link SignerCapabilities} instances. 523 */ 524 public static class Builder { 525 private int mFlags; 526 527 /** 528 * Constructs a new {@code Builder}. 529 */ Builder()530 public Builder() { 531 mFlags = calculateDefaultFlags(); 532 } 533 534 /** 535 * Set the {@code PAST_CERT_INSTALLED_DATA} flag in this capabilities object. This flag 536 * is used by the platform to determine if installed data associated with previous 537 * signing certificate should be trusted. In particular, this capability is required to 538 * perform signing certificate rotation during an upgrade on-device. Without it, the 539 * platform will not permit the app data from the old signing certificate to 540 * propagate to the new version. Typically, this flag should be set to enable signing 541 * certificate rotation, and may be unset later when the app developer is satisfied that 542 * their install base is as migrated as it will be. 543 */ setInstalledData(boolean enabled)544 public Builder setInstalledData(boolean enabled) { 545 if (enabled) { 546 mFlags |= PAST_CERT_INSTALLED_DATA; 547 } else { 548 mFlags &= ~PAST_CERT_INSTALLED_DATA; 549 } 550 return this; 551 } 552 553 /** 554 * Set the {@code PAST_CERT_SHARED_USER_ID} flag in this capabilities object. This flag 555 * is used by the platform to determine if this app is willing to be sharedUid with 556 * other apps which are still signed with the associated signing certificate. This is 557 * useful in situations where sharedUserId apps would like to change their signing 558 * certificate, but can't guarantee the order of updates to those apps. 559 */ setSharedUid(boolean enabled)560 public Builder setSharedUid(boolean enabled) { 561 if (enabled) { 562 mFlags |= PAST_CERT_SHARED_USER_ID; 563 } else { 564 mFlags &= ~PAST_CERT_SHARED_USER_ID; 565 } 566 return this; 567 } 568 569 /** 570 * Set the {@code PAST_CERT_PERMISSION} flag in this capabilities object. This flag 571 * is used by the platform to determine if this app is willing to grant SIGNATURE 572 * permissions to apps signed with the associated signing certificate. Without this 573 * capability, an application signed with the older certificate will not be granted the 574 * SIGNATURE permissions defined by this app. In addition, if multiple apps define the 575 * same SIGNATURE permission, the second one the platform sees will not be installable 576 * if this capability is not set and the signing certificates differ. 577 */ setPermission(boolean enabled)578 public Builder setPermission(boolean enabled) { 579 if (enabled) { 580 mFlags |= PAST_CERT_PERMISSION; 581 } else { 582 mFlags &= ~PAST_CERT_PERMISSION; 583 } 584 return this; 585 } 586 587 /** 588 * Set the {@code PAST_CERT_ROLLBACK} flag in this capabilities object. This flag 589 * is used by the platform to determine if this app is willing to upgrade to a new 590 * version that is signed by one of its past signing certificates. 591 * 592 * <note> WARNING: this effectively removes any benefit of signing certificate changes, 593 * since a compromised key could retake control of an app even after change, and should 594 * only be used if there is a problem encountered when trying to ditch an older cert 595 * </note> 596 */ setRollback(boolean enabled)597 public Builder setRollback(boolean enabled) { 598 if (enabled) { 599 mFlags |= PAST_CERT_ROLLBACK; 600 } else { 601 mFlags &= ~PAST_CERT_ROLLBACK; 602 } 603 return this; 604 } 605 606 /** 607 * Set the {@code PAST_CERT_AUTH} flag in this capabilities object. This flag 608 * is used by the platform to determine whether or not privileged access based on 609 * authenticator module signing certificates should be granted. 610 */ setAuth(boolean enabled)611 public Builder setAuth(boolean enabled) { 612 if (enabled) { 613 mFlags |= PAST_CERT_AUTH; 614 } else { 615 mFlags &= ~PAST_CERT_AUTH; 616 } 617 return this; 618 } 619 620 /** 621 * Returns a new {@code SignerConfig} instance configured based on the configuration of 622 * this builder. 623 */ build()624 public SignerCapabilities build() { 625 return new SignerCapabilities(mFlags); 626 } 627 } 628 } 629 630 /** 631 * Configuration of a signer. Used to add a new entry to the {@link SigningCertificateLineage} 632 * 633 * <p>Use {@link Builder} to obtain configuration instances. 634 */ 635 public static class SignerConfig { 636 private final PrivateKey mPrivateKey; 637 private final X509Certificate mCertificate; 638 SignerConfig( PrivateKey privateKey, X509Certificate certificate)639 private SignerConfig( 640 PrivateKey privateKey, 641 X509Certificate certificate) { 642 mPrivateKey = privateKey; 643 mCertificate = certificate; 644 } 645 646 /** 647 * Returns the signing key of this signer. 648 */ getPrivateKey()649 public PrivateKey getPrivateKey() { 650 return mPrivateKey; 651 } 652 653 /** 654 * Returns the certificate(s) of this signer. The first certificate's public key corresponds 655 * to this signer's private key. 656 */ getCertificate()657 public X509Certificate getCertificate() { 658 return mCertificate; 659 } 660 661 /** 662 * Builder of {@link SignerConfig} instances. 663 */ 664 public static class Builder { 665 private final PrivateKey mPrivateKey; 666 private final X509Certificate mCertificate; 667 668 /** 669 * Constructs a new {@code Builder}. 670 * 671 * @param privateKey signing key 672 * @param certificate the X.509 certificate with a subject public key of the 673 * {@code privateKey}. 674 */ Builder( PrivateKey privateKey, X509Certificate certificate)675 public Builder( 676 PrivateKey privateKey, 677 X509Certificate certificate) { 678 mPrivateKey = privateKey; 679 mCertificate = certificate; 680 } 681 682 /** 683 * Returns a new {@code SignerConfig} instance configured based on the configuration of 684 * this builder. 685 */ build()686 public SignerConfig build() { 687 return new SignerConfig( 688 mPrivateKey, 689 mCertificate); 690 } 691 } 692 } 693 694 /** 695 * Builder of {@link SigningCertificateLineage} instances. 696 */ 697 public static class Builder { 698 private final SignerConfig mOriginalSignerConfig; 699 private final SignerConfig mNewSignerConfig; 700 private SignerCapabilities mOriginalCapabilities; 701 private SignerCapabilities mNewCapabilities; 702 private int mMinSdkVersion; 703 /** 704 * Constructs a new {@code Builder}. 705 * 706 * @param originalSignerConfig first signer in this lineage, parent of the next 707 * @param newSignerConfig new signer in the lineage; the new signing key that the APK will 708 * use 709 */ Builder( SignerConfig originalSignerConfig, SignerConfig newSignerConfig)710 public Builder( 711 SignerConfig originalSignerConfig, 712 SignerConfig newSignerConfig) { 713 if (originalSignerConfig == null || newSignerConfig == null) { 714 throw new NullPointerException("Can't pass null SignerConfigs when constructing a " 715 + "new SigningCertificateLineage"); 716 } 717 mOriginalSignerConfig = originalSignerConfig; 718 mNewSignerConfig = newSignerConfig; 719 } 720 721 /** 722 * Sets the minimum Android platform version (API Level) on which this lineage is expected 723 * to validate. It is possible that newer signers in the lineage may not be recognized on 724 * the given platform, but as long as an older signer is, the lineage can still be used to 725 * sign an APK for the given platform. 726 * 727 * <note> By default, this value is set to the value for the 728 * P release, since this structure was created for that release, and will also be set to 729 * that value if a smaller one is specified. </note> 730 */ setMinSdkVersion(int minSdkVersion)731 public Builder setMinSdkVersion(int minSdkVersion) { 732 mMinSdkVersion = minSdkVersion; 733 return this; 734 } 735 736 /** 737 * Sets capabilities to give {@code mOriginalSignerConfig}. These capabilities allow an 738 * older signing certificate to still be used in some situations on the platform even though 739 * the APK is now being signed by a newer signing certificate. 740 */ setOriginalCapabilities(SignerCapabilities signerCapabilities)741 public Builder setOriginalCapabilities(SignerCapabilities signerCapabilities) { 742 if (signerCapabilities == null) { 743 throw new NullPointerException("signerCapabilities == null"); 744 } 745 mOriginalCapabilities = signerCapabilities; 746 return this; 747 } 748 749 /** 750 * Sets capabilities to give {@code mNewSignerConfig}. These capabilities allow an 751 * older signing certificate to still be used in some situations on the platform even though 752 * the APK is now being signed by a newer signing certificate. By default, the new signer 753 * will have all capabilities, so when first switching to a new signing certificate, these 754 * capabilities have no effect, but they will act as the default level of trust when moving 755 * to a new signing certificate. 756 */ setNewCapabilities(SignerCapabilities signerCapabilities)757 public Builder setNewCapabilities(SignerCapabilities signerCapabilities) { 758 if (signerCapabilities == null) { 759 throw new NullPointerException("signerCapabilities == null"); 760 } 761 mNewCapabilities = signerCapabilities; 762 return this; 763 } 764 build()765 public SigningCertificateLineage build() 766 throws CertificateEncodingException, InvalidKeyException, NoSuchAlgorithmException, 767 SignatureException { 768 if (mMinSdkVersion < AndroidSdkVersion.P) { 769 mMinSdkVersion = AndroidSdkVersion.P; 770 } 771 772 if (mOriginalCapabilities == null) { 773 mOriginalCapabilities = new SignerCapabilities.Builder().build(); 774 } 775 776 if (mNewCapabilities == null) { 777 mNewCapabilities = new SignerCapabilities.Builder().build(); 778 } 779 780 return createSigningLineage( 781 mMinSdkVersion, mOriginalSignerConfig, mOriginalCapabilities, 782 mNewSignerConfig, mNewCapabilities); 783 } 784 } 785 } 786