• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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