The attached patch adds a new class
javax.net.ssl.HostnameVerifierFactory, along with an SPI class and an
implementation. This allows TLS clients to perform host name
verification without referring to the internal HostnameChecker class.
I've updated the existing TLS test case for Kerberos to include host
name checking, and a new test case for host name verification with
certificate authentication. It turns out that HostnameChecker does not
quite implement the algorithm from RFC 2818 (I think only a single
wildcard per entire name is allowed by the RFC), but that could be
changed in a separate patch.
--
Florian Weimer / Red Hat Product Security Team
diff --git a/src/share/classes/javax/net/ssl/HostnameVerifier.java b/src/share/classes/javax/net/ssl/HostnameVerifier.java
--- a/src/share/classes/javax/net/ssl/HostnameVerifier.java
+++ b/src/share/classes/javax/net/ssl/HostnameVerifier.java
@@ -41,6 +41,7 @@
*
* @author Brad R. Wetmore
* @since 1.4
+ * @see HostnameVerifierFactory
*/
public interface HostnameVerifier {
diff --git a/src/share/classes/javax/net/ssl/HostnameVerifierFactory.java b/src/share/classes/javax/net/ssl/HostnameVerifierFactory.java
new file mode 100644
--- /dev/null
+++ b/src/share/classes/javax/net/ssl/HostnameVerifierFactory.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 1999, 2012, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package javax.net.ssl;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.security.Security;
+
+import sun.security.jca.GetInstance;
+
+/**
+ * This class provides a factory for host name verifiers.
+ *
+ * The algorithm for host name verification varies slightly among
+ * application layer protocols. A single factory can create host name
+ * verifiers for different verification algorithms.
+ *
+ * <a name="algorithms">
+ * <h4>Standard host name verification algorithms</h4>
+ *
+ * The {@link #getInstance(String)}, {@link #getInstance(String, String)},
+ * and {@link #getInstance(String, Provider)} methods support at least the
+ * following host name verification algorithms.
+ *
+ * <ul>
+ * <li><code>"HTTP"</code>: The host name is matched against any
+ * <code>dnsName</code> entry in the subject alternative name of the
+ * peer certificate. If there are no <code>dnsName</code> entries at all,
+ * the most specific <code>commonName</code> attribute of the
+ * subject distinguished name is used instead. Matching is case-insensitive
+ * and supports "<code>*</code>" wildcards in the name obtained from the
+ * peer certificate in any position.
+ * (This is approximately the matching algorithm from
+ * from RFC 2818, section 3.1.)
+ * <li><code>"LDAP"</code>: Same as <code>"HTTP"</code>, except that a
+ * wildcard is supported only in the first component.
+ * (This is approximately the matching algorithm from
+ * RFC 4513, section 3.1.3.1.)
+ * </ul>
+ *
+ * The rules above cover TLS session authenticated by an X.509 certificate.
+ * If the TLS session uses Kerberos authentication, both algorithm
+ * compare the host name in a case-insensitive manner, without wildcard
+ * matching.
+ *
+ * @since 1.8
+ * @see HostnameVerifier
+ */
+public class HostnameVerifierFactory {
+ private final HostnameVerifierFactorySpi factorySpi;
+ private final Provider provider;
+ private final String algorithm;
+
+ /**
+ * Creates a <code>HostNameVerifierFactory</code> object.
+ *
+ * @param factorySpi the delegate
+ * @param provider the provider
+ * @param algorithm the algorithm
+ */
+ protected HostnameVerifierFactory(HostnameVerifierFactorySpi factorySpi,
+ Provider provider, String algorithm) {
+ this.factorySpi = factorySpi;
+ this.provider = provider;
+ this.algorithm = algorithm;
+ }
+
+ /**
+ * Returns the algorithm name of this <code>HostnameVerifierFactory</code>
+ * object.
+ *
+ * <p>This is the same name that was specified in one of the
+ * <code>getInstance</code> calls that created this
+ * <code>HostnameVerifierFactory</code> object.
+ *
+ * @return the algorithm name of this <code>HostnameVerifierFactory</code>
+ * object.
+ */
+ public final String getAlgorithm() {
+ return this.algorithm;
+ }
+
+ /**
+ * Returns a <code>HostnameVerifierFactory</code> object that acts as a
+ * factory for host name verifiers.
+ *
+ * <p> This method traverses the list of registered security Providers,
+ * starting with the most preferred Provider.
+ * A new HostnameVerifierFactory object encapsulating the
+ * HostnameVerifierFactorySpi implementation from the first
+ * Provider that supports the specified algorithm is returned.
+ *
+ * <p> Note that the list of registered providers may be retrieved via
+ * the {@link Security#getProviders() Security.getProviders()} method.
+ *
+ * @param algorithm the name of the requested algorithm. See
+ * <a href="#algorithms">Standard host name verification algorithms</a>.
+ *
+ * @return the new <code>HostnameVerifierFactory</code> object.
+ *
+ * @exception NoSuchAlgorithmException if no Provider supports a
+ * HostnameVerifierFactorySpi implementation for the
+ * specified algorithm.
+ * @exception NullPointerException if <code>algorithm</code> is null.
+ *
+ * @see java.security.Provider
+ */
+ public static final HostnameVerifierFactory getInstance(String algorithm)
+ throws NoSuchAlgorithmException {
+ GetInstance.Instance instance = GetInstance.getInstance
+ ("HostnameVerifierFactory", HostnameVerifierFactorySpi.class,
+ algorithm);
+ return new HostnameVerifierFactory(
+ (HostnameVerifierFactorySpi)instance.impl,
+ instance.provider, algorithm);
+ }
+
+ /**
+ * Returns a <code>HostnameVerifierFactory</code> object that acts as a
+ * factory for host name verifier.
+ *
+ * <p> A new HostnameVerifierFactory object encapsulating the
+ * HostnameVerifierFactorySpi implementation from the specified provider
+ * is returned. The specified provider must be registered
+ * in the security provider list.
+ *
+ * <p> Note that the list of registered providers may be retrieved via
+ * the {@link Security#getProviders() Security.getProviders()} method.
+
+ * @param algorithm the name of the requested algorithm. See
+ * <a href="#algorithms">Standard host name verification algorithms</a>.
+
+ * @param provider the name of the provider.
+ *
+ * @return the new <code>HostnameVerifierFactory</code> object.
+ *
+ * @throws NoSuchAlgorithmException if a HostnameVerifierFactorySpi
+ * implementation for the specified algorithm is not
+ * available from the specified provider.
+ *
+ * @throws NoSuchProviderException if the specified provider is not
+ * registered in the security provider list.
+ *
+ * @throws IllegalArgumentException if the provider name is null or empty.
+ * @throws NullPointerException if <code>algorithm</code> is null.
+ *
+ * @see java.security.Provider
+ */
+ public static final HostnameVerifierFactory getInstance(String algorithm,
+ String provider) throws NoSuchAlgorithmException,
+ NoSuchProviderException {
+ GetInstance.Instance instance = GetInstance.getInstance
+ ("HostnameVerifierFactory", HostnameVerifierFactorySpi.class,
+ algorithm, provider);
+ return new HostnameVerifierFactory(
+ (HostnameVerifierFactorySpi)instance.impl,
+ instance.provider, algorithm);
+ }
+
+ /**
+ * Returns a <code>HostnameVerifierFactory</code> object that acts as a
+ * factory for host name verifiers.
+ *
+ * <p> A new HostnameVerifierFactory object encapsulating the
+ * HostnameVerifierFactorySpi implementation from the specified Provider
+ * object is returned. Note that the specified Provider object
+ * does not have to be registered in the provider list.
+ *
+ * @param algorithm the name of the requested algorithm. See
+ * <a href="#algorithms">Standard host name verification algorithms</a>.
+
+ * @param provider an instance of the provider.
+ *
+ * @return the new <code>HostnameVerifierFactory</code> object.
+ *
+ * @throws NoSuchAlgorithmException if a HostnameVerifierFactorySpi
+ * implementation for the specified algorithm is not available
+ * from the specified Provider object.
+ *
+ * @throws IllegalArgumentException if provider is null.
+ * @throws NullPointerException if <code>algorithm</code> is null.
+ *
+ * @see java.security.Provider
+ */
+ public static final HostnameVerifierFactory getInstance(String algorithm,
+ Provider provider) throws NoSuchAlgorithmException {
+ GetInstance.Instance instance = GetInstance.getInstance
+ ("HostnameVerifierFactory", HostnameVerifierFactorySpi.class,
+ algorithm, provider);
+ return new HostnameVerifierFactory(
+ (HostnameVerifierFactorySpi)instance.impl,
+ instance.provider, algorithm);
+ }
+
+ /**
+ * Returns the provider of this <code>HostnameVerifierFactory</code>
+ * object.
+ *
+ * @return the provider of this <code>HostnameVerifierFactory</code> object
+ */
+ public final Provider getProvider() {
+ return this.provider;
+ }
+
+ /**
+ * Returns a host name verifier for the specified algorithm.
+ *
+ * @return the host name verifier
+ */
+ public final HostnameVerifier getHostnameVerifier() {
+ return factorySpi.engineGetHostnameVerifier();
+ }
+}
diff --git a/src/share/classes/javax/net/ssl/HostnameVerifierFactorySpi.java b/src/share/classes/javax/net/ssl/HostnameVerifierFactorySpi.java
new file mode 100644
--- /dev/null
+++ b/src/share/classes/javax/net/ssl/HostnameVerifierFactorySpi.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2012, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package javax.net.ssl;
+
+/**
+ * This class defines the <i>Service Provider Interface</i> (<b>SPI</b>)
+ * for the <code>HostnameVerifierFactory</code> class.
+ *
+ * <p> All the abstract methods in this class must be implemented by each
+ * cryptographic service provider who wishes to supply the implementation
+ * of a particular host name verifier factory.
+ *
+ * @since 1.8
+ * @see HostnameVerifierFactory
+ * @see HostnameVerifier
+ */
+public abstract class HostnameVerifierFactorySpi {
+ /**
+ * Returns a host name verifier.
+ *
+ * @return the host name verifier
+ * @see HostnameVerifierFactory#getHostnameVerifier()
+ */
+ public abstract HostnameVerifier engineGetHostnameVerifier();
+}
diff --git a/src/share/classes/sun/security/ssl/HostnameVerifierFactoryImpl.java b/src/share/classes/sun/security/ssl/HostnameVerifierFactoryImpl.java
new file mode 100644
--- /dev/null
+++ b/src/share/classes/sun/security/ssl/HostnameVerifierFactoryImpl.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2012, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package sun.security.ssl;
+
+import java.security.Principal;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HostnameVerifierFactorySpi;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import sun.security.util.HostnameChecker;
+
+/**
+ * Factory for host name verifiers.
+ *
+ * @see HostnameVerifier
+ */
+class HostnameVerifierFactoryImpl extends HostnameVerifierFactorySpi {
+ private final byte type;
+
+ HostnameVerifierFactoryImpl(byte type) {
+ this.type = type;
+ }
+
+ @Override
+ public HostnameVerifier engineGetHostnameVerifier() {
+ return new Verifier(type);
+ }
+
+ public static class HTTP extends HostnameVerifierFactoryImpl {
+ public HTTP() {
+ super(HostnameChecker.TYPE_TLS);
+ }
+ }
+
+ public static class LDAP extends HostnameVerifierFactoryImpl {
+ public LDAP() {
+ super(HostnameChecker.TYPE_LDAP);
+ }
+ }
+
+ static class Verifier implements HostnameVerifier {
+ private final HostnameChecker checker;
+
+ Verifier(byte type) {
+ checker = HostnameChecker.getInstance(type);
+ }
+
+ @Override
+ public boolean verify(String hostname, SSLSession session) {
+ // Try Kerberos first.
+ Principal peerPrincipal;
+ try {
+ peerPrincipal = session.getPeerPrincipal();
+ } catch (SSLPeerUnverifiedException e) {
+ return false;
+ }
+ if (peerPrincipal instanceof KerberosPrincipal) {
+ return HostnameChecker.match(hostname, peerPrincipal);
+ }
+
+ // Not Kerberos, so try X.509.
+ java.security.cert.Certificate peer;
+ try {
+ peer = session.getPeerCertificates()[0];
+ } catch (SSLPeerUnverifiedException e) {
+ return false;
+ }
+ if (peer instanceof java.security.cert.X509Certificate) {
+ try {
+ checker.match(hostname, (X509Certificate) peer);
+ return true;
+ } catch (CertificateException e) {
+ return false;
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/src/share/classes/sun/security/ssl/SunJSSE.java b/src/share/classes/sun/security/ssl/SunJSSE.java
--- a/src/share/classes/sun/security/ssl/SunJSSE.java
+++ b/src/share/classes/sun/security/ssl/SunJSSE.java
@@ -226,6 +226,11 @@
*/
put("KeyStore.PKCS12",
"sun.security.pkcs12.PKCS12KeyStore");
+
+ put("HostnameVerifierFactory.HTTP",
+ "sun.security.ssl.HostnameVerifierFactoryImpl$HTTP");
+ put("HostnameVerifierFactory.LDAP",
+ "sun.security.ssl.HostnameVerifierFactoryImpl$LDAP");
}
private void subclassCheck() {
diff --git a/test/sun/security/krb5/auto/SSL.java b/test/sun/security/krb5/auto/SSL.java
--- a/test/sun/security/krb5/auto/SSL.java
+++ b/test/sun/security/krb5/auto/SSL.java
@@ -56,6 +56,8 @@
// 0-Not started, 1-Start OK, 2-Failure
private static volatile int serverState = 0;
+ private static String error;
+
public static void main(String[] args) throws Exception {
krb5Cipher = args[0];
@@ -157,6 +159,10 @@
c.startAsClient("host/" + server, GSSUtil.GSS_KRB5_MECH_OID);
c.doAs(new JsseClientAction(), null);
+ if (error != null) {
+ throw new Exception(error);
+ }
+
// Revoke the old key
/*Thread.sleep(2000);
ktab = KeyTab.create(OneKDC.KTAB);
@@ -207,6 +213,16 @@
Principal peer = sslSocket.getSession().getPeerPrincipal();
System.out.println("Server is: " + peer.toString());
+ HostnameVerifier verifier =
+ HostnameVerifierFactory.getInstance("HTTP")
+ .getHostnameVerifier();
+ if (!verifier.verify(server, sslSocket.getSession())) {
+ error = "host name verification failed unexpectedly";
+ }
+ if (verifier.verify("x" + server, sslSocket.getSession())) {
+ error = "host name verification succeded unexpectedly";
+ }
+
sslSocket.close();
// This line should not be needed. It's the server's duty to
// forget the old key
diff --git a/test/sun/security/ssl/javax/net/ssl/HostnameVerification.java b/test/sun/security/ssl/javax/net/ssl/HostnameVerification.java
new file mode 100644
--- /dev/null
+++ b/test/sun/security/ssl/javax/net/ssl/HostnameVerification.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2012, 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
+ * @summary verify that host name verifiers work as expected
+ * @run main/othervm HostnameVerification TLSv1
+ * @run main/othervm HostnameVerification TLSv1.1
+ * @run main/othervm HostnameVerification TLSv1.2
+ */
+
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.SecureRandom;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashSet;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HostnameVerifierFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.X509KeyManager;
+import javax.net.ssl.X509TrustManager;
+
+import sun.security.x509.AlgorithmId;
+import sun.security.x509.CertificateAlgorithmId;
+import sun.security.x509.CertificateSerialNumber;
+import sun.security.x509.CertificateValidity;
+import sun.security.x509.CertificateVersion;
+import sun.security.x509.CertificateX509Key;
+import sun.security.x509.X500Name;
+import sun.security.x509.X509CertImpl;
+import sun.security.x509.X509CertInfo;
+
+public class HostnameVerification {
+
+ private static final String[] testNames = {
+ "example.com", "www.example.com", "test.www.example.com",
+ "*.example.com", "*.www.example.com", "test.*.example.com", "www.*.com",
+ "example.net", "xn--rger-koa.example.com", "dummy-name",
+ };
+
+ private static final HashSet<String> matchExceptions =
+ new HashSet<>(Arrays.asList(new String[] {
+ "HTTP: CN: [*.example.com] matches [www.example.com]",
+ "HTTP: CN: [*.example.com] matches [xn--rger-koa.example.com]",
+ "HTTP: CN: [www.*.com] matches [www.example.com]",
+ "HTTP: CN: [test.*.example.com] matches [test.www.example.com]",
+ "HTTP: CN: [*.www.example.com] matches [test.www.example.com]",
+ "LDAP: CN: [*.example.com] matches [www.example.com]",
+ "LDAP: CN: [*.example.com] matches [xn--rger-koa.example.com]",
+ "LDAP: CN: [*.www.example.com] matches [test.www.example.com]",
+ }));
+
+ private static String protocol;
+ private static final SecureRandom random = new SecureRandom();
+ private static KeyPair keyPair;
+ private static ServerSocket serverSocket;
+
+ private static String matchProtocol;
+ private static HostnameVerifier verifier;
+
+ private static int mismatches;
+
+ public static void main(String[] args) throws Exception {
+ protocol = args[0];
+ serverSocket = new ServerSocket();
+ serverSocket.bind(new InetSocketAddress(
+ InetAddress.getLoopbackAddress(), 0));
+
+ KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA");
+ keygen.initialize(1024, random);
+ keyPair = keygen.generateKeyPair();
+
+ matchProtocol = "HTTP";
+ run();
+ matchProtocol = "LDAP";
+ run();
+ if (mismatches != 0) {
+ throw new Exception(mismatches + " mismatches encountered");
+ }
+ }
+
+ private static void run() throws Exception {
+ HostnameVerifierFactory factory =
+ HostnameVerifierFactory.getInstance(matchProtocol);
+ verifier = factory.getHostnameVerifier();
+ for (final String certName : testNames) {
+ X509Certificate cert =
+ createCertificate(new X500Name("CN=\"" + certName + '"'));
+ run(cert, new ProcessSession() {
+ @Override
+ public void operate(SSLSession sslSession)
+ throws Exception {
+ for (String hostName : testNames) {
+ tryMatch(sslSession, certName, hostName);
+ }
+ }
+ });
+ }
+ }
+
+ private static void tryMatch(SSLSession sslSession,
+ String certName, String hostName) throws Exception {
+ boolean match = verifier.verify(hostName, sslSession);
+ if (match == certName.equalsIgnoreCase(hostName)) {
+ return;
+ }
+ String matchString = match ? "matches" : "does not match";
+ String matchException = String.format("%s: CN: [%s] %s [%s]",
+ matchProtocol, certName, matchString, hostName);
+ if (matchExceptions.contains(matchException)) {
+ return;
+ }
+ System.out.println(matchException);
+ ++mismatches;
+ }
+
+ /**
+ * Callback invoked with a live TLS session.
+ */
+ interface ProcessSession {
+ void operate(SSLSession session) throws Exception;
+ }
+
+ /**
+ * Invoke the callback with a live TLS session for a peer
+ * authenticated by the X.509 certificate.
+ */
+ private static void run(X509Certificate cert, ProcessSession op)
+ throws Exception {
+ SSLContext sslContext = SSLContext.getInstance(protocol);
+ sslContext.init(new X509KeyManager[] {new MyKeyManager(cert)},
+ new X509TrustManager[] {new MyTrustManager()},
+ random);
+ ServerThread serverThread = new ServerThread(sslContext);
+ serverThread.start();
+ SSLSocketFactory factory = sslContext.getSocketFactory();
+ try (SSLSocket clientEnd = (SSLSocket) factory.createSocket()) {
+ clientEnd.connect(serverSocket.getLocalSocketAddress());
+ clientEnd.setTcpNoDelay(true);
+ clientEnd.startHandshake();
+ op.operate(clientEnd.getSession());
+ }
+ serverThread.join();
+ if (!serverThread.good) {
+ throw new Exception("failure in server thread");
+ }
+ }
+
+ /**
+ * Key manager which returns our generated key.
+ */
+ static class MyKeyManager implements X509KeyManager {
+ X509Certificate cert;
+
+ MyKeyManager(X509Certificate cert) {
+ this.cert = cert;
+ }
+
+ @Override
+ public String[] getClientAliases(String keyType, Principal[] issuers) {
+ return new String[0];
+ }
+
+ @Override
+ public String chooseClientAlias(String[] keyType, Principal[] issuers,
+ Socket socket) {
+ return "";
+ }
+
+ @Override
+ public String[] getServerAliases(String keyType, Principal[] issuers) {
+ return new String[0];
+ }
+
+ @Override
+ public String chooseServerAlias(String keyType, Principal[] issuers,
+ Socket socket) {
+ return "";
+ }
+
+ @Override
+ public X509Certificate[] getCertificateChain(String alias) {
+ return new X509Certificate[] {cert};
+ }
+
+ @Override
+ public PrivateKey getPrivateKey(String alias) {
+ return keyPair.getPrivate();
+ }
+ }
+
+ /**
+ * Trust manager which accepts all certificates.
+ */
+ static class MyTrustManager implements X509TrustManager {
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return new X509Certificate[0];
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain,
+ String authType) {
+ }
+
+ public void checkClientTrusted(X509Certificate[] chain,
+ String authType) {
+ }
+ }
+
+ static class ServerThread extends Thread {
+ private SSLContext context;
+ boolean good = true;
+
+ ServerThread(SSLContext context) {
+ this.context = context;
+ }
+
+ @Override
+ public void run() {
+ SSLSocketFactory factory = context.getSocketFactory();
+ try {
+ try (Socket serverEnd = serverSocket.accept();
+ SSLSocket sslServerEnd = (SSLSocket) factory.createSocket(
+ serverEnd, "dummy-name", 0, false)) {
+ serverEnd.setTcpNoDelay(true);
+ sslServerEnd.setUseClientMode(false);
+ // Wait for the client to close the connection.
+ sslServerEnd.getInputStream().read();
+ }
+ } catch (Throwable e) {
+ e.printStackTrace();
+ good = false;
+ }
+ }
+ }
+
+ /**
+ * Creates a self-signed certificate with the indicated
+ * subject/issuer DN.
+ */
+ private static X509Certificate createCertificate(X500Name name)
+ throws Exception {
+ X509CertInfo info = new X509CertInfo();
+ info.set(X509CertInfo.VERSION,
+ new CertificateVersion(CertificateVersion.V3));
+ info.set(X509CertInfo.SERIAL_NUMBER,
+ new CertificateSerialNumber(BigInteger.ONE));
+ info.set(X509CertInfo.ALGORITHM_ID,
+ new CertificateAlgorithmId(AlgorithmId.get("SHA1withRSA")));
+ info.set(X509CertInfo.SUBJECT, name);
+ info.set(X509CertInfo.KEY, new CertificateX509Key(keyPair.getPublic()));
+ Date start = new Date();
+ Date end = new Date();
+ end.setTime(start.getTime() + 3600 * 1000);
+ info.set(X509CertInfo.VALIDITY, new CertificateValidity(start, end));
+ info.set(X509CertInfo.ISSUER, name);
+
+ X509CertImpl cert = new X509CertImpl(info);
+ cert.sign(keyPair.getPrivate(), "SHA1withRSA");
+ return cert;
+ }
+
+}