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 105 private SigningCertificateLineage(int minSdkVersion, List<SigningCertificateNode> list) { 106 mMinSdkVersion = minSdkVersion; 107 mSigningLineage = list; 108 } 109 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 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 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 */ 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 160 public static SigningCertificateLineage readFromApkFile(File apkFile) { 161 throw new UnsupportedOperationException("Not yet implemented"); 162 } 163 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 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 */ 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 */ 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 */ 273 public int size() { 274 return mSigningLineage.size(); 275 } 276 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 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 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 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 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 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 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 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 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 */ 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 */ 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 513 private SignerCapabilities(int flags) { 514 mFlags = flags; 515 } 516 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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