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 209f00d71787774bf79d4b398c28630f670765b791Tobias Thiererimport android.util.apk.ApkSignatureSchemeV2Verifier; 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; 3329045203f3f694cddea7b3f115ea82f648ba0cbaAlex Klyubinimport java.util.List; 348a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.util.Locale; 358a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.util.Map; 36e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubinimport java.util.StringTokenizer; 378a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.util.jar.Attributes; 388a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.util.jar.JarFile; 398a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport sun.security.jca.Providers; 408a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport sun.security.pkcs.PKCS7; 4129045203f3f694cddea7b3f115ea82f648ba0cbaAlex Klyubinimport sun.security.pkcs.SignerInfo; 428a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 438a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak/** 448a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Non-public class used by {@link JarFile} and {@link JarInputStream} to manage 458a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * the verification of signed JARs. {@code JarFile} and {@code JarInputStream} 468a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * objects are expected to have a {@code JarVerifier} instance member which 478a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * can be used to carry out the tasks associated with verifying a signed JAR. 488a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * These tasks would typically include: 498a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * <ul> 508a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * <li>verification of all signed signature files 518a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * <li>confirmation that all signed data was signed only by the party or parties 528a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * specified in the signature block data 538a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * <li>verification that the contents of all signature files (i.e. {@code .SF} 548a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * files) agree with the JAR entries information found in the JAR manifest. 558a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * </ul> 568a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 578a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakclass StrictJarVerifier { 588a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 598a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * List of accepted digest algorithms. This list is in order from most 608a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * preferred to least preferred. 618a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 628a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private static final String[] DIGEST_ALGORITHMS = new String[] { 638a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak "SHA-512", 648a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak "SHA-384", 658a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak "SHA-256", 668a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak "SHA1", 678a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak }; 688a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 698a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final String jarName; 708a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final StrictJarManifest manifest; 718a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final HashMap<String, byte[]> metaEntries; 728a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final int mainAttributesEnd; 739b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin private final boolean signatureSchemeRollbackProtectionsEnforced; 748a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 758a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final Hashtable<String, HashMap<String, Attributes>> signatures = 768a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak new Hashtable<String, HashMap<String, Attributes>>(5); 778a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 788a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final Hashtable<String, Certificate[]> certificates = 798a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak new Hashtable<String, Certificate[]>(5); 808a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 818a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final Hashtable<String, Certificate[][]> verifiedEntries = 828a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak new Hashtable<String, Certificate[][]>(); 838a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 848a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 858a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Stores and a hash and a message digest and verifies that massage digest 868a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * matches the hash. 878a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 888a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak static class VerifierEntry extends OutputStream { 898a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 908a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final String name; 918a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 928a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final MessageDigest digest; 938a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 948a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final byte[] hash; 958a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 968a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final Certificate[][] certChains; 978a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 988a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final Hashtable<String, Certificate[][]> verifiedEntries; 998a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1008a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak VerifierEntry(String name, MessageDigest digest, byte[] hash, 1018a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Certificate[][] certChains, Hashtable<String, Certificate[][]> verifedEntries) { 1028a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak this.name = name; 1038a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak this.digest = digest; 1048a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak this.hash = hash; 1058a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak this.certChains = certChains; 1068a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak this.verifiedEntries = verifedEntries; 1078a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1088a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1098a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 1108a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Updates a digest with one byte. 1118a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 1128a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak @Override 1138a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak public void write(int value) { 1148a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak digest.update((byte) value); 1158a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1168a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1178a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 1188a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Updates a digest with byte array. 1198a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 1208a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak @Override 1218a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak public void write(byte[] buf, int off, int nbytes) { 1228a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak digest.update(buf, off, nbytes); 1238a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1248a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1258a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 1268a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Verifies that the digests stored in the manifest match the decrypted 1278a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * digests from the .SF file. This indicates the validity of the 1288a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * signing, not the integrity of the file, as its digest must be 1298a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * calculated and verified when its contents are read. 1308a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * 1318a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @throws SecurityException 1328a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * if the digest value stored in the manifest does <i>not</i> 1338a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * agree with the decrypted digest as recovered from the 1348a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * <code>.SF</code> file. 1358a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 1368a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak void verify() { 1378a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak byte[] d = digest.digest(); 1389f00d71787774bf79d4b398c28630f670765b791Tobias Thierer if (!verifyMessageDigest(d, hash)) { 1398a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw invalidDigest(JarFile.MANIFEST_NAME, name, name); 1408a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1418a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak verifiedEntries.put(name, certChains); 1428a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1438a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1448a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1458a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private static SecurityException invalidDigest(String signatureFile, String name, 1468a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String jarName) { 1478a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw new SecurityException(signatureFile + " has invalid digest for " + name + 1488a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak " in " + jarName); 1498a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1508a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1518a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private static SecurityException failedVerification(String jarName, String signatureFile) { 1528a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw new SecurityException(jarName + " failed verification of " + signatureFile); 1538a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1548a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1558a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private static SecurityException failedVerification(String jarName, String signatureFile, 1568a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Throwable e) { 1578a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw new SecurityException(jarName + " failed verification of " + signatureFile, e); 1588a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1598a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1608a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1618a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 1628a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Constructs and returns a new instance of {@code JarVerifier}. 1638a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * 1648a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @param name 1658a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * the name of the JAR file being verified. 1669b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin * 1679b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin * @param signatureSchemeRollbackProtectionsEnforced {@code true} to enforce protections against 1689b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin * stripping newer signature schemes (e.g., APK Signature Scheme v2) from the file, or 1699b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin * {@code false} to ignore any such protections. 1708a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 1718a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak StrictJarVerifier(String name, StrictJarManifest manifest, 1729b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin HashMap<String, byte[]> metaEntries, boolean signatureSchemeRollbackProtectionsEnforced) { 1738a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak jarName = name; 1748a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak this.manifest = manifest; 1758a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak this.metaEntries = metaEntries; 1768a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak this.mainAttributesEnd = manifest.getMainAttributesEnd(); 1779b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin this.signatureSchemeRollbackProtectionsEnforced = 1789b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin signatureSchemeRollbackProtectionsEnforced; 1798a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1808a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1818a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 1828a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Invoked for each new JAR entry read operation from the input 1838a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * stream. This method constructs and returns a new {@link VerifierEntry} 1848a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * which contains the certificates used to sign the entry and its hash value 1858a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * as specified in the JAR MANIFEST format. 1868a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * 1878a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @param name 1888a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * the name of an entry in a JAR file which is <b>not</b> in the 1898a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * {@code META-INF} directory. 1908a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @return a new instance of {@link VerifierEntry} which can be used by 1918a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * callers as an {@link OutputStream}. 1928a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 1938a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak VerifierEntry initEntry(String name) { 1948a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // If no manifest is present by the time an entry is found, 1958a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // verification cannot occur. If no signature files have 1968a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // been found, do not verify. 1978a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (manifest == null || signatures.isEmpty()) { 1988a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return null; 1998a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2008a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 2018a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Attributes attributes = manifest.getAttributes(name); 2028a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // entry has no digest 2038a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (attributes == null) { 2048a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return null; 2058a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2068a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 2078a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak ArrayList<Certificate[]> certChains = new ArrayList<Certificate[]>(); 2088a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Iterator<Map.Entry<String, HashMap<String, Attributes>>> it = signatures.entrySet().iterator(); 2098a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak while (it.hasNext()) { 2108a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Map.Entry<String, HashMap<String, Attributes>> entry = it.next(); 2118a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak HashMap<String, Attributes> hm = entry.getValue(); 2128a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (hm.get(name) != null) { 2138a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // Found an entry for entry name in .SF file 2148a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String signatureFile = entry.getKey(); 2158a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Certificate[] certChain = certificates.get(signatureFile); 2168a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (certChain != null) { 2178a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak certChains.add(certChain); 2188a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2198a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2208a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2218a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 2228a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // entry is not signed 2238a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (certChains.isEmpty()) { 2248a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return null; 2258a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2268a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Certificate[][] certChainsArray = certChains.toArray(new Certificate[certChains.size()][]); 2278a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 2288a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak for (int i = 0; i < DIGEST_ALGORITHMS.length; i++) { 2298a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak final String algorithm = DIGEST_ALGORITHMS[i]; 2308a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak final String hash = attributes.getValue(algorithm + "-Digest"); 2318a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (hash == null) { 2328a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak continue; 2338a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2348a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak byte[] hashBytes = hash.getBytes(StandardCharsets.ISO_8859_1); 2358a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 2368a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak try { 2378a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return new VerifierEntry(name, MessageDigest.getInstance(algorithm), hashBytes, 2388a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak certChainsArray, verifiedEntries); 2398a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } catch (NoSuchAlgorithmException ignored) { 2408a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2418a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2428a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return null; 2438a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2448a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 2458a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 2468a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Add a new meta entry to the internal collection of data held on each JAR 2478a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * entry in the {@code META-INF} directory including the manifest 2488a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * file itself. Files associated with the signing of a JAR would also be 2498a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * added to this collection. 2508a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * 2518a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @param name 2528a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * the name of the file located in the {@code META-INF} 2538a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * directory. 2548a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @param buf 2558a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * the file bytes for the file called {@code name}. 2568a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @see #removeMetaEntries() 2578a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 2588a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak void addMetaEntry(String name, byte[] buf) { 2598a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak metaEntries.put(name.toUpperCase(Locale.US), buf); 2608a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2618a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 2628a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 2638a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * If the associated JAR file is signed, check on the validity of all of the 2648a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * known signatures. 2658a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * 2668a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @return {@code true} if the associated JAR is signed and an internal 2678a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * check verifies the validity of the signature(s). {@code false} if 2688a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * the associated JAR file has no entries at all in its {@code 2698a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * META-INF} directory. This situation is indicative of an invalid 2708a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * JAR file. 2718a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * <p> 2728a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Will also return {@code true} if the JAR file is <i>not</i> 2738a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * signed. 2748a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @throws SecurityException 2758a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * if the JAR file is signed and it is determined that a 2768a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * signature block file contains an invalid signature for the 2778a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * corresponding signature file. 2788a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 2798a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak synchronized boolean readCertificates() { 2808a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (metaEntries.isEmpty()) { 2818a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return false; 2828a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2838a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 2848a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Iterator<String> it = metaEntries.keySet().iterator(); 2858a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak while (it.hasNext()) { 2868a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String key = it.next(); 2878a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (key.endsWith(".DSA") || key.endsWith(".RSA") || key.endsWith(".EC")) { 2888a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak verifyCertificate(key); 2898a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak it.remove(); 2908a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2918a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2928a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return true; 2938a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2948a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 2958a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 2968a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Verifies that the signature computed from {@code sfBytes} matches 2978a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * that specified in {@code blockBytes} (which is a PKCS7 block). Returns 2988a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * certificates listed in the PKCS7 block. Throws a {@code GeneralSecurityException} 2998a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * if something goes wrong during verification. 3008a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 3018a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak static Certificate[] verifyBytes(byte[] blockBytes, byte[] sfBytes) 3028a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throws GeneralSecurityException { 3038a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 3048a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Object obj = null; 3058a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak try { 3068a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 3078a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak obj = Providers.startJarVerification(); 3088a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak PKCS7 block = new PKCS7(blockBytes); 30929045203f3f694cddea7b3f115ea82f648ba0cbaAlex Klyubin SignerInfo[] verifiedSignerInfos = block.verify(sfBytes); 31029045203f3f694cddea7b3f115ea82f648ba0cbaAlex Klyubin if ((verifiedSignerInfos == null) || (verifiedSignerInfos.length == 0)) { 31129045203f3f694cddea7b3f115ea82f648ba0cbaAlex Klyubin throw new GeneralSecurityException( 31229045203f3f694cddea7b3f115ea82f648ba0cbaAlex Klyubin "Failed to verify signature: no verified SignerInfos"); 3138a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 31429045203f3f694cddea7b3f115ea82f648ba0cbaAlex Klyubin // Ignore any SignerInfo other than the first one, to be compatible with older Android 31529045203f3f694cddea7b3f115ea82f648ba0cbaAlex Klyubin // platforms which have been doing this for years. See 31629045203f3f694cddea7b3f115ea82f648ba0cbaAlex Klyubin // libcore/luni/src/main/java/org/apache/harmony/security/utils/JarUtils.java 31729045203f3f694cddea7b3f115ea82f648ba0cbaAlex Klyubin // verifySignature method of older platforms. 31829045203f3f694cddea7b3f115ea82f648ba0cbaAlex Klyubin SignerInfo verifiedSignerInfo = verifiedSignerInfos[0]; 31929045203f3f694cddea7b3f115ea82f648ba0cbaAlex Klyubin List<X509Certificate> verifiedSignerCertChain = 32029045203f3f694cddea7b3f115ea82f648ba0cbaAlex Klyubin verifiedSignerInfo.getCertificateChain(block); 32129045203f3f694cddea7b3f115ea82f648ba0cbaAlex Klyubin if (verifiedSignerCertChain == null) { 32229045203f3f694cddea7b3f115ea82f648ba0cbaAlex Klyubin // Should never happen 32329045203f3f694cddea7b3f115ea82f648ba0cbaAlex Klyubin throw new GeneralSecurityException( 32429045203f3f694cddea7b3f115ea82f648ba0cbaAlex Klyubin "Failed to find verified SignerInfo certificate chain"); 32529045203f3f694cddea7b3f115ea82f648ba0cbaAlex Klyubin } else if (verifiedSignerCertChain.isEmpty()) { 32629045203f3f694cddea7b3f115ea82f648ba0cbaAlex Klyubin // Should never happen 32729045203f3f694cddea7b3f115ea82f648ba0cbaAlex Klyubin throw new GeneralSecurityException( 32829045203f3f694cddea7b3f115ea82f648ba0cbaAlex Klyubin "Verified SignerInfo certificate chain is emtpy"); 3298a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 33029045203f3f694cddea7b3f115ea82f648ba0cbaAlex Klyubin return verifiedSignerCertChain.toArray( 33129045203f3f694cddea7b3f115ea82f648ba0cbaAlex Klyubin new X509Certificate[verifiedSignerCertChain.size()]); 3328a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } catch (IOException e) { 3338a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw new GeneralSecurityException("IO exception verifying jar cert", e); 3348a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } finally { 3358a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Providers.stopJarVerification(obj); 3368a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3378a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3388a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 3398a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 3408a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @param certFile 3418a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 3428a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private void verifyCertificate(String certFile) { 3438a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // Found Digital Sig, .SF should already have been read 3448a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String signatureFile = certFile.substring(0, certFile.lastIndexOf('.')) + ".SF"; 3458a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak byte[] sfBytes = metaEntries.get(signatureFile); 3468a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (sfBytes == null) { 3478a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return; 3488a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3498a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 3508a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak byte[] manifestBytes = metaEntries.get(JarFile.MANIFEST_NAME); 3518a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // Manifest entry is required for any verifications. 3528a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (manifestBytes == null) { 3538a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return; 3548a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3558a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 3568a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak byte[] sBlockBytes = metaEntries.get(certFile); 3578a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak try { 3588a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Certificate[] signerCertChain = verifyBytes(sBlockBytes, sfBytes); 3598a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (signerCertChain != null) { 3608a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak certificates.put(signatureFile, signerCertChain); 3618a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3628a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } catch (GeneralSecurityException e) { 3638a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw failedVerification(jarName, signatureFile, e); 3648a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3658a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 3668a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // Verify manifest hash in .sf file 3678a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Attributes attributes = new Attributes(); 3688a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak HashMap<String, Attributes> entries = new HashMap<String, Attributes>(); 3698a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak try { 3708a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak StrictJarManifestReader im = new StrictJarManifestReader(sfBytes, attributes); 3718a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak im.readEntries(entries, null); 3728a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } catch (IOException e) { 3738a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return; 3748a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3758a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 3769b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin // If requested, check whether APK Signature Scheme v2 signature was stripped. 3779b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin if (signatureSchemeRollbackProtectionsEnforced) { 3789b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin String apkSignatureSchemeIdList = 3799b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin attributes.getValue( 3809b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_NAME); 3819b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin if (apkSignatureSchemeIdList != null) { 3829b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin // This field contains a comma-separated list of APK signature scheme IDs which 3839b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin // were used to sign this APK. If an ID is known to us, it means signatures of that 3849b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin // scheme were stripped from the APK because otherwise we wouldn't have fallen back 3859b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin // to verifying the APK using the JAR signature scheme. 3869b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin boolean v2SignatureGenerated = false; 3879b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin StringTokenizer tokenizer = new StringTokenizer(apkSignatureSchemeIdList, ","); 3889b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin while (tokenizer.hasMoreTokens()) { 3899b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin String idText = tokenizer.nextToken().trim(); 3909b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin if (idText.isEmpty()) { 3919b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin continue; 3929b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin } 3939b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin int id; 3949b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin try { 3959b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin id = Integer.parseInt(idText); 3969b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin } catch (Exception ignored) { 3979b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin continue; 3989b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin } 3999b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin if (id == ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID) { 4009b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin // This APK was supposed to be signed with APK Signature Scheme v2 but no 4019b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin // such signature was found. 4029b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin v2SignatureGenerated = true; 4039b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin break; 4049b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin } 405e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubin } 406e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubin 4079b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin if (v2SignatureGenerated) { 4089b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin throw new SecurityException(signatureFile + " indicates " + jarName 4099b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin + " is signed using APK Signature Scheme v2, but no such signature was" 4109b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin + " found. Signature stripped?"); 4119b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin } 412e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubin } 413e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubin } 414e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubin 4158a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // Do we actually have any signatures to look at? 4168a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (attributes.get(Attributes.Name.SIGNATURE_VERSION) == null) { 4178a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return; 4188a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4198a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 4208a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak boolean createdBySigntool = false; 4218a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String createdBy = attributes.getValue("Created-By"); 4228a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (createdBy != null) { 4238a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak createdBySigntool = createdBy.indexOf("signtool") != -1; 4248a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4258a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 4268a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // Use .SF to verify the mainAttributes of the manifest 4278a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // If there is no -Digest-Manifest-Main-Attributes entry in .SF 4288a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // file, such as those created before java 1.5, then we ignore 4298a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // such verification. 4308a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (mainAttributesEnd > 0 && !createdBySigntool) { 4318a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String digestAttribute = "-Digest-Manifest-Main-Attributes"; 4328a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (!verify(attributes, digestAttribute, manifestBytes, 0, mainAttributesEnd, false, true)) { 4338a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw failedVerification(jarName, signatureFile); 4348a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4358a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4368a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 4378a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // Use .SF to verify the whole manifest. 4388a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String digestAttribute = createdBySigntool ? "-Digest" : "-Digest-Manifest"; 4398a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (!verify(attributes, digestAttribute, manifestBytes, 0, manifestBytes.length, false, false)) { 4408a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Iterator<Map.Entry<String, Attributes>> it = entries.entrySet().iterator(); 4418a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak while (it.hasNext()) { 4428a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Map.Entry<String, Attributes> entry = it.next(); 4438a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak StrictJarManifest.Chunk chunk = manifest.getChunk(entry.getKey()); 4448a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (chunk == null) { 4458a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return; 4468a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4478a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (!verify(entry.getValue(), "-Digest", manifestBytes, 4488a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak chunk.start, chunk.end, createdBySigntool, false)) { 4498a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw invalidDigest(signatureFile, entry.getKey(), jarName); 4508a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4518a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4528a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4538a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak metaEntries.put(signatureFile, null); 4548a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak signatures.put(signatureFile, entries); 4558a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4568a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 4578a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 4588a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Returns a <code>boolean</code> indication of whether or not the 4598a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * associated jar file is signed. 4608a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * 4618a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @return {@code true} if the JAR is signed, {@code false} 4628a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * otherwise. 4638a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 4648a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak boolean isSignedJar() { 4658a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return certificates.size() > 0; 4668a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4678a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 4688a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private boolean verify(Attributes attributes, String entry, byte[] data, 4698a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak int start, int end, boolean ignoreSecondEndline, boolean ignorable) { 4708a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak for (int i = 0; i < DIGEST_ALGORITHMS.length; i++) { 4718a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String algorithm = DIGEST_ALGORITHMS[i]; 4728a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String hash = attributes.getValue(algorithm + entry); 4738a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (hash == null) { 4748a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak continue; 4758a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4768a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 4778a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak MessageDigest md; 4788a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak try { 4798a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak md = MessageDigest.getInstance(algorithm); 4808a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } catch (NoSuchAlgorithmException e) { 4818a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak continue; 4828a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4838a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (ignoreSecondEndline && data[end - 1] == '\n' && data[end - 2] == '\n') { 4848a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak md.update(data, start, end - 1 - start); 4858a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } else { 4868a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak md.update(data, start, end - start); 4878a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4888a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak byte[] b = md.digest(); 4899f00d71787774bf79d4b398c28630f670765b791Tobias Thierer byte[] encodedHashBytes = hash.getBytes(StandardCharsets.ISO_8859_1); 4909f00d71787774bf79d4b398c28630f670765b791Tobias Thierer return verifyMessageDigest(b, encodedHashBytes); 4918a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4928a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return ignorable; 4938a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4948a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 4959f00d71787774bf79d4b398c28630f670765b791Tobias Thierer private static boolean verifyMessageDigest(byte[] expected, byte[] encodedActual) { 4969f00d71787774bf79d4b398c28630f670765b791Tobias Thierer byte[] actual; 4979f00d71787774bf79d4b398c28630f670765b791Tobias Thierer try { 4989f00d71787774bf79d4b398c28630f670765b791Tobias Thierer actual = java.util.Base64.getDecoder().decode(encodedActual); 4999f00d71787774bf79d4b398c28630f670765b791Tobias Thierer } catch (IllegalArgumentException e) { 5009f00d71787774bf79d4b398c28630f670765b791Tobias Thierer return false; 5019f00d71787774bf79d4b398c28630f670765b791Tobias Thierer } 5029f00d71787774bf79d4b398c28630f670765b791Tobias Thierer return MessageDigest.isEqual(expected, actual); 5039f00d71787774bf79d4b398c28630f670765b791Tobias Thierer } 5049f00d71787774bf79d4b398c28630f670765b791Tobias Thierer 5058a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 5068a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Returns all of the {@link java.security.cert.Certificate} chains that 5078a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * were used to verify the signature on the JAR entry called 5088a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * {@code name}. Callers must not modify the returned arrays. 5098a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * 5108a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @param name 5118a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * the name of a JAR entry. 5128a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @return an array of {@link java.security.cert.Certificate} chains. 5138a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 5148a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Certificate[][] getCertificateChains(String name) { 5158a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return verifiedEntries.get(name); 5168a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 5178a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 5188a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 5198a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Remove all entries from the internal collection of data held about each 5208a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * JAR entry in the {@code META-INF} directory. 5218a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 5228a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak void removeMetaEntries() { 5238a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak metaEntries.clear(); 5248a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 5258a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak} 526