StrictJarVerifier.java revision 9b59bc459b4cb5415909641bd1e981100bfafb2b
18a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak/* 28a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Licensed to the Apache Software Foundation (ASF) under one or more 38a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * contributor license agreements. See the NOTICE file distributed with 48a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * this work for additional information regarding copyright ownership. 58a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * The ASF licenses this file to You under the Apache License, Version 2.0 68a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * (the "License"); you may not use this file except in compliance with 78a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * the License. You may obtain a copy of the License at 88a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * 98a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * http://www.apache.org/licenses/LICENSE-2.0 108a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * 118a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Unless required by applicable law or agreed to in writing, software 128a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * distributed under the License is distributed on an "AS IS" BASIS, 138a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 148a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * See the License for the specific language governing permissions and 158a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * limitations under the License. 168a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 178a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 188a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakpackage android.util.jar; 198a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 208a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.io.ByteArrayInputStream; 218a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.io.IOException; 228a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.io.OutputStream; 238a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.nio.charset.StandardCharsets; 248a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.security.GeneralSecurityException; 258a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.security.MessageDigest; 268a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.security.NoSuchAlgorithmException; 278a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.security.cert.Certificate; 288a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.security.cert.X509Certificate; 298a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.util.ArrayList; 308a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.util.HashMap; 318a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.util.Hashtable; 328a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.util.Iterator; 338a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.util.Locale; 348a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.util.Map; 35e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubinimport java.util.Set; 36e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubinimport java.util.StringTokenizer; 378a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.util.jar.Attributes; 388a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.util.jar.JarFile; 39e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubinimport android.util.ArraySet; 40e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubinimport android.util.apk.ApkSignatureSchemeV2Verifier; 418a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport libcore.io.Base64; 428a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport sun.security.jca.Providers; 438a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport sun.security.pkcs.PKCS7; 448a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 458a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak/** 468a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Non-public class used by {@link JarFile} and {@link JarInputStream} to manage 478a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * the verification of signed JARs. {@code JarFile} and {@code JarInputStream} 488a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * objects are expected to have a {@code JarVerifier} instance member which 498a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * can be used to carry out the tasks associated with verifying a signed JAR. 508a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * These tasks would typically include: 518a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * <ul> 528a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * <li>verification of all signed signature files 538a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * <li>confirmation that all signed data was signed only by the party or parties 548a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * specified in the signature block data 558a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * <li>verification that the contents of all signature files (i.e. {@code .SF} 568a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * files) agree with the JAR entries information found in the JAR manifest. 578a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * </ul> 588a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 598a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakclass StrictJarVerifier { 608a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 618a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * List of accepted digest algorithms. This list is in order from most 628a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * preferred to least preferred. 638a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 648a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private static final String[] DIGEST_ALGORITHMS = new String[] { 658a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak "SHA-512", 668a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak "SHA-384", 678a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak "SHA-256", 688a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak "SHA1", 698a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak }; 708a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 718a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final String jarName; 728a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final StrictJarManifest manifest; 738a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final HashMap<String, byte[]> metaEntries; 748a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final int mainAttributesEnd; 759b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin private final boolean signatureSchemeRollbackProtectionsEnforced; 768a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 778a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final Hashtable<String, HashMap<String, Attributes>> signatures = 788a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak new Hashtable<String, HashMap<String, Attributes>>(5); 798a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 808a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final Hashtable<String, Certificate[]> certificates = 818a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak new Hashtable<String, Certificate[]>(5); 828a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 838a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final Hashtable<String, Certificate[][]> verifiedEntries = 848a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak new Hashtable<String, Certificate[][]>(); 858a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 868a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 878a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Stores and a hash and a message digest and verifies that massage digest 888a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * matches the hash. 898a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 908a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak static class VerifierEntry extends OutputStream { 918a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 928a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final String name; 938a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 948a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final MessageDigest digest; 958a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 968a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final byte[] hash; 978a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 988a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final Certificate[][] certChains; 998a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1008a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final Hashtable<String, Certificate[][]> verifiedEntries; 1018a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1028a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak VerifierEntry(String name, MessageDigest digest, byte[] hash, 1038a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Certificate[][] certChains, Hashtable<String, Certificate[][]> verifedEntries) { 1048a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak this.name = name; 1058a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak this.digest = digest; 1068a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak this.hash = hash; 1078a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak this.certChains = certChains; 1088a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak this.verifiedEntries = verifedEntries; 1098a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1108a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1118a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 1128a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Updates a digest with one byte. 1138a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 1148a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak @Override 1158a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak public void write(int value) { 1168a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak digest.update((byte) value); 1178a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1188a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1198a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 1208a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Updates a digest with byte array. 1218a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 1228a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak @Override 1238a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak public void write(byte[] buf, int off, int nbytes) { 1248a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak digest.update(buf, off, nbytes); 1258a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1268a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1278a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 1288a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Verifies that the digests stored in the manifest match the decrypted 1298a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * digests from the .SF file. This indicates the validity of the 1308a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * signing, not the integrity of the file, as its digest must be 1318a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * calculated and verified when its contents are read. 1328a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * 1338a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @throws SecurityException 1348a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * if the digest value stored in the manifest does <i>not</i> 1358a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * agree with the decrypted digest as recovered from the 1368a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * <code>.SF</code> file. 1378a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 1388a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak void verify() { 1398a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak byte[] d = digest.digest(); 1408a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (!MessageDigest.isEqual(d, Base64.decode(hash))) { 1418a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw invalidDigest(JarFile.MANIFEST_NAME, name, name); 1428a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1438a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak verifiedEntries.put(name, certChains); 1448a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1458a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1468a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1478a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private static SecurityException invalidDigest(String signatureFile, String name, 1488a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String jarName) { 1498a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw new SecurityException(signatureFile + " has invalid digest for " + name + 1508a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak " in " + jarName); 1518a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1528a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1538a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private static SecurityException failedVerification(String jarName, String signatureFile) { 1548a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw new SecurityException(jarName + " failed verification of " + signatureFile); 1558a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1568a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1578a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private static SecurityException failedVerification(String jarName, String signatureFile, 1588a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Throwable e) { 1598a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw new SecurityException(jarName + " failed verification of " + signatureFile, e); 1608a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1618a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1628a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1638a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 1648a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Constructs and returns a new instance of {@code JarVerifier}. 1658a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * 1668a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @param name 1678a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * the name of the JAR file being verified. 1689b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin * 1699b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin * @param signatureSchemeRollbackProtectionsEnforced {@code true} to enforce protections against 1709b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin * stripping newer signature schemes (e.g., APK Signature Scheme v2) from the file, or 1719b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin * {@code false} to ignore any such protections. 1728a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 1738a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak StrictJarVerifier(String name, StrictJarManifest manifest, 1749b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin HashMap<String, byte[]> metaEntries, boolean signatureSchemeRollbackProtectionsEnforced) { 1758a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak jarName = name; 1768a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak this.manifest = manifest; 1778a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak this.metaEntries = metaEntries; 1788a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak this.mainAttributesEnd = manifest.getMainAttributesEnd(); 1799b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin this.signatureSchemeRollbackProtectionsEnforced = 1809b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin signatureSchemeRollbackProtectionsEnforced; 1818a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1828a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1838a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 1848a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Invoked for each new JAR entry read operation from the input 1858a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * stream. This method constructs and returns a new {@link VerifierEntry} 1868a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * which contains the certificates used to sign the entry and its hash value 1878a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * as specified in the JAR MANIFEST format. 1888a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * 1898a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @param name 1908a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * the name of an entry in a JAR file which is <b>not</b> in the 1918a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * {@code META-INF} directory. 1928a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @return a new instance of {@link VerifierEntry} which can be used by 1938a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * callers as an {@link OutputStream}. 1948a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 1958a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak VerifierEntry initEntry(String name) { 1968a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // If no manifest is present by the time an entry is found, 1978a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // verification cannot occur. If no signature files have 1988a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // been found, do not verify. 1998a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (manifest == null || signatures.isEmpty()) { 2008a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return null; 2018a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2028a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 2038a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Attributes attributes = manifest.getAttributes(name); 2048a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // entry has no digest 2058a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (attributes == null) { 2068a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return null; 2078a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2088a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 2098a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak ArrayList<Certificate[]> certChains = new ArrayList<Certificate[]>(); 2108a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Iterator<Map.Entry<String, HashMap<String, Attributes>>> it = signatures.entrySet().iterator(); 2118a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak while (it.hasNext()) { 2128a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Map.Entry<String, HashMap<String, Attributes>> entry = it.next(); 2138a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak HashMap<String, Attributes> hm = entry.getValue(); 2148a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (hm.get(name) != null) { 2158a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // Found an entry for entry name in .SF file 2168a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String signatureFile = entry.getKey(); 2178a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Certificate[] certChain = certificates.get(signatureFile); 2188a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (certChain != null) { 2198a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak certChains.add(certChain); 2208a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2218a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2228a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2238a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 2248a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // entry is not signed 2258a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (certChains.isEmpty()) { 2268a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return null; 2278a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2288a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Certificate[][] certChainsArray = certChains.toArray(new Certificate[certChains.size()][]); 2298a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 2308a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak for (int i = 0; i < DIGEST_ALGORITHMS.length; i++) { 2318a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak final String algorithm = DIGEST_ALGORITHMS[i]; 2328a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak final String hash = attributes.getValue(algorithm + "-Digest"); 2338a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (hash == null) { 2348a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak continue; 2358a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2368a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak byte[] hashBytes = hash.getBytes(StandardCharsets.ISO_8859_1); 2378a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 2388a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak try { 2398a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return new VerifierEntry(name, MessageDigest.getInstance(algorithm), hashBytes, 2408a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak certChainsArray, verifiedEntries); 2418a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } catch (NoSuchAlgorithmException ignored) { 2428a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2438a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2448a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return null; 2458a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2468a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 2478a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 2488a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Add a new meta entry to the internal collection of data held on each JAR 2498a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * entry in the {@code META-INF} directory including the manifest 2508a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * file itself. Files associated with the signing of a JAR would also be 2518a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * added to this collection. 2528a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * 2538a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @param name 2548a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * the name of the file located in the {@code META-INF} 2558a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * directory. 2568a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @param buf 2578a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * the file bytes for the file called {@code name}. 2588a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @see #removeMetaEntries() 2598a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 2608a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak void addMetaEntry(String name, byte[] buf) { 2618a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak metaEntries.put(name.toUpperCase(Locale.US), buf); 2628a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2638a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 2648a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 2658a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * If the associated JAR file is signed, check on the validity of all of the 2668a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * known signatures. 2678a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * 2688a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @return {@code true} if the associated JAR is signed and an internal 2698a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * check verifies the validity of the signature(s). {@code false} if 2708a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * the associated JAR file has no entries at all in its {@code 2718a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * META-INF} directory. This situation is indicative of an invalid 2728a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * JAR file. 2738a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * <p> 2748a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Will also return {@code true} if the JAR file is <i>not</i> 2758a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * signed. 2768a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @throws SecurityException 2778a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * if the JAR file is signed and it is determined that a 2788a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * signature block file contains an invalid signature for the 2798a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * corresponding signature file. 2808a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 2818a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak synchronized boolean readCertificates() { 2828a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (metaEntries.isEmpty()) { 2838a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return false; 2848a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2858a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 2868a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Iterator<String> it = metaEntries.keySet().iterator(); 2878a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak while (it.hasNext()) { 2888a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String key = it.next(); 2898a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (key.endsWith(".DSA") || key.endsWith(".RSA") || key.endsWith(".EC")) { 2908a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak verifyCertificate(key); 2918a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak it.remove(); 2928a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2938a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2948a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return true; 2958a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2968a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 2978a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 2988a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Verifies that the signature computed from {@code sfBytes} matches 2998a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * that specified in {@code blockBytes} (which is a PKCS7 block). Returns 3008a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * certificates listed in the PKCS7 block. Throws a {@code GeneralSecurityException} 3018a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * if something goes wrong during verification. 3028a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 3038a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak static Certificate[] verifyBytes(byte[] blockBytes, byte[] sfBytes) 3048a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throws GeneralSecurityException { 3058a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 3068a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Object obj = null; 3078a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak try { 3088a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 3098a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak obj = Providers.startJarVerification(); 3108a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak PKCS7 block = new PKCS7(blockBytes); 3118a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (block.verify(sfBytes) == null) { 3128a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw new GeneralSecurityException("Failed to verify signature"); 3138a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3148a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak X509Certificate[] blockCerts = block.getCertificates(); 3158a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Certificate[] signerCertChain = null; 3168a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (blockCerts != null) { 3178a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak signerCertChain = new Certificate[blockCerts.length]; 3188a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak for (int i = 0; i < blockCerts.length; ++i) { 3198a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak signerCertChain[i] = blockCerts[i]; 3208a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3218a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3228a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return signerCertChain; 3238a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } catch (IOException e) { 3248a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw new GeneralSecurityException("IO exception verifying jar cert", e); 3258a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } finally { 3268a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Providers.stopJarVerification(obj); 3278a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3288a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3298a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 3308a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 3318a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @param certFile 3328a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 3338a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private void verifyCertificate(String certFile) { 3348a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // Found Digital Sig, .SF should already have been read 3358a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String signatureFile = certFile.substring(0, certFile.lastIndexOf('.')) + ".SF"; 3368a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak byte[] sfBytes = metaEntries.get(signatureFile); 3378a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (sfBytes == null) { 3388a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return; 3398a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3408a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 3418a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak byte[] manifestBytes = metaEntries.get(JarFile.MANIFEST_NAME); 3428a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // Manifest entry is required for any verifications. 3438a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (manifestBytes == null) { 3448a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return; 3458a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3468a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 3478a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak byte[] sBlockBytes = metaEntries.get(certFile); 3488a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak try { 3498a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Certificate[] signerCertChain = verifyBytes(sBlockBytes, sfBytes); 3508a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (signerCertChain != null) { 3518a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak certificates.put(signatureFile, signerCertChain); 3528a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3538a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } catch (GeneralSecurityException e) { 3548a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw failedVerification(jarName, signatureFile, e); 3558a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3568a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 3578a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // Verify manifest hash in .sf file 3588a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Attributes attributes = new Attributes(); 3598a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak HashMap<String, Attributes> entries = new HashMap<String, Attributes>(); 3608a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak try { 3618a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak StrictJarManifestReader im = new StrictJarManifestReader(sfBytes, attributes); 3628a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak im.readEntries(entries, null); 3638a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } catch (IOException e) { 3648a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return; 3658a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3668a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 3679b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin // If requested, check whether APK Signature Scheme v2 signature was stripped. 3689b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin if (signatureSchemeRollbackProtectionsEnforced) { 3699b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin String apkSignatureSchemeIdList = 3709b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin attributes.getValue( 3719b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_NAME); 3729b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin if (apkSignatureSchemeIdList != null) { 3739b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin // This field contains a comma-separated list of APK signature scheme IDs which 3749b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin // were used to sign this APK. If an ID is known to us, it means signatures of that 3759b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin // scheme were stripped from the APK because otherwise we wouldn't have fallen back 3769b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin // to verifying the APK using the JAR signature scheme. 3779b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin boolean v2SignatureGenerated = false; 3789b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin StringTokenizer tokenizer = new StringTokenizer(apkSignatureSchemeIdList, ","); 3799b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin while (tokenizer.hasMoreTokens()) { 3809b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin String idText = tokenizer.nextToken().trim(); 3819b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin if (idText.isEmpty()) { 3829b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin continue; 3839b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin } 3849b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin int id; 3859b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin try { 3869b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin id = Integer.parseInt(idText); 3879b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin } catch (Exception ignored) { 3889b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin continue; 3899b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin } 3909b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin if (id == ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID) { 3919b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin // This APK was supposed to be signed with APK Signature Scheme v2 but no 3929b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin // such signature was found. 3939b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin v2SignatureGenerated = true; 3949b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin break; 3959b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin } 396e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubin } 397e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubin 3989b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin if (v2SignatureGenerated) { 3999b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin throw new SecurityException(signatureFile + " indicates " + jarName 4009b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin + " is signed using APK Signature Scheme v2, but no such signature was" 4019b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin + " found. Signature stripped?"); 4029b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin } 403e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubin } 404e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubin } 405e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubin 4068a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // Do we actually have any signatures to look at? 4078a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (attributes.get(Attributes.Name.SIGNATURE_VERSION) == null) { 4088a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return; 4098a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4108a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 4118a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak boolean createdBySigntool = false; 4128a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String createdBy = attributes.getValue("Created-By"); 4138a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (createdBy != null) { 4148a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak createdBySigntool = createdBy.indexOf("signtool") != -1; 4158a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4168a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 4178a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // Use .SF to verify the mainAttributes of the manifest 4188a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // If there is no -Digest-Manifest-Main-Attributes entry in .SF 4198a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // file, such as those created before java 1.5, then we ignore 4208a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // such verification. 4218a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (mainAttributesEnd > 0 && !createdBySigntool) { 4228a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String digestAttribute = "-Digest-Manifest-Main-Attributes"; 4238a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (!verify(attributes, digestAttribute, manifestBytes, 0, mainAttributesEnd, false, true)) { 4248a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw failedVerification(jarName, signatureFile); 4258a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4268a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4278a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 4288a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // Use .SF to verify the whole manifest. 4298a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String digestAttribute = createdBySigntool ? "-Digest" : "-Digest-Manifest"; 4308a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (!verify(attributes, digestAttribute, manifestBytes, 0, manifestBytes.length, false, false)) { 4318a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Iterator<Map.Entry<String, Attributes>> it = entries.entrySet().iterator(); 4328a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak while (it.hasNext()) { 4338a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Map.Entry<String, Attributes> entry = it.next(); 4348a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak StrictJarManifest.Chunk chunk = manifest.getChunk(entry.getKey()); 4358a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (chunk == null) { 4368a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return; 4378a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4388a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (!verify(entry.getValue(), "-Digest", manifestBytes, 4398a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak chunk.start, chunk.end, createdBySigntool, false)) { 4408a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw invalidDigest(signatureFile, entry.getKey(), jarName); 4418a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4428a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4438a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4448a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak metaEntries.put(signatureFile, null); 4458a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak signatures.put(signatureFile, entries); 4468a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4478a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 4488a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 4498a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Returns a <code>boolean</code> indication of whether or not the 4508a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * associated jar file is signed. 4518a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * 4528a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @return {@code true} if the JAR is signed, {@code false} 4538a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * otherwise. 4548a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 4558a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak boolean isSignedJar() { 4568a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return certificates.size() > 0; 4578a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4588a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 4598a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private boolean verify(Attributes attributes, String entry, byte[] data, 4608a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak int start, int end, boolean ignoreSecondEndline, boolean ignorable) { 4618a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak for (int i = 0; i < DIGEST_ALGORITHMS.length; i++) { 4628a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String algorithm = DIGEST_ALGORITHMS[i]; 4638a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String hash = attributes.getValue(algorithm + entry); 4648a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (hash == null) { 4658a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak continue; 4668a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4678a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 4688a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak MessageDigest md; 4698a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak try { 4708a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak md = MessageDigest.getInstance(algorithm); 4718a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } catch (NoSuchAlgorithmException e) { 4728a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak continue; 4738a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4748a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (ignoreSecondEndline && data[end - 1] == '\n' && data[end - 2] == '\n') { 4758a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak md.update(data, start, end - 1 - start); 4768a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } else { 4778a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak md.update(data, start, end - start); 4788a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4798a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak byte[] b = md.digest(); 4808a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak byte[] hashBytes = hash.getBytes(StandardCharsets.ISO_8859_1); 4818a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return MessageDigest.isEqual(b, Base64.decode(hashBytes)); 4828a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4838a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return ignorable; 4848a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4858a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 4868a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 4878a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Returns all of the {@link java.security.cert.Certificate} chains that 4888a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * were used to verify the signature on the JAR entry called 4898a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * {@code name}. Callers must not modify the returned arrays. 4908a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * 4918a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @param name 4928a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * the name of a JAR entry. 4938a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @return an array of {@link java.security.cert.Certificate} chains. 4948a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 4958a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Certificate[][] getCertificateChains(String name) { 4968a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return verifiedEntries.get(name); 4978a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4988a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 4998a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 5008a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Remove all entries from the internal collection of data held about each 5018a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * JAR entry in the {@code META-INF} directory. 5028a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 5038a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak void removeMetaEntries() { 5048a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak metaEntries.clear(); 5058a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 5068a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak} 507