1 /* 2 * Copyright (C) 2020 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.util.apk; 18 19 import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm; 20 import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm; 21 import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm; 22 23 import android.os.incremental.IncrementalManager; 24 import android.os.incremental.V4Signature; 25 import android.util.Pair; 26 27 import java.io.ByteArrayInputStream; 28 import java.io.File; 29 import java.io.IOException; 30 import java.security.InvalidAlgorithmParameterException; 31 import java.security.InvalidKeyException; 32 import java.security.KeyFactory; 33 import java.security.NoSuchAlgorithmException; 34 import java.security.PublicKey; 35 import java.security.Signature; 36 import java.security.SignatureException; 37 import java.security.cert.Certificate; 38 import java.security.cert.CertificateException; 39 import java.security.cert.CertificateFactory; 40 import java.security.cert.X509Certificate; 41 import java.security.spec.AlgorithmParameterSpec; 42 import java.security.spec.InvalidKeySpecException; 43 import java.security.spec.X509EncodedKeySpec; 44 import java.util.Arrays; 45 46 /** 47 * APK Signature Scheme v4 verifier. 48 * 49 * @hide for internal use only. 50 */ 51 public class ApkSignatureSchemeV4Verifier { 52 /** 53 * Extracts and verifies APK Signature Scheme v4 signatures of the provided APK and returns the 54 * certificates associated with each signer. 55 */ extractCertificates(String apkFile)56 public static VerifiedSigner extractCertificates(String apkFile) 57 throws SignatureNotFoundException, SecurityException { 58 final File apk = new File(apkFile); 59 final byte[] signatureBytes = IncrementalManager.unsafeGetFileSignature( 60 apk.getAbsolutePath()); 61 if (signatureBytes == null || signatureBytes.length == 0) { 62 throw new SignatureNotFoundException("Failed to obtain signature bytes from IncFS."); 63 } 64 65 final V4Signature signature; 66 final V4Signature.HashingInfo hashingInfo; 67 final V4Signature.SigningInfo signingInfo; 68 try { 69 signature = V4Signature.readFrom(signatureBytes); 70 71 if (!signature.isVersionSupported()) { 72 throw new SecurityException( 73 "v4 signature version " + signature.version + " is not supported"); 74 } 75 76 hashingInfo = V4Signature.HashingInfo.fromByteArray(signature.hashingInfo); 77 signingInfo = V4Signature.SigningInfo.fromByteArray(signature.signingInfo); 78 } catch (IOException e) { 79 throw new SignatureNotFoundException("Failed to read V4 signature.", e); 80 } 81 82 final byte[] signedData = V4Signature.getSigningData(apk.length(), hashingInfo, 83 signingInfo); 84 85 return verifySigner(signingInfo, signedData); 86 } 87 verifySigner(V4Signature.SigningInfo signingInfo, final byte[] signedData)88 private static VerifiedSigner verifySigner(V4Signature.SigningInfo signingInfo, 89 final byte[] signedData) throws SecurityException { 90 if (!isSupportedSignatureAlgorithm(signingInfo.signatureAlgorithmId)) { 91 throw new SecurityException("No supported signatures found"); 92 } 93 94 final int signatureAlgorithmId = signingInfo.signatureAlgorithmId; 95 final byte[] signatureBytes = signingInfo.signature; 96 final byte[] publicKeyBytes = signingInfo.publicKey; 97 final byte[] encodedCert = signingInfo.certificate; 98 99 String keyAlgorithm = getSignatureAlgorithmJcaKeyAlgorithm(signatureAlgorithmId); 100 Pair<String, ? extends AlgorithmParameterSpec> signatureAlgorithmParams = 101 getSignatureAlgorithmJcaSignatureAlgorithm(signatureAlgorithmId); 102 String jcaSignatureAlgorithm = signatureAlgorithmParams.first; 103 AlgorithmParameterSpec jcaSignatureAlgorithmParams = signatureAlgorithmParams.second; 104 boolean sigVerified; 105 try { 106 PublicKey publicKey = 107 KeyFactory.getInstance(keyAlgorithm) 108 .generatePublic(new X509EncodedKeySpec(publicKeyBytes)); 109 Signature sig = Signature.getInstance(jcaSignatureAlgorithm); 110 sig.initVerify(publicKey); 111 if (jcaSignatureAlgorithmParams != null) { 112 sig.setParameter(jcaSignatureAlgorithmParams); 113 } 114 sig.update(signedData); 115 sigVerified = sig.verify(signatureBytes); 116 } catch (NoSuchAlgorithmException | InvalidKeySpecException | InvalidKeyException 117 | InvalidAlgorithmParameterException | SignatureException e) { 118 throw new SecurityException( 119 "Failed to verify " + jcaSignatureAlgorithm + " signature", e); 120 } 121 if (!sigVerified) { 122 throw new SecurityException(jcaSignatureAlgorithm + " signature did not verify"); 123 } 124 125 // Signature over signedData has verified. 126 CertificateFactory certFactory; 127 try { 128 certFactory = CertificateFactory.getInstance("X.509"); 129 } catch (CertificateException e) { 130 throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e); 131 } 132 133 X509Certificate certificate; 134 try { 135 certificate = (X509Certificate) 136 certFactory.generateCertificate(new ByteArrayInputStream(encodedCert)); 137 } catch (CertificateException e) { 138 throw new SecurityException("Failed to decode certificate", e); 139 } 140 certificate = new VerbatimX509Certificate(certificate, encodedCert); 141 142 byte[] certificatePublicKeyBytes = certificate.getPublicKey().getEncoded(); 143 if (!Arrays.equals(publicKeyBytes, certificatePublicKeyBytes)) { 144 throw new SecurityException( 145 "Public key mismatch between certificate and signature record"); 146 } 147 148 return new VerifiedSigner(new Certificate[]{certificate}, signingInfo.apkDigest); 149 } 150 151 /** 152 * Verified APK Signature Scheme v4 signer, including V3 digest. 153 * 154 * @hide for internal use only. 155 */ 156 public static class VerifiedSigner { 157 public final Certificate[] certs; 158 public byte[] apkDigest; 159 VerifiedSigner(Certificate[] certs, byte[] apkDigest)160 public VerifiedSigner(Certificate[] certs, byte[] apkDigest) { 161 this.certs = certs; 162 this.apkDigest = apkDigest; 163 } 164 165 } 166 } 167