--- old/src/java.base/share/classes/sun/security/pkcs10/PKCS10.java	2017-01-23 18:01:54.886144200 +0800
+++ new/src/java.base/share/classes/sun/security/pkcs10/PKCS10.java	2017-01-23 18:01:54.726747500 +0800
@@ -167,7 +167,8 @@
         // key and signature algorithm we found.
         //
         try {
-            sig = Signature.getInstance(id.getName());
+            sigAlg = id.getName();
+            sig = Signature.getInstance(sigAlg);
             sig.initVerify(subjectPublicKeyInfo);
             sig.update(data);
             if (!sig.verify(sigData))
@@ -218,6 +219,7 @@
         signature.update(certificateRequestInfo, 0,
                 certificateRequestInfo.length);
         sig = signature.sign();
+        sigAlg = signature.getAlgorithm();
 
         /*
          * Build guts of SIGNED macro
@@ -251,6 +253,11 @@
         { return subjectPublicKeyInfo; }
 
     /**
+     * Returns the signature algorithm.
+     */
+    public String getSigAlg() { return sigAlg; }
+
+    /**
      * Returns the additional attributes requested.
      */
     public PKCS10Attributes getAttributes()
@@ -348,6 +355,7 @@
 
     private X500Name            subject;
     private PublicKey           subjectPublicKeyInfo;
+    private String              sigAlg;
     private PKCS10Attributes    attributeSet;
     private byte[]              encoded;        // signed
 }
--- old/src/java.base/share/classes/sun/security/provider/certpath/BasicChecker.java	2017-01-23 18:01:55.631400400 +0800
+++ new/src/java.base/share/classes/sun/security/provider/certpath/BasicChecker.java	2017-01-23 18:01:55.452353000 +0800
@@ -51,7 +51,7 @@
 
 /**
  * BasicChecker is a PKIXCertPathChecker that checks the basic information
- * on a PKIX certificate, namely the signature, timestamp, and subject/issuer
+ * on a PKIX certificate, namely the signature, validity, and subject/issuer
  * name chaining.
  *
  * @since       1.4
@@ -125,7 +125,7 @@
     }
 
     /**
-     * Performs the signature, timestamp, and subject/issuer name chaining
+     * Performs the signature, validity, and subject/issuer name chaining
      * checks on the certificate using its internal state. This method does
      * not remove any critical extensions from the Collection.
      *
@@ -141,7 +141,7 @@
         X509Certificate currCert = (X509Certificate)cert;
 
         if (!sigOnly) {
-            verifyTimestamp(currCert);
+            verifyValidity(currCert);
             verifyNameChaining(currCert);
         }
         verifySignature(currCert);
@@ -177,12 +177,12 @@
     }
 
     /**
-     * Internal method to verify the timestamp on a certificate
+     * Internal method to verify the validity on a certificate
      */
-    private void verifyTimestamp(X509Certificate cert)
+    private void verifyValidity(X509Certificate cert)
         throws CertPathValidatorException
     {
-        String msg = "timestamp";
+        String msg = "validity";
         if (debug != null)
             debug.println("---checking " + msg + ":" + date.toString() + "...");
 
--- old/src/java.base/share/classes/sun/security/tools/keytool/Main.java	2017-01-23 18:01:56.365535100 +0800
+++ new/src/java.base/share/classes/sun/security/tools/keytool/Main.java	2017-01-23 18:01:56.190750600 +0800
@@ -27,6 +27,7 @@
 
 import java.io.*;
 import java.security.CodeSigner;
+import java.security.CryptoPrimitive;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
 import java.security.MessageDigest;
@@ -156,6 +157,7 @@
     private boolean protectedPath = false;
     private boolean srcprotectedPath = false;
     private boolean cacerts = false;
+    private boolean nowarn = false;
     private CertificateFactory cf = null;
     private KeyStore caks = null; // "cacerts" keystore
     private char[] srcstorePass = null;
@@ -166,6 +168,16 @@
     private List<String> ids = new ArrayList<>();   // used in GENCRL
     private List<String> v3ext = new ArrayList<>();
 
+    // Warnings on weak algorithms
+    private List<String> weakWarnings = new ArrayList<>();
+
+    private static final DisabledAlgorithmConstraints DISABLED_CHECK =
+            new DisabledAlgorithmConstraints(
+                    DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS);
+
+    private static final Set<CryptoPrimitive> SIG_PRIMITIVE_SET = Collections
+            .unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));
+
     enum Command {
         CERTREQ("Generates.a.certificate.request",
             ALIAS, SIGALG, FILEOUT, KEYPASS, KEYSTORE, DNAME,
@@ -351,7 +363,7 @@
     private static final String NONE = "NONE";
     private static final String P11KEYSTORE = "PKCS11";
     private static final String P12KEYSTORE = "PKCS12";
-    private final String keyAlias = "mykey";
+    private static final String keyAlias = "mykey";
 
     // for i18n
     private static final java.util.ResourceBundle rb =
@@ -387,6 +399,7 @@
                 throw e;
             }
         } finally {
+            printWeakWarningsWithoutNewLine();
             for (char[] pass : passwords) {
                 if (pass != null) {
                     Arrays.fill(pass, ' ');
@@ -476,6 +489,8 @@
                 help = true;
             } else if (collator.compare(flags, "-conf") == 0) {
                 i++;
+            } else if (collator.compare(flags, "-nowarn") == 0) {
+                nowarn = true;
             } else if (collator.compare(flags, "-keystore") == 0) {
                 ksfname = args[++i];
                 if (new File(ksfname).getCanonicalPath().equals(
@@ -1152,11 +1167,11 @@
         } else if (command == LIST) {
             if (storePass == null
                     && !KeyStoreUtil.isWindowsKeyStore(storetype)) {
-                printWarning();
+                printNoIntegrityWarning();
             }
 
             if (alias != null) {
-                doPrintEntry(alias, out);
+                doPrintEntry(null, alias, out);
             } else {
                 doPrintEntries(out);
             }
@@ -1253,6 +1268,12 @@
             throws Exception {
 
 
+        if (keyStore.containsAlias(alias) == false) {
+            MessageFormat form = new MessageFormat
+                    (rb.getString("Alias.alias.does.not.exist"));
+            Object[] source = {alias};
+            throw new Exception(form.format(source));
+        }
         Certificate signerCert = keyStore.getCertificate(alias);
         byte[] encoded = signerCert.getEncoded();
         X509CertImpl signerCertImpl = new X509CertImpl(encoded);
@@ -1306,6 +1327,8 @@
         byte[] rawReq = Pem.decode(new String(sb));
         PKCS10 req = new PKCS10(rawReq);
 
+        checkWeak(rb.getString("the.input"), req);
+
         info.set(X509CertInfo.KEY, new CertificateX509Key(req.getSubjectPublicKeyInfo()));
         info.set(X509CertInfo.SUBJECT,
                     dname==null?req.getSubjectName():new X500Name(dname));
@@ -1335,6 +1358,9 @@
                 }
             }
         }
+
+        checkWeak(rb.getString("the.issuer"), keyStore.getCertificateChain(alias));
+        checkWeak(rb.getString("the.output"), cert);
     }
 
     private void doGenCRL(PrintStream out)
@@ -1385,6 +1411,7 @@
         } else {
             out.write(crl.getEncodedInternal());
         }
+        checkWeak(null, crl, privateKey);
     }
 
     /**
@@ -1431,6 +1458,8 @@
         // Sign the request and base-64 encode it
         request.encodeAndSign(subject, signature);
         request.print(out);
+
+        checkWeak(null, request);
     }
 
     /**
@@ -1454,7 +1483,7 @@
     {
         if (storePass == null
                 && !KeyStoreUtil.isWindowsKeyStore(storetype)) {
-            printWarning();
+            printNoIntegrityWarning();
         }
         if (alias == null) {
             alias = keyAlias;
@@ -1474,6 +1503,7 @@
             throw new Exception(form.format(source));
         }
         dumpCert(cert, out);
+        checkWeak(null, cert);
     }
 
     /**
@@ -1729,6 +1759,8 @@
             keyPass = promptForKeyPass(alias, null, storePass);
         }
         keyStore.setKeyEntry(alias, privKey, keyPass, chain);
+
+        checkWeak(null, chain[0]);
     }
 
     /**
@@ -1810,7 +1842,7 @@
     /**
      * Prints a single keystore entry.
      */
-    private void doPrintEntry(String alias, PrintStream out)
+    private void doPrintEntry(String label, String alias, PrintStream out)
         throws Exception
     {
         if (keyStore.containsAlias(alias) == false) {
@@ -1881,12 +1913,14 @@
                         } else {
                             dumpCert(chain[i], out);
                         }
+                        checkWeak(label, chain[i]);
                     }
                 } else {
                     // Print the digest of the user cert only
                     out.println
                         (rb.getString("Certificate.fingerprint.SHA.256.") +
                         getCertFingerPrint("SHA-256", chain[0]));
+                    checkWeak(label, chain);
                 }
             }
         } else if (keyStore.entryInstanceOf(alias,
@@ -1909,6 +1943,7 @@
                 out.println(rb.getString("Certificate.fingerprint.SHA.256.")
                             + getCertFingerPrint("SHA-256", cert));
             }
+            checkWeak(label, cert);
         } else {
             out.println(rb.getString("Unknown.Entry.Type"));
         }
@@ -1992,7 +2027,7 @@
 
         if (srcstorePass == null
                 && !KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {
-            // anti refactoring, copied from printWarning(),
+            // anti refactoring, copied from printNoIntegrityWarning(),
             // but change 2 lines
             System.err.println();
             System.err.println(rb.getString
@@ -2115,6 +2150,10 @@
                 Object[] source = {alias};
                 MessageFormat form = new MessageFormat(rb.getString("Entry.for.alias.alias.successfully.imported."));
                 System.err.println(form.format(source));
+                Certificate c = srckeystore.getCertificate(alias);
+                if (c != null) {
+                    checkWeak(null, c);
+                }
             } else if (result == 2) {
                 if (!noprompt) {
                     String reply = getYesNoReply("Do you want to quit the import process? [no]:  ");
@@ -2154,7 +2193,7 @@
         for (Enumeration<String> e = keyStore.aliases();
                                         e.hasMoreElements(); ) {
             String alias = e.nextElement();
-            doPrintEntry(alias, out);
+            doPrintEntry("<" + alias + ">", alias, out);
             if (verbose || rfc) {
                 out.println(rb.getString("NEWLINE"));
                 out.println(rb.getString
@@ -2300,19 +2339,28 @@
         for (CRL crl: loadCRLs(src)) {
             printCRL(crl, out);
             String issuer = null;
+            Certificate signer = null;
             if (caks != null) {
                 issuer = verifyCRL(caks, crl);
                 if (issuer != null) {
+                    signer = caks.getCertificate(issuer);
                     out.printf(rb.getString(
-                            "verified.by.s.in.s"), issuer, "cacerts");
+                            "verified.by.s.in.s.weak"),
+                            issuer,
+                            "cacerts",
+                            withWeak(signer.getPublicKey()));
                     out.println();
                 }
             }
             if (issuer == null && keyStore != null) {
                 issuer = verifyCRL(keyStore, crl);
                 if (issuer != null) {
+                    signer = keyStore.getCertificate(issuer);
                     out.printf(rb.getString(
-                            "verified.by.s.in.s"), issuer, "keystore");
+                            "verified.by.s.in.s.weak"),
+                            issuer,
+                            "keystore",
+                            withWeak(signer.getPublicKey()));
                     out.println();
                 }
             }
@@ -2324,18 +2372,30 @@
                 out.println(rb.getString
                         ("STARNN"));
             }
+            checkWeak(null, crl, signer == null ? null : signer.getPublicKey());
         }
     }
 
     private void printCRL(CRL crl, PrintStream out)
             throws Exception {
+        X509CRL xcrl = (X509CRL)crl;
         if (rfc) {
-            X509CRL xcrl = (X509CRL)crl;
             out.println("-----BEGIN X509 CRL-----");
             out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(xcrl.getEncoded()));
             out.println("-----END X509 CRL-----");
         } else {
-            out.println(crl.toString());
+            // Hack: Inject "(weak)" into X509CRLImpl::toString.
+            String s = xcrl.toString();
+            String title = "Signature Algorithm: ";
+            int pos = s.indexOf(title);
+            int pos2 = s.indexOf(",", pos);
+            if (pos > 0 && pos2 > 0) {
+                pos += title.length();
+                s = s.substring(0, pos)
+                        + withWeak(s.substring(pos, pos2))
+                        + s.substring(pos2);
+            }
+            out.println(s);
         }
     }
 
@@ -2362,8 +2422,11 @@
         PKCS10 req = new PKCS10(Pem.decode(new String(sb)));
 
         PublicKey pkey = req.getSubjectPublicKeyInfo();
-        out.printf(rb.getString("PKCS.10.Certificate.Request.Version.1.0.Subject.s.Public.Key.s.format.s.key."),
-                req.getSubjectName(), pkey.getFormat(), pkey.getAlgorithm());
+        out.printf(rb.getString("PKCS.10.with.weak"),
+                req.getSubjectName(),
+                pkey.getFormat(),
+                withWeak(pkey),
+                withWeak(req.getSigAlg()));
         for (PKCS10Attribute attr: req.getAttributes().getAttributes()) {
             ObjectIdentifier oid = attr.getAttributeId();
             if (oid.equals(PKCS9Attribute.EXTENSION_REQUEST_OID)) {
@@ -2386,6 +2449,7 @@
         if (debug) {
             out.println(req);   // Just to see more, say, public key length...
         }
+        checkWeak(null, req);
     }
 
     /**
@@ -2425,6 +2489,15 @@
             if (i < (certs.length-1)) {
                 out.println();
             }
+            checkWeak(oneInMany(i, certs.length), x509Cert);
+        }
+    }
+
+    private static String oneInMany(int i, int num) {
+        if (num == 1) {
+            return null;
+        } else {
+            return String.format(rb.getString("one.in.many"), i+1, num);
         }
     }
 
@@ -2458,7 +2531,11 @@
                             out.println();
                             out.println(rb.getString("Signature."));
                             out.println();
-                            for (Certificate cert: signer.getSignerCertPath().getCertificates()) {
+
+                            List<? extends Certificate> certs
+                                    = signer.getSignerCertPath().getCertificates();
+                            int cc = 0;
+                            for (Certificate cert: certs) {
                                 X509Certificate x = (X509Certificate)cert;
                                 if (rfc) {
                                     out.println(rb.getString("Certificate.owner.") + x.getSubjectDN() + "\n");
@@ -2467,12 +2544,15 @@
                                     printX509Cert(x, out);
                                 }
                                 out.println();
+                                checkWeak(oneInMany(cc++, certs.size()), x);
                             }
                             Timestamp ts = signer.getTimestamp();
                             if (ts != null) {
                                 out.println(rb.getString("Timestamp."));
                                 out.println();
-                                for (Certificate cert: ts.getSignerCertPath().getCertificates()) {
+                                certs = ts.getSignerCertPath().getCertificates();
+                                cc = 0;
+                                for (Certificate cert: certs) {
                                     X509Certificate x = (X509Certificate)cert;
                                     if (rfc) {
                                         out.println(rb.getString("Certificate.owner.") + x.getSubjectDN() + "\n");
@@ -2481,6 +2561,7 @@
                                         printX509Cert(x, out);
                                     }
                                     out.println();
+                                    checkWeak(oneInMany(cc++, certs.size()), x);
                                 }
                             }
                         }
@@ -2523,6 +2604,7 @@
                         printX509Cert((X509Certificate)cert, out);
                         out.println();
                     }
+                    checkWeak(oneInMany(i, chain.size()), cert);
                 } catch (Exception e) {
                     if (debug) {
                         e.printStackTrace();
@@ -2698,7 +2780,7 @@
         }
 
         // Now store the newly established chain in the keystore. The new
-        // chain replaces the old one.
+        // chain replaces the old one. The chain can be null if user chooses no.
         if (newChain != null) {
             keyStore.setKeyEntry(alias, privKey,
                                  (keyPass != null) ? keyPass : storePass,
@@ -2735,6 +2817,12 @@
             throw new Exception(rb.getString("Input.not.an.X.509.certificate"));
         }
 
+        if (noprompt) {
+            keyStore.setCertificateEntry(alias, cert);
+            checkWeak(null, cert);
+            return true;
+        }
+
         // if certificate is self-signed, make sure it verifies
         boolean selfSigned = false;
         if (KeyStoreUtil.isSelfSigned(cert)) {
@@ -2742,11 +2830,6 @@
             selfSigned = true;
         }
 
-        if (noprompt) {
-            keyStore.setCertificateEntry(alias, cert);
-            return true;
-        }
-
         // check if cert already exists in keystore
         String reply = null;
         String trustalias = keyStore.getCertificateAlias(cert);
@@ -2755,6 +2838,8 @@
                 ("Certificate.already.exists.in.keystore.under.alias.trustalias."));
             Object[] source = {trustalias};
             System.err.println(form.format(source));
+            checkWeak(null, cert);
+            printWeakWarnings();
             reply = getYesNoReply
                 (rb.getString("Do.you.still.want.to.add.it.no."));
         } else if (selfSigned) {
@@ -2764,6 +2849,8 @@
                         ("Certificate.already.exists.in.system.wide.CA.keystore.under.alias.trustalias."));
                 Object[] source = {trustalias};
                 System.err.println(form.format(source));
+                checkWeak(null, cert);
+                printWeakWarnings();
                 reply = getYesNoReply
                         (rb.getString("Do.you.still.want.to.add.it.to.your.own.keystore.no."));
             }
@@ -2771,6 +2858,8 @@
                 // Print the cert and ask user if they really want to add
                 // it to their keystore
                 printX509Cert(cert, System.out);
+                checkWeak(null, cert);
+                printWeakWarnings();
                 reply = getYesNoReply
                         (rb.getString("Trust.this.certificate.no."));
             }
@@ -2784,6 +2873,7 @@
             }
         }
 
+        // Not found in this keystore and not self-signed
         // Try to establish trust chain
         try {
             Certificate[] chain = establishCertChain(null, cert);
@@ -2795,6 +2885,8 @@
             // Print the cert and ask user if they really want to add it to
             // their keystore
             printX509Cert(cert, System.out);
+            checkWeak(null, cert);
+            printWeakWarnings();
             reply = getYesNoReply
                 (rb.getString("Trust.this.certificate.no."));
             if ("YES".equals(reply)) {
@@ -2933,6 +3025,24 @@
         return keyPass;
     }
 
+    private String withWeak(String alg) {
+        if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, alg, null)) {
+            return alg;
+        } else {
+            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), key.getAlgorithm());
+        } else {
+            return String.format(rb.getString("key.bit.weak"),
+                    KeyUtil.getKeySize(key), key.getAlgorithm());
+        }
+    }
+
     /**
      * Prints a certificate in a human readable format.
      */
@@ -2941,7 +3051,7 @@
     {
 
         MessageFormat form = new MessageFormat
-                (rb.getString(".PATTERN.printX509Cert"));
+                (rb.getString(".PATTERN.printX509Cert.with.weak"));
         PublicKey pkey = cert.getPublicKey();
         Object[] source = {cert.getSubjectDN().toString(),
                         cert.getIssuerDN().toString(),
@@ -2950,10 +3060,9 @@
                         cert.getNotAfter().toString(),
                         getCertFingerPrint("SHA-1", cert),
                         getCertFingerPrint("SHA-256", cert),
-                        cert.getSigAlgName(),
-                        pkey.getAlgorithm(),
-                        KeyUtil.getKeySize(pkey),
-                        cert.getVersion(),
+                        withWeak(cert.getSigAlgName()),
+                        withWeak(pkey),
+                        cert.getVersion()
                         };
         out.println(form.format(source));
 
@@ -3281,7 +3390,7 @@
     /**
      * Prints warning about missing integrity check.
      */
-    private void printWarning() {
+    private void printNoIntegrityWarning() {
         System.err.println();
         System.err.println(rb.getString
             (".WARNING.WARNING.WARNING."));
@@ -3306,6 +3415,9 @@
                                         Certificate[] replyCerts)
         throws Exception
     {
+
+        checkWeak(rb.getString("reply"), replyCerts);
+
         // order the certs in the reply (bottom-up).
         // we know that all certs in the reply are of type X.509, because
         // we parsed them using an X.509 certificate factory
@@ -3369,6 +3481,7 @@
             printX509Cert((X509Certificate)topCert, System.out);
             System.err.println();
             System.err.print(rb.getString(".is.not.trusted."));
+            printWeakWarnings();
             String reply = getYesNoReply
                     (rb.getString("Install.reply.anyway.no."));
             if ("NO".equals(reply)) {
@@ -3383,6 +3496,15 @@
                                  replyCerts.length);
                 tmpCerts[tmpCerts.length-1] = root;
                 replyCerts = tmpCerts;
+                checkWeak(rb.getString("root"), root);
+            }
+            if (!weakWarnings.isEmpty()) {
+                printWeakWarnings();
+                String reply = getYesNoReply
+                        (rb.getString("Install.reply.anyway.no."));
+                if ("NO".equals(reply)) {
+                    return null;
+                }
             }
         }
 
@@ -3391,11 +3513,15 @@
 
     /**
      * Establishes a certificate chain (using trusted certificates in the
-     * keystore), starting with the user certificate
+     * keystore and cacerts), starting with the reply (certToVerify)
      * and ending at a self-signed certificate found in the keystore.
      *
-     * @param userCert the user certificate of the alias
-     * @param certToVerify the single certificate provided in the reply
+     * @param userCert optional existing certificate, mostly likely be the
+     *                 original self-signed cert created by -genkeypair.
+     *                 It must have the same public key as certToVerify
+     *                 but cannot be the same cert.
+     * @param certToVerify the starting certificate to build the chain
+     * @returns the established chain, might be null if user decides not
      */
     private Certificate[] establishCertChain(Certificate userCert,
                                              Certificate certToVerify)
@@ -3423,30 +3549,46 @@
         // Use the subject distinguished name as the key into the hash table.
         // All certificates associated with the same subject distinguished
         // name are stored in the same hash table entry as a vector.
-        Hashtable<Principal, Vector<Certificate>> certs = null;
+        Hashtable<Principal, Vector<Pair<String,X509Certificate>>> certs = null;
         if (keyStore.size() > 0) {
-            certs = new Hashtable<Principal, Vector<Certificate>>(11);
+            certs = new Hashtable<>(11);
             keystorecerts2Hashtable(keyStore, certs);
         }
         if (trustcacerts) {
             if (caks!=null && caks.size()>0) {
                 if (certs == null) {
-                    certs = new Hashtable<Principal, Vector<Certificate>>(11);
+                    certs = new Hashtable<>(11);
                 }
                 keystorecerts2Hashtable(caks, certs);
             }
         }
 
         // start building chain
-        Vector<Certificate> chain = new Vector<>(2);
-        if (buildChain((X509Certificate)certToVerify, chain, certs)) {
-            Certificate[] newChain = new Certificate[chain.size()];
+        Vector<Pair<String,X509Certificate>> chain = new Vector<>(2);
+        if (buildChain(
+                new Pair<>(rb.getString("the.input"),
+                           (X509Certificate) certToVerify),
+                chain, certs)) {
+            for (Pair<String,X509Certificate> p : chain) {
+                checkWeak(p.fst, p.snd);
+            }
+            if (!weakWarnings.isEmpty() && !noprompt) {
+                printWeakWarnings();
+                String reply = getYesNoReply
+                        (rb.getString("Trust.this.certificate.no."));
+                if (!"YES".equals(reply)) {
+                    return null;
+                }
+
+            }
+            Certificate[] newChain =
+                    new Certificate[chain.size()];
             // buildChain() returns chain with self-signed root-cert first and
             // user-cert last, so we need to invert the chain before we store
             // it
             int j=0;
             for (int i=chain.size()-1; i>=0; i--) {
-                newChain[j] = chain.elementAt(i);
+                newChain[j] = chain.elementAt(i).snd;
                 j++;
             }
             return newChain;
@@ -3457,7 +3599,17 @@
     }
 
     /**
-     * Recursively tries to establish chain from pool of trusted certs.
+     * Recursively tries to establish chain from pool of certs starting from
+     * certToVerify until a self-signed cert is found, and fill the certs found
+     * into chain. Each cert in the chain signs the next one.
+     *
+     * This method is able to recover from an error, say, if certToVerify
+     * is signed by certA but certA has no issuer in certs and itself is not
+     * self-signed, the method can try another certB that also signs
+     * certToVerify and look for signer of certB, etc, etc.
+     *
+     * Each cert in chain comes with a label showing its origin. The label is
+     * used in the warning message when the cert is considered a risk.
      *
      * @param certToVerify the cert that needs to be verified.
      * @param chain the chain that's being built.
@@ -3465,19 +3617,20 @@
      *
      * @return true if successful, false otherwise.
      */
-    private boolean buildChain(X509Certificate certToVerify,
-                        Vector<Certificate> chain,
-                        Hashtable<Principal, Vector<Certificate>> certs) {
-        Principal issuer = certToVerify.getIssuerDN();
-        if (KeyStoreUtil.isSelfSigned(certToVerify)) {
+    private boolean buildChain(Pair<String,X509Certificate> certToVerify,
+            Vector<Pair<String,X509Certificate>> chain,
+            Hashtable<Principal, Vector<Pair<String,X509Certificate>>> certs) {
+        if (KeyStoreUtil.isSelfSigned(certToVerify.snd)) {
             // reached self-signed root cert;
             // no verification needed because it's trusted.
             chain.addElement(certToVerify);
             return true;
         }
 
+        Principal issuer = certToVerify.snd.getIssuerDN();
+
         // Get the issuer's certificate(s)
-        Vector<Certificate> vec = certs.get(issuer);
+        Vector<Pair<String,X509Certificate>> vec = certs.get(issuer);
         if (vec == null) {
             return false;
         }
@@ -3485,13 +3638,12 @@
         // Try out each certificate in the vector, until we find one
         // whose public key verifies the signature of the certificate
         // in question.
-        for (Enumeration<Certificate> issuerCerts = vec.elements();
-             issuerCerts.hasMoreElements(); ) {
-            X509Certificate issuerCert
-                = (X509Certificate)issuerCerts.nextElement();
-            PublicKey issuerPubKey = issuerCert.getPublicKey();
+        for (Enumeration<Pair<String,X509Certificate>> issuerCerts = vec.elements();
+                issuerCerts.hasMoreElements(); ) {
+            Pair<String,X509Certificate> issuerCert = issuerCerts.nextElement();
+            PublicKey issuerPubKey = issuerCert.snd.getPublicKey();
             try {
-                certToVerify.verify(issuerPubKey);
+                certToVerify.snd.verify(issuerPubKey);
             } catch (Exception e) {
                 continue;
             }
@@ -3541,10 +3693,11 @@
     /**
      * Stores the (leaf) certificates of a keystore in a hashtable.
      * All certs belonging to the same CA are stored in a vector that
-     * in turn is stored in the hashtable, keyed by the CA's subject DN
+     * in turn is stored in the hashtable, keyed by the CA's subject DN.
+     * Each cert comes with a string label that shows its origin and alias.
      */
     private void keystorecerts2Hashtable(KeyStore ks,
-                Hashtable<Principal, Vector<Certificate>> hash)
+                Hashtable<Principal, Vector<Pair<String,X509Certificate>>> hash)
         throws Exception {
 
         for (Enumeration<String> aliases = ks.aliases();
@@ -3553,13 +3706,19 @@
             Certificate cert = ks.getCertificate(alias);
             if (cert != null) {
                 Principal subjectDN = ((X509Certificate)cert).getSubjectDN();
-                Vector<Certificate> vec = hash.get(subjectDN);
+                Pair<String,X509Certificate> pair = new Pair<>(
+                        ks == caks ?
+                                (String.format(rb.getString("alias.in.cacerts"),
+                                        alias)) :
+                                ("<" + alias + ">"),
+                        (X509Certificate)cert);
+                Vector<Pair<String,X509Certificate>> vec = hash.get(subjectDN);
                 if (vec == null) {
-                    vec = new Vector<Certificate>();
-                    vec.addElement(cert);
+                    vec = new Vector<>();
+                    vec.addElement(pair);
                 } else {
-                    if (!vec.contains(cert)) {
-                        vec.addElement(cert);
+                    if (!vec.contains(pair)) {
+                        vec.addElement(pair);
                     }
                 }
                 hash.put(subjectDN, vec);
@@ -4157,6 +4316,89 @@
         return result;
     }
 
+    private void checkWeak(String label, String sigAlg, Key key) {
+
+        if (!DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, sigAlg, null)) {
+            if (label == null) {
+                weakWarnings.add(String.format(
+                        rb.getString("sigalg.risk"), sigAlg));
+            } else {
+                weakWarnings.add(String.format(
+                        rb.getString("whose.sigalg.risk"), label, sigAlg));
+            }
+        }
+        if (key != null && !DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
+            if (label == null) {
+                weakWarnings.add(String.format(
+                        rb.getString("key.risk"),
+                        String.format(rb.getString("key.bit"),
+                                KeyUtil.getKeySize(key), key.getAlgorithm())));
+            } else {
+                weakWarnings.add(String.format(
+                        rb.getString("whose.key.risk"),
+                        label,
+                        String.format(rb.getString("key.bit"),
+                                KeyUtil.getKeySize(key), key.getAlgorithm())));
+            }
+        }
+    }
+
+    private void checkWeak(String label, Certificate[] certs) {
+        for (int i = 0; i < certs.length; i++) {
+            Certificate cert = certs[i];
+            if (cert instanceof X509Certificate) {
+                X509Certificate xc = (X509Certificate)cert;
+                String fullLabel = label;
+                if (certs.length > 1) {
+                    fullLabel = oneInMany(i, certs.length);
+                    if (label != null) {
+                        fullLabel = String.format(rb.getString("label.which"), label, fullLabel);
+                    }
+                }
+                checkWeak(fullLabel, xc.getSigAlgName(), xc.getPublicKey());
+            }
+        }
+    }
+
+    private void checkWeak(String label, Certificate cert) {
+        if (cert instanceof X509Certificate) {
+            X509Certificate xc = (X509Certificate)cert;
+            checkWeak(label, xc.getSigAlgName(), xc.getPublicKey());
+        }
+    }
+
+    private void checkWeak(String label, PKCS10 p10) {
+        checkWeak(label, p10.getSigAlg(), p10.getSubjectPublicKeyInfo());
+    }
+
+    private void checkWeak(String label, CRL crl, Key key) {
+        if (crl instanceof X509CRLImpl) {
+            X509CRLImpl impl = (X509CRLImpl)crl;
+            checkWeak(label, impl.getSigAlgName(), key);
+        }
+    }
+
+    private void printWeakWarningsWithoutNewLine() {
+        if (!weakWarnings.isEmpty() && !nowarn) {
+            System.err.println("\nWarning:");
+            for (String warning : weakWarnings) {
+                System.err.println(warning);
+            }
+        }
+        weakWarnings.clear();
+    }
+
+    private void printWeakWarnings() {
+        if (!weakWarnings.isEmpty() && !nowarn) {
+            System.err.println("\nWarning:");
+            for (String warning : weakWarnings) {
+                System.err.println(warning);
+            }
+            System.err.println();
+        }
+        weakWarnings.clear();
+    }
+
     /**
      * Prints the usage of this tool.
      */
--- old/src/java.base/share/classes/sun/security/tools/keytool/Resources.java	2017-01-23 18:01:57.262111100 +0800
+++ new/src/java.base/share/classes/sun/security/tools/keytool/Resources.java	2017-01-23 18:01:57.087527000 +0800
@@ -360,8 +360,6 @@
         {"Enter.alias.name.", "Enter alias name:  "},
         {".RETURN.if.same.as.for.otherAlias.",
                 "\t(RETURN if same as for <{0}>)"},
-        {".PATTERN.printX509Cert",
-                "Owner: {0}\nIssuer: {1}\nSerial number: {2}\nValid from: {3} until: {4}\nCertificate fingerprints:\n\t SHA1: {5}\n\t SHA256: {6}\nSignature algorithm name: {7}\nSubject Public Key Algorithm: {8} ({9,number,#})\nVersion: {10}"},
         {"What.is.your.first.and.last.name.",
                 "What is your first and last name?"},
         {"What.is.the.name.of.your.organizational.unit.",
@@ -428,16 +426,12 @@
         {"Please.provide.keysize.for.secret.key.generation",
                 "Please provide -keysize for secret key generation"},
 
-        {"verified.by.s.in.s", "Verified by %s in %s"},
         {"warning.not.verified.make.sure.keystore.is.correct",
             "WARNING: not verified. Make sure -keystore is correct."},
 
         {"Extensions.", "Extensions: "},
         {".Empty.value.", "(Empty value)"},
         {"Extension.Request.", "Extension Request:"},
-        {"PKCS.10.Certificate.Request.Version.1.0.Subject.s.Public.Key.s.format.s.key.",
-                "PKCS #10 Certificate Request (Version 1.0)\n" +
-                "Subject: %s\nPublic Key: %s format %s key\n"},
         {"Unknown.keyUsage.type.", "Unknown keyUsage type: "},
         {"Unknown.extendedkeyUsage.type.", "Unknown extendedkeyUsage type: "},
         {"Unknown.AccessDescription.type.", "Unknown AccessDescription type: "},
@@ -446,7 +440,31 @@
                  "This extension cannot be marked as critical. "},
         {"Odd.number.of.hex.digits.found.", "Odd number of hex digits found: "},
         {"Unknown.extension.type.", "Unknown extension type: "},
-        {"command.{0}.is.ambiguous.", "command {0} is ambiguous:"}
+        {"command.{0}.is.ambiguous.", "command {0} is ambiguous:"},
+
+        // 8171319: keytool should print out warnings when reading or
+        // generating cert/cert req using weak algorithms
+        {"the.input", "The input"},
+        {"the.output", "The output"},
+        {"the.issuer", "The issuer"},
+        {"reply", "Reply"},
+        {"root", "Root"},
+        {"one.in.many", "#%d of %d"},
+        {"label.which", "%s %s"},
+        {"alias.in.cacerts", "<%s> in cacerts"},
+        {"with.weak", "%s (weak)"},
+        {"key.bit", "%d-bit %s key"},
+        {"key.bit.weak", "%d-bit %s key (weak)"},
+        {".PATTERN.printX509Cert.with.weak",
+                "Owner: {0}\nIssuer: {1}\nSerial number: {2}\nValid from: {3} until: {4}\nCertificate fingerprints:\n\t SHA1: {5}\n\t SHA256: {6}\nSignature algorithm name: {7}\nSubject Public Key Algorithm: {8}\nVersion: {9}"},
+        {"PKCS.10.with.weak",
+                "PKCS #10 Certificate Request (Version 1.0)\n" +
+                        "Subject: %s\nFormat: %s\nPublic Key: %s\nSignature algorithm: %s\n"},
+        {"verified.by.s.in.s.weak", "Verified by %s in %s with a %s"},
+        {"sigalg.risk", "The %s signature algorithm is considered a security risk."},
+        {"whose.sigalg.risk", "%s's %s signature algorithm is considered a security risk."},
+        {"key.risk", "A %s is considered a security risk."},
+        {"whose.key.risk", "%s's %s is considered a security risk."},
     };
 
 
--- old/test/sun/security/tools/jarsigner/TimestampCheck.java	2017-01-23 18:01:58.015727300 +0800
+++ new/test/sun/security/tools/jarsigner/TimestampCheck.java	2017-01-23 18:01:57.839484000 +0800
@@ -575,7 +575,8 @@
             genCmd += " " + s;
         }
         keytool(genCmd);
-        keytool("-alias " + alias + " -importcert -file " + alias + ".cert");
+        keytool("-alias " + alias + " -importcert -file "
+                + alias + ".cert -noprompt");
     }
 
     static void keytool(String cmd) throws Exception {
--- old/test/sun/security/tools/jarsigner/Warning.java	2017-01-23 18:01:58.756008300 +0800
+++ new/test/sun/security/tools/jarsigner/Warning.java	2017-01-23 18:01:58.580159400 +0800
@@ -199,7 +199,7 @@
         }
         String cert = run("keytool", args, req)
                 .shouldHaveExitValue(0).getStdout();
-        run("keytool", "-import -alias " + alias, cert).shouldHaveExitValue(0);
+        run("keytool", "-import -noprompt -alias " + alias, cert).shouldHaveExitValue(0);
     }
 
     // Runs a java tool with command line arguments
--- old/test/sun/security/tools/jarsigner/concise_jarsigner.sh	2017-01-23 18:01:59.489501500 +0800
+++ new/test/sun/security/tools/jarsigner/concise_jarsigner.sh	2017-01-23 18:01:59.308240100 +0800
@@ -235,7 +235,7 @@
 $KT -certreq -alias catwo | $KT -gencert -alias caone -sigalg MD5withRSA -rfc > catwo.cert
 
 # This certchain contains a cross-signed weak catwo.cert
-cat ee.cert catwo.cert | $KT -importcert -alias ee
+cat ee.cert catwo.cert | $KT -importcert -alias ee -noprompt
 
 $JAR cvf a.jar A1.class
 $JARSIGNER -strict -keystore $KS -storepass changeit a.jar ee
--- old/test/sun/security/tools/jarsigner/weaksize.sh	2017-01-23 18:02:00.201340900 +0800
+++ new/test/sun/security/tools/jarsigner/weaksize.sh	2017-01-23 18:02:00.025185000 +0800
@@ -49,7 +49,7 @@
 
 $KT -certreq -alias signer | \
         $KT -gencert -alias ca -ext ku=dS -rfc | \
-        $KT -importcert -alias signer
+        $KT -importcert -alias signer -noprompt
 
 $JAR cvf a.jar ks
 
--- old/test/sun/security/validator/certreplace.sh	2017-01-23 18:02:00.926118800 +0800
+++ new/test/sun/security/validator/certreplace.sh	2017-01-23 18:02:00.758575100 +0800
@@ -64,9 +64,9 @@
 # 2. Signing: ca -> int -> user
 
 $KT -certreq -alias int | $KT -gencert -rfc -alias ca -ext bc \
-    | $KT -import -alias int
+    | $KT -import -alias int -noprompt
 $KT -certreq -alias user | $KT -gencert -rfc -alias int \
-    | $KT -import -alias user
+    | $KT -import -alias user -noprompt
 
 # 3. Create the certchain file
 
--- /dev/null	2017-01-19 08:23:44.229040998 +0800
+++ new/test/sun/security/tools/keytool/WeakAlg.java	2017-01-23 18:02:01.463526100 +0800
@@ -0,0 +1,496 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8171319
+ * @summary keytool should print out warnings when reading or generating
+  *         cert/cert req using weak algorithms
+ * @library /test/lib
+ * @modules java.base/sun.security.tools.keytool
+ *          java.base/sun.security.tools
+ *          java.base/sun.security.util
+ * @run main/othervm/timeout=600 -Duser.language=en -Duser.country=US WeakAlg
+ */
+
+import jdk.test.lib.SecurityTools;
+import jdk.test.lib.process.OutputAnalyzer;
+import sun.security.tools.KeyStoreUtil;
+import sun.security.util.DisabledAlgorithmConstraints;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.security.CryptoPrimitive;
+import java.security.KeyStore;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class WeakAlg {
+
+    public static void main(String[] args) throws Throwable {
+
+        Files.deleteIfExists(Paths.get("ks"));
+
+        // -genkeypair, and -printcert, -list -alias, -exportcert
+        // (w/ different formats)
+        checkGenKeyPair("a", "-keyalg RSA -sigalg MD5withRSA", "MD5withRSA");
+        checkGenKeyPair("b", "-keyalg RSA -keysize 512", "512-bit RSA key");
+        checkGenKeyPair("c", "-keyalg RSA", null);
+
+        kt("-list")
+                .shouldContain("Warning:")
+                .shouldMatch("<a>'s MD5withRSA.*risk")
+                .shouldMatch("<b>'s 512-bit RSA key.*risk");
+        kt("-list -v")
+                .shouldContain("Warning:")
+                .shouldMatch("<a>'s MD5withRSA.*risk")
+                .shouldContain("MD5withRSA (weak)")
+                .shouldMatch("<b>'s 512-bit RSA key.*risk")
+                .shouldContain("512-bit RSA key (weak)");
+
+        // Multiple warnings for multiple cert in -printcert or -list or -exportcert
+
+        // -certreq, -printcertreq, -gencert
+        checkCertReq("a", "", null);
+        gencert("c-a", "")
+                .shouldNotContain("Warning"); // new sigalg is not weak
+        gencert("c-a", "-sigalg MD2withRSA")
+                .shouldContain("Warning:")
+                .shouldMatch("The output's MD2withRSA.*risk");
+
+        checkCertReq("a", "-sigalg MD5withRSA", "MD5withRSA");
+        gencert("c-a", "")
+                .shouldContain("Warning:")
+                .shouldMatch("The input's MD5withRSA.*risk");
+        gencert("c-a", "-sigalg MD2withRSA")
+                .shouldContain("Warning:")
+                .shouldMatch("The input's MD5withRSA.*risk")
+                .shouldMatch("The output's MD2withRSA.*risk");
+
+        checkCertReq("b", "", "512-bit RSA key");
+        gencert("c-b", "")
+                .shouldContain("Warning:")
+                .shouldMatch("The input's 512-bit RSA key.*risk")
+                .shouldMatch("The output's 512-bit RSA key.*risk");
+
+        checkCertReq("c", "", null);
+        gencert("a-c", "")
+                .shouldContain("Warning:")
+                .shouldMatch("The issuer's MD5withRSA.*risk");
+
+        // but the new cert is not weak
+        kt("-printcert -file a-c.cert")
+                .shouldNotContain("Warning")
+                .shouldNotContain("weak");
+
+        gencert("b-c", "")
+                .shouldContain("Warning:")
+                .shouldMatch("The issuer's 512-bit RSA key.*risk");
+
+        // -importcert
+        checkImport();
+
+        // -gencrl, -printcrl
+
+        checkGenCRL("a", "", null);
+        checkGenCRL("a", "-sigalg MD5withRSA", "MD5withRSA");
+        checkGenCRL("b", "", "512-bit RSA key");
+        checkGenCRL("c", "", null);
+
+        kt("-delete -alias b");
+        kt("-printcrl -file b.crl")
+                .shouldContain("WARNING: not verified");
+    }
+
+    static void checkImport() throws Exception {
+
+        saveStore();
+
+        // add trusted cert
+
+        // cert already in
+        kt("-importcert -alias d -file a.cert", "no")
+                .shouldContain("Certificate already exists in keystore")
+                .shouldContain("Warning")
+                .shouldMatch("MD5withRSA.*risk")
+                .shouldContain("Do you still want to add it?");
+        kt("-importcert -alias d -file a.cert -noprompt")
+                .shouldContain("Warning")
+                .shouldMatch("MD5withRSA.*risk")
+                .shouldNotContain("[no]");
+
+        // cert is self-signed
+        kt("-delete -alias a");
+        kt("-delete -alias d");
+        kt("-importcert -alias d -file a.cert", "no")
+                .shouldContain("Warning")
+                .shouldContain("MD5withRSA (weak)")
+                .shouldMatch("MD5withRSA.*risk")
+                .shouldContain("Trust this certificate?");
+        kt("-importcert -alias d -file a.cert -noprompt")
+                .shouldContain("Warning")
+                .shouldMatch("MD5withRSA.*risk")
+                .shouldNotContain("[no]");
+
+        // cert is self-signed cacerts
+        String weakSigAlgCA = null;
+        KeyStore ks = KeyStoreUtil.getCacertsKeyStore();
+        if (ks != null) {
+            DisabledAlgorithmConstraints disabledCheck =
+                    new DisabledAlgorithmConstraints(
+                            DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS);
+            Set<CryptoPrimitive> sigPrimitiveSet = Collections
+                    .unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));
+
+            for (String s : Collections.list(ks.aliases())) {
+                if (ks.isCertificateEntry(s)) {
+                    X509Certificate c = (X509Certificate)ks.getCertificate(s);
+                    String sigAlg = c.getSigAlgName();
+                    if (!disabledCheck.permits(sigPrimitiveSet, sigAlg, null)) {
+                        weakSigAlgCA = sigAlg;
+                        Files.write(Paths.get("ca.cert"),
+                                ks.getCertificate(s).getEncoded());
+                        break;
+                    }
+                }
+            }
+        }
+        if (weakSigAlgCA != null) {
+            kt("-delete -alias d");
+            kt("-importcert -alias d -trustcacerts -file ca.cert", "no")
+                    .shouldContain("Certificate already exists in system-wide CA")
+                    .shouldContain("Warning")
+                    .shouldMatch(weakSigAlgCA + ".*risk")
+                    .shouldContain("Do you still want to add it to your own keystore?");
+            kt("-importcert -alias d -file ca.cert -noprompt")
+                    .shouldContain("Warning")
+                    .shouldMatch(weakSigAlgCA + ".*risk")
+                    .shouldNotContain("[no]");
+        }
+
+        // a non self-signed weak cert
+        reStore();
+        certreq("b", "");
+        gencert("c-b", "");
+        kt("-importcert -alias d -file c-b.cert", "no") // weak but trusted
+                .shouldContain("Warning")
+                .shouldNotContain("512-bit RSA key (weak)")
+                .shouldMatch("The input's 512-bit RSA key.*risk")
+                .shouldContain("Trust this certificate?");
+        kt("-importcert -alias d -file c-b.cert -noprompt")
+                .shouldContain("Warning")
+                .shouldMatch("512-bit RSA key.*risk")
+                .shouldNotContain("[no]");
+
+        kt("-delete -alias b");
+        kt("-delete -alias c");
+        kt("-delete -alias d");
+
+        kt("-importcert -alias d -file c-b.cert", "no") // weak and not trusted
+                .shouldContain("Warning")
+                .shouldContain("512-bit RSA key (weak)")
+                .shouldMatch("512-bit RSA key.*risk")
+                .shouldContain("Trust this certificate?");
+        kt("-importcert -alias d -file c-b.cert -noprompt")
+                .shouldContain("Warning")
+                .shouldMatch("512-bit RSA key.*risk")
+                .shouldNotContain("[no]");
+
+        // a non self-signed strong cert
+        reStore();
+        certreq("a", "");
+        gencert("c-a", "");
+        kt("-importcert -alias d -file c-a.cert") // trusted
+                .shouldNotContain("Warning")
+                .shouldNotContain("[no]");
+
+        kt("-delete -alias a");
+        kt("-delete -alias c");
+        kt("-delete -alias d");
+
+        kt("-importcert -alias d -file c-a.cert", "no") // not trusted
+                .shouldNotContain("Warning")
+                .shouldContain("Trust this certificate?");
+        kt("-importcert -alias d -file c-a.cert -noprompt")
+                .shouldNotContain("Warning")
+                .shouldNotContain("[no]");
+
+        // install reply
+
+        reStore();
+
+        kt("-importcert -alias a -file c-a.cert")
+                .shouldNotContain("Warning");
+
+        kt("-importcert -alias b -file c-b.cert", "no")
+                .shouldContain("Warning")
+                .shouldMatch("The input's 512-bit RSA key.*risk")
+                .shouldContain("Trust this certificate?");
+
+        reStore();
+        gencert("b-a", "");
+        ByteArrayOutputStream bout = new ByteArrayOutputStream();
+        bout.write(Files.readAllBytes(Paths.get("b-a.cert")));
+        bout.write(Files.readAllBytes(Paths.get("c-b.cert")));
+        Files.write(Paths.get("c-b-a.cert"), bout.toByteArray());
+
+
+        kt("-delete -alias b");
+
+        kt("-importcert -alias a -file c-b-a.cert", "no")
+                .shouldContain("Warning")
+                .shouldMatch("Reply #2 of 2's 512-bit RSA key.*risk")
+                .shouldContain("Install reply anyway?");
+        kt("-importcert -alias a -file c-b-a.cert -noprompt")
+                .shouldContain("Warning")
+                .shouldMatch("Reply #2 of 2's 512-bit RSA key.*risk")
+                .shouldNotContain("[no]");
+
+        kt("-delete -alias c");
+        kt("-importcert -alias a -file c-b-a.cert", "no")
+                .shouldContain("Top-level certificate in reply:")
+                .shouldContain("512-bit RSA key (weak)")
+                .shouldContain("Warning")
+                .shouldMatch("Reply #2 of 2's 512-bit RSA key.*risk")
+                .shouldContain("Install reply anyway?");
+        kt("-importcert -alias a -file c-b-a.cert -noprompt")
+                .shouldContain("Warning")
+                .shouldMatch("Reply #2 of 2's 512-bit RSA key.*risk")
+                .shouldNotContain("[no]");
+
+        reStore();
+    }
+
+    static void checkGenCRL(String alias, String options, String bad) {
+
+        OutputAnalyzer oa = kt("-gencrl -alias " + alias
+                + " -id 1 -file " + alias + ".crl " + options);
+        if (bad == null) {
+            oa.shouldNotContain("Warning");
+        } else {
+            oa.shouldContain("Warning")
+                    .shouldMatch(bad + ".*risk");
+        }
+
+        oa = kt("-printcrl -file " + alias + ".crl");
+        if (bad == null) {
+            oa.shouldNotContain("Warning")
+                    .shouldContain("Verified by " + alias + " in keystore")
+                    .shouldNotContain("(weak");
+        } else {
+            oa.shouldContain("Warning:")
+                    .shouldMatch(bad + ".*risk")
+                    .shouldContain("Verified by " + alias + " in keystore")
+                    .shouldContain(bad + " (weak)");
+        }
+    }
+
+    static void checkCertReq(
+            String alias, String options, String bad) {
+
+        OutputAnalyzer oa = certreq(alias, options);
+        if (bad == null) {
+            oa.shouldNotContain("Warning");
+        } else {
+            oa.shouldContain("Warning")
+                    .shouldMatch(bad + ".*risk");
+        }
+
+        oa = kt("-printcertreq -file " + alias + ".req");
+        if (bad == null) {
+            oa.shouldNotContain("Warning")
+                    .shouldNotContain("(weak)");
+        } else {
+            oa.shouldContain("Warning")
+                    .shouldMatch(bad + ".*risk")
+                    .shouldContain(bad + " (weak)");
+        }
+    }
+
+    static void checkGenKeyPair(
+            String alias, String options, String bad) {
+
+        OutputAnalyzer oa = genkeypair(alias, options);
+        if (bad == null) {
+            oa.shouldNotContain("Warning");
+        } else {
+            oa.shouldContain("Warning")
+                    .shouldMatch(bad + ".*risk");
+        }
+
+        oa = kt("-exportcert -alias " + alias + " -file " + alias + ".cert");
+        if (bad == null) {
+            oa.shouldNotContain("Warning");
+        } else {
+            oa.shouldContain("Warning")
+                    .shouldMatch(bad + ".*risk");
+        }
+
+        oa = kt("-exportcert -rfc -alias " + alias + " -file " + alias + ".cert");
+        if (bad == null) {
+            oa.shouldNotContain("Warning");
+        } else {
+            oa.shouldContain("Warning")
+                    .shouldMatch(bad + ".*risk");
+        }
+
+        oa = kt("-printcert -rfc -file " + alias + ".cert");
+        if (bad == null) {
+            oa.shouldNotContain("Warning");
+        } else {
+            oa.shouldContain("Warning")
+                    .shouldMatch(bad + ".*risk");
+        }
+
+        oa = kt("-list -alias " + alias);
+        if (bad == null) {
+            oa.shouldNotContain("Warning");
+        } else {
+            oa.shouldContain("Warning")
+                    .shouldMatch(bad + ".*risk");
+        }
+
+        // With cert content
+
+        oa = kt("-printcert -file " + alias + ".cert");
+        if (bad == null) {
+            oa.shouldNotContain("Warning");
+        } else {
+            oa.shouldContain("Warning")
+                    .shouldContain(bad + " (weak)")
+                    .shouldMatch(bad + ".*risk");
+        }
+
+        oa = kt("-list -v -alias " + alias);
+        if (bad == null) {
+            oa.shouldNotContain("Warning");
+        } else {
+            oa.shouldContain("Warning")
+                    .shouldContain(bad + " (weak)")
+                    .shouldMatch(bad + ".*risk");
+        }
+    }
+
+    // This is slow, but real keytool process is launched.
+    static OutputAnalyzer kt1(String cmd, String... input) {
+        cmd = "-keystore ks -storepass changeit " +
+                "-keypass changeit " + cmd;
+        System.out.println("---------------------------------------------");
+        try {
+            SecurityTools.setResponse(input);
+            return SecurityTools.keytool(cmd);
+        } catch (Throwable e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    // Fast keytool execution by directly calling its main() method
+    static OutputAnalyzer kt(String cmd, String... input) {
+        PrintStream out = System.out;
+        PrintStream err = System.err;
+        InputStream ins = System.in;
+        ByteArrayOutputStream bout = new ByteArrayOutputStream();
+        ByteArrayOutputStream berr = new ByteArrayOutputStream();
+        boolean succeed = true;
+        try {
+            cmd = "-keystore ks -storepass changeit " +
+                    "-keypass changeit " + cmd;
+            System.out.println("---------------------------------------------");
+            System.out.println("$ keytool " + cmd);
+            System.out.println();
+            String feed = "";
+            if (input.length > 0) {
+                feed = Stream.of(input).collect(Collectors.joining("\n")) + "\n";
+            }
+            System.setIn(new ByteArrayInputStream(feed.getBytes()));
+            System.setOut(new PrintStream(bout));
+            System.setErr(new PrintStream(berr));
+            sun.security.tools.keytool.Main.main(
+                    cmd.trim().split("\\s+"));
+        } catch (Exception e) {
+            // Might be a normal exception when -debug is on or
+            // SecurityException (thrown by jtreg) when System.exit() is called
+            if (!(e instanceof SecurityException)) {
+                e.printStackTrace();
+            }
+            succeed = false;
+        } finally {
+            System.setOut(out);
+            System.setErr(err);
+            System.setIn(ins);
+        }
+        String sout = new String(bout.toByteArray());
+        String serr = new String(berr.toByteArray());
+        System.out.println("STDOUT:\n" + sout + "\nSTDERR:\n" + serr);
+        if (!succeed) {
+            throw new RuntimeException();
+        }
+        return new OutputAnalyzer(sout, serr);
+    }
+
+    static OutputAnalyzer genkeypair(String alias, String options) {
+        return kt("-genkeypair -alias " + alias + " -dname CN=" + alias
+                + " -keyalg RSA -storetype JKS " + options);
+    }
+
+    static OutputAnalyzer certreq(String alias, String options) {
+        return kt("-certreq -alias " + alias
+                + " -file " + alias + ".req " + options);
+    }
+
+    static OutputAnalyzer exportcert(String alias) {
+        return kt("-exportcert -alias " + alias + " -file " + alias + ".cert");
+    }
+
+    static OutputAnalyzer gencert(String relation, String options) {
+        int pos = relation.indexOf("-");
+        String issuer = relation.substring(0, pos);
+        String subject = relation.substring(pos + 1);
+        return kt(" -gencert -alias " + issuer + " -infile " + subject
+                + ".req -outfile " + relation + ".cert " + options);
+    }
+
+    static void saveStore() throws IOException {
+        System.out.println("---------------------------------------------");
+        System.out.println("$ cp ks ks2");
+        Files.copy(Paths.get("ks"), Paths.get("ks2"),
+                StandardCopyOption.REPLACE_EXISTING);
+    }
+
+    static void reStore() throws IOException {
+        System.out.println("---------------------------------------------");
+        System.out.println("$ cp ks2 ks");
+        Files.copy(Paths.get("ks2"), Paths.get("ks"),
+                StandardCopyOption.REPLACE_EXISTING);
+    }
+}