From c849a27b02c93b35408b9d0b2e2778d8faf9d55d Mon Sep 17 00:00:00 2001 From: weijun Date: Wed, 9 Jul 2008 12:03:16 +0800 Subject: [PATCH] 6480981: keytool should be able to import certificates from remote SSL servers Reviewed-by: vinnie, wetmore --- .../classes/sun/security/tools/KeyTool.java | 129 ++++++++++++++---- .../classes/sun/security/util/Resources.java | 8 +- test/sun/security/tools/keytool/PrintSSL.java | 55 ++++++++ test/sun/security/tools/keytool/printssl.sh | 58 ++++++++ 4 files changed, 218 insertions(+), 32 deletions(-) create mode 100644 test/sun/security/tools/keytool/PrintSSL.java create mode 100644 test/sun/security/tools/keytool/printssl.sh diff --git a/src/share/classes/sun/security/tools/KeyTool.java b/src/share/classes/sun/security/tools/KeyTool.java index 41cfa2dc9..68fb2fa95 100644 --- a/src/share/classes/sun/security/tools/KeyTool.java +++ b/src/share/classes/sun/security/tools/KeyTool.java @@ -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.lang.reflect.Constructor; 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.SystemIdentity; 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 @@ public final class KeyTool { 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 @@ public final class KeyTool { } 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 @@ public final class KeyTool { 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 @@ public final class KeyTool { * 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 c = null; @@ -1770,13 +1756,98 @@ public final class KeyTool { 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 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 @@ public final class KeyTool { System.err.println(); System.err.println(rb.getString - ("-printcert [-v] [-file ]")); + ("-printcert [-v] [-rfc] [-file | -sslserver ]")); System.err.println(); System.err.println(rb.getString diff --git a/src/share/classes/sun/security/util/Resources.java b/src/share/classes/sun/security/util/Resources.java index 7f765ee36..0f5982183 100644 --- a/src/share/classes/sun/security/util/Resources.java +++ b/src/share/classes/sun/security/util/Resources.java @@ -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 @@ public class Resources extends java.util.ListResourceBundle { {"\t [-alias ]", "\t [-alias ]"}, /** rest is same as -certreq starting from -keystore **/ - {"-printcert [-v] [-file ]", - "-printcert [-v] [-file ]"}, + {"-printcert [-v] [-rfc] [-file | -sslserver ]", + "-printcert [-v] [-rfc] [-file | -sslserver ]"}, + {"No certificate from the SSL server", + "No certificate from the SSL server"}, //{"-selfcert [-v] [-protected]", // "-selfcert [-v] [-protected]"}, diff --git a/test/sun/security/tools/keytool/PrintSSL.java b/test/sun/security/tools/keytool/PrintSSL.java new file mode 100644 index 000000000..7e9ce7b95 --- /dev/null +++ b/test/sun/security/tools/keytool/PrintSSL.java @@ -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(); + } +} diff --git a/test/sun/security/tools/keytool/printssl.sh b/test/sun/security/tools/keytool/printssl.sh new file mode 100644 index 000000000..9fc19cd9b --- /dev/null +++ b/test/sun/security/tools/keytool/printssl.sh @@ -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 -- GitLab