6480981: keytool should be able to import certificates from remote SSL servers
authorweijun
Wed, 09 Jul 2008 12:03:16 +0800
changeset 904 eadc9fa4b700
parent 903 c324f61770cf
child 905 8445a646acfb
6480981: keytool should be able to import certificates from remote SSL servers Reviewed-by: vinnie, wetmore
jdk/src/share/classes/sun/security/tools/KeyTool.java
jdk/src/share/classes/sun/security/util/Resources.java
jdk/test/sun/security/tools/keytool/PrintSSL.java
jdk/test/sun/security/tools/keytool/printssl.sh
--- a/jdk/src/share/classes/sun/security/tools/KeyTool.java	Mon Jul 07 13:06:58 2008 -0700
+++ b/jdk/src/share/classes/sun/security/tools/KeyTool.java	Wed Jul 09 12:03:16 2008 +0800
@@ -1,5 +1,5 @@
 /*
- * Copyright 1997-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 1997-2008 Sun Microsystems, Inc.  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
@@ -26,35 +26,23 @@
 package sun.security.tools;
 
 import java.io.*;
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.InvalidParameterException;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
 import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
 import java.security.Key;
 import java.security.PublicKey;
 import java.security.PrivateKey;
 import java.security.Security;
 import java.security.Signature;
-import java.security.SignatureException;
 import java.security.UnrecoverableEntryException;
 import java.security.UnrecoverableKeyException;
 import java.security.Principal;
 import java.security.Provider;
 import java.security.Identity;
-import java.security.Signer;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.security.cert.CertificateException;
-import java.security.interfaces.DSAParams;
-import java.security.interfaces.DSAPrivateKey;
-import java.security.interfaces.DSAPublicKey;
-import java.security.interfaces.RSAPrivateCrtKey;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.interfaces.RSAPublicKey;
 import java.text.Collator;
 import java.text.MessageFormat;
 import java.util.*;
@@ -62,7 +50,6 @@
 import java.net.URL;
 import java.net.URLClassLoader;
 
-import sun.misc.BASE64Decoder;
 import sun.misc.BASE64Encoder;
 import sun.security.util.ObjectIdentifier;
 import sun.security.pkcs.PKCS10;
@@ -72,11 +59,16 @@
 import sun.security.provider.X509Factory;
 import sun.security.util.DerOutputStream;
 import sun.security.util.Password;
-import sun.security.util.Resources;
 import sun.security.util.PathList;
 import javax.crypto.KeyGenerator;
 import javax.crypto.SecretKey;
 
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
 import sun.security.x509.*;
 
 import static java.security.KeyStore.*;
@@ -132,6 +124,7 @@
     private String ksfname = null;
     private File ksfile = null;
     private InputStream ksStream = null; // keystore stream
+    private String sslserver = null;
     private KeyStore keyStore = null;
     private boolean token = false;
     private boolean nullStream = false;
@@ -347,6 +340,9 @@
             } else if (collator.compare(flags, "-file") == 0) {
                 if (++i == args.length) errorNeedArgument(flags);
                 filename = args[i];
+            } else if (collator.compare(flags, "-sslserver") == 0) {
+                if (++i == args.length) errorNeedArgument(flags);
+                sslserver = args[i];
             } else if (collator.compare(flags, "-srckeystore") == 0) {
                 if (++i == args.length) errorNeedArgument(flags);
                 srcksfname = args[i];
@@ -924,17 +920,7 @@
                 doPrintEntries(out);
             }
         } else if (command == PRINTCERT) {
-            InputStream inStream = System.in;
-            if (filename != null) {
-                inStream = new FileInputStream(filename);
-            }
-            try {
-                doPrintCert(inStream, out);
-            } finally {
-                if (inStream != System.in) {
-                    inStream.close();
-                }
-            }
+            doPrintCert(out);
         } else if (command == SELFCERT) {
             doSelfCert(alias, dname, sigAlgName);
             kssave = true;
@@ -1744,7 +1730,7 @@
      * Reads a certificate (or certificate chain) and prints its contents in
      * a human readbable format.
      */
-    private void doPrintCert(InputStream in, PrintStream out)
+    private void printCertFromStream(InputStream in, PrintStream out)
         throws Exception
     {
         Collection<? extends Certificate> c = null;
@@ -1770,13 +1756,98 @@
                 Object[] source = {new Integer(i + 1)};
                 out.println(form.format(source));
             }
-            printX509Cert(x509Cert, out);
+            if (rfc) dumpCert(x509Cert, out);
+            else printX509Cert(x509Cert, out);
             if (i < (certs.length-1)) {
                 out.println();
             }
         }
     }
 
+    private void doPrintCert(final PrintStream out) throws Exception {
+        if (sslserver != null) {
+            SSLContext sc = SSLContext.getInstance("SSL");
+            final boolean[] certPrinted = new boolean[1];
+            sc.init(null, new TrustManager[] {
+                new X509TrustManager() {
+
+                    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+                        return null;
+                    }
+
+                    public void checkClientTrusted(
+                        java.security.cert.X509Certificate[] certs, String authType) {
+                    }
+
+                    public void checkServerTrusted(
+                            java.security.cert.X509Certificate[] certs, String authType) {
+                        for (int i=0; i<certs.length; i++) {
+                            X509Certificate cert = certs[i];
+                            try {
+                                if (rfc) {
+                                    dumpCert(cert, out);
+                                } else {
+                                    out.println("Certificate #" + i);
+                                    out.println("====================================");
+                                    printX509Cert(cert, out);
+                                    out.println();
+                                }
+                            } catch (Exception e) {
+                                if (debug) {
+                                    e.printStackTrace();
+                                }
+                            }
+                        }
+
+                        // Set to true where there's something to print
+                        if (certs.length > 0) {
+                            certPrinted[0] = true;
+                        }
+                    }
+                }
+            }, null);
+            HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
+            HttpsURLConnection.setDefaultHostnameVerifier(
+                    new HostnameVerifier() {
+                        public boolean verify(String hostname, SSLSession session) {
+                            return true;
+                        }
+                    });
+            // HTTPS instead of raw SSL, so that -Dhttps.proxyHost and
+            // -Dhttps.proxyPort can be used. Since we only go through
+            // the handshake process, an HTTPS server is not needed.
+            // This program should be able to deal with any SSL-based
+            // network service.
+            Exception ex = null;
+            try {
+                new URL("https://" + sslserver).openConnection().connect();
+            } catch (Exception e) {
+                ex = e;
+            }
+            // If the certs are not printed out, we consider it an error even
+            // if the URL connection is successful.
+            if (!certPrinted[0]) {
+                Exception e = new Exception(
+                        rb.getString("No certificate from the SSL server"));
+                if (ex != null) {
+                    e.initCause(ex);
+                }
+                throw e;
+            }
+        } else {
+            InputStream inStream = System.in;
+            if (filename != null) {
+                inStream = new FileInputStream(filename);
+            }
+            try {
+                printCertFromStream(inStream, out);
+            } finally {
+                if (inStream != System.in) {
+                    inStream.close();
+                }
+            }
+        }
+    }
     /**
      * Creates a self-signed certificate, and stores it as a single-element
      * certificate chain.
@@ -3127,7 +3198,7 @@
         System.err.println();
 
         System.err.println(rb.getString
-                ("-printcert   [-v] [-file <cert_file>]"));
+                ("-printcert   [-v] [-rfc] [-file <cert_file> | -sslserver <host[:port]>]"));
         System.err.println();
 
         System.err.println(rb.getString
--- a/jdk/src/share/classes/sun/security/util/Resources.java	Mon Jul 07 13:06:58 2008 -0700
+++ b/jdk/src/share/classes/sun/security/util/Resources.java	Wed Jul 09 12:03:16 2008 +0800
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2005 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2000-2008 Sun Microsystems, Inc.  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
@@ -386,8 +386,10 @@
         {"\t     [-alias <alias>]", "\t     [-alias <alias>]"},
         /** rest is same as -certreq starting from -keystore **/
 
-        {"-printcert   [-v] [-file <cert_file>]",
-                "-printcert   [-v] [-file <cert_file>]"},
+        {"-printcert   [-v] [-rfc] [-file <cert_file> | -sslserver <host[:port]>]",
+                "-printcert   [-v] [-rfc] [-file <cert_file> | -sslserver <host[:port]>]"},
+        {"No certificate from the SSL server",
+                "No certificate from the SSL server"},
 
         //{"-selfcert    [-v] [-protected]",
         //      "-selfcert    [-v] [-protected]"},
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/tools/keytool/PrintSSL.java	Wed Jul 09 12:03:16 2008 +0800
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+// Read printssl.sh, this Java program starts an SSL server
+
+import java.net.ServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+
+public class PrintSSL {
+    public static void main(String[] args) throws Exception {
+        System.setProperty("javax.net.ssl.keyStorePassword", "passphrase");
+        System.setProperty("javax.net.ssl.keyStore",
+                System.getProperty("test.src", "./") + "/../../ssl/etc/keystore");
+        SSLServerSocketFactory sslssf =
+                (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
+        final ServerSocket server = sslssf.createServerSocket(0);
+        System.out.println(server.getLocalPort());
+        System.out.flush();
+        Thread t = new Thread() {
+            public void run() {
+                try {
+                    Thread.sleep(30000);
+                    server.close();
+                } catch (Exception e) {
+                    ;
+                }
+                throw new RuntimeException("Timeout");
+            }
+        };
+        t.setDaemon(true);
+        t.start();
+        ((SSLSocket)server.accept()).startHandshake();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/tools/keytool/printssl.sh	Wed Jul 09 12:03:16 2008 +0800
@@ -0,0 +1,58 @@
+#
+# Copyright 2008 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+# CA 95054 USA or visit www.sun.com if you need additional information or
+# have any questions.
+#
+
+# @test
+# @bug 6480981
+# @summary keytool should be able to import certificates from remote SSL servers
+
+if [ "${TESTSRC}" = "" ] ; then
+  TESTSRC="."
+fi
+if [ "${TESTJAVA}" = "" ] ; then
+  echo "TESTJAVA not set.  Test cannot execute."
+  echo "FAILED!!!"
+  exit 1
+fi
+
+# set platform-dependent variables
+OS=`uname -s`
+case "$OS" in
+  SunOS | Linux )
+    FS="/"
+    ;;
+  Windows_* )
+    FS="\\"
+    ;;
+  * )
+    echo "Unrecognized operating system!"
+    exit 1;
+    ;;
+esac
+
+${TESTJAVA}${FS}bin${FS}javac -d . ${TESTSRC}${FS}PrintSSL.java || exit 10
+${TESTJAVA}${FS}bin${FS}java -Dtest.src=$TESTSRC PrintSSL | ( read PORT; ${TESTJAVA}${FS}bin${FS}keytool -printcert -sslserver localhost:$PORT )
+status=$?
+
+rm PrintSSL*.class
+
+exit $status