8162739: Create new keytool option to access cacerts file
authorweijun
Wed, 10 Aug 2016 11:30:53 +0800
changeset 40253 08809866c0bc
parent 40252 8f320a3f83b8
child 40254 a68a0f6758df
8162739: Create new keytool option to access cacerts file Reviewed-by: erikj, vinnie, mullan
jdk/make/copy/Copy-java.base.gmk
jdk/src/java.base/share/classes/sun/security/tools/KeyStoreUtil.java
jdk/src/java.base/share/classes/sun/security/tools/keytool/Main.java
jdk/src/java.base/share/classes/sun/security/tools/keytool/Resources.java
jdk/src/java.base/share/conf/security/cacerts
jdk/src/java.base/share/lib/security/cacerts
jdk/test/sun/security/tools/keytool/CacertsOption.java
--- a/jdk/make/copy/Copy-java.base.gmk	Wed Aug 10 11:21:01 2016 +0800
+++ b/jdk/make/copy/Copy-java.base.gmk	Wed Aug 10 11:30:53 2016 +0800
@@ -203,7 +203,7 @@
 ################################################################################
 
 ifeq ($(CACERTS_FILE), )
-  CACERTS_FILE := $(JDK_TOPDIR)/src/java.base/share/conf/security/cacerts
+  CACERTS_FILE := $(JDK_TOPDIR)/src/java.base/share/lib/security/cacerts
 endif
 
 CACERTS_DST := $(LIB_DST_DIR)/security/cacerts
--- a/jdk/src/java.base/share/classes/sun/security/tools/KeyStoreUtil.java	Wed Aug 10 11:21:01 2016 +0800
+++ b/jdk/src/java.base/share/classes/sun/security/tools/KeyStoreUtil.java	Wed Aug 10 11:30:53 2016 +0800
@@ -63,8 +63,6 @@
         // this class is not meant to be instantiated
     }
 
-    private static final String JKS = "jks";
-
     private static final Collator collator = Collator.getInstance();
     static {
         // this is for case insensitive string comparisons
@@ -113,24 +111,24 @@
     }
 
     /**
+     * Returns the file name of the keystore with the configured CA certificates.
+     */
+    public static String getCacerts() {
+        String sep = File.separator;
+        return System.getProperty("java.home") + sep
+                + "lib" + sep + "security" + sep
+                + "cacerts";
+    }
+
+    /**
      * Returns the keystore with the configured CA certificates.
      */
-    public static KeyStore getCacertsKeyStore()
-        throws Exception
-    {
-        String sep = File.separator;
-        File file = new File(System.getProperty("java.home") + sep
-                             + "lib" + sep + "security" + sep
-                             + "cacerts");
+    public static KeyStore getCacertsKeyStore() throws Exception {
+        File file = new File(getCacerts());
         if (!file.exists()) {
             return null;
         }
-        KeyStore caks = null;
-        try (FileInputStream fis = new FileInputStream(file)) {
-            caks = KeyStore.getInstance(JKS);
-            caks.load(fis, null);
-        }
-        return caks;
+        return KeyStore.getInstance(file, (char[])null);
     }
 
     public static char[] getPassWithModifier(String modifier, String arg,
--- a/jdk/src/java.base/share/classes/sun/security/tools/keytool/Main.java	Wed Aug 10 11:21:01 2016 +0800
+++ b/jdk/src/java.base/share/classes/sun/security/tools/keytool/Main.java	Wed Aug 10 11:30:53 2016 +0800
@@ -153,6 +153,7 @@
     private boolean trustcacerts = false;
     private boolean protectedPath = false;
     private boolean srcprotectedPath = false;
+    private boolean cacerts = false;
     private CertificateFactory cf = null;
     private KeyStore caks = null; // "cacerts" keystore
     private char[] srcstorePass = null;
@@ -169,15 +170,15 @@
             STOREPASS, STORETYPE, PROVIDERNAME, ADDPROVIDER,
             PROVIDERCLASS, PROVIDERPATH, V, PROTECTED),
         CHANGEALIAS("Changes.an.entry.s.alias",
-            ALIAS, DESTALIAS, KEYPASS, KEYSTORE, STOREPASS,
+            ALIAS, DESTALIAS, KEYPASS, KEYSTORE, CACERTS, STOREPASS,
             STORETYPE, PROVIDERNAME, ADDPROVIDER, PROVIDERCLASS,
             PROVIDERPATH, V, PROTECTED),
         DELETE("Deletes.an.entry",
-            ALIAS, KEYSTORE, STOREPASS, STORETYPE,
+            ALIAS, KEYSTORE, CACERTS, STOREPASS, STORETYPE,
             PROVIDERNAME, ADDPROVIDER, PROVIDERCLASS,
             PROVIDERPATH, V, PROTECTED),
         EXPORTCERT("Exports.certificate",
-            RFC, ALIAS, FILEOUT, KEYSTORE, STOREPASS,
+            RFC, ALIAS, FILEOUT, KEYSTORE, CACERTS, STOREPASS,
             STORETYPE, PROVIDERNAME, ADDPROVIDER, PROVIDERCLASS,
             PROVIDERPATH, V, PROTECTED),
         GENKEYPAIR("Generates.a.key.pair",
@@ -196,7 +197,7 @@
             PROVIDERCLASS, PROVIDERPATH, V, PROTECTED),
         IMPORTCERT("Imports.a.certificate.or.a.certificate.chain",
             NOPROMPT, TRUSTCACERTS, PROTECTED, ALIAS, FILEIN,
-            KEYPASS, KEYSTORE, STOREPASS, STORETYPE,
+            KEYPASS, KEYSTORE, CACERTS, STOREPASS, STORETYPE,
             PROVIDERNAME, ADDPROVIDER, PROVIDERCLASS,
             PROVIDERPATH, V),
         IMPORTPASS("Imports.a.password",
@@ -215,7 +216,7 @@
             STORETYPE, PROVIDERNAME, ADDPROVIDER, PROVIDERCLASS,
             PROVIDERPATH, V),
         LIST("Lists.entries.in.a.keystore",
-            RFC, ALIAS, KEYSTORE, STOREPASS, STORETYPE,
+            RFC, ALIAS, KEYSTORE, CACERTS, STOREPASS, STORETYPE,
             PROVIDERNAME, ADDPROVIDER, PROVIDERCLASS,
             PROVIDERPATH, V, PROTECTED),
         PRINTCERT("Prints.the.content.of.a.certificate",
@@ -225,7 +226,7 @@
         PRINTCRL("Prints.the.content.of.a.CRL.file",
             FILEIN, V),
         STOREPASSWD("Changes.the.store.password.of.a.keystore",
-            NEW, KEYSTORE, STOREPASS, STORETYPE, PROVIDERNAME,
+            NEW, KEYSTORE, CACERTS, STOREPASS, STORETYPE, PROVIDERNAME,
             ADDPROVIDER, PROVIDERCLASS, PROVIDERPATH, V),
 
         // Undocumented start here, KEYCLONE is used a marker in -help;
@@ -306,6 +307,7 @@
         KEYPASS("keypass", "<arg>", "key.password"),
         KEYSIZE("keysize", "<size>", "key.bit.size"),
         KEYSTORE("keystore", "<keystore>", "keystore.name"),
+        CACERTS("cacerts", null, "access.the.cacerts.keystore"),
         NEW("new", "<arg>", "new.password"),
         NOPROMPT("noprompt", null, "do.not.prompt"),
         OUTFILE("outfile", "<file>", "output.file.name"),
@@ -472,14 +474,16 @@
                 help = true;
             } else if (collator.compare(flags, "-conf") == 0) {
                 i++;
-            }
-
-            /*
-             * specifiers
-             */
-            else if (collator.compare(flags, "-keystore") == 0 ||
-                    collator.compare(flags, "-destkeystore") == 0) {
+            } else if (collator.compare(flags, "-keystore") == 0) {
                 ksfname = args[++i];
+                if (new File(ksfname).getCanonicalPath().equals(
+                        new File(KeyStoreUtil.getCacerts()).getCanonicalPath())) {
+                    System.err.println(rb.getString("warning.cacerts.option"));
+                }
+            } else if (collator.compare(flags, "-destkeystore") == 0) {
+                ksfname = args[++i];
+            } else if (collator.compare(flags, "-cacerts") == 0) {
+                cacerts = true;
             } else if (collator.compare(flags, "-storepass") == 0 ||
                     collator.compare(flags, "-deststorepass") == 0) {
                 storePass = getPass(modifier, args[++i]);
@@ -636,6 +640,15 @@
      * Execute the commands.
      */
     void doCommands(PrintStream out) throws Exception {
+
+        if (cacerts) {
+            if (ksfname != null || storetype != null) {
+                throw new IllegalArgumentException(rb.getString
+                        ("the.keystore.or.storetype.option.cannot.be.used.with.the.cacerts.option"));
+            }
+            ksfname = KeyStoreUtil.getCacerts();
+        }
+
         if (storetype == null) {
             storetype = KeyStore.getDefaultType();
         }
--- a/jdk/src/java.base/share/classes/sun/security/tools/keytool/Resources.java	Wed Aug 10 11:21:01 2016 +0800
+++ b/jdk/src/java.base/share/classes/sun/security/tools/keytool/Resources.java	Wed Aug 10 11:30:53 2016 +0800
@@ -127,6 +127,10 @@
                 "key bit size"}, //-keysize
         {"keystore.name",
                 "keystore name"}, //-keystore
+        {"access.the.cacerts.keystore",
+                "access the cacerts keystore"}, // -cacerts
+        {"warning.cacerts.option",
+                "Warning: use -cacerts option to access cacerts keystore"},
         {"new.password",
                 "new password"}, //-new
         {"do.not.prompt",
@@ -194,6 +198,8 @@
         {"Command.option.flag.needs.an.argument.", "Command option {0} needs an argument."},
         {"Warning.Different.store.and.key.passwords.not.supported.for.PKCS12.KeyStores.Ignoring.user.specified.command.value.",
                 "Warning:  Different store and key passwords not supported for PKCS12 KeyStores. Ignoring user-specified {0} value."},
+        {"the.keystore.or.storetype.option.cannot.be.used.with.the.cacerts.option",
+            "The -keystore or -storetype option cannot be used with the -cacerts option"},
         {".keystore.must.be.NONE.if.storetype.is.{0}",
                 "-keystore must be NONE if -storetype is {0}"},
         {"Too.many.retries.program.terminated",
Binary file jdk/src/java.base/share/conf/security/cacerts has changed
Binary file jdk/src/java.base/share/lib/security/cacerts has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/tools/keytool/CacertsOption.java	Wed Aug 10 11:30:53 2016 +0800
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2016, 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 8162739
+ * @summary Create new keytool option to access cacerts file
+ * @modules java.base/sun.security.tools.keytool
+ *          java.base/sun.security.tools
+ * @run main/othervm -Duser.language=en -Duser.country=US CacertsOption
+ */
+
+import sun.security.tools.KeyStoreUtil;
+import sun.security.tools.keytool.Main;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.PrintStream;
+import java.security.KeyStore;
+import java.util.Collections;
+
+public class CacertsOption {
+
+    public static void main(String[] args) throws Exception {
+
+        run("-help -list");
+        if (!msg.contains("-cacerts")) {
+            throw new Exception("No cacerts in help:\n" + msg);
+        }
+
+        String cacerts = KeyStoreUtil.getCacerts();
+
+        run("-list -keystore " + cacerts);
+        if (!msg.contains("Warning:")) {
+            throw new Exception("No warning in output:\n" + msg);
+        }
+
+        run("-list -cacerts");
+        KeyStore ks = KeyStore.getInstance(new File(cacerts), (char[])null);
+        for (String alias: Collections.list(ks.aliases())) {
+            if (!msg.contains(alias)) {
+                throw new Exception(alias + " not found in\n" + msg);
+            }
+        }
+
+        try {
+            run("-list -cacerts -storetype jks");
+            throw new Exception("Should fail");
+        } catch (IllegalArgumentException iae) {
+            if (!msg.contains("cannot be used with")) {
+                throw new Exception("Bad error msg\n" + msg);
+            }
+        }
+    }
+
+    private static String msg = null;
+
+    private static void run(String cmd) throws Exception {
+        msg = null;
+        cmd += " -storepass changeit -debug";
+        ByteArrayOutputStream bout = new ByteArrayOutputStream();
+        PrintStream ps = new PrintStream(bout);
+        PrintStream oldOut = System.out;
+        PrintStream oldErr = System.err;
+        try {
+            System.setOut(ps);
+            System.setErr(ps);
+            Main.main(cmd.split(" "));
+        } finally {
+            System.setErr(oldErr);
+            System.setOut(oldOut);
+            msg = new String(bout.toByteArray());
+        }
+    }
+}