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 android.content.pm; 18 19 import android.annotation.FlaggedApi; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.content.pm.SigningDetails.SignatureSchemeVersion; 23 import android.os.Parcel; 24 import android.os.Parcelable; 25 import android.util.ArraySet; 26 27 import java.security.PublicKey; 28 import java.util.Collection; 29 30 /** 31 * Information pertaining to the signing certificates used to sign a package. 32 */ 33 public final class SigningInfo implements Parcelable { 34 35 @NonNull 36 private final SigningDetails mSigningDetails; 37 SigningInfo()38 public SigningInfo() { 39 mSigningDetails = SigningDetails.UNKNOWN; 40 } 41 42 /** 43 * Creates a new instance of information used to sign the APK. 44 * 45 * @param schemeVersion version of signing schema. 46 * @param apkContentsSigners signing certificates. 47 * @param publicKeys for the signing certificates. 48 * @param signingCertificateHistory All signing certificates the package has proven it is 49 * authorized to use. 50 * 51 * @see 52 * <a href="https://source.android.com/docs/security/features/apksigning#schemes">APK signing 53 * schemas</a> 54 */ 55 @FlaggedApi(Flags.FLAG_ARCHIVING) SigningInfo(@ignatureSchemeVersion int schemeVersion, @Nullable Collection<Signature> apkContentsSigners, @Nullable Collection<PublicKey> publicKeys, @Nullable Collection<Signature> signingCertificateHistory)56 public SigningInfo(@SignatureSchemeVersion int schemeVersion, 57 @Nullable Collection<Signature> apkContentsSigners, 58 @Nullable Collection<PublicKey> publicKeys, 59 @Nullable Collection<Signature> signingCertificateHistory) { 60 if (schemeVersion <= 0 || apkContentsSigners == null) { 61 mSigningDetails = SigningDetails.UNKNOWN; 62 return; 63 } 64 Signature[] signatures = apkContentsSigners != null && !apkContentsSigners.isEmpty() 65 ? apkContentsSigners.toArray(new Signature[apkContentsSigners.size()]) 66 : null; 67 Signature[] pastSignatures = 68 signingCertificateHistory != null && !signingCertificateHistory.isEmpty() 69 ? signingCertificateHistory.toArray(new Signature[signingCertificateHistory.size()]) 70 : null; 71 if (Signature.areExactArraysMatch(signatures, pastSignatures)) { 72 pastSignatures = null; 73 } 74 ArraySet<PublicKey> keys = 75 publicKeys != null && !publicKeys.isEmpty() ? new ArraySet<>(publicKeys) : null; 76 mSigningDetails = new SigningDetails(signatures, schemeVersion, keys, pastSignatures); 77 } 78 79 /** 80 * @hide only packagemanager should be populating this 81 */ SigningInfo(SigningDetails signingDetails)82 public SigningInfo(SigningDetails signingDetails) { 83 mSigningDetails = new SigningDetails(signingDetails); 84 } 85 SigningInfo(SigningInfo orig)86 public SigningInfo(SigningInfo orig) { 87 mSigningDetails = new SigningDetails(orig.mSigningDetails); 88 } 89 SigningInfo(Parcel source)90 private SigningInfo(Parcel source) { 91 mSigningDetails = SigningDetails.CREATOR.createFromParcel(source); 92 } 93 94 /** 95 * Although relatively uncommon, packages may be signed by more than one signer, in which case 96 * their identity is viewed as being the set of all signers, not just any one. 97 */ hasMultipleSigners()98 public boolean hasMultipleSigners() { 99 return mSigningDetails.getSignatures() != null 100 && mSigningDetails.getSignatures().length > 1; 101 } 102 103 /** 104 * APK Signature Scheme v3 enables packages to provide a proof-of-rotation record that the 105 * platform verifies, and uses, to allow the use of new signing certificates. This is only 106 * available to packages that are not signed by multiple signers. In the event of a change to a 107 * new signing certificate, the package's past signing certificates are presented as well. Any 108 * check of a package's signing certificate should also include a search through its entire 109 * signing history, since it could change to a new signing certificate at any time. 110 */ hasPastSigningCertificates()111 public boolean hasPastSigningCertificates() { 112 return mSigningDetails.getPastSigningCertificates() != null 113 && mSigningDetails.getPastSigningCertificates().length > 0; 114 } 115 116 /** 117 * Returns the signing certificates this package has proven it is authorized to use. This 118 * includes both the signing certificate associated with the signer of the package and the past 119 * signing certificates it included as its proof of signing certificate rotation. Signing 120 * certificates are returned in the order of rotation with the original signing certificate at 121 * index 0, and the current signing certificate at the last index. This method is the preferred 122 * replacement for the {@code GET_SIGNATURES} flag used with {@link 123 * PackageManager#getPackageInfo(String, int)}. When determining if a package is signed by a 124 * desired certificate, the returned array should be checked to determine if it is one of the 125 * entries. 126 * 127 * <note> 128 * This method returns null if the package is signed by multiple signing certificates, as 129 * opposed to being signed by one current signer and also providing the history of past 130 * signing certificates. {@link #hasMultipleSigners()} may be used to determine if this 131 * package is signed by multiple signers. Packages which are signed by multiple signers 132 * cannot change their signing certificates and their {@code Signature} array should be 133 * checked to make sure that every entry matches the looked-for signing certificates. 134 * </note> 135 */ getSigningCertificateHistory()136 public Signature[] getSigningCertificateHistory() { 137 if (hasMultipleSigners()) { 138 return null; 139 } else if (!hasPastSigningCertificates()) { 140 141 // this package is only signed by one signer with no history, return it 142 return mSigningDetails.getSignatures(); 143 } else { 144 145 // this package has provided proof of past signing certificates, include them 146 return mSigningDetails.getPastSigningCertificates(); 147 } 148 } 149 150 /** 151 * Returns the signing certificates used to sign the APK contents of this application. Not 152 * including any past signing certificates the package proved it is authorized to use. 153 * <note> 154 * This method should not be used unless {@link #hasMultipleSigners()} returns true, 155 * indicating that {@link #getSigningCertificateHistory()} cannot be used, otherwise {@link 156 * #getSigningCertificateHistory()} should be preferred. 157 * </note> 158 */ getApkContentsSigners()159 public Signature[] getApkContentsSigners() { 160 return mSigningDetails.getSignatures(); 161 } 162 163 /** 164 * Returns the version of signing schema used to sign the APK. 165 * 166 * @see 167 * <a href="https://source.android.com/docs/security/features/apksigning#schemes">APK signing 168 * schemas</a> 169 */ 170 @FlaggedApi(Flags.FLAG_ARCHIVING) getSchemeVersion()171 public @SignatureSchemeVersion int getSchemeVersion() { 172 return mSigningDetails.getSignatureSchemeVersion(); 173 } 174 175 /** 176 * Returns the public keys for the signing certificates. 177 */ 178 @FlaggedApi(Flags.FLAG_ARCHIVING) getPublicKeys()179 public @NonNull Collection<PublicKey> getPublicKeys() { 180 return mSigningDetails.getPublicKeys(); 181 } 182 183 @Override describeContents()184 public int describeContents() { 185 return 0; 186 } 187 188 @Override writeToParcel(Parcel dest, int parcelableFlags)189 public void writeToParcel(Parcel dest, int parcelableFlags) { 190 mSigningDetails.writeToParcel(dest, parcelableFlags); 191 } 192 193 /** 194 * @hide 195 */ 196 @NonNull getSigningDetails()197 public SigningDetails getSigningDetails() { 198 return mSigningDetails; 199 } 200 201 public static final @android.annotation.NonNull Parcelable.Creator<SigningInfo> CREATOR = 202 new Parcelable.Creator<SigningInfo>() { 203 @Override 204 public SigningInfo createFromParcel(Parcel source) { 205 return new SigningInfo(source); 206 } 207 208 @Override 209 public SigningInfo[] newArray(int size) { 210 return new SigningInfo[size]; 211 } 212 }; 213 } 214