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.internal.apk.v3; 18 19 import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodeAsLengthPrefixedElement; 20 import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodeAsSequenceOfLengthPrefixedElements; 21 import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes; 22 import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodeCertificates; 23 import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodePublicKey; 24 25 import com.android.apksig.SigningCertificateLineage; 26 import com.android.apksig.internal.apk.ApkSigningBlockUtils; 27 import com.android.apksig.internal.apk.ApkSigningBlockUtils.SignerConfig; 28 import com.android.apksig.internal.apk.ContentDigestAlgorithm; 29 import com.android.apksig.internal.apk.SignatureAlgorithm; 30 import com.android.apksig.internal.util.Pair; 31 import com.android.apksig.util.DataSource; 32 33 import java.io.IOException; 34 import java.nio.ByteBuffer; 35 import java.nio.ByteOrder; 36 import java.security.InvalidKeyException; 37 import java.security.NoSuchAlgorithmException; 38 import java.security.PublicKey; 39 import java.security.SignatureException; 40 import java.security.cert.CertificateEncodingException; 41 import java.security.interfaces.ECKey; 42 import java.security.interfaces.RSAKey; 43 import java.util.ArrayList; 44 import java.util.Collections; 45 import java.util.List; 46 import java.util.Map; 47 48 /** 49 * APK Signature Scheme v3 signer. 50 * 51 * <p>APK Signature Scheme v3 builds upon APK Signature Scheme v3, and maintains all of the APK 52 * Signature Scheme v2 goals. 53 * 54 * @see <a href="https://source.android.com/security/apksigning/v2.html">APK Signature Scheme v2</a> 55 * 56 * <p> The main contribution of APK Signature Scheme v3 is the introduction of the 57 * {@link SigningCertificateLineage}, which enables an APK to change its signing 58 * certificate as long as it can prove the new siging certificate was signed by the old. 59 */ 60 public abstract class V3SchemeSigner { 61 62 private static final int APK_SIGNATURE_SCHEME_V3_BLOCK_ID = 0xf05368c0; 63 64 /** Hidden constructor to prevent instantiation. */ V3SchemeSigner()65 private V3SchemeSigner() {} 66 67 /** 68 * Gets the APK Signature Scheme v3 signature algorithms to be used for signing an APK using the 69 * provided key. 70 * 71 * @param minSdkVersion minimum API Level of the platform on which the APK may be installed (see 72 * AndroidManifest.xml minSdkVersion attribute). 73 * 74 * @throws InvalidKeyException if the provided key is not suitable for signing APKs using 75 * APK Signature Scheme v3 76 */ getSuggestedSignatureAlgorithms( PublicKey signingKey, int minSdkVersion, boolean apkSigningBlockPaddingSupported)77 public static List<SignatureAlgorithm> getSuggestedSignatureAlgorithms( 78 PublicKey signingKey, int minSdkVersion, boolean apkSigningBlockPaddingSupported) 79 throws InvalidKeyException { 80 String keyAlgorithm = signingKey.getAlgorithm(); 81 if ("RSA".equalsIgnoreCase(keyAlgorithm)) { 82 // Use RSASSA-PKCS1-v1_5 signature scheme instead of RSASSA-PSS to guarantee 83 // deterministic signatures which make life easier for OTA updates (fewer files 84 // changed when deterministic signature schemes are used). 85 86 // Pick a digest which is no weaker than the key. 87 int modulusLengthBits = ((RSAKey) signingKey).getModulus().bitLength(); 88 if (modulusLengthBits <= 3072) { 89 // 3072-bit RSA is roughly 128-bit strong, meaning SHA-256 is a good fit. 90 List<SignatureAlgorithm> algorithms = new ArrayList<>(); 91 algorithms.add(SignatureAlgorithm.RSA_PKCS1_V1_5_WITH_SHA256); 92 if (apkSigningBlockPaddingSupported) { 93 algorithms.add(SignatureAlgorithm.VERITY_RSA_PKCS1_V1_5_WITH_SHA256); 94 } 95 return algorithms; 96 } else { 97 // Keys longer than 3072 bit need to be paired with a stronger digest to avoid the 98 // digest being the weak link. SHA-512 is the next strongest supported digest. 99 return Collections.singletonList(SignatureAlgorithm.RSA_PKCS1_V1_5_WITH_SHA512); 100 } 101 } else if ("DSA".equalsIgnoreCase(keyAlgorithm)) { 102 // DSA is supported only with SHA-256. 103 List<SignatureAlgorithm> algorithms = new ArrayList<>(); 104 algorithms.add(SignatureAlgorithm.DSA_WITH_SHA256); 105 if (apkSigningBlockPaddingSupported) { 106 algorithms.add(SignatureAlgorithm.VERITY_DSA_WITH_SHA256); 107 } 108 return algorithms; 109 } else if ("EC".equalsIgnoreCase(keyAlgorithm)) { 110 // Pick a digest which is no weaker than the key. 111 int keySizeBits = ((ECKey) signingKey).getParams().getOrder().bitLength(); 112 if (keySizeBits <= 256) { 113 // 256-bit Elliptic Curve is roughly 128-bit strong, meaning SHA-256 is a good fit. 114 List<SignatureAlgorithm> algorithms = new ArrayList<>(); 115 algorithms.add(SignatureAlgorithm.ECDSA_WITH_SHA256); 116 if (apkSigningBlockPaddingSupported) { 117 algorithms.add(SignatureAlgorithm.VERITY_ECDSA_WITH_SHA256); 118 } 119 return algorithms; 120 } else { 121 // Keys longer than 256 bit need to be paired with a stronger digest to avoid the 122 // digest being the weak link. SHA-512 is the next strongest supported digest. 123 return Collections.singletonList(SignatureAlgorithm.ECDSA_WITH_SHA512); 124 } 125 } else { 126 throw new InvalidKeyException("Unsupported key algorithm: " + keyAlgorithm); 127 } 128 } 129 generateApkSignatureSchemeV3Block( DataSource beforeCentralDir, DataSource centralDir, DataSource eocd, List<SignerConfig> signerConfigs)130 public static Pair<byte[], Integer> generateApkSignatureSchemeV3Block( 131 DataSource beforeCentralDir, 132 DataSource centralDir, 133 DataSource eocd, 134 List<SignerConfig> signerConfigs) 135 throws IOException, InvalidKeyException, NoSuchAlgorithmException, 136 SignatureException { 137 Pair<List<SignerConfig>, 138 Map<ContentDigestAlgorithm, byte[]>> digestInfo = 139 ApkSigningBlockUtils.computeContentDigests(beforeCentralDir, centralDir, eocd, 140 signerConfigs); 141 return generateApkSignatureSchemeV3Block(digestInfo.getFirst(), digestInfo.getSecond()); 142 } 143 generateApkSignatureSchemeV3Block( List<SignerConfig> signerConfigs, Map<ContentDigestAlgorithm, byte[]> contentDigests)144 private static Pair<byte[], Integer> generateApkSignatureSchemeV3Block( 145 List<SignerConfig> signerConfigs, 146 Map<ContentDigestAlgorithm, byte[]> contentDigests) 147 throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { 148 // FORMAT: 149 // * length-prefixed sequence of length-prefixed signer blocks. 150 151 List<byte[]> signerBlocks = new ArrayList<>(signerConfigs.size()); 152 int signerNumber = 0; 153 for (SignerConfig signerConfig : signerConfigs) { 154 signerNumber++; 155 byte[] signerBlock; 156 try { 157 signerBlock = generateSignerBlock(signerConfig, contentDigests); 158 } catch (InvalidKeyException e) { 159 throw new InvalidKeyException("Signer #" + signerNumber + " failed", e); 160 } catch (SignatureException e) { 161 throw new SignatureException("Signer #" + signerNumber + " failed", e); 162 } 163 signerBlocks.add(signerBlock); 164 } 165 166 return Pair.of(encodeAsSequenceOfLengthPrefixedElements( 167 new byte[][] { 168 encodeAsSequenceOfLengthPrefixedElements(signerBlocks), 169 }), APK_SIGNATURE_SCHEME_V3_BLOCK_ID); 170 } 171 generateSignerBlock( SignerConfig signerConfig, Map<ContentDigestAlgorithm, byte[]> contentDigests)172 private static byte[] generateSignerBlock( 173 SignerConfig signerConfig, 174 Map<ContentDigestAlgorithm, byte[]> contentDigests) 175 throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { 176 if (signerConfig.certificates.isEmpty()) { 177 throw new SignatureException("No certificates configured for signer"); 178 } 179 PublicKey publicKey = signerConfig.certificates.get(0).getPublicKey(); 180 181 byte[] encodedPublicKey = encodePublicKey(publicKey); 182 183 V3SignatureSchemeBlock.SignedData signedData = new V3SignatureSchemeBlock.SignedData(); 184 try { 185 signedData.certificates = encodeCertificates(signerConfig.certificates); 186 } catch (CertificateEncodingException e) { 187 throw new SignatureException("Failed to encode certificates", e); 188 } 189 190 List<Pair<Integer, byte[]>> digests = 191 new ArrayList<>(signerConfig.signatureAlgorithms.size()); 192 for (SignatureAlgorithm signatureAlgorithm : signerConfig.signatureAlgorithms) { 193 ContentDigestAlgorithm contentDigestAlgorithm = 194 signatureAlgorithm.getContentDigestAlgorithm(); 195 byte[] contentDigest = contentDigests.get(contentDigestAlgorithm); 196 if (contentDigest == null) { 197 throw new RuntimeException( 198 contentDigestAlgorithm + " content digest for " + signatureAlgorithm 199 + " not computed"); 200 } 201 digests.add(Pair.of(signatureAlgorithm.getId(), contentDigest)); 202 } 203 signedData.digests = digests; 204 signedData.minSdkVersion = signerConfig.minSdkVersion; 205 signedData.maxSdkVersion = signerConfig.maxSdkVersion; 206 signedData.additionalAttributes = generateAdditionalAttributes(signerConfig); 207 208 V3SignatureSchemeBlock.Signer signer = new V3SignatureSchemeBlock.Signer(); 209 210 signer.signedData = encodeSignedData(signedData); 211 212 signer.minSdkVersion = signerConfig.minSdkVersion; 213 signer.maxSdkVersion = signerConfig.maxSdkVersion; 214 signer.publicKey = encodedPublicKey; 215 signer.signatures = 216 ApkSigningBlockUtils.generateSignaturesOverData(signerConfig, signer.signedData); 217 218 219 return encodeSigner(signer); 220 } 221 encodeSigner(V3SignatureSchemeBlock.Signer signer)222 private static byte[] encodeSigner(V3SignatureSchemeBlock.Signer signer) { 223 byte[] signedData = encodeAsLengthPrefixedElement(signer.signedData); 224 byte[] signatures = 225 encodeAsLengthPrefixedElement( 226 encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes( 227 signer.signatures)); 228 byte[] publicKey = encodeAsLengthPrefixedElement(signer.publicKey); 229 230 // FORMAT: 231 // * length-prefixed signed data 232 // * uint32: minSdkVersion 233 // * uint32: maxSdkVersion 234 // * length-prefixed sequence of length-prefixed signatures: 235 // * uint32: signature algorithm ID 236 // * length-prefixed bytes: signature of signed data 237 // * length-prefixed bytes: public key (X.509 SubjectPublicKeyInfo, ASN.1 DER encoded) 238 int payloadSize = 239 signedData.length 240 + 4 241 + 4 242 + signatures.length 243 + publicKey.length; 244 245 ByteBuffer result = ByteBuffer.allocate(payloadSize); 246 result.order(ByteOrder.LITTLE_ENDIAN); 247 result.put(signedData); 248 result.putInt(signer.minSdkVersion); 249 result.putInt(signer.maxSdkVersion); 250 result.put(signatures); 251 result.put(publicKey); 252 253 return result.array(); 254 } 255 encodeSignedData(V3SignatureSchemeBlock.SignedData signedData)256 private static byte[] encodeSignedData(V3SignatureSchemeBlock.SignedData signedData) { 257 byte[] digests = 258 encodeAsLengthPrefixedElement( 259 encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes( 260 signedData.digests)); 261 byte[] certs = 262 encodeAsLengthPrefixedElement( 263 encodeAsSequenceOfLengthPrefixedElements(signedData.certificates)); 264 byte[] attributes = encodeAsLengthPrefixedElement(signedData.additionalAttributes); 265 266 // FORMAT: 267 // * length-prefixed sequence of length-prefixed digests: 268 // * uint32: signature algorithm ID 269 // * length-prefixed bytes: digest of contents 270 // * length-prefixed sequence of certificates: 271 // * length-prefixed bytes: X.509 certificate (ASN.1 DER encoded). 272 // * uint-32: minSdkVersion 273 // * uint-32: maxSdkVersion 274 // * length-prefixed sequence of length-prefixed additional attributes: 275 // * uint32: ID 276 // * (length - 4) bytes: value 277 // * uint32: Proof-of-rotation ID: 0x3ba06f8c 278 // * length-prefixed roof-of-rotation structure 279 int payloadSize = 280 digests.length 281 + certs.length 282 + 4 283 + 4 284 + attributes.length; 285 286 ByteBuffer result = ByteBuffer.allocate(payloadSize); 287 result.order(ByteOrder.LITTLE_ENDIAN); 288 result.put(digests); 289 result.put(certs); 290 result.putInt(signedData.minSdkVersion); 291 result.putInt(signedData.maxSdkVersion); 292 result.put(attributes); 293 294 return result.array(); 295 } 296 297 public static final int PROOF_OF_ROTATION_ATTR_ID = 0x3ba06f8c; 298 generateAdditionalAttributes(SignerConfig signerConfig)299 private static byte[] generateAdditionalAttributes(SignerConfig signerConfig) { 300 if (signerConfig.mSigningCertificateLineage == null) { 301 return new byte[0]; 302 } 303 return signerConfig.mSigningCertificateLineage.generateV3SignerAttribute(); 304 } 305 306 private static final class V3SignatureSchemeBlock { 307 private static final class Signer { 308 public byte[] signedData; 309 public int minSdkVersion; 310 public int maxSdkVersion; 311 public List<Pair<Integer, byte[]>> signatures; 312 public byte[] publicKey; 313 } 314 315 private static final class SignedData { 316 public List<Pair<Integer, byte[]>> digests; 317 public List<byte[]> certificates; 318 public int minSdkVersion; 319 public int maxSdkVersion; 320 public byte[] additionalAttributes; 321 } 322 } 323 324 } 325