StrictJarVerifier.java revision 8a7c1606d88873c5a1b5764c16cb046b6f2275b2
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; 358a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.util.jar.Attributes; 368a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.util.jar.JarFile; 378a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport libcore.io.Base64; 388a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport sun.security.jca.Providers; 398a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport sun.security.pkcs.PKCS7; 408a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 418a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak/** 428a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Non-public class used by {@link JarFile} and {@link JarInputStream} to manage 438a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * the verification of signed JARs. {@code JarFile} and {@code JarInputStream} 448a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * objects are expected to have a {@code JarVerifier} instance member which 458a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * can be used to carry out the tasks associated with verifying a signed JAR. 468a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * These tasks would typically include: 478a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * <ul> 488a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * <li>verification of all signed signature files 498a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * <li>confirmation that all signed data was signed only by the party or parties 508a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * specified in the signature block data 518a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * <li>verification that the contents of all signature files (i.e. {@code .SF} 528a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * files) agree with the JAR entries information found in the JAR manifest. 538a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * </ul> 548a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 558a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakclass StrictJarVerifier { 568a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 578a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * List of accepted digest algorithms. This list is in order from most 588a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * preferred to least preferred. 598a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 608a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private static final String[] DIGEST_ALGORITHMS = new String[] { 618a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak "SHA-512", 628a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak "SHA-384", 638a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak "SHA-256", 648a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak "SHA1", 658a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak }; 668a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 678a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final String jarName; 688a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final StrictJarManifest manifest; 698a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final HashMap<String, byte[]> metaEntries; 708a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final int mainAttributesEnd; 718a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 728a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final Hashtable<String, HashMap<String, Attributes>> signatures = 738a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak new Hashtable<String, HashMap<String, Attributes>>(5); 748a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 758a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final Hashtable<String, Certificate[]> certificates = 768a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak new Hashtable<String, Certificate[]>(5); 778a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 788a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final Hashtable<String, Certificate[][]> verifiedEntries = 798a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak new Hashtable<String, Certificate[][]>(); 808a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 818a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 828a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Stores and a hash and a message digest and verifies that massage digest 838a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * matches the hash. 848a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 858a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak static class VerifierEntry extends OutputStream { 868a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 878a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final String name; 888a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 898a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final MessageDigest digest; 908a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 918a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final byte[] hash; 928a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 938a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final Certificate[][] certChains; 948a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 958a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final Hashtable<String, Certificate[][]> verifiedEntries; 968a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 978a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak VerifierEntry(String name, MessageDigest digest, byte[] hash, 988a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Certificate[][] certChains, Hashtable<String, Certificate[][]> verifedEntries) { 998a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak this.name = name; 1008a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak this.digest = digest; 1018a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak this.hash = hash; 1028a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak this.certChains = certChains; 1038a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak this.verifiedEntries = verifedEntries; 1048a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1058a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1068a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 1078a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Updates a digest with one byte. 1088a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 1098a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak @Override 1108a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak public void write(int value) { 1118a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak digest.update((byte) value); 1128a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1138a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1148a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 1158a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Updates a digest with byte array. 1168a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 1178a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak @Override 1188a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak public void write(byte[] buf, int off, int nbytes) { 1198a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak digest.update(buf, off, nbytes); 1208a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1218a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1228a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 1238a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Verifies that the digests stored in the manifest match the decrypted 1248a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * digests from the .SF file. This indicates the validity of the 1258a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * signing, not the integrity of the file, as its digest must be 1268a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * calculated and verified when its contents are read. 1278a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * 1288a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @throws SecurityException 1298a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * if the digest value stored in the manifest does <i>not</i> 1308a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * agree with the decrypted digest as recovered from the 1318a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * <code>.SF</code> file. 1328a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 1338a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak void verify() { 1348a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak byte[] d = digest.digest(); 1358a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (!MessageDigest.isEqual(d, Base64.decode(hash))) { 1368a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw invalidDigest(JarFile.MANIFEST_NAME, name, name); 1378a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1388a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak verifiedEntries.put(name, certChains); 1398a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1408a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1418a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1428a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private static SecurityException invalidDigest(String signatureFile, String name, 1438a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String jarName) { 1448a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw new SecurityException(signatureFile + " has invalid digest for " + name + 1458a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak " in " + jarName); 1468a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1478a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1488a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private static SecurityException failedVerification(String jarName, String signatureFile) { 1498a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw new SecurityException(jarName + " failed verification of " + signatureFile); 1508a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1518a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1528a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private static SecurityException failedVerification(String jarName, String signatureFile, 1538a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Throwable e) { 1548a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw new SecurityException(jarName + " failed verification of " + signatureFile, e); 1558a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1568a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1578a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1588a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 1598a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Constructs and returns a new instance of {@code JarVerifier}. 1608a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * 1618a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @param name 1628a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * the name of the JAR file being verified. 1638a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 1648a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak StrictJarVerifier(String name, StrictJarManifest manifest, 1658a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak HashMap<String, byte[]> metaEntries) { 1668a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak jarName = name; 1678a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak this.manifest = manifest; 1688a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak this.metaEntries = metaEntries; 1698a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak this.mainAttributesEnd = manifest.getMainAttributesEnd(); 1708a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1718a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1728a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 1738a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Invoked for each new JAR entry read operation from the input 1748a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * stream. This method constructs and returns a new {@link VerifierEntry} 1758a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * which contains the certificates used to sign the entry and its hash value 1768a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * as specified in the JAR MANIFEST format. 1778a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * 1788a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @param name 1798a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * the name of an entry in a JAR file which is <b>not</b> in the 1808a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * {@code META-INF} directory. 1818a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @return a new instance of {@link VerifierEntry} which can be used by 1828a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * callers as an {@link OutputStream}. 1838a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 1848a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak VerifierEntry initEntry(String name) { 1858a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // If no manifest is present by the time an entry is found, 1868a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // verification cannot occur. If no signature files have 1878a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // been found, do not verify. 1888a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (manifest == null || signatures.isEmpty()) { 1898a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return null; 1908a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1918a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1928a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Attributes attributes = manifest.getAttributes(name); 1938a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // entry has no digest 1948a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (attributes == null) { 1958a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return null; 1968a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1978a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1988a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak ArrayList<Certificate[]> certChains = new ArrayList<Certificate[]>(); 1998a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Iterator<Map.Entry<String, HashMap<String, Attributes>>> it = signatures.entrySet().iterator(); 2008a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak while (it.hasNext()) { 2018a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Map.Entry<String, HashMap<String, Attributes>> entry = it.next(); 2028a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak HashMap<String, Attributes> hm = entry.getValue(); 2038a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (hm.get(name) != null) { 2048a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // Found an entry for entry name in .SF file 2058a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String signatureFile = entry.getKey(); 2068a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Certificate[] certChain = certificates.get(signatureFile); 2078a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (certChain != null) { 2088a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak certChains.add(certChain); 2098a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2108a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2118a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2128a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 2138a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // entry is not signed 2148a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (certChains.isEmpty()) { 2158a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return null; 2168a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2178a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Certificate[][] certChainsArray = certChains.toArray(new Certificate[certChains.size()][]); 2188a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 2198a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak for (int i = 0; i < DIGEST_ALGORITHMS.length; i++) { 2208a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak final String algorithm = DIGEST_ALGORITHMS[i]; 2218a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak final String hash = attributes.getValue(algorithm + "-Digest"); 2228a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (hash == null) { 2238a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak continue; 2248a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2258a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak byte[] hashBytes = hash.getBytes(StandardCharsets.ISO_8859_1); 2268a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 2278a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak try { 2288a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return new VerifierEntry(name, MessageDigest.getInstance(algorithm), hashBytes, 2298a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak certChainsArray, verifiedEntries); 2308a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } catch (NoSuchAlgorithmException ignored) { 2318a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2328a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2338a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return null; 2348a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2358a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 2368a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 2378a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Add a new meta entry to the internal collection of data held on each JAR 2388a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * entry in the {@code META-INF} directory including the manifest 2398a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * file itself. Files associated with the signing of a JAR would also be 2408a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * added to this collection. 2418a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * 2428a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @param name 2438a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * the name of the file located in the {@code META-INF} 2448a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * directory. 2458a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @param buf 2468a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * the file bytes for the file called {@code name}. 2478a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @see #removeMetaEntries() 2488a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 2498a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak void addMetaEntry(String name, byte[] buf) { 2508a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak metaEntries.put(name.toUpperCase(Locale.US), buf); 2518a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2528a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 2538a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 2548a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * If the associated JAR file is signed, check on the validity of all of the 2558a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * known signatures. 2568a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * 2578a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @return {@code true} if the associated JAR is signed and an internal 2588a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * check verifies the validity of the signature(s). {@code false} if 2598a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * the associated JAR file has no entries at all in its {@code 2608a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * META-INF} directory. This situation is indicative of an invalid 2618a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * JAR file. 2628a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * <p> 2638a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Will also return {@code true} if the JAR file is <i>not</i> 2648a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * signed. 2658a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @throws SecurityException 2668a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * if the JAR file is signed and it is determined that a 2678a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * signature block file contains an invalid signature for the 2688a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * corresponding signature file. 2698a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 2708a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak synchronized boolean readCertificates() { 2718a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (metaEntries.isEmpty()) { 2728a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return false; 2738a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2748a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 2758a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Iterator<String> it = metaEntries.keySet().iterator(); 2768a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak while (it.hasNext()) { 2778a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String key = it.next(); 2788a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (key.endsWith(".DSA") || key.endsWith(".RSA") || key.endsWith(".EC")) { 2798a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak verifyCertificate(key); 2808a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak it.remove(); 2818a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2828a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2838a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return true; 2848a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 2858a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 2868a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 2878a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Verifies that the signature computed from {@code sfBytes} matches 2888a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * that specified in {@code blockBytes} (which is a PKCS7 block). Returns 2898a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * certificates listed in the PKCS7 block. Throws a {@code GeneralSecurityException} 2908a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * if something goes wrong during verification. 2918a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 2928a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak static Certificate[] verifyBytes(byte[] blockBytes, byte[] sfBytes) 2938a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throws GeneralSecurityException { 2948a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 2958a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Object obj = null; 2968a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak try { 2978a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 2988a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak obj = Providers.startJarVerification(); 2998a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak PKCS7 block = new PKCS7(blockBytes); 3008a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (block.verify(sfBytes) == null) { 3018a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw new GeneralSecurityException("Failed to verify signature"); 3028a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3038a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak X509Certificate[] blockCerts = block.getCertificates(); 3048a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Certificate[] signerCertChain = null; 3058a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (blockCerts != null) { 3068a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak signerCertChain = new Certificate[blockCerts.length]; 3078a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak for (int i = 0; i < blockCerts.length; ++i) { 3088a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak signerCertChain[i] = blockCerts[i]; 3098a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3108a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3118a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return signerCertChain; 3128a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } catch (IOException e) { 3138a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw new GeneralSecurityException("IO exception verifying jar cert", e); 3148a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } finally { 3158a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Providers.stopJarVerification(obj); 3168a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3178a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3188a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 3198a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 3208a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @param certFile 3218a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 3228a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private void verifyCertificate(String certFile) { 3238a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // Found Digital Sig, .SF should already have been read 3248a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String signatureFile = certFile.substring(0, certFile.lastIndexOf('.')) + ".SF"; 3258a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak byte[] sfBytes = metaEntries.get(signatureFile); 3268a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (sfBytes == null) { 3278a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return; 3288a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3298a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 3308a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak byte[] manifestBytes = metaEntries.get(JarFile.MANIFEST_NAME); 3318a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // Manifest entry is required for any verifications. 3328a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (manifestBytes == null) { 3338a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return; 3348a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3358a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 3368a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak byte[] sBlockBytes = metaEntries.get(certFile); 3378a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak try { 3388a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Certificate[] signerCertChain = verifyBytes(sBlockBytes, sfBytes); 3398a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (signerCertChain != null) { 3408a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak certificates.put(signatureFile, signerCertChain); 3418a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3428a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } catch (GeneralSecurityException e) { 3438a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw failedVerification(jarName, signatureFile, e); 3448a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3458a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 3468a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // Verify manifest hash in .sf file 3478a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Attributes attributes = new Attributes(); 3488a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak HashMap<String, Attributes> entries = new HashMap<String, Attributes>(); 3498a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak try { 3508a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak StrictJarManifestReader im = new StrictJarManifestReader(sfBytes, attributes); 3518a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak im.readEntries(entries, null); 3528a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } catch (IOException e) { 3538a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return; 3548a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3558a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 3568a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // Do we actually have any signatures to look at? 3578a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (attributes.get(Attributes.Name.SIGNATURE_VERSION) == null) { 3588a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return; 3598a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3608a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 3618a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak boolean createdBySigntool = false; 3628a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String createdBy = attributes.getValue("Created-By"); 3638a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (createdBy != null) { 3648a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak createdBySigntool = createdBy.indexOf("signtool") != -1; 3658a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3668a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 3678a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // Use .SF to verify the mainAttributes of the manifest 3688a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // If there is no -Digest-Manifest-Main-Attributes entry in .SF 3698a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // file, such as those created before java 1.5, then we ignore 3708a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // such verification. 3718a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (mainAttributesEnd > 0 && !createdBySigntool) { 3728a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String digestAttribute = "-Digest-Manifest-Main-Attributes"; 3738a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (!verify(attributes, digestAttribute, manifestBytes, 0, mainAttributesEnd, false, true)) { 3748a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw failedVerification(jarName, signatureFile); 3758a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3768a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3778a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 3788a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // Use .SF to verify the whole manifest. 3798a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String digestAttribute = createdBySigntool ? "-Digest" : "-Digest-Manifest"; 3808a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (!verify(attributes, digestAttribute, manifestBytes, 0, manifestBytes.length, false, false)) { 3818a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Iterator<Map.Entry<String, Attributes>> it = entries.entrySet().iterator(); 3828a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak while (it.hasNext()) { 3838a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Map.Entry<String, Attributes> entry = it.next(); 3848a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak StrictJarManifest.Chunk chunk = manifest.getChunk(entry.getKey()); 3858a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (chunk == null) { 3868a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return; 3878a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3888a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (!verify(entry.getValue(), "-Digest", manifestBytes, 3898a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak chunk.start, chunk.end, createdBySigntool, false)) { 3908a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw invalidDigest(signatureFile, entry.getKey(), jarName); 3918a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3928a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3938a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3948a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak metaEntries.put(signatureFile, null); 3958a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak signatures.put(signatureFile, entries); 3968a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 3978a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 3988a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 3998a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Returns a <code>boolean</code> indication of whether or not the 4008a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * associated jar file is signed. 4018a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * 4028a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @return {@code true} if the JAR is signed, {@code false} 4038a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * otherwise. 4048a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 4058a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak boolean isSignedJar() { 4068a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return certificates.size() > 0; 4078a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4088a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 4098a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private boolean verify(Attributes attributes, String entry, byte[] data, 4108a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak int start, int end, boolean ignoreSecondEndline, boolean ignorable) { 4118a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak for (int i = 0; i < DIGEST_ALGORITHMS.length; i++) { 4128a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String algorithm = DIGEST_ALGORITHMS[i]; 4138a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String hash = attributes.getValue(algorithm + entry); 4148a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (hash == null) { 4158a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak continue; 4168a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4178a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 4188a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak MessageDigest md; 4198a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak try { 4208a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak md = MessageDigest.getInstance(algorithm); 4218a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } catch (NoSuchAlgorithmException e) { 4228a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak continue; 4238a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4248a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (ignoreSecondEndline && data[end - 1] == '\n' && data[end - 2] == '\n') { 4258a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak md.update(data, start, end - 1 - start); 4268a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } else { 4278a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak md.update(data, start, end - start); 4288a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4298a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak byte[] b = md.digest(); 4308a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak byte[] hashBytes = hash.getBytes(StandardCharsets.ISO_8859_1); 4318a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return MessageDigest.isEqual(b, Base64.decode(hashBytes)); 4328a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4338a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return ignorable; 4348a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4358a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 4368a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 4378a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Returns all of the {@link java.security.cert.Certificate} chains that 4388a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * were used to verify the signature on the JAR entry called 4398a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * {@code name}. Callers must not modify the returned arrays. 4408a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * 4418a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @param name 4428a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * the name of a JAR entry. 4438a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @return an array of {@link java.security.cert.Certificate} chains. 4448a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 4458a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Certificate[][] getCertificateChains(String name) { 4468a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return verifiedEntries.get(name); 4478a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4488a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 4498a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 4508a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Remove all entries from the internal collection of data held about each 4518a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * JAR entry in the {@code META-INF} directory. 4528a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 4538a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak void removeMetaEntries() { 4548a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak metaEntries.clear(); 4558a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 4568a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak} 457