< prev index next >

src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java

Print this page
rev 15874 : 8163304: jarsigner -verbose -verify should print the algorithms used to sign the jar

@@ -48,10 +48,13 @@
 import java.security.cert.TrustAnchor;
 import java.util.Map.Entry;
 
 import jdk.security.jarsigner.JarSigner;
 import jdk.security.jarsigner.JarSignerException;
+import sun.security.pkcs.PKCS7;
+import sun.security.pkcs.SignerInfo;
+import sun.security.timestamp.TimestampToken;
 import sun.security.tools.KeyStoreUtil;
 import sun.security.x509.*;
 import sun.security.util.*;
 
 

@@ -85,10 +88,19 @@
     private static final String NONE = "NONE";
     private static final String P11KEYSTORE = "PKCS11";
 
     private static final long SIX_MONTHS = 180*24*60*60*1000L; //milliseconds
 
+    private static final DisabledAlgorithmConstraints DISABLED_CHECK =
+            new DisabledAlgorithmConstraints(
+                    DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS);
+
+    private static final Set<CryptoPrimitive> DIGEST_PRIMITIVE_SET = Collections
+            .unmodifiableSet(EnumSet.of(CryptoPrimitive.MESSAGE_DIGEST));
+    private static final Set<CryptoPrimitive> SIG_PRIMITIVE_SET = Collections
+            .unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));
+
     // Attention:
     // This is the entry that get launched by the security tool jarsigner.
     public static void main(String args[]) throws Exception {
         Main js = new Main();
         js.run(args);

@@ -161,10 +173,12 @@
     private boolean badNetscapeCertType = false;
     private boolean signerSelfSigned = false;
 
     private Throwable chainNotValidatedReason = null;
 
+    private boolean seeWeak = false;
+
     CertificateFactory certificateFactory;
     CertPathValidator validator;
     PKIXParameters pkixParameters;
 
     public void run(String args[]) {

@@ -626,30 +640,62 @@
     void verifyJar(String jarName)
         throws Exception
     {
         boolean anySigned = false;  // if there exists entry inside jar signed
         JarFile jf = null;
+        Map<String,String> digestMap = new HashMap<>();
+        Map<String,PKCS7> sigMap = new HashMap<>();
+        Map<String,String> sigNameMap = new HashMap<>();
+        Map<String,String> unparsableSignatures = new HashMap<>();
 
         try {
             jf = new JarFile(jarName, true);
             Vector<JarEntry> entriesVec = new Vector<>();
             byte[] buffer = new byte[8192];
 
             Enumeration<JarEntry> entries = jf.entries();
             while (entries.hasMoreElements()) {
                 JarEntry je = entries.nextElement();
                 entriesVec.addElement(je);
-                InputStream is = null;
-                try {
-                    is = jf.getInputStream(je);
+                try (InputStream is = jf.getInputStream(je)) {
+                    String name = je.getName();
+                    if (signatureRelated(name)
+                            && SignatureFileVerifier.isBlockOrSF(name)) {
+                        String alias = name.substring(name.lastIndexOf('/') + 1,
+                                name.lastIndexOf('.'));
+                        try {
+                            if (name.endsWith(".SF")) {
+                                Manifest sf = new Manifest(is);
+                                boolean found = false;
+                                for (Object obj : sf.getMainAttributes().keySet()) {
+                                    String key = obj.toString();
+                                    if (key.endsWith("-Digest-Manifest")) {
+                                        digestMap.put(alias,
+                                                key.substring(0, key.length() - 16));
+                                        found = true;
+                                        break;
+                                    }
+                                }
+                                if (!found) {
+                                    unparsableSignatures.putIfAbsent(alias,
+                                        String.format(
+                                            rb.getString("history.unparsable"),
+                                            name));
+                                }
+                            } else {
+                                sigNameMap.put(alias, name);
+                                sigMap.put(alias, new PKCS7(is));
+                            }
+                        } catch (IOException ioe) {
+                            unparsableSignatures.putIfAbsent(alias, String.format(
+                                    rb.getString("history.unparsable"), name));
+                        }
+                    } else {
                     while (is.read(buffer, 0, buffer.length) != -1) {
                         // we just read. this will throw a SecurityException
                         // if  a signature/digest check fails.
                     }
-                } finally {
-                    if (is != null) {
-                        is.close();
                     }
                 }
             }
 
             Manifest man = jf.getManifest();

@@ -803,23 +849,115 @@
                     ".k.at.least.one.certificate.was.found.in.keystore"));
                 if (ckaliases.size() > 0) {
                     System.out.println(rb.getString(
                         ".X.not.signed.by.specified.alias.es."));
                 }
-                System.out.println();
             }
-            if (man == null)
+            if (man == null) {
+                System.out.println();
                 System.out.println(rb.getString("no.manifest."));
+            }
 
             // If signer is a trusted cert or private entry in user's own
             // keystore, it can be self-signed.
             if (!aliasNotInStore) {
                 signerSelfSigned = false;
             }
 
+            // Even if the verbose option is not specified, all out strings
+            // must be generated so seeWeak can be updated.
+            if (!digestMap.isEmpty()
+                    || !sigMap.isEmpty()
+                    || !unparsableSignatures.isEmpty()) {
+                if (verbose != null) {
+                    System.out.println();
+                }
+                for (String s : sigMap.keySet()) {
+                    if (!digestMap.containsKey(s)) {
+                        unparsableSignatures.putIfAbsent(s, String.format(
+                                rb.getString("history.nosf"), s));
+                    }
+                }
+                for (String s : digestMap.keySet()) {
+                    PKCS7 p7 = sigMap.get(s);
+                    if (p7 != null) {
+                        String history;
+                        try {
+                            SignerInfo si = p7.getSignerInfos()[0];
+                            X509Certificate signer = si.getCertificate(p7);
+                            String digestAlg = digestMap.get(s);
+                            String sigAlg = AlgorithmId.makeSigAlg(
+                                    si.getDigestAlgorithmId().getName(),
+                                    si.getDigestEncryptionAlgorithmId().getName());
+                            PublicKey key = signer.getPublicKey();
+                            PKCS7 tsToken = si.getTsToken();
+                            if (tsToken != null) {
+                                SignerInfo tsSi = tsToken.getSignerInfos()[0];
+                                X509Certificate tsSigner = tsSi.getCertificate(tsToken);
+                                byte[] encTsTokenInfo = tsToken.getContentInfo().getData();
+                                TimestampToken tsTokenInfo = new TimestampToken(encTsTokenInfo);
+                                PublicKey tsKey = tsSigner.getPublicKey();
+                                String tsDigestAlg = tsTokenInfo.getHashAlgorithm().getName();
+                                String tsSigAlg = AlgorithmId.makeSigAlg(
+                                        tsSi.getDigestAlgorithmId().getName(),
+                                        tsSi.getDigestEncryptionAlgorithmId().getName());
+                                Calendar c = Calendar.getInstance(
+                                        TimeZone.getTimeZone("UTC"),
+                                        Locale.getDefault(Locale.Category.FORMAT));
+                                c.setTime(tsTokenInfo.getDate());
+                                history = String.format(
+                                        rb.getString("history.with.ts"),
+                                        signer.getSubjectX500Principal(),
+                                        withWeak(digestAlg, DIGEST_PRIMITIVE_SET),
+                                        withWeak(sigAlg, SIG_PRIMITIVE_SET),
+                                        withWeak(key),
+                                        c,
+                                        tsSigner.getSubjectX500Principal(),
+                                        withWeak(tsDigestAlg, DIGEST_PRIMITIVE_SET),
+                                        withWeak(tsSigAlg, SIG_PRIMITIVE_SET),
+                                        withWeak(tsKey));
+                            } else {
+                                history = String.format(
+                                        rb.getString("history.without.ts"),
+                                        signer.getSubjectX500Principal(),
+                                        withWeak(digestAlg, DIGEST_PRIMITIVE_SET),
+                                        withWeak(sigAlg, SIG_PRIMITIVE_SET),
+                                        withWeak(key));
+                            }
+                        } catch (Exception e) {
+                            // The only usage of sigNameMap, remember the name
+                            // of the block file if it's invalid.
+                            history = String.format(
+                                    rb.getString("history.unparsable"),
+                                    sigNameMap.get(s));
+                        }
+                        if (verbose != null) {
+                            System.out.println(history);
+                        }
+                    } else {
+                        unparsableSignatures.putIfAbsent(s, String.format(
+                                rb.getString("history.nobk"), s));
+                    }
+                }
+                if (verbose != null) {
+                    for (String s : unparsableSignatures.keySet()) {
+                        System.out.println(unparsableSignatures.get(s));
+                    }
+                }
+            }
+            System.out.println();
             if (!anySigned) {
-                if (hasSignature) {
+                if (seeWeak) {
+                    if (verbose != null) {
+                        System.out.println(rb.getString("jar.treated.unsigned.see.weak.verbose"));
+                        System.out.println("\n  " +
+                                DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS +
+                                "=" + Security.getProperty(DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS));
+                    } else {
+                        System.out.println(rb.getString("jar.treated.unsigned.see.weak"));
+                    }
+                } else if (hasSignature) {
                     System.out.println(rb.getString("jar.treated.unsigned"));
                 } else {
                     System.out.println(rb.getString("jar.is.unsigned"));
                 }
             } else {

@@ -843,11 +981,13 @@
                     }
 
                     if (weakAlg != 0) {
                         // In fact, jarsigner verification did not catch this
                         // since it has not read the JarFile content itself.
-                        // Everything is done with JarFile API.
+                        // Everything is done with JarFile API. The signing
+                        // history (digestMap etc) will show these info and
+                        // print out proper warnings.
                     }
 
                     if (badKeyUsage) {
                         System.out.println(
                             rb.getString("This.jar.contains.entries.whose.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing."));

@@ -934,10 +1074,30 @@
         }
 
         System.exit(1);
     }
 
+    private String withWeak(String alg, Set<CryptoPrimitive> primitiveSet) {
+        if (DISABLED_CHECK.permits(primitiveSet, alg, null)) {
+            return alg;
+        } else {
+            seeWeak = true;
+            return String.format(rb.getString("with.weak"), alg);
+        }
+    }
+
+    private String withWeak(PublicKey key) {
+        if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
+            return String.format(
+                    rb.getString("key.bit"), KeyUtil.getKeySize(key));
+        } else {
+            seeWeak = true;
+            return String.format(
+                    rb.getString("key.bit.weak"), KeyUtil.getKeySize(key));
+        }
+    }
+
     private static MessageFormat validityTimeForm = null;
     private static MessageFormat notYetTimeForm = null;
     private static MessageFormat expiredTimeForm = null;
     private static MessageFormat expiringTimeForm = null;
 

@@ -1123,26 +1283,26 @@
     }
 
     void signJar(String jarName, String alias)
             throws Exception {
 
-        DisabledAlgorithmConstraints dac =
-                new DisabledAlgorithmConstraints(
-                        DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS);
-
-        if (digestalg != null && !dac.permits(
-                Collections.singleton(CryptoPrimitive.MESSAGE_DIGEST), digestalg, null)) {
+        if (digestalg != null && !DISABLED_CHECK.permits(
+                DIGEST_PRIMITIVE_SET, digestalg, null)) {
             weakAlg |= 1;
         }
-        if (tSADigestAlg != null && !dac.permits(
-                Collections.singleton(CryptoPrimitive.MESSAGE_DIGEST), tSADigestAlg, null)) {
+        if (tSADigestAlg != null && !DISABLED_CHECK.permits(
+                DIGEST_PRIMITIVE_SET, tSADigestAlg, null)) {
             weakAlg |= 4;
         }
-        if (sigalg != null && !dac.permits(
-                Collections.singleton(CryptoPrimitive.SIGNATURE), sigalg, null)) {
+        if (sigalg != null && !DISABLED_CHECK.permits(
+                SIG_PRIMITIVE_SET , sigalg, null)) {
             weakAlg |= 2;
         }
+        if (!DISABLED_CHECK.permits(
+                SIG_PRIMITIVE_SET, privateKey)) {
+            weakAlg |= 8;
+        }
 
         boolean aliasUsed = false;
         X509Certificate tsaCert = null;
 
         if (sigfile == null) {

@@ -1383,10 +1543,15 @@
                 if ((weakAlg & 4) == 4) {
                     System.out.println(String.format(
                             rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."),
                             tSADigestAlg, "-tsadigestalg"));
                 }
+                if ((weakAlg & 8) == 8) {
+                    System.out.println(String.format(
+                            rb.getString("The.1.signing.key.has.a.keysize.of.2.which.is.considered.a.security.risk."),
+                            privateKey.getAlgorithm(), KeyUtil.getKeySize(privateKey)));
+                }
             } else {
                 System.out.println(rb.getString("jar.signed."));
             }
             if (hasExpiringCert || noTimestamp) {
                 if (!warningAppeared) {
< prev index next >