changeset 12941:947709b7e632 jdk8u152-b11

Merge
author asaha
date Tue, 08 Aug 2017 09:56:25 -0700
parents b427b577ba12 (current diff) 9d8e863a45b8 (diff)
children 9745635175b8
files .hgtags
diffstat 8 files changed, 1312 insertions(+), 120 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Thu Aug 03 00:24:48 2017 -0700
+++ b/.hgtags	Tue Aug 08 09:56:25 2017 -0700
@@ -720,6 +720,7 @@
 0efdf2c7a21464e5f3d89474ffdfe81db61031fd jdk8u151-b05
 c6c870e267de694bc85dc4af23a648824063f95b jdk8u151-b06
 84b0fbbfb8d664031a9f5283f79b13b98714cc7f jdk8u151-b07
+8fd79358682edc86abaac1c839486834410be74b jdk8u151-b08
 1442bc728814af451e2dd1a6719a64485d27e3a0 jdk8u122-b00
 f6030acfa5aec0e64d45adfac69b9e7e5c12bc74 jdk8u122-b01
 6b072c3a6db7ab06804c91aab77431799dfb5d47 jdk8u122-b02
--- a/src/share/classes/sun/security/pkcs10/PKCS10.java	Thu Aug 03 00:24:48 2017 -0700
+++ b/src/share/classes/sun/security/pkcs10/PKCS10.java	Tue Aug 08 09:56:25 2017 -0700
@@ -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
 }
--- a/src/share/classes/sun/security/provider/certpath/BasicChecker.java	Thu Aug 03 00:24:48 2017 -0700
+++ b/src/share/classes/sun/security/provider/certpath/BasicChecker.java	Tue Aug 08 09:56:25 2017 -0700
@@ -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() + "...");
 
--- a/src/share/classes/sun/security/tools/keytool/Main.java	Thu Aug 03 00:24:48 2017 -0700
+++ b/src/share/classes/sun/security/tools/keytool/Main.java	Tue Aug 08 09:56:25 2017 -0700
@@ -26,7 +26,10 @@
 package sun.security.tools.keytool;
 
 import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Paths;
 import java.security.CodeSigner;
+import java.security.CryptoPrimitive;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
 import java.security.MessageDigest;
@@ -64,6 +67,9 @@
 import java.security.cert.X509CRLSelector;
 import javax.security.auth.x500.X500Principal;
 import java.util.Base64;
+
+import sun.security.util.DisabledAlgorithmConstraints;
+import sun.security.util.KeyUtil;
 import sun.security.util.ObjectIdentifier;
 import sun.security.pkcs10.PKCS10;
 import sun.security.pkcs10.PKCS10Attribute;
@@ -147,6 +153,7 @@
     private boolean kssave = false;
     private boolean noprompt = false;
     private boolean trustcacerts = false;
+    private boolean nowarn = false;
     private boolean protectedPath = false;
     private boolean srcprotectedPath = false;
     private CertificateFactory cf = null;
@@ -159,6 +166,21 @@
     private List<String> ids = new ArrayList<>();   // used in GENCRL
     private List<String> v3ext = new ArrayList<>();
 
+    // In-place importkeystore is special.
+    // A backup is needed, and no need to prompt for deststorepass.
+    private boolean inplaceImport = false;
+    private String inplaceBackupName = null;
+
+    // Warnings on weak algorithms etc
+    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,
@@ -317,7 +339,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 =
@@ -353,6 +375,7 @@
                 throw e;
             }
         } finally {
+            printWeakWarnings(false);
             for (char[] pass : passwords) {
                 if (pass != null) {
                     Arrays.fill(pass, ' ');
@@ -420,12 +443,10 @@
                 command = IMPORTCERT;
             } else if (collator.compare(flags, "-importpassword") == 0) {
                 command = IMPORTPASS;
-            }
-            /*
-             * Help
-             */
-            else if (collator.compare(flags, "-help") == 0) {
+            } else if (collator.compare(flags, "-help") == 0) {
                 help = true;
+            } else if (collator.compare(flags, "-nowarn") == 0) {
+                nowarn = true;
             }
 
             /*
@@ -713,18 +734,34 @@
                 ("New.password.must.be.at.least.6.characters"));
         }
 
+        // Set this before inplaceImport check so we can compare name.
+        if (ksfname == null) {
+            ksfname = System.getProperty("user.home") + File.separator
+                    + ".keystore";
+        }
+
+        KeyStore srcKeyStore = null;
+        if (command == IMPORTKEYSTORE) {
+            inplaceImport = inplaceImportCheck();
+            if (inplaceImport) {
+                // We load srckeystore first so we have srcstorePass that
+                // can be assigned to storePass
+                srcKeyStore = loadSourceKeyStore();
+                if (storePass == null) {
+                    storePass = srcstorePass;
+                }
+            }
+        }
+
         // Check if keystore exists.
         // If no keystore has been specified at the command line, try to use
         // the default, which is located in $HOME/.keystore.
         // If the command is "genkey", "identitydb", "import", or "printcert",
         // it is OK not to have a keystore.
-        if (isKeyStoreRelated(command)) {
-            if (ksfname == null) {
-                ksfname = System.getProperty("user.home") + File.separator
-                    + ".keystore";
-            }
-
-            if (!nullStream) {
+
+        // DO NOT open the existing keystore if this is an in-place import.
+        // The keystore should be created as brand new.
+        if (isKeyStoreRelated(command) && !nullStream && !inplaceImport) {
                 try {
                     ksfile = new File(ksfname);
                     // Check if keystore file is empty
@@ -746,7 +783,6 @@
                     }
                 }
             }
-        }
 
         if ((command == KEYCLONE || command == CHANGEALIAS)
                 && dest == null) {
@@ -792,7 +828,11 @@
          * Null stream keystores are loaded later.
          */
         if (!nullStream) {
+            if (inplaceImport) {
+                keyStore.load(null, storePass);
+            } else {
             keyStore.load(ksStream, storePass);
+            }
             if (ksStream != null) {
                 ksStream.close();
             }
@@ -920,6 +960,13 @@
             cf = CertificateFactory.getInstance("X509");
         }
 
+        // -trustcacerts can only be specified on -importcert.
+        // Reset it so that warnings on CA cert will remain for
+        // -printcert, etc.
+        if (command != IMPORTCERT) {
+            trustcacerts = false;
+        }
+
         if (trustcacerts) {
             caks = KeyStoreUtil.getCacertsKeyStore();
         }
@@ -1021,7 +1068,11 @@
                 }
             }
         } else if (command == IMPORTKEYSTORE) {
-            doImportKeyStore();
+            // When not in-place import, srcKeyStore is not loaded yet.
+            if (srcKeyStore == null) {
+                srcKeyStore = loadSourceKeyStore();
+            }
+            doImportKeyStore(srcKeyStore);
             kssave = true;
         } else if (command == KEYCLONE) {
             keyPassNew = newPass;
@@ -1060,8 +1111,13 @@
             doChangeKeyPasswd(alias);
             kssave = true;
         } else if (command == LIST) {
+            if (storePass == null
+                    && !KeyStoreUtil.isWindowsKeyStore(storetype)) {
+                printNoIntegrityWarning();
+            }
+
             if (alias != null) {
-                doPrintEntry(alias, out, true);
+                doPrintEntry(rb.getString("the.certificate"), alias, out);
             } else {
                 doPrintEntries(out);
             }
@@ -1147,6 +1203,65 @@
                 }
             }
         }
+
+        if (isKeyStoreRelated(command)
+                && !token && !nullStream && ksfname != null) {
+
+            // JKS storetype warning on the final result keystore
+            File f = new File(ksfname);
+            if (f.exists()) {
+                // Read the first 4 bytes to determine
+                // if we're dealing with JKS/JCEKS type store
+                String realType = keyStoreType(f);
+                if (realType.equalsIgnoreCase("JKS")
+                    || realType.equalsIgnoreCase("JCEKS")) {
+                    boolean allCerts = true;
+                    for (String a : Collections.list(keyStore.aliases())) {
+                        if (!keyStore.entryInstanceOf(
+                                a, TrustedCertificateEntry.class)) {
+                            allCerts = false;
+                            break;
+                        }
+                    }
+                    // Don't warn for "cacerts" style keystore.
+                    if (!allCerts) {
+                        weakWarnings.add(String.format(
+                                rb.getString("jks.storetype.warning"),
+                                realType, ksfname));
+                    }
+                }
+                if (inplaceImport) {
+                    String realSourceStoreType =
+                        keyStoreType(new File(inplaceBackupName));
+                    String format =
+                            realType.equalsIgnoreCase(realSourceStoreType) ?
+                            rb.getString("backup.keystore.warning") :
+                            rb.getString("migrate.keystore.warning");
+                    weakWarnings.add(
+                            String.format(format,
+                                    srcksfname,
+                                    realSourceStoreType,
+                                    inplaceBackupName,
+                                    realType));
+                }
+            }
+        }
+    }
+
+    private String keyStoreType(File f) throws IOException {
+        int MAGIC = 0xfeedfeed;
+        int JCEKS_MAGIC = 0xcececece;
+        try (DataInputStream dis = new DataInputStream(
+            new FileInputStream(f))) {
+            int xMagic = dis.readInt();
+            if (xMagic == MAGIC) {
+                return "JKS";
+            } else if (xMagic == JCEKS_MAGIC) {
+                return "JCEKS";
+            } else {
+                return "Non JKS/JCEKS";
+            }
+        }
     }
 
     /**
@@ -1158,6 +1273,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);
@@ -1211,6 +1332,8 @@
         byte[] rawReq = Pem.decode(new String(sb));
         PKCS10 req = new PKCS10(rawReq);
 
+        checkWeak(rb.getString("the.certificate.request"), req);
+
         info.set(X509CertInfo.KEY, new CertificateX509Key(req.getSubjectPublicKeyInfo()));
         info.set(X509CertInfo.SUBJECT,
                     dname==null?req.getSubjectName():new X500Name(dname));
@@ -1240,6 +1363,9 @@
                 }
             }
         }
+
+        checkWeak(rb.getString("the.issuer"), keyStore.getCertificateChain(alias));
+        checkWeak(rb.getString("the.generated.certificate"), cert);
     }
 
     private void doGenCRL(PrintStream out)
@@ -1290,6 +1416,7 @@
         } else {
             out.write(crl.getEncodedInternal());
         }
+        checkWeak(rb.getString("the.generated.crl"), crl, privateKey);
     }
 
     /**
@@ -1336,6 +1463,8 @@
         // Sign the request and base-64 encode it
         request.encodeAndSign(subject, signature);
         request.print(out);
+
+        checkWeak(rb.getString("the.generated.certificate.request"), request);
     }
 
     /**
@@ -1359,7 +1488,7 @@
     {
         if (storePass == null
                 && !KeyStoreUtil.isWindowsKeyStore(storetype)) {
-            printWarning();
+            printNoIntegrityWarning();
         }
         if (alias == null) {
             alias = keyAlias;
@@ -1379,6 +1508,7 @@
             throw new Exception(form.format(source));
         }
         dumpCert(cert, out);
+        checkWeak(rb.getString("the.certificate"), cert);
     }
 
     /**
@@ -1640,6 +1770,7 @@
         if (keyPass == null) {
             keyPass = promptForKeyPass(alias, null, storePass);
         }
+        checkWeak(rb.getString("the.generated.certificate"), chain[0]);
         keyStore.setKeyEntry(alias, privKey, keyPass, chain);
     }
 
@@ -1722,15 +1853,9 @@
     /**
      * Prints a single keystore entry.
      */
-    private void doPrintEntry(String alias, PrintStream out,
-                              boolean printWarning)
+    private void doPrintEntry(String label, String alias, PrintStream out)
         throws Exception
     {
-        if (storePass == null && printWarning
-                && !KeyStoreUtil.isWindowsKeyStore(storetype)) {
-            printWarning();
-        }
-
         if (keyStore.containsAlias(alias) == false) {
             MessageFormat form = new MessageFormat
                 (rb.getString("Alias.alias.does.not.exist"));
@@ -1799,12 +1924,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.SHA1.") +
                         getCertFingerPrint("SHA1", chain[0]));
+                    checkWeak(label, chain[0]);
                 }
             }
         } else if (keyStore.entryInstanceOf(alias,
@@ -1827,19 +1954,49 @@
                 out.println(rb.getString("Certificate.fingerprint.SHA1.")
                             + getCertFingerPrint("SHA1", cert));
             }
+            checkWeak(label, cert);
         } else {
             out.println(rb.getString("Unknown.Entry.Type"));
         }
     }
 
+    boolean inplaceImportCheck() throws Exception {
+        if (P11KEYSTORE.equalsIgnoreCase(srcstoretype) ||
+                KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {
+            return false;
+        }
+
+        if (srcksfname != null) {
+            File srcksfile = new File(srcksfname);
+            if (srcksfile.exists() && srcksfile.length() == 0) {
+                throw new Exception(rb.getString
+                        ("Source.keystore.file.exists.but.is.empty.") +
+                        srcksfname);
+            }
+            if (srcksfile.getCanonicalFile()
+                    .equals(new File(ksfname).getCanonicalFile())) {
+                return true;
+            } else {
+                // Informational, especially if destkeystore is not
+                // provided, which default to ~/.keystore.
+                System.err.println(String.format(rb.getString(
+                        "importing.keystore.status"), srcksfname, ksfname));
+                return false;
+            }
+        } else {
+            throw new Exception(rb.getString
+                    ("Please.specify.srckeystore"));
+        }
+    }
+
     /**
      * Load the srckeystore from a stream, used in -importkeystore
      * @returns the src KeyStore
      */
     KeyStore loadSourceKeyStore() throws Exception {
-        boolean isPkcs11 = false;
 
         InputStream is = null;
+        File srcksfile = null;
 
         if (P11KEYSTORE.equalsIgnoreCase(srcstoretype) ||
                 KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {
@@ -1849,20 +2006,9 @@
                 System.err.println();
                 tinyHelp();
             }
-            isPkcs11 = true;
         } else {
-            if (srcksfname != null) {
-                File srcksfile = new File(srcksfname);
-                    if (srcksfile.exists() && srcksfile.length() == 0) {
-                        throw new Exception(rb.getString
-                                ("Source.keystore.file.exists.but.is.empty.") +
-                                srcksfname);
-                }
+            srcksfile = new File(srcksfname);
                 is = new FileInputStream(srcksfile);
-            } else {
-                throw new Exception(rb.getString
-                        ("Please.specify.srckeystore"));
-            }
         }
 
         KeyStore store;
@@ -1903,7 +2049,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
@@ -1923,17 +2069,32 @@
      * keep alias unchanged if no name conflict, otherwise, prompt.
      * keep keypass unchanged for keys
      */
-    private void doImportKeyStore() throws Exception {
+    private void doImportKeyStore(KeyStore srcKS) throws Exception {
 
         if (alias != null) {
-            doImportKeyStoreSingle(loadSourceKeyStore(), alias);
+            doImportKeyStoreSingle(srcKS, alias);
         } else {
             if (dest != null || srckeyPass != null) {
                 throw new Exception(rb.getString(
                         "if.alias.not.specified.destalias.and.srckeypass.must.not.be.specified"));
             }
-            doImportKeyStoreAll(loadSourceKeyStore());
+            doImportKeyStoreAll(srcKS);
         }
+
+        if (inplaceImport) {
+            // Backup to file.old or file.old2...
+            // The keystore is not rewritten yet now.
+            for (int n = 1; /* forever */; n++) {
+                inplaceBackupName = srcksfname + ".old" + (n == 1 ? "" : n);
+                File bkFile = new File(inplaceBackupName);
+                if (!bkFile.exists()) {
+                    Files.copy(Paths.get(srcksfname), bkFile.toPath());
+                    break;
+                }
+            }
+
+        }
+
         /*
          * Information display rule of -importkeystore
          * 1. inside single, shows failure
@@ -1994,6 +2155,10 @@
         }
 
         try {
+            Certificate c = srckeystore.getCertificate(alias);
+            if (c != null) {
+                checkWeak("<" + newAlias + ">", c);
+            }
             keyStore.setEntry(newAlias, entry, pp);
             // Place the check so that only successful imports are blocked.
             // For example, we don't block a failed SecretEntry import.
@@ -2047,13 +2212,6 @@
     private void doPrintEntries(PrintStream out)
         throws Exception
     {
-        if (storePass == null
-                && !KeyStoreUtil.isWindowsKeyStore(storetype)) {
-            printWarning();
-        } else {
-            out.println();
-        }
-
         out.println(rb.getString("Keystore.type.") + keyStore.getType());
         out.println(rb.getString("Keystore.provider.") +
                 keyStore.getProvider().getName());
@@ -2072,7 +2230,7 @@
         for (Enumeration<String> e = keyStore.aliases();
                                         e.hasMoreElements(); ) {
             String alias = e.nextElement();
-            doPrintEntry(alias, out, false);
+            doPrintEntry("<" + alias + ">", alias, out);
             if (verbose || rfc) {
                 out.println(rb.getString("NEWLINE"));
                 out.println(rb.getString
@@ -2222,19 +2380,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();
                 }
             }
@@ -2246,18 +2413,26 @@
                 out.println(rb.getString
                         ("STARNN"));
             }
+            checkWeak(rb.getString("the.crl"), 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());
+            String s;
+            if (crl instanceof X509CRLImpl) {
+                X509CRLImpl x509crl = (X509CRLImpl) crl;
+                s = x509crl.toStringWithAlgName(withWeak("" + x509crl.getSigAlgId()));
+            } else {
+                s = crl.toString();
+            }
+            out.println(s);
         }
     }
 
@@ -2284,8 +2459,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((Object)PKCS9Attribute.EXTENSION_REQUEST_OID)) {
@@ -2308,6 +2486,7 @@
         if (debug) {
             out.println(req);   // Just to see more, say, public key length...
         }
+        checkWeak(rb.getString("the.certificate.request"), req);
     }
 
     /**
@@ -2347,6 +2526,15 @@
             if (i < (certs.length-1)) {
                 out.println();
             }
+            checkWeak(oneInMany(rb.getString("the.certificate"), i, certs.length), x509Cert);
+        }
+    }
+
+    private static String oneInMany(String label, int i, int num) {
+        if (num == 1) {
+            return label;
+        } else {
+            return String.format(rb.getString("one.in.many"), label, i+1, num);
         }
     }
 
@@ -2376,7 +2564,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");
@@ -2385,12 +2577,15 @@
                                     printX509Cert(x, out);
                                 }
                                 out.println();
+                                checkWeak(oneInMany(rb.getString("the.certificate"), 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");
@@ -2399,6 +2594,7 @@
                                         printX509Cert(x, out);
                                     }
                                     out.println();
+                                    checkWeak(oneInMany(rb.getString("the.tsa.certificate"), cc++, certs.size()), x);
                                 }
                             }
                         }
@@ -2443,6 +2639,7 @@
                         printX509Cert((X509Certificate)cert, out);
                         out.println();
                     }
+                    checkWeak(oneInMany(rb.getString("the.certificate"), i, chain.size()), cert);
                 } catch (Exception e) {
                     if (debug) {
                         e.printStackTrace();
@@ -2618,7 +2815,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,
@@ -2655,6 +2852,12 @@
             throw new Exception(rb.getString("Input.not.an.X.509.certificate"));
         }
 
+        if (noprompt) {
+            checkWeak(rb.getString("the.input"), cert);
+            keyStore.setCertificateEntry(alias, cert);
+            return true;
+        }
+
         // if certificate is self-signed, make sure it verifies
         boolean selfSigned = false;
         if (isSelfSigned(cert)) {
@@ -2662,11 +2865,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);
@@ -2675,6 +2873,8 @@
                 ("Certificate.already.exists.in.keystore.under.alias.trustalias."));
             Object[] source = {trustalias};
             System.err.println(form.format(source));
+            checkWeak(rb.getString("the.input"), cert);
+            printWeakWarnings(true);
             reply = getYesNoReply
                 (rb.getString("Do.you.still.want.to.add.it.no."));
         } else if (selfSigned) {
@@ -2684,6 +2884,8 @@
                         ("Certificate.already.exists.in.system.wide.CA.keystore.under.alias.trustalias."));
                 Object[] source = {trustalias};
                 System.err.println(form.format(source));
+                checkWeak(rb.getString("the.input"), cert);
+                printWeakWarnings(true);
                 reply = getYesNoReply
                         (rb.getString("Do.you.still.want.to.add.it.to.your.own.keystore.no."));
             }
@@ -2691,6 +2893,8 @@
                 // Print the cert and ask user if they really want to add
                 // it to their keystore
                 printX509Cert(cert, System.out);
+                checkWeak(rb.getString("the.input"), cert);
+                printWeakWarnings(true);
                 reply = getYesNoReply
                         (rb.getString("Trust.this.certificate.no."));
             }
@@ -2704,6 +2908,7 @@
             }
         }
 
+        // Not found in this keystore and not self-signed
         // Try to establish trust chain
         try {
             Certificate[] chain = establishCertChain(null, cert);
@@ -2715,6 +2920,8 @@
             // Print the cert and ask user if they really want to add it to
             // their keystore
             printX509Cert(cert, System.out);
+            checkWeak(rb.getString("the.input"), cert);
+            printWeakWarnings(true);
             reply = getYesNoReply
                 (rb.getString("Trust.this.certificate.no."));
             if ("YES".equals(reply)) {
@@ -2853,6 +3060,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.
      */
@@ -2878,7 +3103,13 @@
         */
 
         MessageFormat form = new MessageFormat
-                (rb.getString(".PATTERN.printX509Cert"));
+                (rb.getString(".PATTERN.printX509Cert.with.weak"));
+        PublicKey pkey = cert.getPublicKey();
+        String sigName = cert.getSigAlgName();
+        // No need to warn about sigalg of a trust anchor
+        if (!isTrustedCert(cert)) {
+            sigName = withWeak(sigName);
+        }
         Object[] source = {cert.getSubjectDN().toString(),
                         cert.getIssuerDN().toString(),
                         cert.getSerialNumber().toString(16),
@@ -2887,7 +3118,8 @@
                         getCertFingerPrint("MD5", cert),
                         getCertFingerPrint("SHA1", cert),
                         getCertFingerPrint("SHA-256", cert),
-                        cert.getSigAlgName(),
+                        sigName,
+                        withWeak(pkey),
                         cert.getVersion()
                         };
         out.println(form.format(source));
@@ -2957,12 +3189,12 @@
      * @param ks the keystore to search with, not null
      * @return <code>cert</code> itself if it's already inside <code>ks</code>,
      * or a certificate inside <code>ks</code> who signs <code>cert</code>,
-     * or null otherwise.
+     * or null otherwise. A label is added.
      */
-    private static Certificate getTrustedSigner(Certificate cert, KeyStore ks)
-            throws Exception {
+    private static Pair<String,Certificate>
+            getSigner(Certificate cert, KeyStore ks) throws Exception {
         if (ks.getCertificateAlias(cert) != null) {
-            return cert;
+            return new Pair<>("", cert);
         }
         for (Enumeration<String> aliases = ks.aliases();
                 aliases.hasMoreElements(); ) {
@@ -2971,7 +3203,7 @@
             if (trustedCert != null) {
                 try {
                     cert.verify(trustedCert.getPublicKey());
-                    return trustedCert;
+                    return new Pair<>(name, trustedCert);
                 } catch (Exception e) {
                     // Not verified, skip to the next one
                 }
@@ -3235,7 +3467,7 @@
     /**
      * Prints warning about missing integrity check.
      */
-    private void printWarning() {
+    private void printNoIntegrityWarning() {
         System.err.println();
         System.err.println(rb.getString
             (".WARNING.WARNING.WARNING."));
@@ -3260,6 +3492,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
@@ -3307,9 +3542,11 @@
 
         // do we trust the cert at the top?
         Certificate topCert = replyCerts[replyCerts.length-1];
-        Certificate root = getTrustedSigner(topCert, keyStore);
+        boolean fromKeyStore = true;
+        Pair<String,Certificate> root = getSigner(topCert, keyStore);
         if (root == null && trustcacerts && caks != null) {
-            root = getTrustedSigner(topCert, caks);
+            root = getSigner(topCert, caks);
+            fromKeyStore = false;
         }
         if (root == null) {
             System.err.println();
@@ -3318,33 +3555,42 @@
             printX509Cert((X509Certificate)topCert, System.out);
             System.err.println();
             System.err.print(rb.getString(".is.not.trusted."));
+            printWeakWarnings(true);
             String reply = getYesNoReply
                     (rb.getString("Install.reply.anyway.no."));
             if ("NO".equals(reply)) {
                 return null;
             }
         } else {
-            if (root != topCert) {
+            if (root.snd != topCert) {
                 // append the root CA cert to the chain
                 Certificate[] tmpCerts =
                     new Certificate[replyCerts.length+1];
                 System.arraycopy(replyCerts, 0, tmpCerts, 0,
                                  replyCerts.length);
-                tmpCerts[tmpCerts.length-1] = root;
+                tmpCerts[tmpCerts.length-1] = root.snd;
                 replyCerts = tmpCerts;
+                checkWeak(String.format(rb.getString(fromKeyStore ?
+                                            "alias.in.keystore" :
+                                            "alias.in.cacerts"),
+                                        root.fst),
+                          root.snd);
             }
         }
-
         return replyCerts;
     }
 
     /**
      * 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)
@@ -3372,30 +3618,37 @@
         // 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);
+            }
+            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;
@@ -3406,7 +3659,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.
@@ -3414,19 +3677,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 (isSelfSigned(certToVerify)) {
+    private boolean buildChain(Pair<String,X509Certificate> certToVerify,
+            Vector<Pair<String,X509Certificate>> chain,
+            Hashtable<Principal, Vector<Pair<String,X509Certificate>>> certs) {
+        if (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;
         }
@@ -3434,13 +3698,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();
+        for (Enumeration<Pair<String,X509Certificate>> issuerCerts = vec.elements();
              issuerCerts.hasMoreElements(); ) {
-            X509Certificate issuerCert
-                = (X509Certificate)issuerCerts.nextElement();
-            PublicKey issuerPubKey = issuerCert.getPublicKey();
+            Pair<String,X509Certificate> issuerCert = issuerCerts.nextElement();
+            PublicKey issuerPubKey = issuerCert.snd.getPublicKey();
             try {
-                certToVerify.verify(issuerPubKey);
+                certToVerify.snd.verify(issuerPubKey);
             } catch (Exception e) {
                 continue;
             }
@@ -3489,10 +3752,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();
@@ -3501,13 +3765,20 @@
             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<>(
+                        String.format(
+                                rb.getString(ks == caks ?
+                                        "alias.in.cacerts" :
+                                        "alias.in.keystore"),
+                                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);
@@ -4080,6 +4351,81 @@
         return ext;
     }
 
+    private boolean isTrustedCert(Certificate cert) throws KeyStoreException {
+        if (caks != null && caks.getCertificateAlias(cert) != null) {
+            return true;
+        } else {
+            String inKS = keyStore.getCertificateAlias(cert);
+            return inKS != null && keyStore.isCertificateEntry(inKS);
+        }
+    }
+
+    private void checkWeak(String label, String sigAlg, Key key) {
+
+        if (sigAlg != null && !DISABLED_CHECK.permits(
+                SIG_PRIMITIVE_SET, sigAlg, null)) {
+            weakWarnings.add(String.format(
+                    rb.getString("whose.sigalg.risk"), label, sigAlg));
+        }
+        if (key != null && !DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
+            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)
+            throws KeyStoreException {
+        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(label, i, certs.length);
+                }
+                checkWeak(fullLabel, xc);
+            }
+        }
+    }
+
+    private void checkWeak(String label, Certificate cert)
+            throws KeyStoreException {
+        if (cert instanceof X509Certificate) {
+            X509Certificate xc = (X509Certificate)cert;
+            // No need to check the sigalg of a trust anchor
+            String sigAlg = isTrustedCert(cert) ? null : xc.getSigAlgName();
+            checkWeak(label, sigAlg, 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 printWeakWarnings(boolean newLine) {
+        if (!weakWarnings.isEmpty() && !nowarn) {
+            System.err.println("\nWarning:");
+            for (String warning : weakWarnings) {
+                System.err.println(warning);
+            }
+            if (newLine) {
+                // When calling before a yes/no prompt, add a new line
+                System.err.println();
+            }
+        }
+        weakWarnings.clear();
+    }
+
     /**
      * Prints the usage of this tool.
      */
--- a/src/share/classes/sun/security/tools/keytool/Resources.java	Thu Aug 03 00:24:48 2017 -0700
+++ b/src/share/classes/sun/security/tools/keytool/Resources.java	Tue Aug 08 09:56:25 2017 -0700
@@ -345,8 +345,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 MD5:  {5}\n\t SHA1: {6}\n\t SHA256: {7}\n\t Signature algorithm name: {8}\n\t Version: {9}"},
         {"What.is.your.first.and.last.name.",
                 "What is your first and last name?"},
         {"What.is.the.name.of.your.organizational.unit.",
@@ -413,16 +411,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: "},
@@ -431,7 +425,38 @@
                  "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.certificate.request", "The certificate request"},
+        {"the.issuer", "The issuer"},
+        {"the.generated.certificate", "The generated certificate"},
+        {"the.generated.crl", "The generated CRL"},
+        {"the.generated.certificate.request", "The generated certificate request"},
+        {"the.certificate", "The certificate"},
+        {"the.crl", "The CRL"},
+        {"the.tsa.certificate", "The TSA certificate"},
+        {"the.input", "The input"},
+        {"reply", "Reply"},
+        {"one.in.many", "%s #%d of %d"},
+        {"alias.in.cacerts", "Issuer <%s> in cacerts"},
+        {"alias.in.keystore", "Issuer <%s>"},
+        {"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 MD5:  {5}\n\t SHA1: {6}\n\t SHA256: {7}\nSignature algorithm name: {8}\nSubject Public Key Algorithm: {9}\nVersion: {10}"},
+        {"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"},
+        {"whose.sigalg.risk", "%s uses the %s signature algorithm which is considered a security risk."},
+        {"whose.key.risk", "%s uses a %s which is considered a security risk."},
+        {"jks.storetype.warning", "The %1$s keystore uses a proprietary format. It is recommended to migrate to PKCS12 which is an industry standard format using \"keytool -importkeystore -srckeystore %2$s -destkeystore %2$s -deststoretype pkcs12\"."},
+        {"migrate.keystore.warning", "Migrated \"%1$s\" to %4$s. The %2$s keystore is backed up as \"%3$s\"."},
+        {"backup.keystore.warning", "The original keystore \"%1$s\" is backed up as \"%3$s\"..."},
+        {"importing.keystore.status", "Importing keystore %1$s to %2$s..."},
     };
 
 
--- a/src/share/classes/sun/security/x509/X509CRLImpl.java	Thu Aug 03 00:24:48 2017 -0700
+++ b/src/share/classes/sun/security/x509/X509CRLImpl.java	Tue Aug 08 09:56:25 2017 -0700
@@ -536,10 +536,15 @@
      * @return value of this CRL in a printable form.
      */
     public String toString() {
+        return toStringWithAlgName("" + sigAlgId);
+    }
+
+    // Specifically created for keytool to append a (weak) label to sigAlg
+    public String toStringWithAlgName(String name) {
         StringBuffer sb = new StringBuffer();
         sb.append("X.509 CRL v" + (version+1) + "\n");
         if (sigAlgId != null)
-            sb.append("Signature Algorithm: " + sigAlgId.toString() +
+            sb.append("Signature Algorithm: " + name.toString() +
                   ", OID=" + (sigAlgId.getOID()).toString() + "\n");
         if (issuer != null)
             sb.append("Issuer: " + issuer.toString() + "\n");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/tools/keytool/WeakAlg.java	Tue Aug 08 09:56:25 2017 -0700
@@ -0,0 +1,758 @@
+/*
+ * 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 8177569 8182879
+ * @summary keytool should print out warnings when reading or generating
+  *         cert/cert req using weak algorithms
+ * @library /lib/testlibrary
+ * @run main/othervm/timeout=600 -Duser.language=en -Duser.country=US WeakAlg
+ */
+
+import jdk.testlibrary.Asserts;
+import jdk.testlibrary.SecurityTools;
+import jdk.testlibrary.OutputAnalyzer;
+import sun.security.tools.KeyStoreUtil;
+import sun.security.util.DisabledAlgorithmConstraints;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+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 {
+
+    static String sep = File.separator;
+    static String cacerts_location = System.getProperty("java.home") +
+        sep + "lib" + sep + "security" + sep + "cacerts";
+
+    public static void main(String[] args) throws Throwable {
+
+        rm("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>.*MD5withRSA.*risk")
+                .shouldMatch("<b>.*512-bit RSA key.*risk");
+        kt("-list -v")
+                .shouldContain("Warning:")
+                .shouldMatch("<a>.*MD5withRSA.*risk")
+                .shouldContain("MD5withRSA (weak)")
+                .shouldMatch("<b>.*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 generated certificate.*MD2withRSA.*risk");
+
+        checkCertReq("a", "-sigalg MD5withRSA", "MD5withRSA");
+        gencert("c-a", "")
+                .shouldContain("Warning:")
+                .shouldMatch("The certificate request.*MD5withRSA.*risk");
+        gencert("c-a", "-sigalg MD2withRSA")
+                .shouldContain("Warning:")
+                .shouldMatch("The certificate request.*MD5withRSA.*risk")
+                .shouldMatch("The generated certificate.*MD2withRSA.*risk");
+
+        checkCertReq("b", "", "512-bit RSA key");
+        gencert("c-b", "")
+                .shouldContain("Warning:")
+                .shouldMatch("The certificate request.*512-bit RSA key.*risk")
+                .shouldMatch("The generated certificate.*512-bit RSA key.*risk");
+
+        checkCertReq("c", "", null);
+        gencert("a-c", "")
+                .shouldContain("Warning:")
+                .shouldMatch("The issuer.*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.*512-bit RSA key.*risk");
+
+        // -importcert
+        checkImport();
+
+        // -importkeystore
+        checkImportKeyStore();
+
+        // -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");
+
+        jksTypeCheck();
+
+        checkInplaceImportKeyStore();
+    }
+
+    static void jksTypeCheck() throws Exception {
+
+        rm("ks");
+        rm("ks2");
+
+        kt("-genkeypair -alias a -storetype pkcs12 -dname CN=A")
+                .shouldNotContain("Warning:");
+        kt("-list")
+                .shouldNotContain("Warning:");
+        kt("-list -storetype jks") // no warning if PKCS12 used as JKS
+                .shouldNotContain("Warning:");
+        kt("-exportcert -alias a -file a.crt")
+                .shouldNotContain("Warning:");
+
+        // warn if migrating to JKS
+        importkeystore("ks", "ks2", "-deststoretype jks")
+                .shouldContain("JKS keystore uses a proprietary format");
+
+        rm("ks");
+        rm("ks2");
+        rm("ks3");
+
+        // no warning if all certs
+        kt("-importcert -alias b -file a.crt -storetype jks -noprompt")
+                .shouldNotContain("Warning:");
+        kt("-genkeypair -alias a -dname CN=A")
+                .shouldContain("JKS keystore uses a proprietary format");
+        kt("-list")
+                .shouldContain("JKS keystore uses a proprietary format");
+        kt("-exportcert -alias a -file a.crt")
+                .shouldContain("JKS keystore uses a proprietary format");
+        kt("-printcert -file a.crt") // no warning if keystore not touched
+                .shouldNotContain("Warning:");
+        kt("-certreq -alias a -file a.req")
+               .shouldContain("JKS keystore uses a proprietary format");
+        kt("-printcertreq -file a.req") // no warning if keystore not touched
+                .shouldNotContain("Warning:");
+
+        // Earlier than JDK 9 defaults to JKS
+        importkeystore("ks", "ks2", "")
+                .shouldContain("Warning:");
+
+        importkeystore("ks", "ks3", "-deststoretype pkcs12")
+                .shouldNotContain("Warning:");
+
+        rm("ks");
+
+        kt("-genkeypair -alias a -dname CN=A -storetype jceks")
+                .shouldContain("JCEKS keystore uses a proprietary format");
+        kt("-list -storetype jceks")
+                .shouldContain("JCEKS keystore uses a proprietary format");
+        kt("-importcert -alias b -file a.crt -noprompt -storetype jceks")
+                .shouldContain("JCEKS keystore uses a proprietary format");
+        kt("-exportcert -alias a -file a.crt -storetype jceks")
+                .shouldContain("JCEKS keystore uses a proprietary format");
+        kt("-printcert -file a.crt")
+                .shouldNotContain("Warning:");
+        kt("-certreq -alias a -file a.req -storetype jceks")
+                .shouldContain("JCEKS keystore uses a proprietary format");
+        kt("-printcertreq -file a.req")
+                .shouldNotContain("Warning:");
+        kt("-genseckey -alias c -keyalg AES -keysize 128 -storetype jceks")
+                .shouldContain("JCEKS keystore uses a proprietary format");
+    }
+
+    static void checkImportKeyStore() throws Exception {
+
+        rm("ks2");
+        rm("ks3");
+
+        importkeystore("ks", "ks2", "")
+                .shouldContain("3 entries successfully imported")
+                .shouldContain("Warning")
+                .shouldMatch("<b>.*512-bit RSA key.*risk")
+                .shouldMatch("<a>.*MD5withRSA.*risk");
+
+        importkeystore("ks", "ks3", "-srcalias a")
+                .shouldContain("Warning")
+                .shouldMatch("<a>.*MD5withRSA.*risk");
+    }
+
+    static void checkInplaceImportKeyStore() throws Exception {
+
+        rm("ks");
+        genkeypair("a", "");
+
+        // Same type backup
+        importkeystore("ks", "ks", "")
+                .shouldContain("Warning:")
+                .shouldMatch(".*ks.old");
+
+        importkeystore("ks", "ks", "")
+                .shouldContain("Warning:")
+                .shouldMatch(".*ks.old2");
+
+        importkeystore("ks", "ks", "-srcstoretype jks") // it knows real type
+                .shouldContain("Warning:")
+                .shouldMatch(".*ks.old3");
+
+        String cPath = new File("ks").getCanonicalPath();
+
+        importkeystore("ks", cPath, "")
+                .shouldContain("Warning:")
+                .shouldMatch(".*ks.old4");
+
+        // Migration
+        importkeystore("ks", "ks", "-deststoretype jks")
+                .shouldContain("Warning:")
+                .shouldContain("JKS keystore uses a proprietary format")
+                .shouldMatch("The original.*ks.old5");
+
+        KeyStore test_ks = KeyStore.getInstance("JKS");
+        test_ks.load(new FileInputStream(new File("ks")),
+                "changeit".toCharArray());
+        Asserts.assertEQ(
+                    test_ks.getType(), "JKS");
+
+        importkeystore("ks", "ks", "-deststoretype PKCS12")
+                .shouldContain("Warning:")
+                .shouldNotContain("proprietary format")
+                .shouldMatch("Migrated.*Non.*JKS.*ks.old6");
+
+        test_ks = KeyStore.getInstance("PKCS12");
+        test_ks.load(new FileInputStream(new File("ks")),
+                "changeit".toCharArray());
+        Asserts.assertEQ(
+                test_ks.getType(), "PKCS12");
+
+        test_ks = KeyStore.getInstance("JKS");
+        test_ks.load(new FileInputStream(new File("ks.old6")),
+                "changeit".toCharArray());
+        Asserts.assertEQ(
+                test_ks.getType(), "JKS");
+
+        // One password prompt is enough for migration
+        kt0("-importkeystore -srckeystore ks -destkeystore ks", "changeit")
+                .shouldMatch("backed.*ks.old7");
+
+        // But three if importing to a different keystore
+        rm("ks2");
+        kt0("-importkeystore -srckeystore ks -destkeystore ks2",
+                    "changeit")
+                .shouldContain("Keystore password is too short");
+
+        kt0("-importkeystore -srckeystore ks -destkeystore ks2",
+                "changeit", "changeit", "changeit")
+                .shouldContain("Importing keystore ks to ks2...")
+                .shouldNotContain("original")
+                .shouldNotContain("Migrated");
+    }
+
+    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("The input.*MD5withRSA.*risk")
+                .shouldContain("Do you still want to add it?");
+        kt("-importcert -alias d -file a.cert -noprompt")
+                .shouldContain("Warning")
+                .shouldMatch("The input.*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("The input.*MD5withRSA.*risk")
+                .shouldContain("Trust this certificate?");
+        kt("-importcert -alias d -file a.cert -noprompt")
+                .shouldContain("Warning")
+                .shouldMatch("The input.*MD5withRSA.*risk")
+                .shouldNotContain("[no]");
+
+        // JDK-8177569: no warning for sigalg of trusted cert
+        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) {
+            // The following 2 commands still have a warning on why not using
+            // the -cacerts option directly.
+            kt("-list -keystore " + cacerts_location)
+                    .shouldNotContain("risk");
+            kt("-list -v -keystore " + cacerts_location)
+                    .shouldNotContain("risk");
+
+            // -printcert will always show warnings
+            kt("-printcert -file ca.cert")
+                    .shouldContain("name: " + weakSigAlgCA + " (weak)")
+                    .shouldContain("Warning")
+                    .shouldMatch("The certificate.*" + weakSigAlgCA + ".*risk");
+            kt("-printcert -file ca.cert -trustcacerts") // -trustcacerts useless
+                    .shouldContain("name: " + weakSigAlgCA + " (weak)")
+                    .shouldContain("Warning")
+                    .shouldMatch("The certificate.*" + weakSigAlgCA + ".*risk");
+
+            // Importing with -trustcacerts ignore CA cert's sig alg
+            kt("-delete -alias d");
+            kt("-importcert -alias d -trustcacerts -file ca.cert", "no")
+                    .shouldContain("Certificate already exists in system-wide CA")
+                    .shouldNotContain("risk")
+                    .shouldContain("Do you still want to add it to your own keystore?");
+            kt("-importcert -alias d -trustcacerts -file ca.cert -noprompt")
+                    .shouldNotContain("risk")
+                    .shouldNotContain("[no]");
+
+            // but not without -trustcacerts
+            kt("-delete -alias d");
+            kt("-importcert -alias d -file ca.cert", "no")
+                    .shouldContain("name: " + weakSigAlgCA + " (weak)")
+                    .shouldContain("Warning")
+                    .shouldMatch("The input.*" + weakSigAlgCA + ".*risk")
+                    .shouldContain("Trust this certificate?");
+            kt("-importcert -alias d -file ca.cert -noprompt")
+                    .shouldContain("Warning")
+                    .shouldMatch("The input.*" + weakSigAlgCA + ".*risk")
+                    .shouldNotContain("[no]");
+        }
+
+        // a non self-signed weak cert
+        reStore();
+        certreq("b", "");
+        gencert("c-b", "");
+        kt("-importcert -alias d -file c-b.cert")   // weak only, no prompt
+                .shouldContain("Warning")
+                .shouldNotContain("512-bit RSA key (weak)")
+                .shouldMatch("The input.*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("The input.*512-bit RSA key.*risk")
+                .shouldContain("Trust this certificate?");
+        kt("-importcert -alias d -file c-b.cert -noprompt")
+                .shouldContain("Warning")
+                .shouldMatch("The input.*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();
+        certreq("c", "");
+        gencert("a-c", "");
+        kt("-importcert -alias c -file a-c.cert")
+                .shouldContain("Warning")
+                .shouldMatch("Issuer <a>.*MD5withRSA.*risk");
+
+        // JDK-8177569: no warning for sigalg of trusted cert
+        reStore();
+        // Change a into a TrustedCertEntry
+        kt("-exportcert -alias a -file a.cert");
+        kt("-delete -alias a");
+        kt("-importcert -alias a -file a.cert -noprompt");
+        kt("-list -alias a -v")
+                .shouldNotContain("weak")
+                .shouldNotContain("Warning");
+        // This time a is trusted and no warning on its weak sig alg
+        kt("-importcert -alias c -file a-c.cert")
+                .shouldNotContain("Warning");
+
+        reStore();
+
+        gencert("a-b", "");
+        gencert("b-c", "");
+
+        // Full chain with root
+        cat("a-a-b-c.cert", "b-c.cert", "a-b.cert", "a.cert");
+        kt("-importcert -alias c -file a-a-b-c.cert")   // only weak
+                .shouldContain("Warning")
+                .shouldMatch("Reply #2 of 3.*512-bit RSA key.*risk")
+                .shouldMatch("Reply #3 of 3.*MD5withRSA.*risk")
+                .shouldNotContain("[no]");
+
+        // Without root
+        cat("a-b-c.cert", "b-c.cert", "a-b.cert");
+        kt("-importcert -alias c -file a-b-c.cert")     // only weak
+                .shouldContain("Warning")
+                .shouldMatch("Reply #2 of 2.*512-bit RSA key.*risk")
+                .shouldMatch("Issuer <a>.*MD5withRSA.*risk")
+                .shouldNotContain("[no]");
+
+        reStore();
+        gencert("b-a", "");
+
+        kt("-importcert -alias a -file b-a.cert")
+                .shouldContain("Warning")
+                .shouldMatch("Issuer <b>.*512-bit RSA key.*risk")
+                .shouldNotContain("[no]");
+
+        kt("-importcert -alias a -file c-a.cert")
+                .shouldNotContain("Warning");
+
+        kt("-importcert -alias b -file c-b.cert")
+                .shouldContain("Warning")
+                .shouldMatch("The input.*512-bit RSA key.*risk")
+                .shouldNotContain("[no]");
+
+        reStore();
+        gencert("b-a", "");
+
+        cat("c-b-a.cert", "b-a.cert", "c-b.cert");
+
+        kt("-printcert -file c-b-a.cert")
+                .shouldContain("Warning")
+                .shouldMatch("The certificate #2 of 2.*512-bit RSA key.*risk");
+
+        kt("-delete -alias b");
+
+        kt("-importcert -alias a -file c-b-a.cert")
+                .shouldContain("Warning")
+                .shouldMatch("Reply #2 of 2.*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.*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.*512-bit RSA key.*risk")
+                .shouldNotContain("[no]");
+
+        reStore();
+    }
+
+    private static void cat(String dest, String... src) throws IOException {
+        System.out.println("---------------------------------------------");
+        System.out.printf("$ cat ");
+
+        ByteArrayOutputStream bout = new ByteArrayOutputStream();
+        for (String s : src) {
+            System.out.printf(s + " ");
+            bout.write(Files.readAllBytes(Paths.get(s)));
+        }
+        Files.write(Paths.get(dest), bout.toByteArray());
+        System.out.println("> " + dest);
+    }
+
+    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("The generated CRL.*" + 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("The CRL.*" + 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("The generated certificate request.*" + bad + ".*risk");
+        }
+
+        oa = kt("-printcertreq -file " + alias + ".req");
+        if (bad == null) {
+            oa.shouldNotContain("Warning")
+                    .shouldNotContain("(weak)");
+        } else {
+            oa.shouldContain("Warning")
+                    .shouldMatch("The certificate request.*" + 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("The generated certificate.*" + bad + ".*risk");
+        }
+
+        oa = kt("-exportcert -alias " + alias + " -file " + alias + ".cert");
+        if (bad == null) {
+            oa.shouldNotContain("Warning");
+        } else {
+            oa.shouldContain("Warning")
+                    .shouldMatch("The certificate.*" + bad + ".*risk");
+        }
+
+        oa = kt("-exportcert -rfc -alias " + alias + " -file " + alias + ".cert");
+        if (bad == null) {
+            oa.shouldNotContain("Warning");
+        } else {
+            oa.shouldContain("Warning")
+                    .shouldMatch("The certificate.*" + bad + ".*risk");
+        }
+
+        oa = kt("-printcert -rfc -file " + alias + ".cert");
+        if (bad == null) {
+            oa.shouldNotContain("Warning");
+        } else {
+            oa.shouldContain("Warning")
+                    .shouldMatch("The certificate.*" + bad + ".*risk");
+        }
+
+        oa = kt("-list -alias " + alias);
+        if (bad == null) {
+            oa.shouldNotContain("Warning");
+        } else {
+            oa.shouldContain("Warning")
+                    .shouldMatch("The certificate.*" + 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("The certificate.*" + bad + ".*risk");
+        }
+
+        oa = kt("-list -v -alias " + alias);
+        if (bad == null) {
+            oa.shouldNotContain("Warning");
+        } else {
+            oa.shouldContain("Warning")
+                    .shouldContain(bad + " (weak)")
+                    .shouldMatch("The certificate.*" + 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);
+        }
+    }
+
+    static OutputAnalyzer kt(String cmd, String... input) {
+        return kt0("-keystore ks -storepass changeit " +
+                "-keypass changeit " + cmd, input);
+    }
+
+    // Fast keytool execution by directly calling its main() method
+    static OutputAnalyzer kt0(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;
+        String sout;
+        String serr;
+        try {
+            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);
+            sout = new String(bout.toByteArray());
+            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 importkeystore(String src, String dest,
+                                         String options) {
+        return kt0("-importkeystore "
+                + "-srckeystore " + src + " -destkeystore " + dest
+                + " -srcstorepass changeit -deststorepass changeit " + options);
+    }
+
+    static OutputAnalyzer genkeypair(String alias, String options) {
+        return kt("-genkeypair -alias " + alias + " -dname CN=" + alias
+                + " -keyalg RSA -storetype PKCS12 " + 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);
+    }
+
+    static void rm(String s) throws IOException {
+        System.out.println("---------------------------------------------");
+        System.out.println("$ rm " + s);
+        Files.deleteIfExists(Paths.get(s));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/tools/keytool/keyalg.sh	Tue Aug 08 09:56:25 2017 -0700
@@ -0,0 +1,49 @@
+#
+# 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 8029659
+# @summary Keytool, print key algorithm of certificate or key entry
+#
+
+if [ "${TESTJAVA}" = "" ] ; then
+  JAVAC_CMD=`which javac`
+  TESTJAVA=`dirname $JAVAC_CMD`/..
+fi
+
+TESTTOOLVMOPTS="$TESTTOOLVMOPTS -J-Duser.language=en -J-Duser.country=US"
+
+KS=ks
+KEYTOOL="$TESTJAVA/bin/keytool ${TESTTOOLVMOPTS} -keystore ks -storepass changeit -keypass changeit"
+
+rm $KS 2> /dev/null
+
+$KEYTOOL -genkeypair -alias ca -dname CN=CA -keyalg EC || exit 1
+$KEYTOOL -genkeypair -alias user -dname CN=User -keyalg RSA -keysize 1024 || exit 2
+$KEYTOOL -certreq -alias user |
+        $KEYTOOL -gencert -alias ca -rfc -sigalg SHA1withECDSA |
+        $KEYTOOL -printcert > user.dump || exit 3
+
+cat user.dump | grep "Signature algorithm name:" | grep SHA1withECDSA || exit 4
+cat user.dump | grep "Subject Public Key Algorithm:" | grep RSA | grep 1024 || exit 5
+