7068321: Support TLS Server Name Indication (SNI) Extension in JSSE Server
Reviewed-by: mullan, weijun, wetmore
--- a/jdk/src/share/classes/javax/net/ssl/ExtendedSSLSession.java Wed Oct 17 13:22:39 2012 -0700
+++ b/jdk/src/share/classes/javax/net/ssl/ExtendedSSLSession.java Thu Oct 18 01:14:00 2012 -0700
@@ -25,6 +25,8 @@
package javax.net.ssl;
+import java.util.List;
+
/**
* Extends the <code>SSLSession</code> interface to support additional
* session attributes.
@@ -83,4 +85,34 @@
* @see X509ExtendedKeyManager
*/
public abstract String[] getPeerSupportedSignatureAlgorithms();
+
+ /**
+ * Obtains a {@link List} containing all {@link SNIServerName}s
+ * of the requested Server Name Indication (SNI) extension.
+ * <P>
+ * In server mode, unless the return {@link List} is empty,
+ * the server should use the requested server names to guide its
+ * selection of an appropriate authentication certificate, and/or
+ * other aspects of security policy.
+ * <P>
+ * In client mode, unless the return {@link List} is empty,
+ * the client should use the requested server names to guide its
+ * endpoint identification of the peer's identity, and/or
+ * other aspects of security policy.
+ *
+ * @return a non-null immutable list of {@link SNIServerName}s of the
+ * requested server name indications. The returned list may be
+ * empty if no server name indications were requested.
+ * @throws UnsupportedOperationException if the underlying provider
+ * does not implement the operation
+ *
+ * @see SNIServerName
+ * @see X509ExtendedTrustManager
+ * @see X509ExtendedKeyManager
+ *
+ * @since 1.8
+ */
+ public List<SNIServerName> getRequestedServerNames() {
+ throw new UnsupportedOperationException();
+ }
}
--- a/jdk/src/share/classes/javax/net/ssl/HandshakeCompletedEvent.java Wed Oct 17 13:22:39 2012 -0700
+++ b/jdk/src/share/classes/javax/net/ssl/HandshakeCompletedEvent.java Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -186,8 +186,7 @@
// if the provider does not support it, fallback to peer certs.
// return the X500Principal of the end-entity cert.
Certificate[] certs = getPeerCertificates();
- principal = (X500Principal)
- ((X509Certificate)certs[0]).getSubjectX500Principal();
+ principal = ((X509Certificate)certs[0]).getSubjectX500Principal();
}
return principal;
}
@@ -216,7 +215,7 @@
// return the X500Principal of the end-entity cert.
Certificate[] certs = getLocalCertificates();
if (certs != null) {
- principal = (X500Principal)
+ principal =
((X509Certificate)certs[0]).getSubjectX500Principal();
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/javax/net/ssl/SNIHostName.java Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,394 @@
+/*
+ * 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;
+
+import java.net.IDN;
+import java.nio.ByteBuffer;
+import java.nio.charset.CodingErrorAction;
+import java.nio.charset.StandardCharsets;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CharacterCodingException;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.regex.Pattern;
+
+/**
+ * Instances of this class represent a server name of type
+ * {@link StandardConstants#SNI_HOST_NAME host_name} in a Server Name
+ * Indication (SNI) extension.
+ * <P>
+ * As described in section 3, "Server Name Indication", of
+ * <A HREF="http://www.ietf.org/rfc/rfc6066.txt">TLS Extensions (RFC 6066)</A>,
+ * "HostName" contains the fully qualified DNS hostname of the server, as
+ * understood by the client. The encoded server name value of a hostname is
+ * represented as a byte string using ASCII encoding without a trailing dot.
+ * This allows the support of Internationalized Domain Names (IDN) through
+ * the use of A-labels (the ASCII-Compatible Encoding (ACE) form of a valid
+ * string of Internationalized Domain Names for Applications (IDNA)) defined
+ * in <A HREF="http://www.ietf.org/rfc/rfc5890.txt">RFC 5890</A>.
+ * <P>
+ * Note that {@code SNIHostName} objects are immutable.
+ *
+ * @see SNIServerName
+ * @see StandardConstants#SNI_HOST_NAME
+ *
+ * @since 1.8
+ */
+public final class SNIHostName extends SNIServerName {
+
+ // the decoded string value of the server name
+ private final String hostname;
+
+ /**
+ * Creates an {@code SNIHostName} using the specified hostname.
+ * <P>
+ * Note that per <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>,
+ * the encoded server name value of a hostname is
+ * {@link StandardCharsets#US_ASCII}-compliant. In this method,
+ * {@code hostname} can be a user-friendly Internationalized Domain Name
+ * (IDN). {@link IDN#toASCII(String, int)} is used to enforce the
+ * restrictions on ASCII characters in hostnames (see
+ * <A HREF="http://www.ietf.org/rfc/rfc3490.txt">RFC 3490</A>,
+ * <A HREF="http://www.ietf.org/rfc/rfc1122.txt">RFC 1122</A>,
+ * <A HREF="http://www.ietf.org/rfc/rfc1123.txt">RFC 1123</A>) and
+ * translate the {@code hostname} into ASCII Compatible Encoding (ACE), as:
+ * <pre>
+ * IDN.toASCII(hostname, IDN.USE_STD3_ASCII_RULES);
+ * </pre>
+ * <P>
+ * The {@code hostname} argument is illegal if it:
+ * <ul>
+ * <li> {@code hostname} is empty,</li>
+ * <li> {@code hostname} ends with a trailing dot,</li>
+ * <li> {@code hostname} is not a valid Internationalized
+ * Domain Name (IDN) compliant with the RFC 3490 specification.</li>
+ * </ul>
+ * @param hostname
+ * the hostname of this server name
+ *
+ * @throws NullPointerException if {@code hostname} is {@code null}
+ * @throws IllegalArgumentException if {@code hostname} is illegal
+ */
+ public SNIHostName(String hostname) {
+ // IllegalArgumentException will be thrown if {@code hostname} is
+ // not a valid IDN.
+ super(StandardConstants.SNI_HOST_NAME,
+ (hostname = IDN.toASCII(
+ Objects.requireNonNull(hostname,
+ "Server name value of host_name cannot be null"),
+ IDN.USE_STD3_ASCII_RULES))
+ .getBytes(StandardCharsets.US_ASCII));
+
+ this.hostname = hostname;
+
+ // check the validity of the string hostname
+ checkHostName();
+ }
+
+ /**
+ * Creates an {@code SNIHostName} using the specified encoded value.
+ * <P>
+ * This method is normally used to parse the encoded name value in a
+ * requested SNI extension.
+ * <P>
+ * Per <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>,
+ * the encoded name value of a hostname is
+ * {@link StandardCharsets#US_ASCII}-compliant. However, in the previous
+ * version of the SNI extension (
+ * <A HREF="http://www.ietf.org/rfc/rfc4366.txt">RFC 4366</A>),
+ * the encoded hostname is represented as a byte string using UTF-8
+ * encoding. For the purpose of version tolerance, this method allows
+ * that the charset of {@code encoded} argument can be
+ * {@link StandardCharsets#UTF_8}, as well as
+ * {@link StandardCharsets#US_ASCII}. {@link IDN#toASCII(String)} is used
+ * to translate the {@code encoded} argument into ASCII Compatible
+ * Encoding (ACE) hostname.
+ * <P>
+ * It is strongly recommended that this constructor is only used to parse
+ * the encoded name value in a requested SNI extension. Otherwise, to
+ * comply with <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>,
+ * please always use {@link StandardCharsets#US_ASCII}-compliant charset
+ * and enforce the restrictions on ASCII characters in hostnames (see
+ * <A HREF="http://www.ietf.org/rfc/rfc3490.txt">RFC 3490</A>,
+ * <A HREF="http://www.ietf.org/rfc/rfc1122.txt">RFC 1122</A>,
+ * <A HREF="http://www.ietf.org/rfc/rfc1123.txt">RFC 1123</A>)
+ * for {@code encoded} argument, or use {@link SNIHostName(String)} instead.
+ * <P>
+ * The {@code encoded} argument is illegal if it:
+ * <ul>
+ * <li> {@code encoded} is empty,</li>
+ * <li> {@code encoded} ends with a trailing dot,</li>
+ * <li> {@code encoded} is not encoded in
+ * {@link StandardCharsets#US_ASCII} or
+ * {@link StandardCharsets#UTF_8}-compliant charset,</li>
+ * <li> {@code encoded} is not a valid Internationalized
+ * Domain Name (IDN) compliant with the RFC 3490 specification.</li>
+ * </ul>
+ *
+ * <P>
+ * Note that the {@code encoded} byte array is cloned
+ * to protect against subsequent modification.
+ *
+ * @param encoded
+ * the encoded hostname of this server name
+ *
+ * @throws NullPointerException if {@code encoded} is {@code null}
+ * @throws IllegalArgumentException if {@code encoded} is illegal
+ */
+ public SNIHostName(byte[] encoded) {
+ // NullPointerException will be thrown if {@code encoded} is null
+ super(StandardConstants.SNI_HOST_NAME, encoded);
+
+ // Compliance: RFC 4366 requires that the hostname is represented
+ // as a byte string using UTF_8 encoding [UTF8]
+ try {
+ // Please don't use {@link String} constructors because they
+ // do not report coding errors.
+ CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder()
+ .onMalformedInput(CodingErrorAction.REPORT)
+ .onUnmappableCharacter(CodingErrorAction.REPORT);
+
+ this.hostname = IDN.toASCII(
+ decoder.decode(ByteBuffer.wrap(encoded)).toString());
+ } catch (RuntimeException | CharacterCodingException e) {
+ throw new IllegalArgumentException(
+ "The encoded server name value is invalid", e);
+ }
+
+ // check the validity of the string hostname
+ checkHostName();
+ }
+
+ /**
+ * Returns the {@link StandardCharsets#US_ASCII}-compliant hostname of
+ * this {@code SNIHostName} object.
+ * <P>
+ * Note that, per
+ * <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>, the
+ * returned hostname may be an internationalized domain name that
+ * contains A-labels. See
+ * <A HREF="http://www.ietf.org/rfc/rfc5890.txt">RFC 5890</A>
+ * for more information about the detailed A-label specification.
+ *
+ * @return the {@link StandardCharsets#US_ASCII}-compliant hostname
+ * of this {@code SNIHostName} object
+ */
+ public String getAsciiName() {
+ return hostname;
+ }
+
+ /**
+ * Compares this server name to the specified object.
+ * <P>
+ * Per <A HREF="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</A>, DNS
+ * hostnames are case-insensitive. Two server hostnames are equal if,
+ * and only if, they have the same name type, and the hostnames are
+ * equal in a case-independent comparison.
+ *
+ * @param other
+ * the other server name object to compare with.
+ * @return true if, and only if, the {@code other} is considered
+ * equal to this instance
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (other instanceof SNIHostName) {
+ return hostname.equalsIgnoreCase(((SNIHostName)other).hostname);
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns a hash code value for this {@code SNIHostName}.
+ * <P>
+ * The hash code value is generated using the case-insensitive hostname
+ * of this {@code SNIHostName}.
+ *
+ * @return a hash code value for this {@code SNIHostName}.
+ */
+ @Override
+ public int hashCode() {
+ int result = 17; // 17/31: prime number to decrease collisions
+ result = 31 * result + hostname.toUpperCase(Locale.ENGLISH).hashCode();
+
+ return result;
+ }
+
+ /**
+ * Returns a string representation of the object, including the DNS
+ * hostname in this {@code SNIHostName} object.
+ * <P>
+ * The exact details of the representation are unspecified and subject
+ * to change, but the following may be regarded as typical:
+ * <pre>
+ * "type=host_name (0), value={@literal <hostname>}"
+ * </pre>
+ * The "{@literal <hostname>}" is an ASCII representation of the hostname,
+ * which may contains A-labels. For example, a returned value of an pseudo
+ * hostname may look like:
+ * <pre>
+ * "type=host_name (0), value=www.example.com"
+ * </pre>
+ * or
+ * <pre>
+ * "type=host_name (0), value=xn--fsqu00a.xn--0zwm56d"
+ * </pre>
+ * <P>
+ * Please NOTE that the exact details of the representation are unspecified
+ * and subject to change.
+ *
+ * @return a string representation of the object.
+ */
+ @Override
+ public String toString() {
+ return "type=host_name (0), value=" + hostname;
+ }
+
+ /**
+ * Creates an {@link SNIMatcher} object for {@code SNIHostName}s.
+ * <P>
+ * This method can be used by a server to verify the acceptable
+ * {@code SNIHostName}s. For example,
+ * <pre>
+ * SNIMatcher matcher =
+ * SNIHostName.createSNIMatcher("www\\.example\\.com");
+ * </pre>
+ * will accept the hostname "www.example.com".
+ * <pre>
+ * SNIMatcher matcher =
+ * SNIHostName.createSNIMatcher("www\\.example\\.(com|org)");
+ * </pre>
+ * will accept hostnames "www.example.com" and "www.example.org".
+ *
+ * @param regex
+ * the <a href="{@docRoot}/java/util/regex/Pattern.html#sum">
+ * regular expression pattern</a>
+ * representing the hostname(s) to match
+ * @throws NullPointerException if {@code regex} is
+ * {@code null}
+ * @throws PatternSyntaxException if the regular expression's syntax
+ * is invalid
+ */
+ public static SNIMatcher createSNIMatcher(String regex) {
+ if (regex == null) {
+ throw new NullPointerException(
+ "The regular expression cannot be null");
+ }
+
+ return new SNIHostNameMatcher(regex);
+ }
+
+ // check the validity of the string hostname
+ private void checkHostName() {
+ if (hostname.isEmpty()) {
+ throw new IllegalArgumentException(
+ "Server name value of host_name cannot be empty");
+ }
+
+ if (hostname.endsWith(".")) {
+ throw new IllegalArgumentException(
+ "Server name value of host_name cannot have the trailing dot");
+ }
+ }
+
+ private final static class SNIHostNameMatcher extends SNIMatcher {
+
+ // the compiled representation of a regular expression.
+ private final Pattern pattern;
+
+ /**
+ * Creates an SNIHostNameMatcher object.
+ *
+ * @param regex
+ * the <a href="{@docRoot}/java/util/regex/Pattern.html#sum">
+ * regular expression pattern</a>
+ * representing the hostname(s) to match
+ * @throws NullPointerException if {@code regex} is
+ * {@code null}
+ * @throws PatternSyntaxException if the regular expression's syntax
+ * is invalid
+ */
+ SNIHostNameMatcher(String regex) {
+ super(StandardConstants.SNI_HOST_NAME);
+ pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
+ }
+
+ /**
+ * Attempts to match the given {@link SNIServerName}.
+ *
+ * @param serverName
+ * the {@link SNIServerName} instance on which this matcher
+ * performs match operations
+ *
+ * @return {@code true} if, and only if, the matcher matches the
+ * given {@code serverName}
+ *
+ * @throws NullPointerException if {@code serverName} is {@code null}
+ * @throws IllegalArgumentException if {@code serverName} is
+ * not of {@code StandardConstants#SNI_HOST_NAME} type
+ *
+ * @see SNIServerName
+ */
+ @Override
+ public boolean matches(SNIServerName serverName) {
+ if (serverName == null) {
+ throw new NullPointerException(
+ "The SNIServerName argument cannot be null");
+ }
+
+ SNIHostName hostname;
+ if (!(serverName instanceof SNIHostName)) {
+ if (serverName.getType() != StandardConstants.SNI_HOST_NAME) {
+ throw new IllegalArgumentException(
+ "The server name type is not host_name");
+ }
+
+ try {
+ hostname = new SNIHostName(serverName.getEncoded());
+ } catch (NullPointerException | IllegalArgumentException e) {
+ return false;
+ }
+ } else {
+ hostname = (SNIHostName)serverName;
+ }
+
+ // Let's first try the ascii name matching
+ String asciiName = hostname.getAsciiName();
+ if (pattern.matcher(asciiName).matches()) {
+ return true;
+ }
+
+ // May be an internationalized domain name, check the Unicode
+ // representations.
+ return pattern.matcher(IDN.toUnicode(asciiName)).matches();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/javax/net/ssl/SNIMatcher.java Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,105 @@
+/*
+ * 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;
+
+/**
+ * Instances of this class represent a matcher that performs match
+ * operations on an {@link SNIServerName} instance.
+ * <P>
+ * Servers can use Server Name Indication (SNI) information to decide if
+ * specific {@link SSLSocket} or {@link SSLEngine} instances should accept
+ * a connection. For example, when multiple "virtual" or "name-based"
+ * servers are hosted on a single underlying network address, the server
+ * application can use SNI information to determine whether this server is
+ * the exact server that the client wants to access. Instances of this
+ * class can be used by a server to verify the acceptable server names of
+ * a particular type, such as host names.
+ * <P>
+ * {@code SNIMatcher} objects are immutable. Subclasses should not provide
+ * methods that can change the state of an instance once it has been created.
+ *
+ * @see SNIServerName
+ * @see SNIHostName
+ * @see SSLParameters#getSNIMatchers()
+ * @see SSLParameters#setSNIMatchers(Collection<SNIMatcher>)
+ *
+ * @since 1.8
+ */
+public abstract class SNIMatcher {
+
+ // the type of the server name that this matcher performs on
+ private final int type;
+
+ /**
+ * Creates an {@code SNIMatcher} using the specified server name type.
+ *
+ * @param type
+ * the type of the server name that this matcher performs on
+ *
+ * @throws IllegalArgumentException if {@code type} is not in the range
+ * of 0 to 255, inclusive.
+ */
+ protected SNIMatcher(int type) {
+ if (type < 0) {
+ throw new IllegalArgumentException(
+ "Server name type cannot be less than zero");
+ } else if (type > 255) {
+ throw new IllegalArgumentException(
+ "Server name type cannot be greater than 255");
+ }
+
+ this.type = type;
+ }
+
+ /**
+ * Returns the server name type of this {@code SNIMatcher} object.
+ *
+ * @return the server name type of this {@code SNIMatcher} object.
+ *
+ * @see SNIServerName
+ */
+ public final int getType() {
+ return type;
+ }
+
+ /**
+ * Attempts to match the given {@link SNIServerName}.
+ *
+ * @param serverName
+ * the {@link SNIServerName} instance on which this matcher
+ * performs match operations
+ *
+ * @return {@code true} if, and only if, the matcher matches the
+ * given {@code serverName}
+ *
+ * @throws NullPointerException if {@code serverName} is {@code null}
+ * @throws IllegalArgumentException if {@code serverName} is
+ * not of the given server name type of this matcher
+ *
+ * @see SNIServerName
+ */
+ public abstract boolean matches(SNIServerName serverName);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/javax/net/ssl/SNIServerName.java Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,213 @@
+/*
+ * 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;
+
+import java.util.Arrays;
+
+/**
+ * Instances of this class represent a server name in a Server Name
+ * Indication (SNI) extension.
+ * <P>
+ * The SNI extension is a feature that extends the SSL/TLS protocols to
+ * indicate what server name the client is attempting to connect to during
+ * handshaking. See section 3, "Server Name Indication", of <A
+ * HREF="http://www.ietf.org/rfc/rfc6066.txt">TLS Extensions (RFC 6066)</A>.
+ * <P>
+ * {@code SNIServerName} objects are immutable. Subclasses should not provide
+ * methods that can change the state of an instance once it has been created.
+ *
+ * @see SSLParameters#getServerNames()
+ * @see SSLParameters#setServerNames(List<SNIServerName>)
+ *
+ * @since 1.8
+ */
+public abstract class SNIServerName {
+
+ // the type of the server name
+ private final int type;
+
+ // the encoded value of the server name
+ private final byte[] encoded;
+
+ // the hex digitals
+ private static final char[] HEXES = "0123456789ABCDEF".toCharArray();
+
+ /**
+ * Creates an {@code SNIServerName} using the specified name type and
+ * encoded value.
+ * <P>
+ * Note that the {@code encoded} byte array is cloned to protect against
+ * subsequent modification.
+ *
+ * @param type
+ * the type of the server name
+ * @param encoded
+ * the encoded value of the server name
+ *
+ * @throws IllegalArgumentException if {@code type} is not in the range
+ * of 0 to 255, inclusive.
+ * @throws NullPointerException if {@code encoded} is null
+ */
+ protected SNIServerName(int type, byte[] encoded) {
+ if (type < 0) {
+ throw new IllegalArgumentException(
+ "Server name type cannot be less than zero");
+ } else if (type > 255) {
+ throw new IllegalArgumentException(
+ "Server name type cannot be greater than 255");
+ }
+ this.type = type;
+
+ if (encoded == null) {
+ throw new NullPointerException(
+ "Server name encoded value cannot be null");
+ }
+ this.encoded = encoded.clone();
+ }
+
+
+ /**
+ * Returns the name type of this server name.
+ *
+ * @return the name type of this server name
+ */
+ public final int getType() {
+ return type;
+ }
+
+ /**
+ * Returns a copy of the encoded server name value of this server name.
+ *
+ * @return a copy of the encoded server name value of this server name
+ */
+ public final byte[] getEncoded() {
+ return encoded.clone();
+ }
+
+ /**
+ * Indicates whether some other object is "equal to" this server name.
+ *
+ * @return true if, and only if, {@code other} is of the same class
+ * of this object, and has the same name type and
+ * encoded value as this server name.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (this.getClass() != other.getClass()) {
+ return false;
+ }
+
+ SNIServerName that = (SNIServerName)other;
+ return (this.type == that.type) &&
+ Arrays.equals(this.encoded, that.encoded);
+ }
+
+ /**
+ * Returns a hash code value for this server name.
+ * <P>
+ * The hash code value is generated using the name type and encoded
+ * value of this server name.
+ *
+ * @return a hash code value for this server name.
+ */
+ @Override
+ public int hashCode() {
+ int result = 17; // 17/31: prime number to decrease collisions
+ result = 31 * result + type;
+ result = 31 * result + Arrays.hashCode(encoded);
+
+ return result;
+ }
+
+ /**
+ * Returns a string representation of this server name, including the server
+ * name type and the encoded server name value in this
+ * {@code SNIServerName} object.
+ * <P>
+ * The exact details of the representation are unspecified and subject
+ * to change, but the following may be regarded as typical:
+ * <pre>
+ * "type={@literal <name type>}, value={@literal <name value>}"
+ * </pre>
+ * <P>
+ * In this class, the format of "{@literal <name type>}" is
+ * "[LITERAL] (INTEGER)", where the optional "LITERAL" is the literal
+ * name, and INTEGER is the integer value of the name type. The format
+ * of "{@literal <name value>}" is "XX:...:XX", where "XX" is the
+ * hexadecimal digit representation of a byte value. For example, a
+ * returned value of an pseudo server name may look like:
+ * <pre>
+ * "type=(31), value=77:77:77:2E:65:78:61:6D:70:6C:65:2E:63:6E"
+ * </pre>
+ * or
+ * <pre>
+ * "type=host_name (0), value=77:77:77:2E:65:78:61:6D:70:6C:65:2E:63:6E"
+ * </pre>
+ *
+ * <P>
+ * Please NOTE that the exact details of the representation are unspecified
+ * and subject to change, and subclasses may override the method with
+ * their own formats.
+ *
+ * @return a string representation of this server name
+ */
+ @Override
+ public String toString() {
+ if (type == StandardConstants.SNI_HOST_NAME) {
+ return "type=host_name (0), value=" + toHexString(encoded);
+ } else {
+ return "type=(" + type + "), value=" + toHexString(encoded);
+ }
+ }
+
+ // convert byte array to hex string
+ private static String toHexString(byte[] bytes) {
+ if (bytes.length == 0) {
+ return "(empty)";
+ }
+
+ StringBuilder sb = new StringBuilder(bytes.length * 3 - 1);
+ boolean isInitial = true;
+ for (byte b : bytes) {
+ if (isInitial) {
+ isInitial = false;
+ } else {
+ sb.append(':');
+ }
+
+ int k = b & 0xFF;
+ sb.append(HEXES[k >>> 4]);
+ sb.append(HEXES[k & 0xF]);
+ }
+
+ return sb.toString();
+ }
+}
+
--- a/jdk/src/share/classes/javax/net/ssl/SSLEngine.java Wed Oct 17 13:22:39 2012 -0700
+++ b/jdk/src/share/classes/javax/net/ssl/SSLEngine.java Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 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
@@ -1214,15 +1214,19 @@
*
* <p>This means:
* <ul>
- * <li>if <code>params.getCipherSuites()</code> is non-null,
- * <code>setEnabledCipherSuites()</code> is called with that value
- * <li>if <code>params.getProtocols()</code> is non-null,
- * <code>setEnabledProtocols()</code> is called with that value
- * <li>if <code>params.getNeedClientAuth()</code> or
- * <code>params.getWantClientAuth()</code> return <code>true</code>,
- * <code>setNeedClientAuth(true)</code> and
- * <code>setWantClientAuth(true)</code> are called, respectively;
- * otherwise <code>setWantClientAuth(false)</code> is called.
+ * <li>If {@code params.getCipherSuites()} is non-null,
+ * {@code setEnabledCipherSuites()} is called with that value.</li>
+ * <li>If {@code params.getProtocols()} is non-null,
+ * {@code setEnabledProtocols()} is called with that value.</li>
+ * <li>If {@code params.getNeedClientAuth()} or
+ * {@code params.getWantClientAuth()} return {@code true},
+ * {@code setNeedClientAuth(true)} and
+ * {@code setWantClientAuth(true)} are called, respectively;
+ * otherwise {@code setWantClientAuth(false)} is called.</li>
+ * <li>If {@code params.getServerNames()} is non-null, the engine will
+ * configure its server names with that value.</li>
+ * <li>If {@code params.getSNIMatchers()} is non-null, the engine will
+ * configure its SNI matchers with that value.</li>
* </ul>
*
* @param params the parameters
--- a/jdk/src/share/classes/javax/net/ssl/SSLParameters.java Wed Oct 17 13:22:39 2012 -0700
+++ b/jdk/src/share/classes/javax/net/ssl/SSLParameters.java Thu Oct 18 01:14:00 2012 -0700
@@ -26,13 +26,23 @@
package javax.net.ssl;
import java.security.AlgorithmConstraints;
+import java.util.Map;
+import java.util.List;
+import java.util.HashSet;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.regex.Pattern;
/**
* Encapsulates parameters for an SSL/TLS connection. The parameters
* are the list of ciphersuites to be accepted in an SSL/TLS handshake,
* the list of protocols to be allowed, the endpoint identification
- * algorithm during SSL/TLS handshaking, the algorithm constraints and
- * whether SSL/TLS servers should request or require client authentication.
+ * algorithm during SSL/TLS handshaking, the Server Name Indication (SNI),
+ * the algorithm constraints and whether SSL/TLS servers should request
+ * or require client authentication.
* <p>
* SSLParameters can be created via the constructors in this class.
* Objects can also be obtained using the <code>getSSLParameters()</code>
@@ -47,7 +57,7 @@
* SSLParameters can be applied to a connection via the methods
* {@link SSLSocket#setSSLParameters SSLSocket.setSSLParameters()} and
* {@link SSLServerSocket#setSSLParameters SSLServerSocket.setSSLParameters()}
- * and {@link SSLEngine#setSSLParameters SSLEngine.getSSLParameters()}.
+ * and {@link SSLEngine#setSSLParameters SSLEngine.setSSLParameters()}.
*
* @see SSLSocket
* @see SSLEngine
@@ -63,11 +73,15 @@
private boolean needClientAuth;
private String identificationAlgorithm;
private AlgorithmConstraints algorithmConstraints;
+ private Map<Integer, SNIServerName> sniNames = null;
+ private Map<Integer, SNIMatcher> sniMatchers = null;
/**
* Constructs SSLParameters.
* <p>
- * The cipherSuites and protocols values are set to <code>null</code>,
+ * The values of cipherSuites, protocols, cryptographic algorithm
+ * constraints, endpoint identification algorithm, server names and
+ * server name matchers are set to <code>null</code>,
* wantClientAuth and needClientAuth are set to <code>false</code>.
*/
public SSLParameters() {
@@ -254,4 +268,173 @@
this.identificationAlgorithm = algorithm;
}
+ /**
+ * Sets the desired {@link SNIServerName}s of the Server Name
+ * Indication (SNI) parameter.
+ * <P>
+ * This method is only useful to {@link SSLSocket}s or {@link SSLEngine}s
+ * operating in client mode.
+ * <P>
+ * Note that the {@code serverNames} list is cloned
+ * to protect against subsequent modification.
+ *
+ * @param serverNames
+ * the list of desired {@link SNIServerName}s (or null)
+ *
+ * @throws NullPointerException if the {@code serverNames}
+ * contains {@code null} element
+ * @throws IllegalArgumentException if the {@code serverNames}
+ * contains more than one name of the same name type
+ *
+ * @see SNIServerName
+ * @see #getServerNames()
+ *
+ * @since 1.8
+ */
+ public void setServerNames(List<SNIServerName> serverNames) {
+ if (serverNames != null) {
+ if (!serverNames.isEmpty()) {
+ sniNames = new LinkedHashMap<>(serverNames.size());
+ for (SNIServerName serverName : serverNames) {
+ if (sniNames.put(serverName.getType(),
+ serverName) != null) {
+ throw new IllegalArgumentException(
+ "Duplicated server name of type " +
+ serverName.getType());
+ }
+ }
+ } else {
+ sniNames = Collections.<Integer, SNIServerName>emptyMap();
+ }
+ } else {
+ sniNames = null;
+ }
+ }
+
+ /**
+ * Returns a {@link List} containing all {@link SNIServerName}s of the
+ * Server Name Indication (SNI) parameter, or null if none has been set.
+ * <P>
+ * This method is only useful to {@link SSLSocket}s or {@link SSLEngine}s
+ * operating in client mode.
+ * <P>
+ * For SSL/TLS connections, the underlying SSL/TLS provider
+ * may specify a default value for a certain server name type. In
+ * client mode, it is recommended that, by default, providers should
+ * include the server name indication whenever the server can be located
+ * by a supported server name type.
+ * <P>
+ * It is recommended that providers initialize default Server Name
+ * Indications when creating {@code SSLSocket}/{@code SSLEngine}s.
+ * In the following examples, the server name could be represented by an
+ * instance of {@link SNIHostName} which has been initialized with the
+ * hostname "www.example.com" and type
+ * {@link StandardConstants#SNI_HOST_NAME}.
+ *
+ * <pre>
+ * Socket socket =
+ * sslSocketFactory.createSocket("www.example.com", 443);
+ * </pre>
+ * or
+ * <pre>
+ * SSLEngine engine =
+ * sslContext.createSSLEngine("www.example.com", 443);
+ * </pre>
+ * <P>
+ *
+ * @return null or an immutable list of non-null {@link SNIServerName}s
+ *
+ * @see List
+ * @see #setServerNames(List<SNIServerName>)
+ *
+ * @since 1.8
+ */
+ public List<SNIServerName> getServerNames() {
+ if (sniNames != null) {
+ if (!sniNames.isEmpty()) {
+ return Collections.<SNIServerName>unmodifiableList(
+ new ArrayList<>(sniNames.values()));
+ } else {
+ return Collections.<SNIServerName>emptyList();
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Sets the {@link SNIMatcher}s of the Server Name Indication (SNI)
+ * parameter.
+ * <P>
+ * This method is only useful to {@link SSLSocket}s or {@link SSLEngine}s
+ * operating in server mode.
+ * <P>
+ * Note that the {@code matchers} collection is cloned to protect
+ * against subsequent modification.
+ *
+ * @param matchers
+ * the collection of {@link SNIMatcher}s (or null)
+ *
+ * @throws NullPointerException if the {@code matchers}
+ * contains {@code null} element
+ * @throws IllegalArgumentException if the {@code matchers}
+ * contains more than one name of the same name type
+ *
+ * @see Collection
+ * @see SNIMatcher
+ * @see #getSNIMatchers()
+ *
+ * @since 1.8
+ */
+ public void setSNIMatchers(Collection<SNIMatcher> matchers) {
+ if (matchers != null) {
+ if (!matchers.isEmpty()) {
+ sniMatchers = new HashMap<>(matchers.size());
+ for (SNIMatcher matcher : matchers) {
+ if (sniMatchers.put(matcher.getType(),
+ matcher) != null) {
+ throw new IllegalArgumentException(
+ "Duplicated server name of type " +
+ matcher.getType());
+ }
+ }
+ } else {
+ sniMatchers = Collections.<Integer, SNIMatcher>emptyMap();
+ }
+ } else {
+ sniMatchers = null;
+ }
+ }
+
+ /**
+ * Returns a {@link Collection} containing all {@link SNIMatcher}s of the
+ * Server Name Indication (SNI) parameter, or null if none has been set.
+ * <P>
+ * This method is only useful to {@link SSLSocket}s or {@link SSLEngine}s
+ * operating in server mode.
+ * <P>
+ * For better interoperability, providers generally will not define
+ * default matchers so that by default servers will ignore the SNI
+ * extension and continue the handshake.
+ *
+ * @return null or an immutable collection of non-null {@link SNIMatcher}s
+ *
+ * @see SNIMatcher
+ * @see #setSNIMatchers(Collection<SNIMatcher>)
+ *
+ * @since 1.8
+ */
+ public Collection<SNIMatcher> getSNIMatchers() {
+ if (sniMatchers != null) {
+ if (!sniMatchers.isEmpty()) {
+ return Collections.<SNIMatcher>unmodifiableList(
+ new ArrayList<>(sniMatchers.values()));
+ } else {
+ return Collections.<SNIMatcher>emptyList();
+ }
+ }
+
+ return null;
+ }
}
+
--- a/jdk/src/share/classes/javax/net/ssl/SSLServerSocket.java Wed Oct 17 13:22:39 2012 -0700
+++ b/jdk/src/share/classes/javax/net/ssl/SSLServerSocket.java Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -484,15 +484,19 @@
*
* <p>This means:
* <ul>
- * <li>if <code>params.getCipherSuites()</code> is non-null,
- * <code>setEnabledCipherSuites()</code> is called with that value
- * <li>if <code>params.getProtocols()</code> is non-null,
- * <code>setEnabledProtocols()</code> is called with that value
- * <li>if <code>params.getNeedClientAuth()</code> or
- * <code>params.getWantClientAuth()</code> return <code>true</code>,
- * <code>setNeedClientAuth(true)</code> and
- * <code>setWantClientAuth(true)</code> are called, respectively;
- * otherwise <code>setWantClientAuth(false)</code> is called.
+ * <li>If {@code params.getCipherSuites()} is non-null,
+ * {@code setEnabledCipherSuites()} is called with that value.</li>
+ * <li>If {@code params.getProtocols()} is non-null,
+ * {@code setEnabledProtocols()} is called with that value.</li>
+ * <li>If {@code params.getNeedClientAuth()} or
+ * {@code params.getWantClientAuth()} return {@code true},
+ * {@code setNeedClientAuth(true)} and
+ * {@code setWantClientAuth(true)} are called, respectively;
+ * otherwise {@code setWantClientAuth(false)} is called.</li>
+ * <li>If {@code params.getServerNames()} is non-null, the socket will
+ * configure its server names with that value.</li>
+ * <li>If {@code params.getSNIMatchers()} is non-null, the socket will
+ * configure its SNI matchers with that value.</li>
* </ul>
*
* @param params the parameters
--- a/jdk/src/share/classes/javax/net/ssl/SSLSocket.java Wed Oct 17 13:22:39 2012 -0700
+++ b/jdk/src/share/classes/javax/net/ssl/SSLSocket.java Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -626,15 +626,19 @@
*
* <p>This means:
* <ul>
- * <li>if <code>params.getCipherSuites()</code> is non-null,
- * <code>setEnabledCipherSuites()</code> is called with that value
- * <li>if <code>params.getProtocols()</code> is non-null,
- * <code>setEnabledProtocols()</code> is called with that value
- * <li>if <code>params.getNeedClientAuth()</code> or
- * <code>params.getWantClientAuth()</code> return <code>true</code>,
- * <code>setNeedClientAuth(true)</code> and
- * <code>setWantClientAuth(true)</code> are called, respectively;
- * otherwise <code>setWantClientAuth(false)</code> is called.
+ * <li>If {@code params.getCipherSuites()} is non-null,
+ * {@code setEnabledCipherSuites()} is called with that value.</li>
+ * <li>If {@code params.getProtocols()} is non-null,
+ * {@code setEnabledProtocols()} is called with that value.</li>
+ * <li>If {@code params.getNeedClientAuth()} or
+ * {@code params.getWantClientAuth()} return {@code true},
+ * {@code setNeedClientAuth(true)} and
+ * {@code setWantClientAuth(true)} are called, respectively;
+ * otherwise {@code setWantClientAuth(false)} is called.</li>
+ * <li>If {@code params.getServerNames()} is non-null, the socket will
+ * configure its server names with that value.</li>
+ * <li>If {@code params.getSNIMatchers()} is non-null, the socket will
+ * configure its SNI matchers with that value.</li>
* </ul>
*
* @param params the parameters
--- a/jdk/src/share/classes/javax/net/ssl/SSLSocketFactory.java Wed Oct 17 13:22:39 2012 -0700
+++ b/jdk/src/share/classes/javax/net/ssl/SSLSocketFactory.java Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -29,6 +29,7 @@
import java.net.*;
import javax.net.SocketFactory;
import java.io.IOException;
+import java.io.InputStream;
import java.security.*;
import java.util.Locale;
@@ -180,8 +181,55 @@
* @throws NullPointerException if the parameter s is null
*/
public abstract Socket createSocket(Socket s, String host,
- int port, boolean autoClose)
- throws IOException;
+ int port, boolean autoClose) throws IOException;
+
+ /**
+ * Creates a server mode {@link Socket} layered over an
+ * existing connected socket, and is able to read data which has
+ * already been consumed/removed from the {@link Socket}'s
+ * underlying {@link InputStream}.
+ * <p>
+ * This method can be used by a server application that needs to
+ * observe the inbound data but still create valid SSL/TLS
+ * connections: for example, inspection of Server Name Indication
+ * (SNI) extensions (See section 3 of <A
+ * HREF="http://www.ietf.org/rfc/rfc6066.txt">TLS Extensions
+ * (RFC6066)</A>). Data that has been already removed from the
+ * underlying {@link InputStream} should be loaded into the
+ * {@code consumed} stream before this method is called, perhaps
+ * using a {@link ByteArrayInputStream}. When this {@link Socket}
+ * begins handshaking, it will read all of the data in
+ * {@code consumed} until it reaches {@code EOF}, then all further
+ * data is read from the underlying {@link InputStream} as
+ * usual.
+ * <p>
+ * The returned socket is configured using the socket options
+ * established for this factory, and is set to use server mode when
+ * handshaking (see {@link SSLSocket#setUseClientMode(boolean)}).
+ *
+ * @param s
+ * the existing socket
+ * @param consumed
+ * the consumed inbound network data that has already been
+ * removed from the existing {@link Socket}
+ * {@link InputStream}. This parameter may be
+ * {@code null} if no data has been removed.
+ * @param autoClose close the underlying socket when this socket is closed.
+ *
+ * @return the {@link Socket} compliant with the socket options
+ * established for this factory
+ *
+ * @throws IOException if an I/O error occurs when creating the socket
+ * @throws UnsupportedOperationException if the underlying provider
+ * does not implement the operation
+ * @throws NullPointerException if {@code s} is {@code null}
+ *
+ * @since 1.8
+ */
+ public Socket createSocket(Socket s, InputStream consumed,
+ boolean autoClose) throws IOException {
+ throw new UnsupportedOperationException();
+ }
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/javax/net/ssl/StandardConstants.java Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,56 @@
+/*
+ * 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;
+
+/**
+ * Standard constants definitions
+ *
+ * @since 1.8
+ */
+public final class StandardConstants {
+
+ // Suppress default constructor for noninstantiability
+ private StandardConstants() {
+ throw new AssertionError(
+ "No javax.net.ssl.StandardConstants instances for you!");
+ }
+
+ /**
+ * The "host_name" type representing of a DNS hostname
+ * (see {@link SNIHostName}) in a Server Name Indication (SNI) extension.
+ * <P>
+ * The SNI extension is a feature that extends the SSL/TLS protocols to
+ * indicate what server name the client is attempting to connect to during
+ * handshaking. See section 3, "Server Name Indication", of <A
+ * HREF="http://www.ietf.org/rfc/rfc6066.txt">TLS Extensions (RFC 6066)</A>.
+ * <P>
+ * The value of this constant is {@value}.
+ *
+ * @see SNIServerName
+ * @see SNIHostName
+ */
+ public static final int SNI_HOST_NAME = 0x00;
+}
--- a/jdk/src/share/classes/sun/security/ssl/BaseSSLSocketImpl.java Wed Oct 17 13:22:39 2012 -0700
+++ b/jdk/src/share/classes/sun/security/ssl/BaseSSLSocketImpl.java Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 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
@@ -23,7 +23,6 @@
* questions.
*/
-
package sun.security.ssl;
import java.io.*;
@@ -55,16 +54,25 @@
* recurse infinitely ... e.g. close() calling itself, or doing
* I/O in terms of our own streams.
*/
- final Socket self;
+ final private Socket self;
+ final private InputStream consumedInput;
BaseSSLSocketImpl() {
super();
this.self = this;
+ this.consumedInput = null;
}
BaseSSLSocketImpl(Socket socket) {
super();
this.self = socket;
+ this.consumedInput = null;
+ }
+
+ BaseSSLSocketImpl(Socket socket, InputStream consumed) {
+ super();
+ this.self = socket;
+ this.consumedInput = consumed;
}
//
@@ -541,4 +549,57 @@
}
}
+ @Override
+ public String toString() {
+ if (self == this) {
+ return super.toString();
+ }
+
+ return self.toString();
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ if (self == this) {
+ return super.getInputStream();
+ }
+
+ if (consumedInput != null) {
+ return new SequenceInputStream(consumedInput,
+ self.getInputStream());
+ }
+
+ return self.getInputStream();
+ }
+
+ @Override
+ public OutputStream getOutputStream() throws IOException {
+ if (self == this) {
+ return super.getOutputStream();
+ }
+
+ return self.getOutputStream();
+ }
+
+ @Override
+ public synchronized void close() throws IOException {
+ if (self == this) {
+ super.close();
+ } else {
+ self.close();
+ }
+ }
+
+ @Override
+ public synchronized void setSoTimeout(int timeout) throws SocketException {
+ if (self == this) {
+ super.setSoTimeout(timeout);
+ } else {
+ self.setSoTimeout(timeout);
+ }
+ }
+
+ boolean isLayered() {
+ return (self != this);
+ }
}
--- a/jdk/src/share/classes/sun/security/ssl/ClientHandshaker.java Wed Oct 17 13:22:39 2012 -0700
+++ b/jdk/src/share/classes/sun/security/ssl/ClientHandshaker.java Thu Oct 18 01:14:00 2012 -0700
@@ -48,8 +48,6 @@
import sun.security.ssl.CipherSuite.*;
import static sun.security.ssl.CipherSuite.KeyExchange.*;
-import sun.net.util.IPAddressUtil;
-
/**
* ClientHandshaker does the protocol handshaking from the point
* of view of a client. It is driven asychronously by handshake messages
@@ -92,6 +90,9 @@
private final static boolean enableSNIExtension =
Debug.getBooleanProperty("jsse.enableSNIExtension", true);
+ private List<SNIServerName> requestedServerNames =
+ Collections.<SNIServerName>emptyList();
+
/*
* Constructors
*/
@@ -579,6 +580,7 @@
session = new SSLSessionImpl(protocolVersion, cipherSuite,
getLocalSupportedSignAlgs(),
mesg.sessionId, getHostSE(), getPortSE());
+ session.setRequestedServerNames(requestedServerNames);
setHandshakeSessionSE(session);
if (debug != null && Debug.isOn("handshake")) {
System.out.println("** " + cipherSuite);
@@ -1246,17 +1248,14 @@
// add server_name extension
if (enableSNIExtension) {
- // We cannot use the hostname resolved from name services. For
- // virtual hosting, multiple hostnames may be bound to the same IP
- // address, so the hostname resolved from name services is not
- // reliable.
- String hostname = getRawHostnameSE();
+ if (session != null) {
+ requestedServerNames = session.getRequestedServerNames();
+ } else {
+ requestedServerNames = serverNames;
+ }
- // we only allow FQDN
- if (hostname != null && hostname.indexOf('.') > 0 &&
- !IPAddressUtil.isIPv4LiteralAddress(hostname) &&
- !IPAddressUtil.isIPv6LiteralAddress(hostname)) {
- clientHelloMessage.addServerNameIndicationExtension(hostname);
+ if (!requestedServerNames.isEmpty()) {
+ clientHelloMessage.addSNIExtension(requestedServerNames);
}
}
--- a/jdk/src/share/classes/sun/security/ssl/HandshakeInStream.java Wed Oct 17 13:22:39 2012 -0700
+++ b/jdk/src/share/classes/sun/security/ssl/HandshakeInStream.java Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 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
@@ -121,7 +121,8 @@
r.mark(readlimit);
}
- public void reset() {
+ @Override
+ public void reset() throws IOException {
r.reset();
}
--- a/jdk/src/share/classes/sun/security/ssl/HandshakeMessage.java Wed Oct 17 13:22:39 2012 -0700
+++ b/jdk/src/share/classes/sun/security/ssl/HandshakeMessage.java Thu Oct 18 01:14:00 2012 -0700
@@ -256,13 +256,9 @@
}
// add server_name extension
- void addServerNameIndicationExtension(String hostname) {
- // We would have checked that the hostname ia a FQDN.
- ArrayList<String> hostnames = new ArrayList<>(1);
- hostnames.add(hostname);
-
+ void addSNIExtension(List<SNIServerName> serverNames) {
try {
- extensions.add(new ServerNameExtension(hostnames));
+ extensions.add(new ServerNameExtension(serverNames));
} catch (IOException ioe) {
// ignore the exception and return
}
--- a/jdk/src/share/classes/sun/security/ssl/Handshaker.java Wed Oct 17 13:22:39 2012 -0700
+++ b/jdk/src/share/classes/sun/security/ssl/Handshaker.java Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 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
@@ -112,6 +112,12 @@
*/
private CipherSuiteList activeCipherSuites;
+ // The server name indication and matchers
+ List<SNIServerName> serverNames =
+ Collections.<SNIServerName>emptyList();
+ Collection<SNIMatcher> sniMatchers =
+ Collections.<SNIMatcher>emptyList();
+
private boolean isClient;
private boolean needCertVerify;
@@ -287,14 +293,7 @@
}
}
- String getRawHostnameSE() {
- if (conn != null) {
- return conn.getRawHostname();
- } else {
- return engine.getPeerHost();
- }
- }
-
+ // ONLY used by ClientHandshaker to setup the peer host in SSLSession.
String getHostSE() {
if (conn != null) {
return conn.getHost();
@@ -303,6 +302,7 @@
}
}
+ // ONLY used by ServerHandshaker to setup the peer host in SSLSession.
String getHostAddressSE() {
if (conn != null) {
return conn.getInetAddress().getHostAddress();
@@ -436,6 +436,22 @@
}
/**
+ * Sets the server name indication of the handshake.
+ */
+ void setSNIServerNames(List<SNIServerName> serverNames) {
+ // The serverNames parameter is unmodifiable.
+ this.serverNames = serverNames;
+ }
+
+ /**
+ * Sets the server name matchers of the handshaking.
+ */
+ void setSNIMatchers(Collection<SNIMatcher> sniMatchers) {
+ // The sniMatchers parameter is unmodifiable.
+ this.sniMatchers = sniMatchers;
+ }
+
+ /**
* Prior to handshaking, activate the handshake and initialize the version,
* input stream and output stream.
*/
--- a/jdk/src/share/classes/sun/security/ssl/HelloExtensions.java Wed Oct 17 13:22:39 2012 -0700
+++ b/jdk/src/share/classes/sun/security/ssl/HelloExtensions.java Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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
@@ -28,11 +28,10 @@
import java.io.IOException;
import java.io.PrintStream;
import java.util.*;
-
+import javax.net.ssl.*;
+import java.nio.charset.StandardCharsets;
import java.security.spec.ECParameterSpec;
-import javax.net.ssl.SSLProtocolException;
-
/**
* This file contains all the classes relevant to TLS Extensions for the
* ClientHello and ServerHello messages. The extension mechanism and
@@ -274,11 +273,11 @@
}
/*
- * [RFC4366] To facilitate secure connections to servers that host multiple
- * 'virtual' servers at a single underlying network address, clients MAY
- * include an extension of type "server_name" in the (extended) client hello.
- * The "extension_data" field of this extension SHALL contain "ServerNameList"
- * where:
+ * [RFC 4366/6066] To facilitate secure connections to servers that host
+ * multiple 'virtual' servers at a single underlying network address, clients
+ * MAY include an extension of type "server_name" in the (extended) client
+ * hello. The "extension_data" field of this extension SHALL contain
+ * "ServerNameList" where:
*
* struct {
* NameType name_type;
@@ -299,44 +298,47 @@
*/
final class ServerNameExtension extends HelloExtension {
- final static int NAME_HOST_NAME = 0;
-
- private List<ServerName> names;
+ // For backward compatibility, all future data structures associated with
+ // new NameTypes MUST begin with a 16-bit length field.
+ final static int NAME_HEADER_LENGTH = 3; // NameType: 1 byte
+ // Name length: 2 bytes
+ private Map<Integer, SNIServerName> sniMap;
private int listLength; // ServerNameList length
- ServerNameExtension(List<String> hostnames) throws IOException {
+ // constructor for ServerHello
+ ServerNameExtension() throws IOException {
+ super(ExtensionType.EXT_SERVER_NAME);
+
+ listLength = 0;
+ sniMap = Collections.<Integer, SNIServerName>emptyMap();
+ }
+
+ // constructor for ClientHello
+ ServerNameExtension(List<SNIServerName> serverNames)
+ throws IOException {
super(ExtensionType.EXT_SERVER_NAME);
listLength = 0;
- names = new ArrayList<ServerName>(hostnames.size());
- for (String hostname : hostnames) {
- if (hostname != null && hostname.length() != 0) {
- // we only support DNS hostname now.
- ServerName serverName =
- new ServerName(NAME_HOST_NAME, hostname);
- names.add(serverName);
- listLength += serverName.length;
+ sniMap = new LinkedHashMap<>();
+ for (SNIServerName serverName : serverNames) {
+ // check for duplicated server name type
+ if (sniMap.put(serverName.getType(), serverName) != null) {
+ // unlikely to happen, but in case ...
+ throw new RuntimeException(
+ "Duplicated server name of type " + serverName.getType());
}
+
+ listLength += serverName.getEncoded().length + NAME_HEADER_LENGTH;
}
- // As we only support DNS hostname now, the hostname list must
- // not contain more than one hostname
- if (names.size() > 1) {
- throw new SSLProtocolException(
- "The ServerNameList MUST NOT contain more than " +
- "one name of the same name_type");
- }
-
- // We only need to add "server_name" extension in ClientHello unless
- // we support SNI in server side in the future. It is possible that
- // the SNI is empty in ServerHello. As we don't support SNI in
- // ServerHello now, we will throw exception for empty list for now.
+ // This constructor is used for ClientHello only. Empty list is
+ // not allowed in client mode.
if (listLength == 0) {
- throw new SSLProtocolException(
- "The ServerNameList cannot be empty");
+ throw new RuntimeException("The ServerNameList cannot be empty");
}
}
+ // constructor for ServerHello for parsing SNI extension
ServerNameExtension(HandshakeInStream s, int len)
throws IOException {
super(ExtensionType.EXT_SERVER_NAME);
@@ -350,17 +352,54 @@
}
remains -= 2;
- names = new ArrayList<ServerName>();
+ sniMap = new LinkedHashMap<>();
while (remains > 0) {
- ServerName name = new ServerName(s);
- names.add(name);
- remains -= name.length;
+ int code = s.getInt8(); // NameType
- // we may need to check the duplicated ServerName type
+ // HostName (length read in getBytes16);
+ byte[] encoded = s.getBytes16();
+ SNIServerName serverName;
+ switch (code) {
+ case StandardConstants.SNI_HOST_NAME:
+ if (encoded.length == 0) {
+ throw new SSLProtocolException(
+ "Empty HostName in server name indication");
+ }
+ try {
+ serverName = new SNIHostName(encoded);
+ } catch (IllegalArgumentException iae) {
+ SSLProtocolException spe = new SSLProtocolException(
+ "Illegal server name, type=host_name(" +
+ code + "), name=" +
+ (new String(encoded, StandardCharsets.UTF_8)) +
+ ", value=" + Debug.toString(encoded));
+ spe.initCause(iae);
+ throw spe;
+ }
+ break;
+ default:
+ try {
+ serverName = new UnknownServerName(code, encoded);
+ } catch (IllegalArgumentException iae) {
+ SSLProtocolException spe = new SSLProtocolException(
+ "Illegal server name, type=(" + code +
+ "), value=" + Debug.toString(encoded));
+ spe.initCause(iae);
+ throw spe;
+ }
+ }
+ // check for duplicated server name type
+ if (sniMap.put(serverName.getType(), serverName) != null) {
+ throw new SSLProtocolException(
+ "Duplicated server name of type " +
+ serverName.getType());
+ }
+
+ remains -= encoded.length + NAME_HEADER_LENGTH;
}
} else if (len == 0) { // "server_name" extension in ServerHello
listLength = 0;
- names = Collections.<ServerName>emptyList();
+ sniMap = Collections.<Integer, SNIServerName>emptyMap();
}
if (remains != 0) {
@@ -368,39 +407,72 @@
}
}
- static class ServerName {
- final int length;
- final int type;
- final byte[] data;
- final String hostname;
-
- ServerName(int type, String hostname) throws IOException {
- this.type = type; // NameType
- this.hostname = hostname;
- this.data = hostname.getBytes("UTF8"); // HostName
- this.length = data.length + 3; // NameType: 1 byte
- // HostName length: 2 bytes
+ List<SNIServerName> getServerNames() {
+ if (sniMap != null && !sniMap.isEmpty()) {
+ return Collections.<SNIServerName>unmodifiableList(
+ new ArrayList<>(sniMap.values()));
}
- ServerName(HandshakeInStream s) throws IOException {
- type = s.getInt8(); // NameType
- data = s.getBytes16(); // HostName (length read in getBytes16)
- length = data.length + 3; // NameType: 1 byte
- // HostName length: 2 bytes
- if (type == NAME_HOST_NAME) {
- hostname = new String(data, "UTF8");
- } else {
- hostname = null;
+ return Collections.<SNIServerName>emptyList();
+ }
+
+ /*
+ * Is the extension recognized by the corresponding matcher?
+ *
+ * This method is used to check whether the server name indication can
+ * be recognized by the server name matchers.
+ *
+ * Per RFC 6066, if the server understood the ClientHello extension but
+ * does not recognize the server name, the server SHOULD take one of two
+ * actions: either abort the handshake by sending a fatal-level
+ * unrecognized_name(112) alert or continue the handshake.
+ *
+ * If there is an instance of SNIMatcher defined for a particular name
+ * type, it must be used to perform match operations on the server name.
+ */
+ boolean isMatched(Collection<SNIMatcher> matchers) {
+ if (sniMap != null && !sniMap.isEmpty()) {
+ for (SNIMatcher matcher : matchers) {
+ SNIServerName sniName = sniMap.get(matcher.getType());
+ if (sniName != null && (!matcher.matches(sniName))) {
+ return false;
+ }
}
}
- public String toString() {
- if (type == NAME_HOST_NAME) {
- return "host_name: " + hostname;
- } else {
- return "unknown-" + type + ": " + Debug.toString(data);
+ return true;
+ }
+
+ /*
+ * Is the extension is identical to a server name list?
+ *
+ * This method is used to check the server name indication during session
+ * resumption.
+ *
+ * Per RFC 6066, when the server is deciding whether or not to accept a
+ * request to resume a session, the contents of a server_name extension
+ * MAY be used in the lookup of the session in the session cache. The
+ * client SHOULD include the same server_name extension in the session
+ * resumption request as it did in the full handshake that established
+ * the session. A server that implements this extension MUST NOT accept
+ * the request to resume the session if the server_name extension contains
+ * a different name. Instead, it proceeds with a full handshake to
+ * establish a new session. When resuming a session, the server MUST NOT
+ * include a server_name extension in the server hello.
+ */
+ boolean isIdentical(List<SNIServerName> other) {
+ if (other.size() == sniMap.size()) {
+ for(SNIServerName sniInOther : other) {
+ SNIServerName sniName = sniMap.get(sniInOther.getType());
+ if (sniName == null || !sniInOther.equals(sniName)) {
+ return false;
+ }
}
+
+ return true;
}
+
+ return false;
}
int length() {
@@ -409,25 +481,34 @@
void send(HandshakeOutStream s) throws IOException {
s.putInt16(type.id);
- s.putInt16(listLength + 2);
- if (listLength != 0) {
- s.putInt16(listLength);
+ if (listLength == 0) {
+ s.putInt16(listLength); // in ServerHello, empty extension_data
+ } else {
+ s.putInt16(listLength + 2); // length of extension_data
+ s.putInt16(listLength); // length of ServerNameList
- for (ServerName name : names) {
- s.putInt8(name.type); // NameType
- s.putBytes16(name.data); // HostName
+ for (SNIServerName sniName : sniMap.values()) {
+ s.putInt8(sniName.getType()); // server name type
+ s.putBytes16(sniName.getEncoded()); // server name value
}
}
}
public String toString() {
StringBuffer buffer = new StringBuffer();
- for (ServerName name : names) {
- buffer.append("[" + name + "]");
+ for (SNIServerName sniName : sniMap.values()) {
+ buffer.append("[" + sniName + "]");
}
return "Extension " + type + ", server_name: " + buffer;
}
+
+ private static class UnknownServerName extends SNIServerName {
+ UnknownServerName(int code, byte[] encoded) {
+ super(code, encoded);
+ }
+ }
+
}
final class SupportedEllipticCurvesExtension extends HelloExtension {
--- a/jdk/src/share/classes/sun/security/ssl/ProtocolList.java Wed Oct 17 13:22:39 2012 -0700
+++ b/jdk/src/share/classes/sun/security/ssl/ProtocolList.java Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 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
@@ -79,7 +79,7 @@
throw new IllegalArgumentException("Protocols may not be null");
}
- ArrayList<ProtocolVersion> versions = new ArrayList<>(3);
+ ArrayList<ProtocolVersion> versions = new ArrayList<>(names.length);
for (int i = 0; i < names.length; i++ ) {
ProtocolVersion version = ProtocolVersion.valueOf(names[i]);
if (versions.contains(version) == false) {
--- a/jdk/src/share/classes/sun/security/ssl/SSLEngineImpl.java Wed Oct 17 13:22:39 2012 -0700
+++ b/jdk/src/share/classes/sun/security/ssl/SSLEngineImpl.java Thu Oct 18 01:14:00 2012 -0700
@@ -27,6 +27,7 @@
import java.io.*;
import java.nio.*;
+import java.util.*;
import java.security.*;
import javax.crypto.BadPaddingException;
@@ -34,7 +35,6 @@
import javax.net.ssl.*;
import javax.net.ssl.SSLEngineResult.*;
-
/**
* Implementation of an non-blocking SSLEngine.
*
@@ -253,6 +253,12 @@
// The cryptographic algorithm constraints
private AlgorithmConstraints algorithmConstraints = null;
+ // The server name indication and matchers
+ List<SNIServerName> serverNames =
+ Collections.<SNIServerName>emptyList();
+ Collection<SNIMatcher> sniMatchers =
+ Collections.<SNIMatcher>emptyList();
+
// Have we been told whether we're client or server?
private boolean serverModeSet = false;
private boolean roleIsServer;
@@ -361,6 +367,10 @@
roleIsServer = true;
connectionState = cs_START;
+ // default server name indication
+ serverNames =
+ Utilities.addToSNIServerNameList(serverNames, getPeerHost());
+
/*
* default read and write side cipher and MAC support
*
@@ -459,11 +469,13 @@
enabledProtocols, doClientAuth,
protocolVersion, connectionState == cs_HANDSHAKE,
secureRenegotiation, clientVerifyData, serverVerifyData);
+ handshaker.setSNIMatchers(sniMatchers);
} else {
handshaker = new ClientHandshaker(this, sslContext,
enabledProtocols,
protocolVersion, connectionState == cs_HANDSHAKE,
secureRenegotiation, clientVerifyData, serverVerifyData);
+ handshaker.setSNIServerNames(serverNames);
}
handshaker.setEnabledCipherSuites(enabledCipherSuites);
handshaker.setEnableSessionCreation(enableSessionCreation);
@@ -1100,7 +1112,7 @@
// TLS requires that unrecognized records be ignored.
//
if (debug != null && Debug.isOn("ssl")) {
- System.out.println(threadName() +
+ System.out.println(Thread.currentThread().getName() +
", Received record type: "
+ inputRecord.contentType());
}
@@ -1384,7 +1396,7 @@
* for handshaking and bad_record_mac for other records.
*/
if (debug != null && Debug.isOn("ssl")) {
- System.out.println(threadName() +
+ System.out.println(Thread.currentThread().getName() +
", sequence number extremely close to overflow " +
"(2^64-1 packets). Closing connection.");
}
@@ -1402,7 +1414,8 @@
*/
if ((type != Record.ct_handshake) && mac.seqNumIsHuge()) {
if (debug != null && Debug.isOn("ssl")) {
- System.out.println(threadName() + ", request renegotiation " +
+ System.out.println(Thread.currentThread().getName() +
+ ", request renegotiation " +
"to avoid sequence number overflow");
}
@@ -1420,7 +1433,8 @@
private void closeOutboundInternal() {
if ((debug != null) && Debug.isOn("ssl")) {
- System.out.println(threadName() + ", closeOutboundInternal()");
+ System.out.println(Thread.currentThread().getName() +
+ ", closeOutboundInternal()");
}
/*
@@ -1467,7 +1481,8 @@
* Dump out a close_notify to the remote side
*/
if ((debug != null) && Debug.isOn("ssl")) {
- System.out.println(threadName() + ", called closeOutbound()");
+ System.out.println(Thread.currentThread().getName() +
+ ", called closeOutbound()");
}
closeOutboundInternal();
@@ -1487,7 +1502,8 @@
private void closeInboundInternal() {
if ((debug != null) && Debug.isOn("ssl")) {
- System.out.println(threadName() + ", closeInboundInternal()");
+ System.out.println(Thread.currentThread().getName() +
+ ", closeInboundInternal()");
}
/*
@@ -1519,7 +1535,8 @@
* someday in the future.
*/
if ((debug != null) && Debug.isOn("ssl")) {
- System.out.println(threadName() + ", called closeInbound()");
+ System.out.println(Thread.currentThread().getName() +
+ ", called closeInbound()");
}
/*
@@ -1642,7 +1659,7 @@
*/
if (closeReason != null) {
if ((debug != null) && Debug.isOn("ssl")) {
- System.out.println(threadName() +
+ System.out.println(Thread.currentThread().getName() +
", fatal: engine already closed. Rethrowing " +
cause.toString());
}
@@ -1656,7 +1673,7 @@
}
if ((debug != null) && Debug.isOn("ssl")) {
- System.out.println(threadName()
+ System.out.println(Thread.currentThread().getName()
+ ", fatal error: " + description +
": " + diagnostic + "\n" + cause.toString());
}
@@ -1723,7 +1740,7 @@
if (debug != null && (Debug.isOn("record") ||
Debug.isOn("handshake"))) {
synchronized (System.out) {
- System.out.print(threadName());
+ System.out.print(Thread.currentThread().getName());
System.out.print(", RECV " + protocolVersion + " ALERT: ");
if (level == Alerts.alert_fatal) {
System.out.print("fatal, ");
@@ -1790,7 +1807,7 @@
boolean useDebug = debug != null && Debug.isOn("ssl");
if (useDebug) {
synchronized (System.out) {
- System.out.print(threadName());
+ System.out.print(Thread.currentThread().getName());
System.out.print(", SEND " + protocolVersion + " ALERT: ");
if (level == Alerts.alert_fatal) {
System.out.print("fatal, ");
@@ -1810,7 +1827,7 @@
writeRecord(r);
} catch (IOException e) {
if (useDebug) {
- System.out.println(threadName() +
+ System.out.println(Thread.currentThread().getName() +
", Exception sending alert: " + e);
}
}
@@ -1948,7 +1965,7 @@
default:
if (debug != null && Debug.isOn("ssl")) {
- System.out.println(threadName() +
+ System.out.println(Thread.currentThread().getName() +
", setUseClientMode() invoked in state = " +
connectionState);
}
@@ -2050,6 +2067,8 @@
// the super implementation does not handle the following parameters
params.setEndpointIdentificationAlgorithm(identificationProtocol);
params.setAlgorithmConstraints(algorithmConstraints);
+ params.setSNIMatchers(sniMatchers);
+ params.setServerNames(serverNames);
return params;
}
@@ -2063,20 +2082,29 @@
// the super implementation does not handle the following parameters
identificationProtocol = params.getEndpointIdentificationAlgorithm();
algorithmConstraints = params.getAlgorithmConstraints();
+
+ List<SNIServerName> sniNames = params.getServerNames();
+ if (sniNames != null) {
+ serverNames = sniNames;
+ }
+
+ Collection<SNIMatcher> matchers = params.getSNIMatchers();
+ if (matchers != null) {
+ sniMatchers = matchers;
+ }
+
if ((handshaker != null) && !handshaker.started()) {
handshaker.setIdentificationProtocol(identificationProtocol);
handshaker.setAlgorithmConstraints(algorithmConstraints);
+ if (roleIsServer) {
+ handshaker.setSNIMatchers(sniMatchers);
+ } else {
+ handshaker.setSNIServerNames(serverNames);
+ }
}
}
/**
- * Return the name of the current thread. Utility method.
- */
- private static String threadName() {
- return Thread.currentThread().getName();
- }
-
- /**
* Returns a printable representation of this end of the connection.
*/
public String toString() {
--- a/jdk/src/share/classes/sun/security/ssl/SSLServerSocketImpl.java Wed Oct 17 13:22:39 2012 -0700
+++ b/jdk/src/share/classes/sun/security/ssl/SSLServerSocketImpl.java Thu Oct 18 01:14:00 2012 -0700
@@ -39,6 +39,7 @@
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SNIMatcher;
/**
@@ -92,6 +93,10 @@
// The cryptographic algorithm constraints
private AlgorithmConstraints algorithmConstraints = null;
+ // The server name indication
+ Collection<SNIMatcher> sniMatchers =
+ Collections.<SNIMatcher>emptyList();
+
/**
* Create an SSL server socket on a port, using a non-default
* authentication context and a specified connection backlog.
@@ -289,6 +294,7 @@
// the super implementation does not handle the following parameters
params.setEndpointIdentificationAlgorithm(identificationProtocol);
params.setAlgorithmConstraints(algorithmConstraints);
+ params.setSNIMatchers(sniMatchers);
return params;
}
@@ -302,6 +308,10 @@
// the super implementation does not handle the following parameters
identificationProtocol = params.getEndpointIdentificationAlgorithm();
algorithmConstraints = params.getAlgorithmConstraints();
+ Collection<SNIMatcher> matchers = params.getSNIMatchers();
+ if (matchers != null) {
+ sniMatchers = params.getSNIMatchers();
+ }
}
/**
@@ -312,7 +322,8 @@
public Socket accept() throws IOException {
SSLSocketImpl s = new SSLSocketImpl(sslContext, useServerMode,
enabledCipherSuites, doClientAuth, enableSessionCreation,
- enabledProtocols, identificationProtocol, algorithmConstraints);
+ enabledProtocols, identificationProtocol, algorithmConstraints,
+ sniMatchers);
implAccept(s);
s.doneConnect();
--- a/jdk/src/share/classes/sun/security/ssl/SSLSessionImpl.java Wed Oct 17 13:22:39 2012 -0700
+++ b/jdk/src/share/classes/sun/security/ssl/SSLSessionImpl.java Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 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
@@ -33,6 +33,9 @@
import java.util.Vector;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.ArrayList;
import java.security.Principal;
import java.security.PrivateKey;
@@ -51,6 +54,7 @@
import javax.net.ssl.SSLPermission;
import javax.net.ssl.SSLException;
import javax.net.ssl.ExtendedSSLSession;
+import javax.net.ssl.SNIServerName;
import javax.security.auth.x500.X500Principal;
@@ -111,6 +115,8 @@
private PrivateKey localPrivateKey;
private String[] localSupportedSignAlgs;
private String[] peerSupportedSignAlgs;
+ private List<SNIServerName> requestedServerNames;
+
// Principals for non-certificate based cipher suites
private Principal peerPrincipal;
@@ -212,6 +218,10 @@
SignatureAndHashAlgorithm.getAlgorithmNames(algorithms);
}
+ void setRequestedServerNames(List<SNIServerName> requestedServerNames) {
+ this.requestedServerNames = new ArrayList<>(requestedServerNames);
+ }
+
/**
* Set the peer principal.
*/
@@ -748,6 +758,7 @@
* Gets an array of supported signature algorithms that the local side is
* willing to verify.
*/
+ @Override
public String[] getLocalSupportedSignatureAlgorithms() {
if (localSupportedSignAlgs != null) {
return localSupportedSignAlgs.clone();
@@ -760,6 +771,7 @@
* Gets an array of supported signature algorithms that the peer is
* able to verify.
*/
+ @Override
public String[] getPeerSupportedSignatureAlgorithms() {
if (peerSupportedSignAlgs != null) {
return peerSupportedSignAlgs.clone();
@@ -768,6 +780,20 @@
return new String[0];
}
+ /**
+ * Obtains a <code>List</code> containing all {@link SNIServerName}s
+ * of the requested Server Name Indication (SNI) extension.
+ */
+ @Override
+ public List<SNIServerName> getRequestedServerNames() {
+ if (requestedServerNames != null && !requestedServerNames.isEmpty()) {
+ return Collections.<SNIServerName>unmodifiableList(
+ requestedServerNames);
+ }
+
+ return Collections.<SNIServerName>emptyList();
+ }
+
/** Returns a string representation of this SSL session */
public String toString() {
return "[Session-" + sessionCount
--- a/jdk/src/share/classes/sun/security/ssl/SSLSocketFactoryImpl.java Wed Oct 17 13:22:39 2012 -0700
+++ b/jdk/src/share/classes/sun/security/ssl/SSLSocketFactoryImpl.java Thu Oct 18 01:14:00 2012 -0700
@@ -109,6 +109,16 @@
return new SSLSocketImpl(context, s, host, port, autoClose);
}
+ @Override
+ public Socket createSocket(Socket s, InputStream consumed,
+ boolean autoClose) throws IOException {
+ if (s == null) {
+ throw new NullPointerException(
+ "the existing socket cannot be null");
+ }
+
+ return new SSLSocketImpl(context, s, consumed, autoClose);
+ }
/**
* Constructs an SSL connection to a server at a specified address
--- a/jdk/src/share/classes/sun/security/ssl/SSLSocketImpl.java Wed Oct 17 13:22:39 2012 -0700
+++ b/jdk/src/share/classes/sun/security/ssl/SSLSocketImpl.java Thu Oct 18 01:14:00 2012 -0700
@@ -36,9 +36,9 @@
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
+import java.nio.charset.StandardCharsets;
import javax.crypto.BadPaddingException;
-
import javax.net.ssl.*;
/**
@@ -198,14 +198,6 @@
private boolean autoClose = true;
private AccessControlContext acc;
- /*
- * We cannot use the hostname resolved from name services. For
- * virtual hosting, multiple hostnames may be bound to the same IP
- * address, so the hostname resolved from name services is not
- * reliable.
- */
- private String rawHostname;
-
// The cipher suites enabled for use on this connection.
private CipherSuiteList enabledCipherSuites;
@@ -215,6 +207,12 @@
// The cryptographic algorithm constraints
private AlgorithmConstraints algorithmConstraints = null;
+ // The server name indication and matchers
+ List<SNIServerName> serverNames =
+ Collections.<SNIServerName>emptyList();
+ Collection<SNIMatcher> sniMatchers =
+ Collections.<SNIMatcher>emptyList();
+
/*
* READ ME * READ ME * READ ME * READ ME * READ ME * READ ME *
* IMPORTANT STUFF TO UNDERSTANDING THE SYNCHRONIZATION ISSUES.
@@ -397,7 +395,8 @@
throws IOException, UnknownHostException {
super();
this.host = host;
- this.rawHostname = host;
+ this.serverNames =
+ Utilities.addToSNIServerNameList(this.serverNames, this.host);
init(context, false);
SocketAddress socketAddress =
host != null ? new InetSocketAddress(host, port) :
@@ -440,7 +439,8 @@
throws IOException, UnknownHostException {
super();
this.host = host;
- this.rawHostname = host;
+ this.serverNames =
+ Utilities.addToSNIServerNameList(this.serverNames, this.host);
init(context, false);
bind(new InetSocketAddress(localAddr, localPort));
SocketAddress socketAddress =
@@ -482,13 +482,15 @@
CipherSuiteList suites, byte clientAuth,
boolean sessionCreation, ProtocolList protocols,
String identificationProtocol,
- AlgorithmConstraints algorithmConstraints) throws IOException {
+ AlgorithmConstraints algorithmConstraints,
+ Collection<SNIMatcher> sniMatchers) throws IOException {
super();
doClientAuth = clientAuth;
enableSessionCreation = sessionCreation;
this.identificationProtocol = identificationProtocol;
this.algorithmConstraints = algorithmConstraints;
+ this.sniMatchers = sniMatchers;
init(context, serverMode);
/*
@@ -535,13 +537,36 @@
throw new SocketException("Underlying socket is not connected");
}
this.host = host;
- this.rawHostname = host;
+ this.serverNames =
+ Utilities.addToSNIServerNameList(this.serverNames, this.host);
init(context, false);
this.autoClose = autoClose;
doneConnect();
}
/**
+ * Creates a server mode {@link Socket} layered over an
+ * existing connected socket, and is able to read data which has
+ * already been consumed/removed from the {@link Socket}'s
+ * underlying {@link InputStream}.
+ */
+ SSLSocketImpl(SSLContextImpl context, Socket sock,
+ InputStream consumed, boolean autoClose) throws IOException {
+ super(sock, consumed);
+ // We always layer over a connected socket
+ if (!sock.isConnected()) {
+ throw new SocketException("Underlying socket is not connected");
+ }
+
+ // In server mode, it is not necessary to set host and serverNames.
+ // Otherwise, would require a reverse DNS lookup to get the hostname.
+
+ init(context, true);
+ this.autoClose = autoClose;
+ doneConnect();
+ }
+
+ /**
* Initializes the client socket.
*/
private void init(SSLContextImpl context, boolean isServer) {
@@ -604,7 +629,7 @@
public void connect(SocketAddress endpoint, int timeout)
throws IOException {
- if (self != this) {
+ if (isLayered()) {
throw new SocketException("Already connected");
}
@@ -628,13 +653,8 @@
* java.net actually connects using the socket "self", else
* we get some pretty bizarre failure modes.
*/
- if (self == this) {
- sockInput = super.getInputStream();
- sockOutput = super.getOutputStream();
- } else {
- sockInput = self.getInputStream();
- sockOutput = self.getOutputStream();
- }
+ sockInput = super.getInputStream();
+ sockOutput = super.getOutputStream();
/*
* Move to handshaking state, with pending session initialized
@@ -761,13 +781,14 @@
// For layered, non-autoclose sockets, we are not
// able to bring them into a usable state, so we
// treat it as fatal error.
- if (self != this && !autoClose) {
+ if (isLayered() && !autoClose) {
// Note that the alert description is
// specified as -1, so no message will be send
// to peer anymore.
fatal((byte)(-1), ssle);
} else if ((debug != null) && Debug.isOn("ssl")) {
- System.out.println(threadName() +
+ System.out.println(
+ Thread.currentThread().getName() +
", received Exception: " + ssle);
}
@@ -935,7 +956,7 @@
boolean handshaking = (getConnectionState() <= cs_HANDSHAKE);
boolean rethrow = requireCloseNotify || handshaking;
if ((debug != null) && Debug.isOn("ssl")) {
- System.out.println(threadName() +
+ System.out.println(Thread.currentThread().getName() +
", received EOFException: "
+ (rethrow ? "error" : "ignored"));
}
@@ -1119,7 +1140,7 @@
// TLS requires that unrecognized records be ignored.
//
if (debug != null && Debug.isOn("ssl")) {
- System.out.println(threadName() +
+ System.out.println(Thread.currentThread().getName() +
", Received record type: "
+ r.contentType());
}
@@ -1183,7 +1204,7 @@
* for handshaking and bad_record_mac for other records.
*/
if (debug != null && Debug.isOn("ssl")) {
- System.out.println(threadName() +
+ System.out.println(Thread.currentThread().getName() +
", sequence number extremely close to overflow " +
"(2^64-1 packets). Closing connection.");
@@ -1200,7 +1221,8 @@
*/
if ((type != Record.ct_handshake) && mac.seqNumIsHuge()) {
if (debug != null && Debug.isOn("ssl")) {
- System.out.println(threadName() + ", request renegotiation " +
+ System.out.println(Thread.currentThread().getName() +
+ ", request renegotiation " +
"to avoid sequence number overflow");
}
@@ -1278,11 +1300,13 @@
enabledProtocols, doClientAuth,
protocolVersion, connectionState == cs_HANDSHAKE,
secureRenegotiation, clientVerifyData, serverVerifyData);
+ handshaker.setSNIMatchers(sniMatchers);
} else {
handshaker = new ClientHandshaker(this, sslContext,
enabledProtocols,
protocolVersion, connectionState == cs_HANDSHAKE,
secureRenegotiation, clientVerifyData, serverVerifyData);
+ handshaker.setSNIServerNames(serverNames);
}
handshaker.setEnabledCipherSuites(enabledCipherSuites);
handshaker.setEnableSessionCreation(enableSessionCreation);
@@ -1509,24 +1533,20 @@
protected void closeSocket() throws IOException {
if ((debug != null) && Debug.isOn("ssl")) {
- System.out.println(threadName() + ", called closeSocket()");
+ System.out.println(Thread.currentThread().getName() +
+ ", called closeSocket()");
}
- if (self == this) {
- super.close();
- } else {
- self.close();
- }
+
+ super.close();
}
private void closeSocket(boolean selfInitiated) throws IOException {
if ((debug != null) && Debug.isOn("ssl")) {
- System.out.println(threadName() +
+ System.out.println(Thread.currentThread().getName() +
", called closeSocket(" + selfInitiated + ")");
}
- if (self == this) {
+ if (!isLayered() || autoClose) {
super.close();
- } else if (autoClose) {
- self.close();
} else if (selfInitiated) {
// layered && non-autoclose
// read close_notify alert to clear input stream
@@ -1549,7 +1569,8 @@
*/
public void close() throws IOException {
if ((debug != null) && Debug.isOn("ssl")) {
- System.out.println(threadName() + ", called close()");
+ System.out.println(Thread.currentThread().getName() +
+ ", called close()");
}
closeInternal(true); // caller is initiating close
setConnectionState(cs_APP_CLOSED);
@@ -1567,8 +1588,8 @@
*/
private void closeInternal(boolean selfInitiated) throws IOException {
if ((debug != null) && Debug.isOn("ssl")) {
- System.out.println(threadName() + ", called closeInternal("
- + selfInitiated + ")");
+ System.out.println(Thread.currentThread().getName() +
+ ", called closeInternal(" + selfInitiated + ")");
}
int state = getConnectionState();
@@ -1630,7 +1651,7 @@
// closing since it is already in progress.
if (state == cs_SENT_CLOSE) {
if (debug != null && Debug.isOn("ssl")) {
- System.out.println(threadName() +
+ System.out.println(Thread.currentThread().getName() +
", close invoked again; state = " +
getConnectionState());
}
@@ -1653,7 +1674,7 @@
}
}
if ((debug != null) && Debug.isOn("ssl")) {
- System.out.println(threadName() +
+ System.out.println(Thread.currentThread().getName() +
", after primary close; state = " +
getConnectionState());
}
@@ -1701,7 +1722,7 @@
*/
void waitForClose(boolean rethrow) throws IOException {
if (debug != null && Debug.isOn("ssl")) {
- System.out.println(threadName() +
+ System.out.println(Thread.currentThread().getName() +
", waiting for close_notify or alert: state "
+ getConnectionState());
}
@@ -1726,7 +1747,7 @@
inrec = null;
} catch (IOException e) {
if (debug != null && Debug.isOn("ssl")) {
- System.out.println(threadName() +
+ System.out.println(Thread.currentThread().getName() +
", Exception while waiting for close " +e);
}
if (rethrow) {
@@ -1788,8 +1809,8 @@
synchronized private void handleException(Exception e, boolean resumable)
throws IOException {
if ((debug != null) && Debug.isOn("ssl")) {
- System.out.println(threadName()
- + ", handling exception: " + e.toString());
+ System.out.println(Thread.currentThread().getName() +
+ ", handling exception: " + e.toString());
}
// don't close the Socket in case of timeouts or interrupts if
@@ -1935,7 +1956,7 @@
if (debug != null && (Debug.isOn("record") ||
Debug.isOn("handshake"))) {
synchronized (System.out) {
- System.out.print(threadName());
+ System.out.print(Thread.currentThread().getName());
System.out.print(", RECV " + protocolVersion + " ALERT: ");
if (level == Alerts.alert_fatal) {
System.out.print("fatal, ");
@@ -2001,7 +2022,7 @@
boolean useDebug = debug != null && Debug.isOn("ssl");
if (useDebug) {
synchronized (System.out) {
- System.out.print(threadName());
+ System.out.print(Thread.currentThread().getName());
System.out.print(", SEND " + protocolVersion + " ALERT: ");
if (level == Alerts.alert_fatal) {
System.out.print("fatal, ");
@@ -2021,7 +2042,7 @@
writeRecord(r);
} catch (IOException e) {
if (useDebug) {
- System.out.println(threadName() +
+ System.out.println(Thread.currentThread().getName() +
", Exception sending alert: " + e);
}
}
@@ -2118,14 +2139,15 @@
return host;
}
- synchronized String getRawHostname() {
- return rawHostname;
- }
-
// ONLY used by HttpsClient to setup the URI specified hostname
+ //
+ // Please NOTE that this method MUST be called before calling to
+ // SSLSocket.setSSLParameters(). Otherwise, the {@code host} parameter
+ // may override SNIHostName in the customized server name indication.
synchronized public void setHost(String host) {
this.host = host;
- this.rawHostname = host;
+ this.serverNames =
+ Utilities.addToSNIServerNameList(this.serverNames, this.host);
}
/**
@@ -2186,7 +2208,7 @@
} catch (IOException e) {
// handshake failed. log and return a nullSession
if (debug != null && Debug.isOn("handshake")) {
- System.out.println(threadName() +
+ System.out.println(Thread.currentThread().getName() +
", IOException in getSession(): " + e);
}
}
@@ -2328,7 +2350,7 @@
default:
if (debug != null && Debug.isOn("ssl")) {
- System.out.println(threadName() +
+ System.out.println(Thread.currentThread().getName() +
", setUseClientMode() invoked in state = " +
connectionState);
}
@@ -2422,14 +2444,11 @@
*/
public void setSoTimeout(int timeout) throws SocketException {
if ((debug != null) && Debug.isOn("ssl")) {
- System.out.println(threadName() +
+ System.out.println(Thread.currentThread().getName() +
", setSoTimeout(" + timeout + ") called");
}
- if (self == this) {
- super.setSoTimeout(timeout);
- } else {
- self.setSoTimeout(timeout);
- }
+
+ super.setSoTimeout(timeout);
}
/**
@@ -2474,6 +2493,8 @@
// the super implementation does not handle the following parameters
params.setEndpointIdentificationAlgorithm(identificationProtocol);
params.setAlgorithmConstraints(algorithmConstraints);
+ params.setSNIMatchers(sniMatchers);
+ params.setServerNames(serverNames);
return params;
}
@@ -2487,9 +2508,25 @@
// the super implementation does not handle the following parameters
identificationProtocol = params.getEndpointIdentificationAlgorithm();
algorithmConstraints = params.getAlgorithmConstraints();
+
+ List<SNIServerName> sniNames = params.getServerNames();
+ if (sniNames != null) {
+ serverNames = sniNames;
+ }
+
+ Collection<SNIMatcher> matchers = params.getSNIMatchers();
+ if (matchers != null) {
+ sniMatchers = matchers;
+ }
+
if ((handshaker != null) && !handshaker.started()) {
handshaker.setIdentificationProtocol(identificationProtocol);
handshaker.setAlgorithmConstraints(algorithmConstraints);
+ if (roleIsServer) {
+ handshaker.setSNIMatchers(sniMatchers);
+ } else {
+ handshaker.setSNIServerNames(serverNames);
+ }
}
}
@@ -2531,13 +2568,6 @@
}
/**
- * Return the name of the current thread. Utility method.
- */
- private static String threadName() {
- return Thread.currentThread().getName();
- }
-
- /**
* Returns a printable representation of this end of the connection.
*/
public String toString() {
@@ -2548,11 +2578,7 @@
retval.append(sess.getCipherSuite());
retval.append(": ");
- if (self == this) {
- retval.append(super.toString());
- } else {
- retval.append(self.toString());
- }
+ retval.append(super.toString());
retval.append("]");
return retval.toString();
--- a/jdk/src/share/classes/sun/security/ssl/ServerHandshaker.java Wed Oct 17 13:22:39 2012 -0700
+++ b/jdk/src/share/classes/sun/security/ssl/ServerHandshaker.java Thu Oct 18 01:14:00 2012 -0700
@@ -276,6 +276,18 @@
mesg.print(System.out);
}
+ // check the server name indication if required
+ ServerNameExtension clientHelloSNIExt = (ServerNameExtension)
+ mesg.extensions.get(ExtensionType.EXT_SERVER_NAME);
+ if (!sniMatchers.isEmpty()) {
+ // we do not reject client without SNI extension
+ if (clientHelloSNIExt != null &&
+ !clientHelloSNIExt.isMatched(sniMatchers)) {
+ fatalSE(Alerts.alert_unrecognized_name,
+ "Unrecognized server name indication");
+ }
+ }
+
// Does the message include security renegotiation indication?
boolean renegotiationIndicated = false;
@@ -474,6 +486,26 @@
}
}
+ // cannot resume session with different server name indication
+ if (resumingSession) {
+ List<SNIServerName> oldServerNames =
+ previous.getRequestedServerNames();
+ if (clientHelloSNIExt != null) {
+ if (!clientHelloSNIExt.isIdentical(oldServerNames)) {
+ resumingSession = false;
+ }
+ } else if (!oldServerNames.isEmpty()) {
+ resumingSession = false;
+ }
+
+ if (!resumingSession &&
+ debug != null && Debug.isOn("handshake")) {
+ System.out.println(
+ "The requested server name indication " +
+ "is not identical to the previous one");
+ }
+ }
+
if (resumingSession &&
(doClientAuth == SSLEngineImpl.clauth_required)) {
try {
@@ -613,6 +645,14 @@
// algorithms in chooseCipherSuite()
}
+ // set the server name indication in the session
+ List<SNIServerName> clientHelloSNI =
+ Collections.<SNIServerName>emptyList();
+ if (clientHelloSNIExt != null) {
+ clientHelloSNI = clientHelloSNIExt.getServerNames();
+ }
+ session.setRequestedServerNames(clientHelloSNI);
+
// set the handshake session
setHandshakeSessionSE(session);
@@ -654,6 +694,15 @@
m1.extensions.add(serverHelloRI);
}
+ if (!sniMatchers.isEmpty() && clientHelloSNIExt != null) {
+ // When resuming a session, the server MUST NOT include a
+ // server_name extension in the server hello.
+ if (!resumingSession) {
+ ServerNameExtension serverHelloSNI = new ServerNameExtension();
+ m1.extensions.add(serverHelloSNI);
+ }
+ }
+
if (debug != null && Debug.isOn("handshake")) {
m1.print(System.out);
System.out.println("Cipher suite: " + session.getSuite());
--- a/jdk/src/share/classes/sun/security/ssl/SunJSSE.java Wed Oct 17 13:22:39 2012 -0700
+++ b/jdk/src/share/classes/sun/security/ssl/SunJSSE.java Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -103,7 +103,7 @@
// standard constructor
protected SunJSSE() {
- super("SunJSSE", 1.7d, info);
+ super("SunJSSE", 1.8d, info);
subclassCheck();
if (Boolean.TRUE.equals(fips)) {
throw new ProviderException
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/security/ssl/Utilities.java Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,115 @@
+/*
+ * 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 javax.net.ssl.*;
+import java.util.*;
+import sun.net.util.IPAddressUtil;
+
+/**
+ * A utility class to share the static methods.
+ */
+final class Utilities {
+ /**
+ * Puts {@code hostname} into the {@code serverNames} list.
+ * <P>
+ * If the {@code serverNames} does not look like a legal FQDN, it will
+ * not be put into the returned list.
+ * <P>
+ * Note that the returned list does not allow duplicated name type.
+ *
+ * @return a list of {@link SNIServerName}
+ */
+ static List<SNIServerName> addToSNIServerNameList(
+ List<SNIServerName> serverNames, String hostname) {
+
+ SNIHostName sniHostName = rawToSNIHostName(hostname);
+ if (sniHostName == null) {
+ return serverNames;
+ }
+
+ int size = serverNames.size();
+ List<SNIServerName> sniList = (size != 0) ?
+ new ArrayList<SNIServerName>(serverNames) :
+ new ArrayList<SNIServerName>(1);
+
+ boolean reset = false;
+ for (int i = 0; i < size; i++) {
+ SNIServerName serverName = sniList.get(i);
+ if (serverName.getType() == StandardConstants.SNI_HOST_NAME) {
+ sniList.set(i, sniHostName);
+ if (Debug.isOn("ssl")) {
+ System.out.println(Thread.currentThread().getName() +
+ ", the previous server name in SNI (" + serverName +
+ ") was replaced with (" + sniHostName + ")");
+ }
+ reset = true;
+ break;
+ }
+ }
+
+ if (!reset) {
+ sniList.add(sniHostName);
+ }
+
+ return Collections.<SNIServerName>unmodifiableList(sniList);
+ }
+
+ /**
+ * Converts string hostname to {@code SNIHostName}.
+ * <P>
+ * Note that to check whether a hostname is a valid domain name, we cannot
+ * use the hostname resolved from name services. For virtual hosting,
+ * multiple hostnames may be bound to the same IP address, so the hostname
+ * resolved from name services is not always reliable.
+ *
+ * @param hostname
+ * the raw hostname
+ * @return an instance of {@link SNIHostName}, or null if the hostname does
+ * not look like a FQDN
+ */
+ private static SNIHostName rawToSNIHostName(String hostname) {
+ SNIHostName sniHostName = null;
+ if (hostname != null && hostname.indexOf('.') > 0 &&
+ !hostname.endsWith(".") &&
+ !IPAddressUtil.isIPv4LiteralAddress(hostname) &&
+ !IPAddressUtil.isIPv6LiteralAddress(hostname)) {
+
+ try {
+ sniHostName = new SNIHostName(hostname);
+ } catch (IllegalArgumentException iae) {
+ // don't bother to handle illegal host_name
+ if (Debug.isOn("ssl")) {
+ System.out.println(Thread.currentThread().getName() +
+ ", \"" + hostname + "\" " +
+ "is not a legal HostName for server name indication");
+ }
+ }
+ }
+
+ return sniHostName;
+ }
+}
--- a/jdk/src/share/classes/sun/security/ssl/X509KeyManagerImpl.java Wed Oct 17 13:22:39 2012 -0700
+++ b/jdk/src/share/classes/sun/security/ssl/X509KeyManagerImpl.java Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 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
@@ -128,13 +128,35 @@
public String chooseServerAlias(String keyType,
Principal[] issuers, Socket socket) {
return chooseAlias(getKeyTypes(keyType), issuers, CheckType.SERVER,
- getAlgorithmConstraints(socket));
+ getAlgorithmConstraints(socket),
+ X509TrustManagerImpl.getRequestedServerNames(socket),
+ "HTTPS"); // The SNI HostName is a fully qualified domain name.
+ // The certificate selection scheme for SNI HostName
+ // is similar to HTTPS endpoint identification scheme
+ // implemented in this provider.
+ //
+ // Using HTTPS endpoint identification scheme to guide
+ // the selection of an appropriate authentication
+ // certificate according to requested SNI extension.
+ //
+ // It is not a really HTTPS endpoint identification.
}
public String chooseEngineServerAlias(String keyType,
Principal[] issuers, SSLEngine engine) {
return chooseAlias(getKeyTypes(keyType), issuers, CheckType.SERVER,
- getAlgorithmConstraints(engine));
+ getAlgorithmConstraints(engine),
+ X509TrustManagerImpl.getRequestedServerNames(engine),
+ "HTTPS"); // The SNI HostName is a fully qualified domain name.
+ // The certificate selection scheme for SNI HostName
+ // is similar to HTTPS endpoint identification scheme
+ // implemented in this provider.
+ //
+ // Using HTTPS endpoint identification scheme to guide
+ // the selection of an appropriate authentication
+ // certificate according to requested SNI extension.
+ //
+ // It is not a really HTTPS endpoint identification.
}
public String[] getClientAliases(String keyType, Principal[] issuers) {
@@ -321,8 +343,8 @@
* The algorithm we use is:
* . scan through all the aliases in all builders in order
* . as soon as we find a perfect match, return
- * (i.e. a match with a cert that has appropriate key usage
- * and is not expired).
+ * (i.e. a match with a cert that has appropriate key usage,
+ * qualified endpoint identity, and is not expired).
* . if we do not find a perfect match, keep looping and remember
* the imperfect matches
* . at the end, sort the imperfect matches. we prefer expired certs
@@ -331,6 +353,15 @@
*/
private String chooseAlias(List<KeyType> keyTypeList, Principal[] issuers,
CheckType checkType, AlgorithmConstraints constraints) {
+
+ return chooseAlias(keyTypeList, issuers,
+ checkType, constraints, null, null);
+ }
+
+ private String chooseAlias(List<KeyType> keyTypeList, Principal[] issuers,
+ CheckType checkType, AlgorithmConstraints constraints,
+ List<SNIServerName> requestedServerNames, String idAlgorithm) {
+
if (keyTypeList == null || keyTypeList.isEmpty()) {
return null;
}
@@ -340,7 +371,8 @@
for (int i = 0, n = builders.size(); i < n; i++) {
try {
List<EntryStatus> results = getAliases(i, keyTypeList,
- issuerSet, false, checkType, constraints);
+ issuerSet, false, checkType, constraints,
+ requestedServerNames, idAlgorithm);
if (results != null) {
// the results will either be a single perfect match
// or 1 or more imperfect matches
@@ -394,7 +426,8 @@
for (int i = 0, n = builders.size(); i < n; i++) {
try {
List<EntryStatus> results = getAliases(i, keyTypeList,
- issuerSet, true, checkType, constraints);
+ issuerSet, true, checkType, constraints,
+ null, null);
if (results != null) {
if (allResults == null) {
allResults = new ArrayList<EntryStatus>();
@@ -504,7 +537,9 @@
// first check extensions, if they match, check expiration
// note: we may want to move this code into the sun.security.validator
// package
- CheckResult check(X509Certificate cert, Date date) {
+ CheckResult check(X509Certificate cert, Date date,
+ List<SNIServerName> serverNames, String idAlgorithm) {
+
if (this == NONE) {
return CheckResult.OK;
}
@@ -553,11 +588,11 @@
return CheckResult.EXTENSION_MISMATCH;
}
// For servers, also require key agreement.
- // This is not totally accurate as the keyAgreement bit
- // is only necessary for static ECDH key exchange and
- // not ephemeral ECDH. We leave it in for now until
- // there are signs that this check causes problems
- // for real world EC certificates.
+ // This is not totally accurate as the keyAgreement
+ // bit is only necessary for static ECDH key
+ // exchange and not ephemeral ECDH. We leave it in
+ // for now until there are signs that this check
+ // causes problems for real world EC certificates.
if ((this == SERVER) && (getBit(ku, 4) == false)) {
return CheckResult.EXTENSION_MISMATCH;
}
@@ -571,10 +606,50 @@
try {
cert.checkValidity(date);
- return CheckResult.OK;
} catch (CertificateException e) {
return CheckResult.EXPIRED;
}
+
+ if (serverNames != null && !serverNames.isEmpty()) {
+ for (SNIServerName serverName : serverNames) {
+ if (serverName.getType() ==
+ StandardConstants.SNI_HOST_NAME) {
+ if (!(serverName instanceof SNIHostName)) {
+ try {
+ serverName =
+ new SNIHostName(serverName.getEncoded());
+ } catch (IllegalArgumentException iae) {
+ // unlikely to happen, just in case ...
+ if (useDebug) {
+ debug.println(
+ "Illegal server name: " + serverName);
+ }
+
+ return CheckResult.INSENSITIVE;
+ }
+ }
+ String hostname =
+ ((SNIHostName)serverName).getAsciiName();
+
+ try {
+ X509TrustManagerImpl.checkIdentity(hostname,
+ cert, idAlgorithm);
+ } catch (CertificateException e) {
+ if (useDebug) {
+ debug.println(
+ "Certificate identity does not match " +
+ "Server Name Inidication (SNI): " +
+ hostname);
+ }
+ return CheckResult.INSENSITIVE;
+ }
+
+ break;
+ }
+ }
+ }
+
+ return CheckResult.OK;
}
}
@@ -583,6 +658,7 @@
// for sorting, i.e. OK is best, followed by EXPIRED and EXTENSION_MISMATCH
private static enum CheckResult {
OK, // ok or not checked
+ INSENSITIVE, // server name indication insensitive
EXPIRED, // extensions valid but cert expired
EXTENSION_MISMATCH, // extensions invalid (expiration not checked)
}
@@ -616,7 +692,10 @@
private List<EntryStatus> getAliases(int builderIndex,
List<KeyType> keyTypes, Set<Principal> issuerSet,
boolean findAll, CheckType checkType,
- AlgorithmConstraints constraints) throws Exception {
+ AlgorithmConstraints constraints,
+ List<SNIServerName> requestedServerNames,
+ String idAlgorithm) throws Exception {
+
Builder builder = builders.get(builderIndex);
KeyStore ks = builder.getKeyStore();
List<EntryStatus> results = null;
@@ -699,7 +778,8 @@
date = new Date();
}
CheckResult checkResult =
- checkType.check((X509Certificate)chain[0], date);
+ checkType.check((X509Certificate)chain[0], date,
+ requestedServerNames, idAlgorithm);
EntryStatus status =
new EntryStatus(builderIndex, keyIndex,
alias, chain, checkResult);
--- a/jdk/src/share/classes/sun/security/ssl/X509TrustManagerImpl.java Wed Oct 17 13:22:39 2012 -0700
+++ b/jdk/src/share/classes/sun/security/ssl/X509TrustManagerImpl.java Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -28,15 +28,14 @@
import java.net.Socket;
import javax.net.ssl.SSLSession;
+import java.nio.charset.StandardCharsets;
import java.util.*;
import java.security.*;
import java.security.cert.*;
-
import javax.net.ssl.*;
import sun.security.validator.*;
-
import sun.security.util.HostnameChecker;
/**
@@ -199,8 +198,8 @@
String identityAlg = sslSocket.getSSLParameters().
getEndpointIdentificationAlgorithm();
if (identityAlg != null && identityAlg.length() != 0) {
- String hostname = session.getPeerHost();
- checkIdentity(hostname, chain[0], identityAlg);
+ checkIdentity(session, chain[0], identityAlg, isClient,
+ getRequestedServerNames(socket));
}
// create the algorithm constraints
@@ -251,8 +250,8 @@
String identityAlg = engine.getSSLParameters().
getEndpointIdentificationAlgorithm();
if (identityAlg != null && identityAlg.length() != 0) {
- String hostname = session.getPeerHost();
- checkIdentity(hostname, chain[0], identityAlg);
+ checkIdentity(session, chain[0], identityAlg, isClient,
+ getRequestedServerNames(engine));
}
// create the algorithm constraints
@@ -329,6 +328,117 @@
}
}
+ // Get string representation of HostName from a list of server names.
+ //
+ // We are only accepting host_name name type in the list.
+ private static String getHostNameInSNI(List<SNIServerName> sniNames) {
+
+ SNIHostName hostname = null;
+ for (SNIServerName sniName : sniNames) {
+ if (sniName.getType() != StandardConstants.SNI_HOST_NAME) {
+ continue;
+ }
+
+ if (sniName instanceof SNIHostName) {
+ hostname = (SNIHostName)sniName;
+ } else {
+ try {
+ hostname = new SNIHostName(sniName.getEncoded());
+ } catch (IllegalArgumentException iae) {
+ // unlikely to happen, just in case ...
+ if ((debug != null) && Debug.isOn("trustmanager")) {
+ byte[] encoded = hostname.getEncoded();
+ System.out.println("Illegal server name: " + sniName);
+ }
+ }
+ }
+
+ // no more than server name of the same name type
+ break;
+ }
+
+ if (hostname != null) {
+ return hostname.getAsciiName();
+ }
+
+ return null;
+ }
+
+ // Also used by X509KeyManagerImpl
+ static List<SNIServerName> getRequestedServerNames(Socket socket) {
+ if (socket != null && socket.isConnected() &&
+ socket instanceof SSLSocket) {
+
+ SSLSocket sslSocket = (SSLSocket)socket;
+ SSLSession session = sslSocket.getHandshakeSession();
+
+ if (session != null && (session instanceof ExtendedSSLSession)) {
+ ExtendedSSLSession extSession = (ExtendedSSLSession)session;
+ return extSession.getRequestedServerNames();
+ }
+ }
+
+ return Collections.<SNIServerName>emptyList();
+ }
+
+ // Also used by X509KeyManagerImpl
+ static List<SNIServerName> getRequestedServerNames(SSLEngine engine) {
+ if (engine != null) {
+ SSLSession session = engine.getHandshakeSession();
+
+ if (session != null && (session instanceof ExtendedSSLSession)) {
+ ExtendedSSLSession extSession = (ExtendedSSLSession)session;
+ return extSession.getRequestedServerNames();
+ }
+ }
+
+ return Collections.<SNIServerName>emptyList();
+ }
+
+ /*
+ * Per RFC 6066, if an application negotiates a server name using an
+ * application protocol and then upgrades to TLS, and if a server_name
+ * extension is sent, then the extension SHOULD contain the same name
+ * that was negotiated in the application protocol. If the server_name
+ * is established in the TLS session handshake, the client SHOULD NOT
+ * attempt to request a different server name at the application layer.
+ *
+ * According to the above spec, we only need to check either the identity
+ * in server_name extension or the peer host of the connection. Peer host
+ * is not always a reliable fully qualified domain name. The HostName in
+ * server_name extension is more reliable than peer host. So we prefer
+ * the identity checking aginst the server_name extension if present, and
+ * may failove to peer host checking.
+ */
+ private static void checkIdentity(SSLSession session,
+ X509Certificate cert,
+ String algorithm,
+ boolean isClient,
+ List<SNIServerName> sniNames) throws CertificateException {
+
+ boolean identifiable = false;
+ String peerHost = session.getPeerHost();
+ if (isClient) {
+ String hostname = getHostNameInSNI(sniNames);
+ if (hostname != null) {
+ try {
+ checkIdentity(hostname, cert, algorithm);
+ identifiable = true;
+ } catch (CertificateException ce) {
+ if (hostname.equalsIgnoreCase(peerHost)) {
+ throw ce;
+ }
+
+ // otherwisw, failover to check peer host
+ }
+ }
+ }
+
+ if (!identifiable) {
+ checkIdentity(peerHost, cert, algorithm);
+ }
+ }
+
/*
* Identify the peer by its certificate and hostname.
*
--- a/jdk/test/sun/security/ssl/javax/net/ssl/NewAPIs/SSLEngine/LargePacket.java Wed Oct 17 13:22:39 2012 -0700
+++ b/jdk/test/sun/security/ssl/javax/net/ssl/NewAPIs/SSLEngine/LargePacket.java Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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
@@ -21,13 +21,18 @@
* questions.
*/
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
/*
* @test
*
* @bug 6388456
* @summary Need adjustable TLS max record size for interoperability
* with non-compliant
- * @run main/othervm -Djsse.enableCBCProtection=false LargePacket
+ * @run main/othervm LargePacket
*
* @author Xuelei Fan
*/
@@ -83,12 +88,12 @@
SocketChannel sc = ssc.accept();
// Complete connection.
- while (!sc.finishConnect() ) {
+ while (!sc.finishConnect()) {
// waiting for the connection completed.
}
// handshaking
- handshaking(ssle, sc);
+ handshaking(ssle, sc, null);
// receive application data
receive(ssle, sc);
@@ -131,7 +136,7 @@
}
// handshaking
- handshaking(ssle, sc);
+ handshaking(ssle, sc, null);
// send out application data
deliver(ssle, sc);
@@ -169,6 +174,8 @@
* Fork off the other side, then do your work.
*/
LargePacket() throws Exception {
+ super("../../../../../etc");
+
if (separateServerThread) {
startServer(true);
startClient(false);
--- a/jdk/test/sun/security/ssl/javax/net/ssl/NewAPIs/SSLEngine/SSLEngineService.java Wed Oct 17 13:22:39 2012 -0700
+++ b/jdk/test/sun/security/ssl/javax/net/ssl/NewAPIs/SSLEngine/SSLEngineService.java Thu Oct 18 01:14:00 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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
@@ -22,8 +22,6 @@
*/
/*
- *
- *
* @bug 6388456
* @summary Need adjustable TLS max record size for interoperability
* with non-compliant stacks
@@ -42,17 +40,31 @@
public class SSLEngineService {
- private static String pathToStores = "../../../../../etc";
private static String keyStoreFile = "keystore";
private static String trustStoreFile = "truststore";
private static char[] passphrase = "passphrase".toCharArray();
- private static String keyFilename =
+ private String pathToStores;
+ private String keyFilename;
+ private String trustFilename;
+
+ protected SSLEngineService() {
+ init("../../../../../etc");
+ }
+
+ protected SSLEngineService(String pathToStores) {
+ init(pathToStores);
+ }
+
+ private void init(String pathToStores) {
+ this.pathToStores = pathToStores;
+ this.keyFilename =
System.getProperty("test.src", "./") + "/" + pathToStores +
"/" + keyStoreFile;
- private static String trustFilename =
+ this.trustFilename =
System.getProperty("test.src", "./") + "/" + pathToStores +
"/" + trustStoreFile;
+ }
// deliver local application data.
protected static void deliver(SSLEngine ssle, SocketChannel sc)
@@ -143,9 +155,12 @@
ByteBuffer peerNetData = ByteBuffer.allocate(netBufferMax/2);
int received = -1;
+ boolean needToReadMore = true;
while (received != 0) {
- if (ssle.isInboundDone() || sc.read(peerNetData) < 0) {
- break;
+ if (needToReadMore) {
+ if (ssle.isInboundDone() || sc.read(peerNetData) < 0) {
+ break;
+ }
}
peerNetData.flip();
@@ -186,6 +201,8 @@
" bytes large packet ");
}
+ needToReadMore = (peerNetData.position() > 0) ? false : true;
+
break;
case BUFFER_OVERFLOW :
@@ -206,6 +223,8 @@
" bytes for BUFFER_UNDERFLOW");
peerNetData = enlargeBuffer(peerNetData, size);
}
+
+ needToReadMore = true;
break;
default : // CLOSED :
@@ -215,8 +234,8 @@
}
}
- protected static void handshaking(SSLEngine ssle, SocketChannel sc)
- throws Exception {
+ protected static void handshaking(SSLEngine ssle, SocketChannel sc,
+ ByteBuffer additional) throws Exception {
int appBufferMax = ssle.getSession().getApplicationBufferSize();
int netBufferMax = ssle.getSession().getPacketBufferSize();
@@ -232,15 +251,39 @@
SSLEngineResult.HandshakeStatus hs = ssle.getHandshakeStatus();
// start handshaking from unwrap
+ byte[] buffer = new byte[0xFF];
+ boolean underflow = false;
do {
switch (hs) {
case NEED_UNWRAP :
if (peerNetData.position() == 0) {
+ if (additional != null && additional.hasRemaining()) {
+ do {
+ int len = Math.min(buffer.length,
+ peerNetData.remaining());
+ len = Math.min(len, additional.remaining());
+ if (len != 0) {
+ additional.get(buffer, 0, len);
+ peerNetData.put(buffer, 0, len);
+ }
+ } while (peerNetData.remaining() > 0 &&
+ additional.hasRemaining());
+ } else {
+ if (sc.read(peerNetData) < 0) {
+ ssle.closeInbound();
+ return;
+ }
+ }
+ }
+
+ if (underflow) {
if (sc.read(peerNetData) < 0) {
ssle.closeInbound();
return;
}
+
+ underflow = false;
}
peerNetData.flip();
@@ -259,6 +302,8 @@
size + " bytes for BUFFER_UNDERFLOW");
peerNetData = enlargeBuffer(peerNetData, size);
}
+
+ underflow = true;
break;
case BUFFER_OVERFLOW :
// maybe need to enlarge the peer application data buffer.
@@ -339,7 +384,7 @@
/*
* Create an initialized SSLContext to use for this test.
*/
- protected static SSLEngine createSSLEngine(boolean mode) throws Exception {
+ protected SSLEngine createSSLEngine(boolean mode) throws Exception {
SSLEngine ssle;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/javax/net/ssl/ServerName/SSLEngineExplorer.java Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,335 @@
+/*
+ * 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.
+ */
+
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
+/*
+ * @test
+ * @bug 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @library ../NewAPIs/SSLEngine ../../../../templates
+ * @build SSLEngineService SSLCapabilities SSLExplorer
+ * @run main/othervm SSLEngineExplorer SSLv2Hello,SSLv3
+ * @run main/othervm SSLEngineExplorer SSLv3
+ * @run main/othervm SSLEngineExplorer TLSv1
+ * @run main/othervm SSLEngineExplorer TLSv1.1
+ * @run main/othervm SSLEngineExplorer TLSv1.2
+ */
+
+import javax.net.ssl.*;
+import java.nio.*;
+import java.net.*;
+import java.util.*;
+import java.nio.channels.*;
+
+public class SSLEngineExplorer extends SSLEngineService {
+
+ /*
+ * =============================================================
+ * Set the various variables needed for the tests, then
+ * specify what tests to run on each side.
+ */
+
+ /*
+ * Should we run the client or server in a separate thread?
+ * Both sides can throw exceptions, but do you have a preference
+ * as to which side should be the main thread.
+ */
+ static boolean separateServerThread = true;
+
+ // Is the server ready to serve?
+ volatile static boolean serverReady = false;
+
+ /*
+ * Turn on SSL debugging?
+ */
+ static boolean debug = false;
+
+ /*
+ * Define the server side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doServerSide() throws Exception {
+
+ // create SSLEngine.
+ SSLEngine ssle = createSSLEngine(false);
+
+ // Create a server socket channel.
+ InetSocketAddress isa =
+ new InetSocketAddress(InetAddress.getLocalHost(), serverPort);
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ ssc.socket().bind(isa);
+ serverPort = ssc.socket().getLocalPort();
+
+ // Signal Client, we're ready for his connect.
+ serverReady = true;
+
+ // Accept a socket channel.
+ SocketChannel sc = ssc.accept();
+ sc.configureBlocking(false);
+
+ // Complete connection.
+ while (!sc.finishConnect()) {
+ Thread.sleep(50);
+ // waiting for the connection completed.
+ }
+
+ ByteBuffer buffer = ByteBuffer.allocate(0xFF);
+ int position = 0;
+ SSLCapabilities capabilities = null;
+
+ // Read the header of TLS record
+ buffer.limit(SSLExplorer.RECORD_HEADER_SIZE);
+ while (position < SSLExplorer.RECORD_HEADER_SIZE) {
+ int n = sc.read(buffer);
+ if (n < 0) {
+ throw new Exception("unexpected end of stream!");
+ }
+ position += n;
+ }
+ buffer.flip();
+
+ int recordLength = SSLExplorer.getRequiredSize(buffer);
+ if (buffer.capacity() < recordLength) {
+ ByteBuffer oldBuffer = buffer;
+ buffer = ByteBuffer.allocate(recordLength);
+ buffer.put(oldBuffer);
+ }
+
+ buffer.position(SSLExplorer.RECORD_HEADER_SIZE);
+ buffer.limit(buffer.capacity());
+ while (position < recordLength) {
+ int n = sc.read(buffer);
+ if (n < 0) {
+ throw new Exception("unexpected end of stream!");
+ }
+ position += n;
+ }
+ buffer.flip();
+
+ capabilities = SSLExplorer.explore(buffer);
+ if (capabilities != null) {
+ System.out.println("Record version: " +
+ capabilities.getRecordVersion());
+ System.out.println("Hello version: " +
+ capabilities.getHelloVersion());
+ }
+
+ // handshaking
+ handshaking(ssle, sc, buffer);
+
+ // receive application data
+ receive(ssle, sc);
+
+ // send out application data
+ deliver(ssle, sc);
+
+ ExtendedSSLSession session = (ExtendedSSLSession)ssle.getSession();
+ checkCapabilities(capabilities, session);
+
+ // close the socket channel.
+ sc.close();
+ ssc.close();
+ }
+
+ /*
+ * Define the client side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doClientSide() throws Exception {
+ // create SSLEngine.
+ SSLEngine ssle = createSSLEngine(true);
+
+ /*
+ * Wait for server to get started.
+ */
+ while (!serverReady) {
+ Thread.sleep(50);
+ }
+
+ // Create a non-blocking socket channel.
+ SocketChannel sc = SocketChannel.open();
+ sc.configureBlocking(false);
+ InetSocketAddress isa =
+ new InetSocketAddress(InetAddress.getLocalHost(), serverPort);
+ sc.connect(isa);
+
+ // Complete connection.
+ while (!sc.finishConnect() ) {
+ Thread.sleep(50);
+ // waiting for the connection completed.
+ }
+
+ // enable the specified TLS protocol
+ ssle.setEnabledProtocols(supportedProtocols);
+
+ // handshaking
+ handshaking(ssle, sc, null);
+
+ // send out application data
+ deliver(ssle, sc);
+
+ // receive application data
+ receive(ssle, sc);
+
+ // close the socket channel.
+ sc.close();
+ }
+
+ void checkCapabilities(SSLCapabilities capabilities,
+ ExtendedSSLSession session) throws Exception {
+
+ List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+ if (!sessionSNI.equals(capabilities.getServerNames())) {
+ throw new Exception(
+ "server name indication does not match capabilities");
+ }
+ }
+
+ private static String[] supportedProtocols; // supported protocols
+
+ private static void parseArguments(String[] args) {
+ supportedProtocols = args[0].split(",");
+ }
+
+
+ /*
+ * =============================================================
+ * The remainder is just support stuff
+ */
+ volatile Exception serverException = null;
+ volatile Exception clientException = null;
+
+ // use any free port by default
+ volatile int serverPort = 0;
+
+ public static void main(String args[]) throws Exception {
+ if (debug)
+ System.setProperty("javax.net.debug", "all");
+
+ /*
+ * Get the customized arguments.
+ */
+ parseArguments(args);
+
+ new SSLEngineExplorer();
+ }
+
+ Thread clientThread = null;
+ Thread serverThread = null;
+
+ /*
+ * Primary constructor, used to drive remainder of the test.
+ *
+ * Fork off the other side, then do your work.
+ */
+ SSLEngineExplorer() throws Exception {
+ super("../../../../etc");
+
+ if (separateServerThread) {
+ startServer(true);
+ startClient(false);
+ } else {
+ startClient(true);
+ startServer(false);
+ }
+
+ /*
+ * Wait for other side to close down.
+ */
+ if (separateServerThread) {
+ serverThread.join();
+ } else {
+ clientThread.join();
+ }
+
+ /*
+ * When we get here, the test is pretty much over.
+ *
+ * If the main thread excepted, that propagates back
+ * immediately. If the other thread threw an exception, we
+ * should report back.
+ */
+ if (serverException != null) {
+ System.out.print("Server Exception:");
+ throw serverException;
+ }
+ if (clientException != null) {
+ System.out.print("Client Exception:");
+ throw clientException;
+ }
+ }
+
+ void startServer(boolean newThread) throws Exception {
+ if (newThread) {
+ serverThread = new Thread() {
+ public void run() {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ /*
+ * Our server thread just died.
+ *
+ * Release the client, if not active already...
+ */
+ System.err.println("Server died...");
+ System.err.println(e);
+ serverReady = true;
+ serverException = e;
+ }
+ }
+ };
+ serverThread.start();
+ } else {
+ doServerSide();
+ }
+ }
+
+ void startClient(boolean newThread) throws Exception {
+ if (newThread) {
+ clientThread = new Thread() {
+ public void run() {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ /*
+ * Our client thread just died.
+ */
+ System.err.println("Client died...");
+ clientException = e;
+ }
+ }
+ };
+ clientThread.start();
+ } else {
+ doClientSide();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/javax/net/ssl/ServerName/SSLEngineExplorerMatchedSNI.java Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,393 @@
+/*
+ * 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.
+ */
+
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
+/*
+ * @test
+ * @bug 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @library ../NewAPIs/SSLEngine ../../../../templates
+ * @build SSLEngineService SSLCapabilities SSLExplorer
+ * @run main/othervm SSLEngineExplorerMatchedSNI www.example.com
+ * www\.example\.com
+ * @run main/othervm SSLEngineExplorerMatchedSNI www.example.com
+ * www\.example\.(com|org)
+ * @run main/othervm SSLEngineExplorerMatchedSNI example.com
+ * (.*\.)*example\.(com|org)
+ * @run main/othervm SSLEngineExplorerMatchedSNI www.example.com
+ * (.*\.)*example\.(com|org)
+ * @run main/othervm SSLEngineExplorerMatchedSNI www.us.example.com
+ * (.*\.)*example\.(com|org)
+ */
+
+import javax.net.ssl.*;
+import java.nio.*;
+import java.net.*;
+import java.util.*;
+import java.nio.channels.*;
+
+public class SSLEngineExplorerMatchedSNI extends SSLEngineService {
+
+ /*
+ * =============================================================
+ * Set the various variables needed for the tests, then
+ * specify what tests to run on each side.
+ */
+
+ /*
+ * Should we run the client or server in a separate thread?
+ * Both sides can throw exceptions, but do you have a preference
+ * as to which side should be the main thread.
+ */
+ static boolean separateServerThread = false;
+
+ // Is the server ready to serve?
+ volatile static boolean serverReady = false;
+
+ /*
+ * Turn on SSL debugging?
+ */
+ static boolean debug = false;
+
+ /*
+ * Define the server side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doServerSide() throws Exception {
+
+ // create SSLEngine.
+ SSLEngine ssle = createSSLEngine(false);
+
+ // Create a server socket channel.
+ InetSocketAddress isa =
+ new InetSocketAddress(InetAddress.getLocalHost(), serverPort);
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ ssc.socket().bind(isa);
+ serverPort = ssc.socket().getLocalPort();
+
+ // Signal Client, we're ready for his connect.
+ serverReady = true;
+
+ // Accept a socket channel.
+ SocketChannel sc = ssc.accept();
+
+ // Complete connection.
+ while (!sc.finishConnect()) {
+ Thread.sleep(50);
+ // waiting for the connection completed.
+ }
+
+ ByteBuffer buffer = ByteBuffer.allocate(0xFF);
+ int position = 0;
+ SSLCapabilities capabilities = null;
+
+ // Read the header of TLS record
+ buffer.limit(SSLExplorer.RECORD_HEADER_SIZE);
+ while (position < SSLExplorer.RECORD_HEADER_SIZE) {
+ int n = sc.read(buffer);
+ if (n < 0) {
+ throw new Exception("unexpected end of stream!");
+ }
+ position += n;
+ }
+ buffer.flip();
+
+ int recordLength = SSLExplorer.getRequiredSize(buffer);
+ if (buffer.capacity() < recordLength) {
+ ByteBuffer oldBuffer = buffer;
+ buffer = ByteBuffer.allocate(recordLength);
+ buffer.put(oldBuffer);
+ }
+
+ buffer.position(SSLExplorer.RECORD_HEADER_SIZE);
+ buffer.limit(buffer.capacity());
+ while (position < recordLength) {
+ int n = sc.read(buffer);
+ if (n < 0) {
+ throw new Exception("unexpected end of stream!");
+ }
+ position += n;
+ }
+ buffer.flip();
+
+ capabilities = SSLExplorer.explore(buffer);
+ if (capabilities != null) {
+ System.out.println("Record version: " +
+ capabilities.getRecordVersion());
+ System.out.println("Hello version: " +
+ capabilities.getHelloVersion());
+ }
+
+ // enable server name indication checking
+ SNIMatcher matcher = SNIHostName.createSNIMatcher(
+ serverAcceptableHostname);
+ Collection<SNIMatcher> matchers = new ArrayList<>(1);
+ matchers.add(matcher);
+ SSLParameters params = ssle.getSSLParameters();
+ params.setSNIMatchers(matchers);
+ ssle.setSSLParameters(params);
+
+ // handshaking
+ handshaking(ssle, sc, buffer);
+
+ // receive application data
+ receive(ssle, sc);
+
+ // send out application data
+ deliver(ssle, sc);
+
+ // check server name indication
+ ExtendedSSLSession session = (ExtendedSSLSession)ssle.getSession();
+ checkCapabilities(capabilities, session);
+
+ // close the socket channel.
+ sc.close();
+ ssc.close();
+ }
+
+ /*
+ * Define the client side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doClientSide() throws Exception {
+ // create SSLEngine.
+ SSLEngine ssle = createSSLEngine(true);
+
+ /*
+ * Wait for server to get started.
+ */
+ while (!serverReady) {
+ Thread.sleep(50);
+ }
+
+ // Create a non-blocking socket channel.
+ SocketChannel sc = SocketChannel.open();
+ sc.configureBlocking(false);
+ InetSocketAddress isa =
+ new InetSocketAddress(InetAddress.getLocalHost(), serverPort);
+ sc.connect(isa);
+
+ // Complete connection.
+ while (!sc.finishConnect() ) {
+ Thread.sleep(50);
+ // waiting for the connection completed.
+ }
+
+ SNIHostName serverName = new SNIHostName(clientRequestedHostname);
+ List<SNIServerName> serverNames = new ArrayList<>(1);
+ serverNames.add(serverName);
+ SSLParameters params = ssle.getSSLParameters();
+ params.setServerNames(serverNames);
+ ssle.setSSLParameters(params);
+
+ // handshaking
+ handshaking(ssle, sc, null);
+
+ // send out application data
+ deliver(ssle, sc);
+
+ // receive application data
+ receive(ssle, sc);
+
+ // check server name indication
+ ExtendedSSLSession session = (ExtendedSSLSession)ssle.getSession();
+ checkSNIInSession(session);
+
+ // close the socket channel.
+ sc.close();
+ }
+
+ void checkCapabilities(SSLCapabilities capabilities,
+ ExtendedSSLSession session) throws Exception {
+ List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+ if (!sessionSNI.equals(capabilities.getServerNames())) {
+ for (SNIServerName sni : sessionSNI) {
+ System.out.println("SNI in session is " + sni);
+ }
+
+ List<SNIServerName> capaSNI = capabilities.getServerNames();
+ for (SNIServerName sni : capaSNI) {
+ System.out.println("SNI in session is " + sni);
+ }
+
+ throw new Exception(
+ "server name indication does not match capabilities");
+ }
+
+ checkSNIInSession(session);
+ }
+
+ void checkSNIInSession(ExtendedSSLSession session) throws Exception {
+ List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+ if (sessionSNI.isEmpty()) {
+ throw new Exception(
+ "unexpected empty request server name indication");
+ }
+
+ if (sessionSNI.size() != 1) {
+ throw new Exception(
+ "unexpected request server name indication");
+ }
+
+ SNIServerName serverName = sessionSNI.get(0);
+ if (!(serverName instanceof SNIHostName)) {
+ throw new Exception(
+ "unexpected instance of request server name indication");
+ }
+
+ String hostname = ((SNIHostName)serverName).getAsciiName();
+ if (!clientRequestedHostname.equalsIgnoreCase(hostname)) {
+ throw new Exception(
+ "unexpected request server name indication value");
+ }
+ }
+
+ private static String clientRequestedHostname;
+ private static String serverAcceptableHostname;
+
+ private static void parseArguments(String[] args) {
+ clientRequestedHostname = args[0];
+ serverAcceptableHostname = args[1];
+ }
+
+ /*
+ * =============================================================
+ * The remainder is just support stuff
+ */
+ volatile Exception serverException = null;
+ volatile Exception clientException = null;
+
+ // use any free port by default
+ volatile int serverPort = 0;
+
+ public static void main(String args[]) throws Exception {
+ if (debug)
+ System.setProperty("javax.net.debug", "all");
+
+ /*
+ * Get the customized arguments.
+ */
+ parseArguments(args);
+
+ new SSLEngineExplorerMatchedSNI();
+ }
+
+ Thread clientThread = null;
+ Thread serverThread = null;
+
+ /*
+ * Primary constructor, used to drive remainder of the test.
+ *
+ * Fork off the other side, then do your work.
+ */
+ SSLEngineExplorerMatchedSNI() throws Exception {
+ super("../../../../etc");
+
+ if (separateServerThread) {
+ startServer(true);
+ startClient(false);
+ } else {
+ startClient(true);
+ startServer(false);
+ }
+
+ /*
+ * Wait for other side to close down.
+ */
+ if (separateServerThread) {
+ serverThread.join();
+ } else {
+ clientThread.join();
+ }
+
+ /*
+ * When we get here, the test is pretty much over.
+ *
+ * If the main thread excepted, that propagates back
+ * immediately. If the other thread threw an exception, we
+ * should report back.
+ */
+ if (serverException != null) {
+ System.out.print("Server Exception:");
+ throw serverException;
+ }
+ if (clientException != null) {
+ System.out.print("Client Exception:");
+ throw clientException;
+ }
+ }
+
+ void startServer(boolean newThread) throws Exception {
+ if (newThread) {
+ serverThread = new Thread() {
+ public void run() {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ /*
+ * Our server thread just died.
+ *
+ * Release the client, if not active already...
+ */
+ System.err.println("Server died...");
+ System.err.println(e);
+ serverReady = true;
+ serverException = e;
+ }
+ }
+ };
+ serverThread.start();
+ } else {
+ doServerSide();
+ }
+ }
+
+ void startClient(boolean newThread) throws Exception {
+ if (newThread) {
+ clientThread = new Thread() {
+ public void run() {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ /*
+ * Our client thread just died.
+ */
+ System.err.println("Client died...");
+ clientException = e;
+ }
+ }
+ };
+ clientThread.start();
+ } else {
+ doClientSide();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/javax/net/ssl/ServerName/SSLEngineExplorerUnmatchedSNI.java Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,406 @@
+/*
+ * 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.
+ */
+
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
+/*
+ * @test
+ * @bug 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @library ../NewAPIs/SSLEngine ../../../../templates
+ * @build SSLEngineService SSLCapabilities SSLExplorer
+ * @run main/othervm SSLEngineExplorerUnmatchedSNI www.example.com
+ * www\.example\.org
+ */
+
+import javax.net.ssl.*;
+import java.io.*;
+import java.nio.*;
+import java.net.*;
+import java.util.*;
+import java.nio.channels.*;
+
+public class SSLEngineExplorerUnmatchedSNI extends SSLEngineService {
+
+ /*
+ * =============================================================
+ * Set the various variables needed for the tests, then
+ * specify what tests to run on each side.
+ */
+
+ /*
+ * Should we run the client or server in a separate thread?
+ * Both sides can throw exceptions, but do you have a preference
+ * as to which side should be the main thread.
+ */
+ static boolean separateServerThread = false;
+
+ // Is the server ready to serve?
+ volatile static boolean serverReady = false;
+
+ /*
+ * Turn on SSL debugging?
+ */
+ static boolean debug = false;
+
+ /*
+ * Define the server side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doServerSide() throws Exception {
+
+ // create SSLEngine.
+ SSLEngine ssle = createSSLEngine(false);
+
+ // Create a server socket channel.
+ InetSocketAddress isa =
+ new InetSocketAddress(InetAddress.getLocalHost(), serverPort);
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ ssc.socket().bind(isa);
+ serverPort = ssc.socket().getLocalPort();
+
+ // Signal Client, we're ready for his connect.
+ serverReady = true;
+
+ // Accept a socket channel.
+ SocketChannel sc = ssc.accept();
+
+ // Complete connection.
+ while (!sc.finishConnect()) {
+ Thread.sleep(50);
+ // waiting for the connection completed.
+ }
+
+ ByteBuffer buffer = ByteBuffer.allocate(0xFF);
+ int position = 0;
+ SSLCapabilities capabilities = null;
+
+ // Read the header of TLS record
+ buffer.limit(SSLExplorer.RECORD_HEADER_SIZE);
+ while (position < SSLExplorer.RECORD_HEADER_SIZE) {
+ int n = sc.read(buffer);
+ if (n < 0) {
+ throw new Exception("unexpected end of stream!");
+ }
+ position += n;
+ }
+ buffer.flip();
+
+ int recordLength = SSLExplorer.getRequiredSize(buffer);
+ if (buffer.capacity() < recordLength) {
+ ByteBuffer oldBuffer = buffer;
+ buffer = ByteBuffer.allocate(recordLength);
+ buffer.put(oldBuffer);
+ }
+
+ buffer.position(SSLExplorer.RECORD_HEADER_SIZE);
+ buffer.limit(buffer.capacity());
+ while (position < recordLength) {
+ int n = sc.read(buffer);
+ if (n < 0) {
+ throw new Exception("unexpected end of stream!");
+ }
+ position += n;
+ }
+ buffer.flip();
+
+ capabilities = SSLExplorer.explore(buffer);
+ if (capabilities != null) {
+ System.out.println("Record version: " +
+ capabilities.getRecordVersion());
+ System.out.println("Hello version: " +
+ capabilities.getHelloVersion());
+ }
+
+ // enable server name indication checking
+ SNIMatcher matcher = SNIHostName.createSNIMatcher(
+ serverAcceptableHostname);
+ Collection<SNIMatcher> matchers = new ArrayList<>(1);
+ matchers.add(matcher);
+ SSLParameters params = ssle.getSSLParameters();
+ params.setSNIMatchers(matchers);
+ ssle.setSSLParameters(params);
+
+ try {
+ // handshaking
+ handshaking(ssle, sc, buffer);
+
+ // receive application data
+ receive(ssle, sc);
+
+ // send out application data
+ deliver(ssle, sc);
+
+ // check server name indication
+ ExtendedSSLSession session = (ExtendedSSLSession)ssle.getSession();
+ checkCapabilities(capabilities, session);
+
+ throw new Exception(
+ "Mismatched server name indication was accepted");
+ } catch (SSLHandshakeException sslhe) {
+ // the expected unrecognized server name indication exception
+ } catch (IOException ioe) {
+ // the peer may have closed the socket because of the unmatched
+ // server name indication.
+ } finally {
+ // close the socket channel.
+ sc.close();
+ ssc.close();
+ }
+ }
+
+ /*
+ * Define the client side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doClientSide() throws Exception {
+ // create SSLEngine.
+ SSLEngine ssle = createSSLEngine(true);
+
+ /*
+ * Wait for server to get started.
+ */
+ while (!serverReady) {
+ Thread.sleep(50);
+ }
+
+ // Create a non-blocking socket channel.
+ SocketChannel sc = SocketChannel.open();
+ sc.configureBlocking(false);
+ InetSocketAddress isa =
+ new InetSocketAddress(InetAddress.getLocalHost(), serverPort);
+ sc.connect(isa);
+
+ // Complete connection.
+ while (!sc.finishConnect() ) {
+ Thread.sleep(50);
+ // waiting for the connection completed.
+ }
+
+ SNIHostName serverName = new SNIHostName(clientRequestedHostname);
+ List<SNIServerName> serverNames = new ArrayList<>(1);
+ serverNames.add(serverName);
+ SSLParameters params = ssle.getSSLParameters();
+ params.setServerNames(serverNames);
+ ssle.setSSLParameters(params);
+
+ try {
+ // handshaking
+ handshaking(ssle, sc, null);
+
+ // send out application data
+ deliver(ssle, sc);
+
+ // receive application data
+ receive(ssle, sc);
+
+ // check server name indication
+ ExtendedSSLSession session = (ExtendedSSLSession)ssle.getSession();
+ checkSNIInSession(session);
+
+ throw new Exception(
+ "Mismatched server name indication was accepted");
+ } catch (SSLHandshakeException sslhe) {
+ // the expected unrecognized server name indication exception
+ } catch (IOException ioe) {
+ // the peer may have closed the socket because of the unmatched
+ // server name indication.
+ } finally {
+ // close the socket channel.
+ sc.close();
+ }
+ }
+
+ void checkCapabilities(SSLCapabilities capabilities,
+ ExtendedSSLSession session) throws Exception {
+ List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+ if (!sessionSNI.equals(capabilities.getServerNames())) {
+ for (SNIServerName sni : sessionSNI) {
+ System.out.println("SNI in session is " + sni);
+ }
+
+ List<SNIServerName> capaSNI = capabilities.getServerNames();
+ for (SNIServerName sni : capaSNI) {
+ System.out.println("SNI in session is " + sni);
+ }
+
+ throw new Exception(
+ "server name indication does not match capabilities");
+ }
+
+ checkSNIInSession(session);
+ }
+
+ void checkSNIInSession(ExtendedSSLSession session) throws Exception {
+ List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+ if (sessionSNI.isEmpty()) {
+ throw new Exception(
+ "unexpected empty request server name indication");
+ }
+
+ if (sessionSNI.size() != 1) {
+ throw new Exception(
+ "unexpected request server name indication");
+ }
+
+ SNIServerName serverName = sessionSNI.get(0);
+ if (!(serverName instanceof SNIHostName)) {
+ throw new Exception(
+ "unexpected instance of request server name indication");
+ }
+
+ String hostname = ((SNIHostName)serverName).getAsciiName();
+ if (!clientRequestedHostname.equalsIgnoreCase(hostname)) {
+ throw new Exception(
+ "unexpected request server name indication value");
+ }
+ }
+
+ private static String clientRequestedHostname;
+ private static String serverAcceptableHostname;
+
+ private static void parseArguments(String[] args) {
+ clientRequestedHostname = args[0];
+ serverAcceptableHostname = args[1];
+ }
+
+ /*
+ * =============================================================
+ * The remainder is just support stuff
+ */
+ volatile Exception serverException = null;
+ volatile Exception clientException = null;
+
+ // use any free port by default
+ volatile int serverPort = 0;
+
+ public static void main(String args[]) throws Exception {
+ if (debug)
+ System.setProperty("javax.net.debug", "all");
+
+ /*
+ * Get the customized arguments.
+ */
+ parseArguments(args);
+
+ new SSLEngineExplorerUnmatchedSNI();
+ }
+
+ Thread clientThread = null;
+ Thread serverThread = null;
+
+ /*
+ * Primary constructor, used to drive remainder of the test.
+ *
+ * Fork off the other side, then do your work.
+ */
+ SSLEngineExplorerUnmatchedSNI() throws Exception {
+ super("../../../../etc");
+
+ if (separateServerThread) {
+ startServer(true);
+ startClient(false);
+ } else {
+ startClient(true);
+ startServer(false);
+ }
+
+ /*
+ * Wait for other side to close down.
+ */
+ if (separateServerThread) {
+ serverThread.join();
+ } else {
+ clientThread.join();
+ }
+
+ /*
+ * When we get here, the test is pretty much over.
+ *
+ * If the main thread excepted, that propagates back
+ * immediately. If the other thread threw an exception, we
+ * should report back.
+ */
+ if (serverException != null) {
+ System.out.print("Server Exception:");
+ throw serverException;
+ }
+ if (clientException != null) {
+ System.out.print("Client Exception:");
+ throw clientException;
+ }
+ }
+
+ void startServer(boolean newThread) throws Exception {
+ if (newThread) {
+ serverThread = new Thread() {
+ public void run() {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ /*
+ * Our server thread just died.
+ *
+ * Release the client, if not active already...
+ */
+ System.err.println("Server died...");
+ System.err.println(e);
+ serverReady = true;
+ serverException = e;
+ }
+ }
+ };
+ serverThread.start();
+ } else {
+ doServerSide();
+ }
+ }
+
+ void startClient(boolean newThread) throws Exception {
+ if (newThread) {
+ clientThread = new Thread() {
+ public void run() {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ /*
+ * Our client thread just died.
+ */
+ System.err.println("Client died...");
+ clientException = e;
+ }
+ }
+ };
+ clientThread.start();
+ } else {
+ doClientSide();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/javax/net/ssl/ServerName/SSLEngineExplorerWithCli.java Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,365 @@
+/*
+ * 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.
+ */
+
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
+/*
+ * @test
+ * @bug 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @library ../NewAPIs/SSLEngine ../../../../templates
+ * @build SSLEngineService SSLCapabilities SSLExplorer
+ * @run main/othervm SSLEngineExplorerWithCli
+ */
+
+import javax.net.ssl.*;
+import java.nio.*;
+import java.net.*;
+import java.util.*;
+import java.nio.channels.*;
+
+public class SSLEngineExplorerWithCli extends SSLEngineService {
+
+ /*
+ * =============================================================
+ * Set the various variables needed for the tests, then
+ * specify what tests to run on each side.
+ */
+
+ /*
+ * Should we run the client or server in a separate thread?
+ * Both sides can throw exceptions, but do you have a preference
+ * as to which side should be the main thread.
+ */
+ static boolean separateServerThread = true;
+
+ // Is the server ready to serve?
+ volatile static boolean serverReady = false;
+
+ /*
+ * Turn on SSL debugging?
+ */
+ static boolean debug = false;
+
+ /*
+ * Define the server side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doServerSide() throws Exception {
+
+ // create SSLEngine.
+ SSLEngine ssle = createSSLEngine(false);
+
+ // Create a server socket channel.
+ InetSocketAddress isa =
+ new InetSocketAddress(InetAddress.getLocalHost(), serverPort);
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ ssc.socket().bind(isa);
+ serverPort = ssc.socket().getLocalPort();
+
+ // Signal Client, we're ready for his connect.
+ serverReady = true;
+
+ // Accept a socket channel.
+ SocketChannel sc = ssc.accept();
+
+ // Complete connection.
+ while (!sc.finishConnect()) {
+ Thread.sleep(50);
+ // waiting for the connection completed.
+ }
+
+ ByteBuffer buffer = ByteBuffer.allocate(0xFF);
+ int position = 0;
+ SSLCapabilities capabilities = null;
+
+ // Read the header of TLS record
+ buffer.limit(SSLExplorer.RECORD_HEADER_SIZE);
+ while (position < SSLExplorer.RECORD_HEADER_SIZE) {
+ int n = sc.read(buffer);
+ if (n < 0) {
+ throw new Exception("unexpected end of stream!");
+ }
+ position += n;
+ }
+ buffer.flip();
+
+ int recordLength = SSLExplorer.getRequiredSize(buffer);
+ if (buffer.capacity() < recordLength) {
+ ByteBuffer oldBuffer = buffer;
+ buffer = ByteBuffer.allocate(recordLength);
+ buffer.put(oldBuffer);
+ }
+
+ buffer.position(SSLExplorer.RECORD_HEADER_SIZE);
+ buffer.limit(buffer.capacity());
+ while (position < recordLength) {
+ int n = sc.read(buffer);
+ if (n < 0) {
+ throw new Exception("unexpected end of stream!");
+ }
+ position += n;
+ }
+ buffer.flip();
+
+ capabilities = SSLExplorer.explore(buffer);
+ if (capabilities != null) {
+ System.out.println("Record version: " +
+ capabilities.getRecordVersion());
+ System.out.println("Hello version: " +
+ capabilities.getHelloVersion());
+ }
+
+ // handshaking
+ handshaking(ssle, sc, buffer);
+
+ // receive application data
+ receive(ssle, sc);
+
+ // send out application data
+ deliver(ssle, sc);
+
+ ExtendedSSLSession session = (ExtendedSSLSession)ssle.getSession();
+ checkCapabilities(capabilities, session);
+
+ // close the socket channel.
+ sc.close();
+ ssc.close();
+ }
+
+ /*
+ * Define the client side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doClientSide() throws Exception {
+ // create SSLEngine.
+ SSLEngine ssle = createSSLEngine(true);
+
+ /*
+ * Wait for server to get started.
+ */
+ while (!serverReady) {
+ Thread.sleep(50);
+ }
+
+ // Create a non-blocking socket channel.
+ SocketChannel sc = SocketChannel.open();
+ sc.configureBlocking(false);
+ InetSocketAddress isa =
+ new InetSocketAddress(InetAddress.getLocalHost(), serverPort);
+ sc.connect(isa);
+
+ // Complete connection.
+ while (!sc.finishConnect() ) {
+ Thread.sleep(50);
+ // waiting for the connection completed.
+ }
+
+ SNIHostName serverName = new SNIHostName(clientRequestedHostname);
+ List<SNIServerName> serverNames = new ArrayList<>(1);
+ serverNames.add(serverName);
+ SSLParameters params = ssle.getSSLParameters();
+ params.setServerNames(serverNames);
+ ssle.setSSLParameters(params);
+
+ // handshaking
+ handshaking(ssle, sc, null);
+
+ // send out application data
+ deliver(ssle, sc);
+
+ // receive application data
+ receive(ssle, sc);
+
+ // check server name indication
+ ExtendedSSLSession session = (ExtendedSSLSession)ssle.getSession();
+ checkSNIInSession(session);
+
+ // close the socket channel.
+ sc.close();
+ }
+
+ private static String clientRequestedHostname = "www.example.com";
+ private static String serverAcceptableHostname =
+ "www\\.example\\.(com|org)";
+
+ void checkCapabilities(SSLCapabilities capabilities,
+ ExtendedSSLSession session) throws Exception {
+ List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+ if (!sessionSNI.equals(capabilities.getServerNames())) {
+ for (SNIServerName sni : sessionSNI) {
+ System.out.println("SNI in session is " + sni);
+ }
+
+ List<SNIServerName> capaSNI = capabilities.getServerNames();
+ for (SNIServerName sni : capaSNI) {
+ System.out.println("SNI in session is " + sni);
+ }
+
+ throw new Exception(
+ "server name indication does not match capabilities");
+ }
+
+ checkSNIInSession(session);
+ }
+
+ void checkSNIInSession(ExtendedSSLSession session) throws Exception {
+ List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+ if (sessionSNI.isEmpty()) {
+ throw new Exception(
+ "unexpected empty request server name indication");
+ }
+
+ if (sessionSNI.size() != 1) {
+ throw new Exception(
+ "unexpected request server name indication");
+ }
+
+ SNIServerName serverName = sessionSNI.get(0);
+ if (!(serverName instanceof SNIHostName)) {
+ throw new Exception(
+ "unexpected instance of request server name indication");
+ }
+
+ String hostname = ((SNIHostName)serverName).getAsciiName();
+ if (!clientRequestedHostname.equalsIgnoreCase(hostname)) {
+ throw new Exception(
+ "unexpected request server name indication value");
+ }
+ }
+
+ /*
+ * =============================================================
+ * The remainder is just support stuff
+ */
+ volatile Exception serverException = null;
+ volatile Exception clientException = null;
+
+ // use any free port by default
+ volatile int serverPort = 0;
+
+ public static void main(String args[]) throws Exception {
+ if (debug)
+ System.setProperty("javax.net.debug", "all");
+
+ new SSLEngineExplorerWithCli();
+ }
+
+ Thread clientThread = null;
+ Thread serverThread = null;
+
+ /*
+ * Primary constructor, used to drive remainder of the test.
+ *
+ * Fork off the other side, then do your work.
+ */
+ SSLEngineExplorerWithCli() throws Exception {
+ super("../../../../etc");
+
+ if (separateServerThread) {
+ startServer(true);
+ startClient(false);
+ } else {
+ startClient(true);
+ startServer(false);
+ }
+
+ /*
+ * Wait for other side to close down.
+ */
+ if (separateServerThread) {
+ serverThread.join();
+ } else {
+ clientThread.join();
+ }
+
+ /*
+ * When we get here, the test is pretty much over.
+ *
+ * If the main thread excepted, that propagates back
+ * immediately. If the other thread threw an exception, we
+ * should report back.
+ */
+ if (serverException != null) {
+ System.out.print("Server Exception:");
+ throw serverException;
+ }
+ if (clientException != null) {
+ System.out.print("Client Exception:");
+ throw clientException;
+ }
+ }
+
+ void startServer(boolean newThread) throws Exception {
+ if (newThread) {
+ serverThread = new Thread() {
+ public void run() {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ /*
+ * Our server thread just died.
+ *
+ * Release the client, if not active already...
+ */
+ System.err.println("Server died...");
+ System.err.println(e);
+ serverReady = true;
+ serverException = e;
+ }
+ }
+ };
+ serverThread.start();
+ } else {
+ doServerSide();
+ }
+ }
+
+ void startClient(boolean newThread) throws Exception {
+ if (newThread) {
+ clientThread = new Thread() {
+ public void run() {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ /*
+ * Our client thread just died.
+ */
+ System.err.println("Client died...");
+ clientException = e;
+ }
+ }
+ };
+ clientThread.start();
+ } else {
+ doClientSide();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/javax/net/ssl/ServerName/SSLEngineExplorerWithSrv.java Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,351 @@
+/*
+ * 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.
+ */
+
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
+/*
+ * @test
+ * @bug 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @library ../NewAPIs/SSLEngine ../../../../templates
+ * @build SSLEngineService SSLCapabilities SSLExplorer
+ * @run main/othervm SSLEngineExplorerWithSrv
+ */
+
+import javax.net.ssl.*;
+import java.nio.*;
+import java.net.*;
+import java.util.*;
+import java.nio.channels.*;
+
+public class SSLEngineExplorerWithSrv extends SSLEngineService {
+
+ /*
+ * =============================================================
+ * Set the various variables needed for the tests, then
+ * specify what tests to run on each side.
+ */
+
+ /*
+ * Should we run the client or server in a separate thread?
+ * Both sides can throw exceptions, but do you have a preference
+ * as to which side should be the main thread.
+ */
+ static boolean separateServerThread = true;
+
+ // Is the server ready to serve?
+ volatile static boolean serverReady = false;
+
+ /*
+ * Turn on SSL debugging?
+ */
+ static boolean debug = false;
+
+ /*
+ * Define the server side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doServerSide() throws Exception {
+
+ // create SSLEngine.
+ SSLEngine ssle = createSSLEngine(false);
+
+ // Create a server socket channel.
+ InetSocketAddress isa =
+ new InetSocketAddress(InetAddress.getLocalHost(), serverPort);
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ ssc.socket().bind(isa);
+ serverPort = ssc.socket().getLocalPort();
+
+ // Signal Client, we're ready for his connect.
+ serverReady = true;
+
+ // Accept a socket channel.
+ SocketChannel sc = ssc.accept();
+
+ // Complete connection.
+ while (!sc.finishConnect()) {
+ Thread.sleep(50);
+ // waiting for the connection completed.
+ }
+
+ ByteBuffer buffer = ByteBuffer.allocate(0xFF);
+ int position = 0;
+ SSLCapabilities capabilities = null;
+
+ // Read the header of TLS record
+ buffer.limit(SSLExplorer.RECORD_HEADER_SIZE);
+ while (position < SSLExplorer.RECORD_HEADER_SIZE) {
+ int n = sc.read(buffer);
+ if (n < 0) {
+ throw new Exception("unexpected end of stream!");
+ }
+ position += n;
+ }
+ buffer.flip();
+
+ int recordLength = SSLExplorer.getRequiredSize(buffer);
+ if (buffer.capacity() < recordLength) {
+ ByteBuffer oldBuffer = buffer;
+ buffer = ByteBuffer.allocate(recordLength);
+ buffer.put(oldBuffer);
+ }
+
+ buffer.position(SSLExplorer.RECORD_HEADER_SIZE);
+ buffer.limit(buffer.capacity());
+ while (position < recordLength) {
+ int n = sc.read(buffer);
+ if (n < 0) {
+ throw new Exception("unexpected end of stream!");
+ }
+ position += n;
+ }
+ buffer.flip();
+
+ capabilities = SSLExplorer.explore(buffer);
+ if (capabilities != null) {
+ System.out.println("Record version: " +
+ capabilities.getRecordVersion());
+ System.out.println("Hello version: " +
+ capabilities.getHelloVersion());
+ }
+
+ // enable server name indication checking
+ SNIMatcher matcher = SNIHostName.createSNIMatcher(
+ serverAcceptableHostname);
+ Collection<SNIMatcher> matchers = new ArrayList<>(1);
+ matchers.add(matcher);
+ SSLParameters params = ssle.getSSLParameters();
+ params.setSNIMatchers(matchers);
+ ssle.setSSLParameters(params);
+
+ // handshaking
+ handshaking(ssle, sc, buffer);
+
+ // receive application data
+ receive(ssle, sc);
+
+ // send out application data
+ deliver(ssle, sc);
+
+ // check server name indication
+ ExtendedSSLSession session = (ExtendedSSLSession)ssle.getSession();
+ checkCapabilities(capabilities, session);
+
+ // close the socket channel.
+ sc.close();
+ ssc.close();
+ }
+
+ /*
+ * Define the client side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doClientSide() throws Exception {
+ // create SSLEngine.
+ SSLEngine ssle = createSSLEngine(true);
+
+ /*
+ * Wait for server to get started.
+ */
+ while (!serverReady) {
+ Thread.sleep(50);
+ }
+
+ // Create a non-blocking socket channel.
+ SocketChannel sc = SocketChannel.open();
+ sc.configureBlocking(false);
+ InetSocketAddress isa =
+ new InetSocketAddress(InetAddress.getLocalHost(), serverPort);
+ sc.connect(isa);
+
+ // Complete connection.
+ while (!sc.finishConnect() ) {
+ Thread.sleep(50);
+ // waiting for the connection completed.
+ }
+
+ // handshaking
+ handshaking(ssle, sc, null);
+
+ // send out application data
+ deliver(ssle, sc);
+
+ // receive application data
+ receive(ssle, sc);
+
+ // check server name indication
+ ExtendedSSLSession session = (ExtendedSSLSession)ssle.getSession();
+ checkSNIInSession(session);
+
+ // close the socket channel.
+ sc.close();
+ }
+
+ private static String clientRequestedHostname = "www.example.com";
+ private static String serverAcceptableHostname =
+ "www\\.example\\.(com|org)";
+
+ void checkCapabilities(SSLCapabilities capabilities,
+ ExtendedSSLSession session) throws Exception {
+ List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+ if (!sessionSNI.equals(capabilities.getServerNames())) {
+ for (SNIServerName sni : sessionSNI) {
+ System.out.println("SNI in session is " + sni);
+ }
+
+ List<SNIServerName> capaSNI = capabilities.getServerNames();
+ for (SNIServerName sni : capaSNI) {
+ System.out.println("SNI in session is " + sni);
+ }
+
+ throw new Exception(
+ "server name indication does not match capabilities");
+ }
+
+ checkSNIInSession(session);
+ }
+
+ void checkSNIInSession(ExtendedSSLSession session) throws Exception {
+ List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+ if (!sessionSNI.isEmpty()) {
+ throw new Exception(
+ "should be empty request server name indication");
+ }
+ }
+
+ /*
+ * =============================================================
+ * The remainder is just support stuff
+ */
+ volatile Exception serverException = null;
+ volatile Exception clientException = null;
+
+ // use any free port by default
+ volatile int serverPort = 0;
+
+ public static void main(String args[]) throws Exception {
+ if (debug)
+ System.setProperty("javax.net.debug", "all");
+
+ new SSLEngineExplorerWithSrv();
+ }
+
+ Thread clientThread = null;
+ Thread serverThread = null;
+
+ /*
+ * Primary constructor, used to drive remainder of the test.
+ *
+ * Fork off the other side, then do your work.
+ */
+ SSLEngineExplorerWithSrv() throws Exception {
+ super("../../../../etc");
+
+ if (separateServerThread) {
+ startServer(true);
+ startClient(false);
+ } else {
+ startClient(true);
+ startServer(false);
+ }
+
+ /*
+ * Wait for other side to close down.
+ */
+ if (separateServerThread) {
+ serverThread.join();
+ } else {
+ clientThread.join();
+ }
+
+ /*
+ * When we get here, the test is pretty much over.
+ *
+ * If the main thread excepted, that propagates back
+ * immediately. If the other thread threw an exception, we
+ * should report back.
+ */
+ if (serverException != null) {
+ System.out.print("Server Exception:");
+ throw serverException;
+ }
+ if (clientException != null) {
+ System.out.print("Client Exception:");
+ throw clientException;
+ }
+ }
+
+ void startServer(boolean newThread) throws Exception {
+ if (newThread) {
+ serverThread = new Thread() {
+ public void run() {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ /*
+ * Our server thread just died.
+ *
+ * Release the client, if not active already...
+ */
+ System.err.println("Server died...");
+ System.err.println(e);
+ serverReady = true;
+ serverException = e;
+ }
+ }
+ };
+ serverThread.start();
+ } else {
+ doServerSide();
+ }
+ }
+
+ void startClient(boolean newThread) throws Exception {
+ if (newThread) {
+ clientThread = new Thread() {
+ public void run() {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ /*
+ * Our client thread just died.
+ */
+ System.err.println("Client died...");
+ clientException = e;
+ }
+ }
+ };
+ clientThread.start();
+ } else {
+ doClientSide();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/javax/net/ssl/ServerName/SSLSocketConsistentSNI.java Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,357 @@
+/*
+ * 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.
+ */
+
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
+/**
+ * @test
+ * @bug 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @run main/othervm SSLSocketConsistentSNI
+ */
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.net.*;
+import javax.net.ssl.*;
+
+public class SSLSocketConsistentSNI {
+
+ /*
+ * =============================================================
+ * Set the various variables needed for the tests, then
+ * specify what tests to run on each side.
+ */
+
+ /*
+ * Should we run the client or server in a separate thread?
+ * Both sides can throw exceptions, but do you have a preference
+ * as to which side should be the main thread.
+ */
+ static boolean separateServerThread = true;
+
+ /*
+ * Where do we find the keystores?
+ */
+ static String pathToStores = "../../../../etc";
+ static String keyStoreFile = "keystore";
+ static String trustStoreFile = "truststore";
+ static String passwd = "passphrase";
+
+ /*
+ * Is the server ready to serve?
+ */
+ volatile static boolean serverReady = false;
+
+ /*
+ * Turn on SSL debugging?
+ */
+ static boolean debug = false;
+
+ /*
+ * If the client or server is doing some kind of object creation
+ * that the other side depends on, and that thread prematurely
+ * exits, you may experience a hang. The test harness will
+ * terminate all hung threads after its timeout has expired,
+ * currently 3 minutes by default, but you might try to be
+ * smart about it....
+ */
+
+ /*
+ * Define the server side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doServerSide() throws Exception {
+ SSLServerSocketFactory sslssf =
+ (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
+ SSLServerSocket sslServerSocket =
+ (SSLServerSocket) sslssf.createServerSocket(serverPort);
+
+ SNIMatcher matcher = SNIHostName.createSNIMatcher(
+ serverAcceptableHostname);
+ Collection<SNIMatcher> matchers = new ArrayList<>(1);
+ matchers.add(matcher);
+ SSLParameters params = sslServerSocket.getSSLParameters();
+ params.setSNIMatchers(matchers);
+ sslServerSocket.setSSLParameters(params);
+
+ serverPort = sslServerSocket.getLocalPort();
+
+ /*
+ * Signal Client, we're ready for his connect.
+ */
+ serverReady = true;
+
+ SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
+ try {
+ InputStream sslIS = sslSocket.getInputStream();
+ OutputStream sslOS = sslSocket.getOutputStream();
+
+ sslIS.read();
+ sslOS.write(85);
+ sslOS.flush();
+
+ ExtendedSSLSession session =
+ (ExtendedSSLSession)sslSocket.getSession();
+ checkSNIInSession(session);
+ } finally {
+ sslSocket.close();
+ sslServerSocket.close();
+ }
+ }
+
+
+ /*
+ * Define the client side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doClientSide() throws Exception {
+
+ /*
+ * Wait for server to get started.
+ */
+ while (!serverReady) {
+ Thread.sleep(50);
+ }
+
+ SSLSocketFactory sslsf =
+ (SSLSocketFactory) SSLSocketFactory.getDefault();
+ SSLSocket sslSocket = (SSLSocket)
+ sslsf.createSocket("localhost", serverPort);
+
+ SNIHostName serverName = new SNIHostName(clientRequestedHostname);
+ List<SNIServerName> serverNames = new ArrayList<>(1);
+ serverNames.add(serverName);
+ SSLParameters params = sslSocket.getSSLParameters();
+ params.setServerNames(serverNames);
+ sslSocket.setSSLParameters(params);
+
+ try {
+ InputStream sslIS = sslSocket.getInputStream();
+ OutputStream sslOS = sslSocket.getOutputStream();
+
+ sslOS.write(280);
+ sslOS.flush();
+ sslIS.read();
+
+ ExtendedSSLSession session =
+ (ExtendedSSLSession)sslSocket.getSession();
+ checkSNIInSession(session);
+ } finally {
+ sslSocket.close();
+ }
+ }
+
+ private static String clientRequestedHostname = "www.example.com";
+ private static String serverAcceptableHostname = "www\\.example\\.com";
+
+ void checkSNIInSession(ExtendedSSLSession session) throws Exception {
+ List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+ if (sessionSNI.isEmpty()) {
+ throw new Exception(
+ "unexpected empty request server name indication");
+ }
+
+ if (sessionSNI.size() != 1) {
+ throw new Exception(
+ "unexpected request server name indication");
+ }
+
+ SNIServerName serverName = sessionSNI.get(0);
+ if (!(serverName instanceof SNIHostName)) {
+ throw new Exception(
+ "unexpected instance of request server name indication");
+ }
+
+ String hostname = ((SNIHostName)serverName).getAsciiName();
+ if (!clientRequestedHostname.equalsIgnoreCase(hostname)) {
+ throw new Exception(
+ "unexpected request server name indication value");
+ }
+ }
+
+ /*
+ * =============================================================
+ * The remainder is just support stuff
+ */
+
+ // use any free port by default
+ volatile int serverPort = 0;
+
+ volatile Exception serverException = null;
+ volatile Exception clientException = null;
+
+ public static void main(String[] args) throws Exception {
+ String keyFilename =
+ System.getProperty("test.src", ".") + "/" + pathToStores +
+ "/" + keyStoreFile;
+ String trustFilename =
+ System.getProperty("test.src", ".") + "/" + pathToStores +
+ "/" + trustStoreFile;
+
+ System.setProperty("javax.net.ssl.keyStore", keyFilename);
+ System.setProperty("javax.net.ssl.keyStorePassword", passwd);
+ System.setProperty("javax.net.ssl.trustStore", trustFilename);
+ System.setProperty("javax.net.ssl.trustStorePassword", passwd);
+
+ if (debug)
+ System.setProperty("javax.net.debug", "all");
+
+ /*
+ * Start the tests.
+ */
+ new SSLSocketConsistentSNI();
+ }
+
+ Thread clientThread = null;
+ Thread serverThread = null;
+
+ /*
+ * Primary constructor, used to drive remainder of the test.
+ *
+ * Fork off the other side, then do your work.
+ */
+ SSLSocketConsistentSNI() throws Exception {
+ try {
+ if (separateServerThread) {
+ startServer(true);
+ startClient(false);
+ } else {
+ startClient(true);
+ startServer(false);
+ }
+ } catch (Exception e) {
+ // swallow for now. Show later
+ }
+
+ /*
+ * Wait for other side to close down.
+ */
+ if (separateServerThread) {
+ serverThread.join();
+ } else {
+ clientThread.join();
+ }
+
+ /*
+ * When we get here, the test is pretty much over.
+ * Which side threw the error?
+ */
+ Exception local;
+ Exception remote;
+ String whichRemote;
+
+ if (separateServerThread) {
+ remote = serverException;
+ local = clientException;
+ whichRemote = "server";
+ } else {
+ remote = clientException;
+ local = serverException;
+ whichRemote = "client";
+ }
+
+ /*
+ * If both failed, return the curthread's exception, but also
+ * print the remote side Exception
+ */
+ if ((local != null) && (remote != null)) {
+ System.out.println(whichRemote + " also threw:");
+ remote.printStackTrace();
+ System.out.println();
+ throw local;
+ }
+
+ if (remote != null) {
+ throw remote;
+ }
+
+ if (local != null) {
+ throw local;
+ }
+ }
+
+ void startServer(boolean newThread) throws Exception {
+ if (newThread) {
+ serverThread = new Thread() {
+ public void run() {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ /*
+ * Our server thread just died.
+ *
+ * Release the client, if not active already...
+ */
+ System.err.println("Server died...");
+ serverReady = true;
+ serverException = e;
+ }
+ }
+ };
+ serverThread.start();
+ } else {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ serverException = e;
+ } finally {
+ serverReady = true;
+ }
+ }
+ }
+
+ void startClient(boolean newThread) throws Exception {
+ if (newThread) {
+ clientThread = new Thread() {
+ public void run() {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ /*
+ * Our client thread just died.
+ */
+ System.err.println("Client died...");
+ clientException = e;
+ }
+ }
+ };
+ clientThread.start();
+ } else {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ clientException = e;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/javax/net/ssl/ServerName/SSLSocketExplorer.java Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,375 @@
+/*
+ * 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.
+ */
+
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
+/**
+ * @test
+ * @bug 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @library ../../../../templates
+ * @build SSLCapabilities SSLExplorer
+ * @run main/othervm SSLSocketExplorer SSLv2Hello,SSLv3
+ * @run main/othervm SSLSocketExplorer SSLv3
+ * @run main/othervm SSLSocketExplorer TLSv1
+ * @run main/othervm SSLSocketExplorer TLSv1.1
+ * @run main/othervm SSLSocketExplorer TLSv1.2
+ */
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.net.*;
+import javax.net.ssl.*;
+
+public class SSLSocketExplorer {
+
+ /*
+ * =============================================================
+ * Set the various variables needed for the tests, then
+ * specify what tests to run on each side.
+ */
+
+ /*
+ * Should we run the client or server in a separate thread?
+ * Both sides can throw exceptions, but do you have a preference
+ * as to which side should be the main thread.
+ */
+ static boolean separateServerThread = true;
+
+ /*
+ * Where do we find the keystores?
+ */
+ static String pathToStores = "../../../../etc";
+ static String keyStoreFile = "keystore";
+ static String trustStoreFile = "truststore";
+ static String passwd = "passphrase";
+
+ /*
+ * Is the server ready to serve?
+ */
+ volatile static boolean serverReady = false;
+
+ /*
+ * Turn on SSL debugging?
+ */
+ static boolean debug = false;
+
+ /*
+ * If the client or server is doing some kind of object creation
+ * that the other side depends on, and that thread prematurely
+ * exits, you may experience a hang. The test harness will
+ * terminate all hung threads after its timeout has expired,
+ * currently 3 minutes by default, but you might try to be
+ * smart about it....
+ */
+
+ /*
+ * Define the server side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doServerSide() throws Exception {
+
+ ServerSocket serverSocket = new ServerSocket(serverPort);
+
+ // Signal Client, we're ready for his connect.
+ serverPort = serverSocket.getLocalPort();
+ serverReady = true;
+
+ Socket socket = serverSocket.accept();
+ InputStream ins = socket.getInputStream();
+
+ byte[] buffer = new byte[0xFF];
+ int position = 0;
+ SSLCapabilities capabilities = null;
+
+ // Read the header of TLS record
+ while (position < SSLExplorer.RECORD_HEADER_SIZE) {
+ int count = SSLExplorer.RECORD_HEADER_SIZE - position;
+ int n = ins.read(buffer, position, count);
+ if (n < 0) {
+ throw new Exception("unexpected end of stream!");
+ }
+ position += n;
+ }
+
+ int recordLength = SSLExplorer.getRequiredSize(buffer, 0, position);
+ if (buffer.length < recordLength) {
+ buffer = Arrays.copyOf(buffer, recordLength);
+ }
+
+ while (position < recordLength) {
+ int count = recordLength - position;
+ int n = ins.read(buffer, position, count);
+ if (n < 0) {
+ throw new Exception("unexpected end of stream!");
+ }
+ position += n;
+ }
+
+ capabilities = SSLExplorer.explore(buffer, 0, recordLength);;
+ if (capabilities != null) {
+ System.out.println("Record version: " +
+ capabilities.getRecordVersion());
+ System.out.println("Hello version: " +
+ capabilities.getHelloVersion());
+ }
+
+ SSLSocketFactory sslsf =
+ (SSLSocketFactory) SSLSocketFactory.getDefault();
+ ByteArrayInputStream bais =
+ new ByteArrayInputStream(buffer, 0, position);
+ SSLSocket sslSocket = (SSLSocket)sslsf.createSocket(socket, bais, true);
+
+ InputStream sslIS = sslSocket.getInputStream();
+ OutputStream sslOS = sslSocket.getOutputStream();
+
+ sslIS.read();
+ sslOS.write(85);
+ sslOS.flush();
+
+ ExtendedSSLSession session = (ExtendedSSLSession)sslSocket.getSession();
+ checkCapabilities(capabilities, session);
+
+ sslSocket.close();
+ serverSocket.close();
+ }
+
+
+ /*
+ * Define the client side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doClientSide() throws Exception {
+
+ /*
+ * Wait for server to get started.
+ */
+ while (!serverReady) {
+ Thread.sleep(50);
+ }
+
+ SSLSocketFactory sslsf =
+ (SSLSocketFactory) SSLSocketFactory.getDefault();
+ SSLSocket sslSocket = (SSLSocket)
+ sslsf.createSocket("localhost", serverPort);
+
+ // enable the specified TLS protocol
+ sslSocket.setEnabledProtocols(supportedProtocols);
+
+ InputStream sslIS = sslSocket.getInputStream();
+ OutputStream sslOS = sslSocket.getOutputStream();
+
+ sslOS.write(280);
+ sslOS.flush();
+ sslIS.read();
+
+ sslSocket.close();
+ }
+
+ void checkCapabilities(SSLCapabilities capabilities,
+ ExtendedSSLSession session) throws Exception {
+
+ List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+ if (!sessionSNI.equals(capabilities.getServerNames())) {
+ throw new Exception(
+ "server name indication does not match capabilities");
+ }
+ }
+
+ private static String[] supportedProtocols; // supported protocols
+
+ private static void parseArguments(String[] args) {
+ supportedProtocols = args[0].split(",");
+ }
+
+
+ /*
+ * =============================================================
+ * The remainder is just support stuff
+ */
+
+ // use any free port by default
+ volatile int serverPort = 0;
+
+ volatile Exception serverException = null;
+ volatile Exception clientException = null;
+
+ public static void main(String[] args) throws Exception {
+ String keyFilename =
+ System.getProperty("test.src", ".") + "/" + pathToStores +
+ "/" + keyStoreFile;
+ String trustFilename =
+ System.getProperty("test.src", ".") + "/" + pathToStores +
+ "/" + trustStoreFile;
+
+ System.setProperty("javax.net.ssl.keyStore", keyFilename);
+ System.setProperty("javax.net.ssl.keyStorePassword", passwd);
+ System.setProperty("javax.net.ssl.trustStore", trustFilename);
+ System.setProperty("javax.net.ssl.trustStorePassword", passwd);
+
+ if (debug)
+ System.setProperty("javax.net.debug", "all");
+
+ /*
+ * Get the customized arguments.
+ */
+ parseArguments(args);
+
+ /*
+ * Start the tests.
+ */
+ new SSLSocketExplorer();
+ }
+
+ Thread clientThread = null;
+ Thread serverThread = null;
+
+ /*
+ * Primary constructor, used to drive remainder of the test.
+ *
+ * Fork off the other side, then do your work.
+ */
+ SSLSocketExplorer() throws Exception {
+ try {
+ if (separateServerThread) {
+ startServer(true);
+ startClient(false);
+ } else {
+ startClient(true);
+ startServer(false);
+ }
+ } catch (Exception e) {
+ // swallow for now. Show later
+ }
+
+ /*
+ * Wait for other side to close down.
+ */
+ if (separateServerThread) {
+ serverThread.join();
+ } else {
+ clientThread.join();
+ }
+
+ /*
+ * When we get here, the test is pretty much over.
+ * Which side threw the error?
+ */
+ Exception local;
+ Exception remote;
+ String whichRemote;
+
+ if (separateServerThread) {
+ remote = serverException;
+ local = clientException;
+ whichRemote = "server";
+ } else {
+ remote = clientException;
+ local = serverException;
+ whichRemote = "client";
+ }
+
+ /*
+ * If both failed, return the curthread's exception, but also
+ * print the remote side Exception
+ */
+ if ((local != null) && (remote != null)) {
+ System.out.println(whichRemote + " also threw:");
+ remote.printStackTrace();
+ System.out.println();
+ throw local;
+ }
+
+ if (remote != null) {
+ throw remote;
+ }
+
+ if (local != null) {
+ throw local;
+ }
+ }
+
+ void startServer(boolean newThread) throws Exception {
+ if (newThread) {
+ serverThread = new Thread() {
+ public void run() {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ /*
+ * Our server thread just died.
+ *
+ * Release the client, if not active already...
+ */
+ System.err.println("Server died...");
+ serverReady = true;
+ serverException = e;
+ }
+ }
+ };
+ serverThread.start();
+ } else {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ serverException = e;
+ } finally {
+ serverReady = true;
+ }
+ }
+ }
+
+ void startClient(boolean newThread) throws Exception {
+ if (newThread) {
+ clientThread = new Thread() {
+ public void run() {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ /*
+ * Our client thread just died.
+ */
+ System.err.println("Client died...");
+ clientException = e;
+ }
+ }
+ };
+ clientThread.start();
+ } else {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ clientException = e;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/javax/net/ssl/ServerName/SSLSocketExplorerFailure.java Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,383 @@
+/*
+ * 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.
+ */
+
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
+/**
+ * @test
+ * @bug 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @library ../../../../templates
+ * @build SSLCapabilities SSLExplorer
+ * @run main/othervm SSLSocketExplorerFailure SSLv2Hello,SSLv3
+ * @run main/othervm SSLSocketExplorerFailure SSLv3
+ * @run main/othervm SSLSocketExplorerFailure TLSv1
+ * @run main/othervm SSLSocketExplorerFailure TLSv1.1
+ * @run main/othervm SSLSocketExplorerFailure TLSv1.2
+ */
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.net.*;
+import javax.net.ssl.*;
+
+public class SSLSocketExplorerFailure {
+
+ /*
+ * =============================================================
+ * Set the various variables needed for the tests, then
+ * specify what tests to run on each side.
+ */
+
+ /*
+ * Should we run the client or server in a separate thread?
+ * Both sides can throw exceptions, but do you have a preference
+ * as to which side should be the main thread.
+ */
+ static boolean separateServerThread = true;
+
+ /*
+ * Where do we find the keystores?
+ */
+ static String pathToStores = "../../../../etc";
+ static String keyStoreFile = "keystore";
+ static String trustStoreFile = "truststore";
+ static String passwd = "passphrase";
+
+ /*
+ * Is the server ready to serve?
+ */
+ volatile static boolean serverReady = false;
+
+ /*
+ * Turn on SSL debugging?
+ */
+ static boolean debug = false;
+
+ /*
+ * If the client or server is doing some kind of object creation
+ * that the other side depends on, and that thread prematurely
+ * exits, you may experience a hang. The test harness will
+ * terminate all hung threads after its timeout has expired,
+ * currently 3 minutes by default, but you might try to be
+ * smart about it....
+ */
+
+ /*
+ * Define the server side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doServerSide() throws Exception {
+
+ ServerSocket serverSocket = new ServerSocket(serverPort);
+
+ // Signal Client, we're ready for his connect.
+ serverPort = serverSocket.getLocalPort();
+ serverReady = true;
+
+ Socket socket = serverSocket.accept();
+ InputStream ins = socket.getInputStream();
+
+ byte[] buffer = new byte[0xFF];
+ int position = 0;
+ SSLCapabilities capabilities = null;
+ boolean failed = false;
+ try {
+ // Read the header of TLS record
+ while (position < SSLExplorer.RECORD_HEADER_SIZE) {
+ int count = SSLExplorer.RECORD_HEADER_SIZE - position;
+ int n = ins.read(buffer, position, count);
+ if (n < 0) {
+ throw new Exception("unexpected end of stream!");
+ }
+ position += n;
+ }
+
+ int recordLength = SSLExplorer.getRequiredSize(buffer, 0, position);
+ if (buffer.length < recordLength) {
+ buffer = Arrays.copyOf(buffer, recordLength);
+ }
+
+ while (position < recordLength) {
+ int count = recordLength - position;
+ int n = ins.read(buffer, position, count);
+ if (n < 0) {
+ throw new Exception("unexpected end of stream!");
+ }
+ position += n;
+ }
+
+ capabilities = SSLExplorer.explore(buffer, 0, recordLength);;
+ if (capabilities != null) {
+ System.out.println("Record version: " +
+ capabilities.getRecordVersion());
+ System.out.println("Hello version: " +
+ capabilities.getHelloVersion());
+ }
+
+ // want an I/O exception
+ throw new IOException("We just want a I/O exception");
+ } catch (Exception e) {
+ failed = true;
+ }
+
+ // off course, the above explore failed. Faile to failure handler
+ SSLContext context = SSLContext.getInstance("TLS");
+ context.init(null, null, null);
+ SSLSocketFactory sslsf = context.getSocketFactory();
+ ByteArrayInputStream bais =
+ new ByteArrayInputStream(buffer, 0, position);
+ SSLSocket sslSocket = (SSLSocket)sslsf.createSocket(socket, bais, true);
+
+ try {
+ InputStream sslIS = sslSocket.getInputStream();
+ OutputStream sslOS = sslSocket.getOutputStream();
+
+ sslIS.read();
+ if (!failed) {
+ sslOS.write(85);
+ sslOS.flush();
+ } else {
+ sslSocket.close();
+ }
+ } catch (Exception e) {
+ System.out.println("server exception " + e);
+ } finally {
+ sslSocket.close();
+ serverSocket.close();
+ }
+ }
+
+
+ /*
+ * Define the client side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doClientSide() throws Exception {
+
+ /*
+ * Wait for server to get started.
+ */
+ while (!serverReady) {
+ Thread.sleep(50);
+ }
+
+ SSLSocketFactory sslsf =
+ (SSLSocketFactory) SSLSocketFactory.getDefault();
+ SSLSocket sslSocket = (SSLSocket)
+ sslsf.createSocket("localhost", serverPort);
+
+ // enable the specified TLS protocol
+ sslSocket.setEnabledProtocols(supportedProtocols);
+
+ try {
+ InputStream sslIS = sslSocket.getInputStream();
+ OutputStream sslOS = sslSocket.getOutputStream();
+
+ sslOS.write(280);
+ sslOS.flush();
+ sslIS.read();
+ } catch (Exception e) {
+ System.out.println("client exception " + e);
+ } finally {
+ sslSocket.close();
+ }
+ }
+
+ private static String[] supportedProtocols; // supported protocols
+
+ private static void parseArguments(String[] args) {
+ supportedProtocols = args[0].split(",");
+ }
+
+
+ /*
+ * =============================================================
+ * The remainder is just support stuff
+ */
+
+ // use any free port by default
+ volatile int serverPort = 0;
+
+ volatile Exception serverException = null;
+ volatile Exception clientException = null;
+
+ public static void main(String[] args) throws Exception {
+ String keyFilename =
+ System.getProperty("test.src", ".") + "/" + pathToStores +
+ "/" + keyStoreFile;
+ String trustFilename =
+ System.getProperty("test.src", ".") + "/" + pathToStores +
+ "/" + trustStoreFile;
+
+ System.setProperty("javax.net.ssl.keyStore", keyFilename);
+ System.setProperty("javax.net.ssl.keyStorePassword", passwd);
+ System.setProperty("javax.net.ssl.trustStore", trustFilename);
+ System.setProperty("javax.net.ssl.trustStorePassword", passwd);
+
+ if (debug)
+ System.setProperty("javax.net.debug", "all");
+
+ /*
+ * Get the customized arguments.
+ */
+ parseArguments(args);
+
+ /*
+ * Start the tests.
+ */
+ new SSLSocketExplorerFailure();
+ }
+
+ Thread clientThread = null;
+ Thread serverThread = null;
+
+ /*
+ * Primary constructor, used to drive remainder of the test.
+ *
+ * Fork off the other side, then do your work.
+ */
+ SSLSocketExplorerFailure() throws Exception {
+ try {
+ if (separateServerThread) {
+ startServer(true);
+ startClient(false);
+ } else {
+ startClient(true);
+ startServer(false);
+ }
+ } catch (Exception e) {
+ // swallow for now. Show later
+ }
+
+ /*
+ * Wait for other side to close down.
+ */
+ if (separateServerThread) {
+ serverThread.join();
+ } else {
+ clientThread.join();
+ }
+
+ /*
+ * When we get here, the test is pretty much over.
+ * Which side threw the error?
+ */
+ Exception local;
+ Exception remote;
+ String whichRemote;
+
+ if (separateServerThread) {
+ remote = serverException;
+ local = clientException;
+ whichRemote = "server";
+ } else {
+ remote = clientException;
+ local = serverException;
+ whichRemote = "client";
+ }
+
+ /*
+ * If both failed, return the curthread's exception, but also
+ * print the remote side Exception
+ */
+ if ((local != null) && (remote != null)) {
+ System.out.println(whichRemote + " also threw:");
+ remote.printStackTrace();
+ System.out.println();
+ throw local;
+ }
+
+ if (remote != null) {
+ throw remote;
+ }
+
+ if (local != null) {
+ throw local;
+ }
+ }
+
+ void startServer(boolean newThread) throws Exception {
+ if (newThread) {
+ serverThread = new Thread() {
+ public void run() {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ /*
+ * Our server thread just died.
+ *
+ * Release the client, if not active already...
+ */
+ System.err.println("Server died...");
+ serverReady = true;
+ serverException = e;
+ }
+ }
+ };
+ serverThread.start();
+ } else {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ serverException = e;
+ } finally {
+ serverReady = true;
+ }
+ }
+ }
+
+ void startClient(boolean newThread) throws Exception {
+ if (newThread) {
+ clientThread = new Thread() {
+ public void run() {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ /*
+ * Our client thread just died.
+ */
+ System.err.println("Client died...");
+ clientException = e;
+ }
+ }
+ };
+ clientThread.start();
+ } else {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ clientException = e;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/javax/net/ssl/ServerName/SSLSocketExplorerMatchedSNI.java Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,435 @@
+/*
+ * 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.
+ */
+
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
+/**
+ * @test
+ * @bug 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @library ../../../../templates
+ * @build SSLCapabilities SSLExplorer
+ * @run main/othervm SSLSocketExplorerMatchedSNI www.example.com
+ * www\.example\.com
+ * @run main/othervm SSLSocketExplorerMatchedSNI www.example.com
+ * www\.example\.(com|org)
+ * @run main/othervm SSLSocketExplorerMatchedSNI example.com
+ * (.*\.)*example\.(com|org)
+ * @run main/othervm SSLSocketExplorerMatchedSNI www.example.com
+ * (.*\.)*example\.(com|org)
+ * @run main/othervm SSLSocketExplorerMatchedSNI www.us.example.com
+ * (.*\.)*example\.(com|org)
+ */
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.net.*;
+import javax.net.ssl.*;
+
+public class SSLSocketExplorerMatchedSNI {
+
+ /*
+ * =============================================================
+ * Set the various variables needed for the tests, then
+ * specify what tests to run on each side.
+ */
+
+ /*
+ * Should we run the client or server in a separate thread?
+ * Both sides can throw exceptions, but do you have a preference
+ * as to which side should be the main thread.
+ */
+ static boolean separateServerThread = false;
+
+ /*
+ * Where do we find the keystores?
+ */
+ static String pathToStores = "../../../../etc";
+ static String keyStoreFile = "keystore";
+ static String trustStoreFile = "truststore";
+ static String passwd = "passphrase";
+
+ /*
+ * Is the server ready to serve?
+ */
+ volatile static boolean serverReady = false;
+
+ /*
+ * Turn on SSL debugging?
+ */
+ static boolean debug = false;
+
+ /*
+ * If the client or server is doing some kind of object creation
+ * that the other side depends on, and that thread prematurely
+ * exits, you may experience a hang. The test harness will
+ * terminate all hung threads after its timeout has expired,
+ * currently 3 minutes by default, but you might try to be
+ * smart about it....
+ */
+
+ /*
+ * Define the server side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doServerSide() throws Exception {
+
+ ServerSocket serverSocket = new ServerSocket(serverPort);
+
+ // Signal Client, we're ready for his connect.
+ serverPort = serverSocket.getLocalPort();
+ serverReady = true;
+
+ Socket socket = serverSocket.accept();
+ InputStream ins = socket.getInputStream();
+
+ byte[] buffer = new byte[0xFF];
+ int position = 0;
+ SSLCapabilities capabilities = null;
+
+ // Read the header of TLS record
+ while (position < SSLExplorer.RECORD_HEADER_SIZE) {
+ int count = SSLExplorer.RECORD_HEADER_SIZE - position;
+ int n = ins.read(buffer, position, count);
+ if (n < 0) {
+ throw new Exception("unexpected end of stream!");
+ }
+ position += n;
+ }
+
+ int recordLength = SSLExplorer.getRequiredSize(buffer, 0, position);
+ if (buffer.length < recordLength) {
+ buffer = Arrays.copyOf(buffer, recordLength);
+ }
+
+ while (position < recordLength) {
+ int count = recordLength - position;
+ int n = ins.read(buffer, position, count);
+ if (n < 0) {
+ throw new Exception("unexpected end of stream!");
+ }
+ position += n;
+ }
+
+ capabilities = SSLExplorer.explore(buffer, 0, recordLength);;
+ if (capabilities != null) {
+ System.out.println("Record version: " +
+ capabilities.getRecordVersion());
+ System.out.println("Hello version: " +
+ capabilities.getHelloVersion());
+ }
+
+ SSLSocketFactory sslsf =
+ (SSLSocketFactory) SSLSocketFactory.getDefault();
+ ByteArrayInputStream bais =
+ new ByteArrayInputStream(buffer, 0, position);
+ SSLSocket sslSocket = (SSLSocket)sslsf.createSocket(socket, bais, true);
+
+ SNIMatcher matcher = SNIHostName.createSNIMatcher(
+ serverAcceptableHostname);
+ Collection<SNIMatcher> matchers = new ArrayList<>(1);
+ matchers.add(matcher);
+ SSLParameters params = sslSocket.getSSLParameters();
+ params.setSNIMatchers(matchers);
+ sslSocket.setSSLParameters(params);
+
+ InputStream sslIS = sslSocket.getInputStream();
+ OutputStream sslOS = sslSocket.getOutputStream();
+
+ sslIS.read();
+ sslOS.write(85);
+ sslOS.flush();
+
+ ExtendedSSLSession session = (ExtendedSSLSession)sslSocket.getSession();
+ checkCapabilities(capabilities, session);
+
+ sslSocket.close();
+ serverSocket.close();
+ }
+
+
+ /*
+ * Define the client side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doClientSide() throws Exception {
+
+ /*
+ * Wait for server to get started.
+ */
+ while (!serverReady) {
+ Thread.sleep(50);
+ }
+
+ SSLSocketFactory sslsf =
+ (SSLSocketFactory) SSLSocketFactory.getDefault();
+ SSLSocket sslSocket = (SSLSocket)
+ sslsf.createSocket("localhost", serverPort);
+
+ SNIHostName serverName = new SNIHostName(clientRequestedHostname);
+ List<SNIServerName> serverNames = new ArrayList<>(1);
+ serverNames.add(serverName);
+ SSLParameters params = sslSocket.getSSLParameters();
+ params.setServerNames(serverNames);
+ sslSocket.setSSLParameters(params);
+
+ InputStream sslIS = sslSocket.getInputStream();
+ OutputStream sslOS = sslSocket.getOutputStream();
+
+ sslOS.write(280);
+ sslOS.flush();
+ sslIS.read();
+
+ ExtendedSSLSession session = (ExtendedSSLSession)sslSocket.getSession();
+ checkSNIInSession(session);
+
+ sslSocket.close();
+ }
+
+
+ void checkCapabilities(SSLCapabilities capabilities,
+ ExtendedSSLSession session) throws Exception {
+
+ List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+ if (!sessionSNI.equals(capabilities.getServerNames())) {
+ for (SNIServerName sni : sessionSNI) {
+ System.out.println("SNI in session is " + sni);
+ }
+
+ List<SNIServerName> capaSNI = capabilities.getServerNames();
+ for (SNIServerName sni : capaSNI) {
+ System.out.println("SNI in session is " + sni);
+ }
+
+ throw new Exception(
+ "server name indication does not match capabilities");
+ }
+
+ checkSNIInSession(session);
+ }
+
+ void checkSNIInSession(ExtendedSSLSession session) throws Exception {
+ List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+ if (sessionSNI.isEmpty()) {
+ throw new Exception(
+ "unexpected empty request server name indication");
+ }
+
+ if (sessionSNI.size() != 1) {
+ throw new Exception(
+ "unexpected request server name indication");
+ }
+
+ SNIServerName serverName = sessionSNI.get(0);
+ if (!(serverName instanceof SNIHostName)) {
+ throw new Exception(
+ "unexpected instance of request server name indication");
+ }
+
+ String hostname = ((SNIHostName)serverName).getAsciiName();
+ if (!clientRequestedHostname.equalsIgnoreCase(hostname)) {
+ throw new Exception(
+ "unexpected request server name indication value");
+ }
+ }
+
+ private static String clientRequestedHostname;
+ private static String serverAcceptableHostname;
+
+ private static void parseArguments(String[] args) {
+ clientRequestedHostname = args[0];
+ serverAcceptableHostname = args[1];
+ }
+
+
+ /*
+ * =============================================================
+ * The remainder is just support stuff
+ */
+
+ // use any free port by default
+ volatile int serverPort = 0;
+
+ volatile Exception serverException = null;
+ volatile Exception clientException = null;
+
+
+ public static void main(String[] args) throws Exception {
+ String keyFilename =
+ System.getProperty("test.src", ".") + "/" + pathToStores +
+ "/" + keyStoreFile;
+ String trustFilename =
+ System.getProperty("test.src", ".") + "/" + pathToStores +
+ "/" + trustStoreFile;
+
+ System.setProperty("javax.net.ssl.keyStore", keyFilename);
+ System.setProperty("javax.net.ssl.keyStorePassword", passwd);
+ System.setProperty("javax.net.ssl.trustStore", trustFilename);
+ System.setProperty("javax.net.ssl.trustStorePassword", passwd);
+
+ if (debug)
+ System.setProperty("javax.net.debug", "all");
+
+ /*
+ * Get the customized arguments.
+ */
+ parseArguments(args);
+
+ /*
+ * Start the tests.
+ */
+ new SSLSocketExplorerMatchedSNI();
+ }
+
+ Thread clientThread = null;
+ Thread serverThread = null;
+
+ /*
+ * Primary constructor, used to drive remainder of the test.
+ *
+ * Fork off the other side, then do your work.
+ */
+ SSLSocketExplorerMatchedSNI() throws Exception {
+ try {
+ if (separateServerThread) {
+ startServer(true);
+ startClient(false);
+ } else {
+ startClient(true);
+ startServer(false);
+ }
+ } catch (Exception e) {
+ // swallow for now. Show later
+ }
+
+ /*
+ * Wait for other side to close down.
+ */
+ if (separateServerThread) {
+ serverThread.join();
+ } else {
+ clientThread.join();
+ }
+
+ /*
+ * When we get here, the test is pretty much over.
+ * Which side threw the error?
+ */
+ Exception local;
+ Exception remote;
+ String whichRemote;
+
+ if (separateServerThread) {
+ remote = serverException;
+ local = clientException;
+ whichRemote = "server";
+ } else {
+ remote = clientException;
+ local = serverException;
+ whichRemote = "client";
+ }
+
+ /*
+ * If both failed, return the curthread's exception, but also
+ * print the remote side Exception
+ */
+ if ((local != null) && (remote != null)) {
+ System.out.println(whichRemote + " also threw:");
+ remote.printStackTrace();
+ System.out.println();
+ throw local;
+ }
+
+ if (remote != null) {
+ throw remote;
+ }
+
+ if (local != null) {
+ throw local;
+ }
+ }
+
+ void startServer(boolean newThread) throws Exception {
+ if (newThread) {
+ serverThread = new Thread() {
+ public void run() {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ /*
+ * Our server thread just died.
+ *
+ * Release the client, if not active already...
+ */
+ System.err.println("Server died...");
+ serverReady = true;
+ serverException = e;
+ }
+ }
+ };
+ serverThread.start();
+ } else {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ serverException = e;
+ } finally {
+ serverReady = true;
+ }
+ }
+ }
+
+ void startClient(boolean newThread) throws Exception {
+ if (newThread) {
+ clientThread = new Thread() {
+ public void run() {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ /*
+ * Our client thread just died.
+ */
+ System.err.println("Client died...");
+ clientException = e;
+ }
+ }
+ };
+ clientThread.start();
+ } else {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ clientException = e;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/javax/net/ssl/ServerName/SSLSocketExplorerUnmatchedSNI.java Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,392 @@
+/*
+ * 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.
+ */
+
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
+/**
+ * @test
+ * @bug 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @library ../../../../templates
+ * @build SSLCapabilities SSLExplorer
+ * @run main/othervm SSLSocketExplorerUnmatchedSNI www.example.com
+ * www\.example\.org
+ */
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.net.*;
+import javax.net.ssl.*;
+
+public class SSLSocketExplorerUnmatchedSNI {
+
+ /*
+ * =============================================================
+ * Set the various variables needed for the tests, then
+ * specify what tests to run on each side.
+ */
+
+ /*
+ * Should we run the client or server in a separate thread?
+ * Both sides can throw exceptions, but do you have a preference
+ * as to which side should be the main thread.
+ */
+ static boolean separateServerThread = false;
+
+ /*
+ * Where do we find the keystores?
+ */
+ static String pathToStores = "../../../../etc";
+ static String keyStoreFile = "keystore";
+ static String trustStoreFile = "truststore";
+ static String passwd = "passphrase";
+
+ /*
+ * Is the server ready to serve?
+ */
+ volatile static boolean serverReady = false;
+
+ /*
+ * Turn on SSL debugging?
+ */
+ static boolean debug = false;
+
+ /*
+ * If the client or server is doing some kind of object creation
+ * that the other side depends on, and that thread prematurely
+ * exits, you may experience a hang. The test harness will
+ * terminate all hung threads after its timeout has expired,
+ * currently 3 minutes by default, but you might try to be
+ * smart about it....
+ */
+
+ /*
+ * Define the server side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doServerSide() throws Exception {
+
+ ServerSocket serverSocket = new ServerSocket(serverPort);
+
+ // Signal Client, we're ready for his connect.
+ serverPort = serverSocket.getLocalPort();
+ serverReady = true;
+
+ Socket socket = serverSocket.accept();
+ InputStream ins = socket.getInputStream();
+
+ byte[] buffer = new byte[0xFF];
+ int position = 0;
+ SSLCapabilities capabilities = null;
+
+ // Read the header of TLS record
+ while (position < SSLExplorer.RECORD_HEADER_SIZE) {
+ int count = SSLExplorer.RECORD_HEADER_SIZE - position;
+ int n = ins.read(buffer, position, count);
+ if (n < 0) {
+ throw new Exception("unexpected end of stream!");
+ }
+ position += n;
+ }
+
+ int recordLength = SSLExplorer.getRequiredSize(buffer, 0, position);
+ if (buffer.length < recordLength) {
+ buffer = Arrays.copyOf(buffer, recordLength);
+ }
+
+ while (position < recordLength) {
+ int count = recordLength - position;
+ int n = ins.read(buffer, position, count);
+ if (n < 0) {
+ throw new Exception("unexpected end of stream!");
+ }
+ position += n;
+ }
+
+ capabilities = SSLExplorer.explore(buffer, 0, recordLength);;
+ if (capabilities != null) {
+ System.out.println("Record version: " +
+ capabilities.getRecordVersion());
+ System.out.println("Hello version: " +
+ capabilities.getHelloVersion());
+ }
+
+ SSLSocketFactory sslsf =
+ (SSLSocketFactory) SSLSocketFactory.getDefault();
+ ByteArrayInputStream bais =
+ new ByteArrayInputStream(buffer, 0, position);
+ SSLSocket sslSocket = (SSLSocket)sslsf.createSocket(socket, bais, true);
+
+ SNIMatcher matcher = SNIHostName.createSNIMatcher(
+ serverAcceptableHostname);
+ Collection<SNIMatcher> matchers = new ArrayList<>(1);
+ matchers.add(matcher);
+ SSLParameters params = sslSocket.getSSLParameters();
+ params.setSNIMatchers(matchers);
+ sslSocket.setSSLParameters(params);
+
+ InputStream sslIS = sslSocket.getInputStream();
+ OutputStream sslOS = sslSocket.getOutputStream();
+
+ try {
+ sslIS.read();
+ sslOS.write(85);
+ sslOS.flush();
+
+ throw new Exception(
+ "Mismatched server name indication was accepted");
+ } catch (SSLHandshakeException sslhe) {
+ // the expected unrecognized server name indication exception
+ } catch (IOException ioe) {
+ // the peer may have closed the socket because of the unmatched
+ // server name indication.
+ } finally {
+ sslSocket.close();
+ serverSocket.close();
+ }
+ }
+
+
+ /*
+ * Define the client side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doClientSide() throws Exception {
+
+ /*
+ * Wait for server to get started.
+ */
+ while (!serverReady) {
+ Thread.sleep(50);
+ }
+
+ SSLSocketFactory sslsf =
+ (SSLSocketFactory) SSLSocketFactory.getDefault();
+ SSLSocket sslSocket = (SSLSocket)
+ sslsf.createSocket("localhost", serverPort);
+
+ SNIHostName serverName = new SNIHostName(clientRequestedHostname);
+ List<SNIServerName> serverNames = new ArrayList<>(1);
+ serverNames.add(serverName);
+ SSLParameters params = sslSocket.getSSLParameters();
+ params.setServerNames(serverNames);
+ sslSocket.setSSLParameters(params);
+
+ InputStream sslIS = sslSocket.getInputStream();
+ OutputStream sslOS = sslSocket.getOutputStream();
+
+ try {
+ sslOS.write(280);
+ sslOS.flush();
+ sslIS.read();
+
+ throw new Exception(
+ "Mismatched server name indication was accepted");
+ } catch (SSLHandshakeException sslhe) {
+ // the expected unrecognized server name indication exception
+ } catch (IOException ioe) {
+ // the peer may have closed the socket because of the unmatched
+ // server name indication.
+ } finally {
+ sslSocket.close();
+ }
+ }
+
+ private static String clientRequestedHostname;
+ private static String serverAcceptableHostname;
+
+ private static void parseArguments(String[] args) {
+ clientRequestedHostname = args[0];
+ serverAcceptableHostname = args[1];
+ }
+
+ /*
+ * =============================================================
+ * The remainder is just support stuff
+ */
+
+ // use any free port by default
+ volatile int serverPort = 0;
+
+ volatile Exception serverException = null;
+ volatile Exception clientException = null;
+
+ public static void main(String[] args) throws Exception {
+ String keyFilename =
+ System.getProperty("test.src", ".") + "/" + pathToStores +
+ "/" + keyStoreFile;
+ String trustFilename =
+ System.getProperty("test.src", ".") + "/" + pathToStores +
+ "/" + trustStoreFile;
+
+ System.setProperty("javax.net.ssl.keyStore", keyFilename);
+ System.setProperty("javax.net.ssl.keyStorePassword", passwd);
+ System.setProperty("javax.net.ssl.trustStore", trustFilename);
+ System.setProperty("javax.net.ssl.trustStorePassword", passwd);
+
+ if (debug)
+ System.setProperty("javax.net.debug", "all");
+
+ /*
+ * Get the customized arguments.
+ */
+ parseArguments(args);
+
+ /*
+ * Start the tests.
+ */
+ new SSLSocketExplorerUnmatchedSNI();
+ }
+
+ Thread clientThread = null;
+ Thread serverThread = null;
+
+ /*
+ * Primary constructor, used to drive remainder of the test.
+ *
+ * Fork off the other side, then do your work.
+ */
+ SSLSocketExplorerUnmatchedSNI() throws Exception {
+ try {
+ if (separateServerThread) {
+ startServer(true);
+ startClient(false);
+ } else {
+ startClient(true);
+ startServer(false);
+ }
+ } catch (Exception e) {
+ // swallow for now. Show later
+ }
+
+ /*
+ * Wait for other side to close down.
+ */
+ if (separateServerThread) {
+ serverThread.join();
+ } else {
+ clientThread.join();
+ }
+
+ /*
+ * When we get here, the test is pretty much over.
+ * Which side threw the error?
+ */
+ Exception local;
+ Exception remote;
+ String whichRemote;
+
+ if (separateServerThread) {
+ remote = serverException;
+ local = clientException;
+ whichRemote = "server";
+ } else {
+ remote = clientException;
+ local = serverException;
+ whichRemote = "client";
+ }
+
+ /*
+ * If both failed, return the curthread's exception, but also
+ * print the remote side Exception
+ */
+ if ((local != null) && (remote != null)) {
+ System.out.println(whichRemote + " also threw:");
+ remote.printStackTrace();
+ System.out.println();
+ throw local;
+ }
+
+ if (remote != null) {
+ throw remote;
+ }
+
+ if (local != null) {
+ throw local;
+ }
+ }
+
+ void startServer(boolean newThread) throws Exception {
+ if (newThread) {
+ serverThread = new Thread() {
+ public void run() {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ /*
+ * Our server thread just died.
+ *
+ * Release the client, if not active already...
+ */
+ System.err.println("Server died...");
+ serverReady = true;
+ serverException = e;
+ }
+ }
+ };
+ serverThread.start();
+ } else {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ serverException = e;
+ } finally {
+ serverReady = true;
+ }
+ }
+ }
+
+ void startClient(boolean newThread) throws Exception {
+ if (newThread) {
+ clientThread = new Thread() {
+ public void run() {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ /*
+ * Our client thread just died.
+ */
+ System.err.println("Client died...");
+ clientException = e;
+ }
+ }
+ };
+ clientThread.start();
+ } else {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ clientException = e;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/javax/net/ssl/ServerName/SSLSocketExplorerWithCliSNI.java Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,407 @@
+/*
+ * 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.
+ */
+
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
+/**
+ * @test
+ * @bug 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @library ../../../../templates
+ * @build SSLCapabilities SSLExplorer
+ * @run main/othervm SSLSocketExplorerWithCliSNI
+ */
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.net.*;
+import javax.net.ssl.*;
+
+public class SSLSocketExplorerWithCliSNI {
+
+ /*
+ * =============================================================
+ * Set the various variables needed for the tests, then
+ * specify what tests to run on each side.
+ */
+
+ /*
+ * Should we run the client or server in a separate thread?
+ * Both sides can throw exceptions, but do you have a preference
+ * as to which side should be the main thread.
+ */
+ static boolean separateServerThread = true;
+
+ /*
+ * Where do we find the keystores?
+ */
+ static String pathToStores = "../../../../etc";
+ static String keyStoreFile = "keystore";
+ static String trustStoreFile = "truststore";
+ static String passwd = "passphrase";
+
+ /*
+ * Is the server ready to serve?
+ */
+ volatile static boolean serverReady = false;
+
+ /*
+ * Turn on SSL debugging?
+ */
+ static boolean debug = false;
+
+ /*
+ * If the client or server is doing some kind of object creation
+ * that the other side depends on, and that thread prematurely
+ * exits, you may experience a hang. The test harness will
+ * terminate all hung threads after its timeout has expired,
+ * currently 3 minutes by default, but you might try to be
+ * smart about it....
+ */
+
+ /*
+ * Define the server side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doServerSide() throws Exception {
+
+ ServerSocket serverSocket = new ServerSocket(serverPort);
+
+ // Signal Client, we're ready for his connect.
+ serverPort = serverSocket.getLocalPort();
+ serverReady = true;
+
+ Socket socket = serverSocket.accept();
+ InputStream ins = socket.getInputStream();
+
+ byte[] buffer = new byte[0xFF];
+ int position = 0;
+ SSLCapabilities capabilities = null;
+
+ // Read the header of TLS record
+ while (position < SSLExplorer.RECORD_HEADER_SIZE) {
+ int count = SSLExplorer.RECORD_HEADER_SIZE - position;
+ int n = ins.read(buffer, position, count);
+ if (n < 0) {
+ throw new Exception("unexpected end of stream!");
+ }
+ position += n;
+ }
+
+ int recordLength = SSLExplorer.getRequiredSize(buffer, 0, position);
+ if (buffer.length < recordLength) {
+ buffer = Arrays.copyOf(buffer, recordLength);
+ }
+
+ while (position < recordLength) {
+ int count = recordLength - position;
+ int n = ins.read(buffer, position, count);
+ if (n < 0) {
+ throw new Exception("unexpected end of stream!");
+ }
+ position += n;
+ }
+
+ capabilities = SSLExplorer.explore(buffer, 0, recordLength);;
+ if (capabilities != null) {
+ System.out.println("Record version: " +
+ capabilities.getRecordVersion());
+ System.out.println("Hello version: " +
+ capabilities.getHelloVersion());
+ }
+
+ SSLSocketFactory sslsf =
+ (SSLSocketFactory) SSLSocketFactory.getDefault();
+ ByteArrayInputStream bais =
+ new ByteArrayInputStream(buffer, 0, position);
+ SSLSocket sslSocket = (SSLSocket)sslsf.createSocket(socket, bais, true);
+
+ InputStream sslIS = sslSocket.getInputStream();
+ OutputStream sslOS = sslSocket.getOutputStream();
+
+ sslIS.read();
+ sslOS.write(85);
+ sslOS.flush();
+
+ ExtendedSSLSession session = (ExtendedSSLSession)sslSocket.getSession();
+ checkCapabilities(capabilities, session);
+
+ sslSocket.close();
+ serverSocket.close();
+ }
+
+
+ /*
+ * Define the client side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doClientSide() throws Exception {
+
+ /*
+ * Wait for server to get started.
+ */
+ while (!serverReady) {
+ Thread.sleep(50);
+ }
+
+ SSLSocketFactory sslsf =
+ (SSLSocketFactory) SSLSocketFactory.getDefault();
+ SSLSocket sslSocket = (SSLSocket)
+ sslsf.createSocket("localhost", serverPort);
+
+ SNIHostName serverName = new SNIHostName(clientRequestedHostname);
+ List<SNIServerName> serverNames = new ArrayList<>(1);
+ serverNames.add(serverName);
+ SSLParameters params = sslSocket.getSSLParameters();
+ params.setServerNames(serverNames);
+ sslSocket.setSSLParameters(params);
+
+ InputStream sslIS = sslSocket.getInputStream();
+ OutputStream sslOS = sslSocket.getOutputStream();
+
+ sslOS.write(280);
+ sslOS.flush();
+ sslIS.read();
+
+ ExtendedSSLSession session = (ExtendedSSLSession)sslSocket.getSession();
+ checkSNIInSession(session);
+
+ sslSocket.close();
+ }
+
+ private static String clientRequestedHostname = "www.example.com";
+ private static String serverAcceptableHostname =
+ "www\\.example\\.(com|org)";
+
+ void checkCapabilities(SSLCapabilities capabilities,
+ ExtendedSSLSession session) throws Exception {
+
+ List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+ if (!sessionSNI.equals(capabilities.getServerNames())) {
+ for (SNIServerName sni : sessionSNI) {
+ System.out.println("SNI in session is " + sni);
+ }
+
+ List<SNIServerName> capaSNI = capabilities.getServerNames();
+ for (SNIServerName sni : capaSNI) {
+ System.out.println("SNI in session is " + sni);
+ }
+
+ throw new Exception(
+ "server name indication does not match capabilities");
+ }
+
+ checkSNIInSession(session);
+ }
+
+ void checkSNIInSession(ExtendedSSLSession session) throws Exception {
+ List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+ if (sessionSNI.isEmpty()) {
+ throw new Exception(
+ "unexpected empty request server name indication");
+ }
+
+ if (sessionSNI.size() != 1) {
+ throw new Exception(
+ "unexpected request server name indication");
+ }
+
+ SNIServerName serverName = sessionSNI.get(0);
+ if (!(serverName instanceof SNIHostName)) {
+ throw new Exception(
+ "unexpected instance of request server name indication");
+ }
+
+ String hostname = ((SNIHostName)serverName).getAsciiName();
+ if (!clientRequestedHostname.equalsIgnoreCase(hostname)) {
+ throw new Exception(
+ "unexpected request server name indication value");
+ }
+ }
+
+
+ /*
+ * =============================================================
+ * The remainder is just support stuff
+ */
+
+ // use any free port by default
+ volatile int serverPort = 0;
+
+ volatile Exception serverException = null;
+ volatile Exception clientException = null;
+
+ public static void main(String[] args) throws Exception {
+ String keyFilename =
+ System.getProperty("test.src", ".") + "/" + pathToStores +
+ "/" + keyStoreFile;
+ String trustFilename =
+ System.getProperty("test.src", ".") + "/" + pathToStores +
+ "/" + trustStoreFile;
+
+ System.setProperty("javax.net.ssl.keyStore", keyFilename);
+ System.setProperty("javax.net.ssl.keyStorePassword", passwd);
+ System.setProperty("javax.net.ssl.trustStore", trustFilename);
+ System.setProperty("javax.net.ssl.trustStorePassword", passwd);
+
+ if (debug)
+ System.setProperty("javax.net.debug", "all");
+
+ /*
+ * Start the tests.
+ */
+ new SSLSocketExplorerWithCliSNI();
+ }
+
+ Thread clientThread = null;
+ Thread serverThread = null;
+
+ /*
+ * Primary constructor, used to drive remainder of the test.
+ *
+ * Fork off the other side, then do your work.
+ */
+ SSLSocketExplorerWithCliSNI() throws Exception {
+ try {
+ if (separateServerThread) {
+ startServer(true);
+ startClient(false);
+ } else {
+ startClient(true);
+ startServer(false);
+ }
+ } catch (Exception e) {
+ // swallow for now. Show later
+ }
+
+ /*
+ * Wait for other side to close down.
+ */
+ if (separateServerThread) {
+ serverThread.join();
+ } else {
+ clientThread.join();
+ }
+
+ /*
+ * When we get here, the test is pretty much over.
+ * Which side threw the error?
+ */
+ Exception local;
+ Exception remote;
+ String whichRemote;
+
+ if (separateServerThread) {
+ remote = serverException;
+ local = clientException;
+ whichRemote = "server";
+ } else {
+ remote = clientException;
+ local = serverException;
+ whichRemote = "client";
+ }
+
+ /*
+ * If both failed, return the curthread's exception, but also
+ * print the remote side Exception
+ */
+ if ((local != null) && (remote != null)) {
+ System.out.println(whichRemote + " also threw:");
+ remote.printStackTrace();
+ System.out.println();
+ throw local;
+ }
+
+ if (remote != null) {
+ throw remote;
+ }
+
+ if (local != null) {
+ throw local;
+ }
+ }
+
+ void startServer(boolean newThread) throws Exception {
+ if (newThread) {
+ serverThread = new Thread() {
+ public void run() {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ /*
+ * Our server thread just died.
+ *
+ * Release the client, if not active already...
+ */
+ System.err.println("Server died...");
+ serverReady = true;
+ serverException = e;
+ }
+ }
+ };
+ serverThread.start();
+ } else {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ serverException = e;
+ } finally {
+ serverReady = true;
+ }
+ }
+ }
+
+ void startClient(boolean newThread) throws Exception {
+ if (newThread) {
+ clientThread = new Thread() {
+ public void run() {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ /*
+ * Our client thread just died.
+ */
+ System.err.println("Client died...");
+ clientException = e;
+ }
+ }
+ };
+ clientThread.start();
+ } else {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ clientException = e;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/javax/net/ssl/ServerName/SSLSocketExplorerWithSrvSNI.java Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,390 @@
+/*
+ * 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.
+ */
+
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
+/**
+ * @test
+ * @bug 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @library ../../../../templates
+ * @build SSLCapabilities SSLExplorer
+ * @run main/othervm SSLSocketExplorerWithSrvSNI
+ */
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.net.*;
+import javax.net.ssl.*;
+
+public class SSLSocketExplorerWithSrvSNI {
+
+ /*
+ * =============================================================
+ * Set the various variables needed for the tests, then
+ * specify what tests to run on each side.
+ */
+
+ /*
+ * Should we run the client or server in a separate thread?
+ * Both sides can throw exceptions, but do you have a preference
+ * as to which side should be the main thread.
+ */
+ static boolean separateServerThread = true;
+
+ /*
+ * Where do we find the keystores?
+ */
+ static String pathToStores = "../../../../etc";
+ static String keyStoreFile = "keystore";
+ static String trustStoreFile = "truststore";
+ static String passwd = "passphrase";
+
+ /*
+ * Is the server ready to serve?
+ */
+ volatile static boolean serverReady = false;
+
+ /*
+ * Turn on SSL debugging?
+ */
+ static boolean debug = false;
+
+ /*
+ * If the client or server is doing some kind of object creation
+ * that the other side depends on, and that thread prematurely
+ * exits, you may experience a hang. The test harness will
+ * terminate all hung threads after its timeout has expired,
+ * currently 3 minutes by default, but you might try to be
+ * smart about it....
+ */
+
+ /*
+ * Define the server side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doServerSide() throws Exception {
+
+ ServerSocket serverSocket = new ServerSocket(serverPort);
+
+ // Signal Client, we're ready for his connect.
+ serverPort = serverSocket.getLocalPort();
+ serverReady = true;
+
+ Socket socket = serverSocket.accept();
+ InputStream ins = socket.getInputStream();
+
+ byte[] buffer = new byte[0xFF];
+ int position = 0;
+ SSLCapabilities capabilities = null;
+
+ // Read the header of TLS record
+ while (position < SSLExplorer.RECORD_HEADER_SIZE) {
+ int count = SSLExplorer.RECORD_HEADER_SIZE - position;
+ int n = ins.read(buffer, position, count);
+ if (n < 0) {
+ throw new Exception("unexpected end of stream!");
+ }
+ position += n;
+ }
+
+ int recordLength = SSLExplorer.getRequiredSize(buffer, 0, position);
+ if (buffer.length < recordLength) {
+ buffer = Arrays.copyOf(buffer, recordLength);
+ }
+
+ while (position < recordLength) {
+ int count = recordLength - position;
+ int n = ins.read(buffer, position, count);
+ if (n < 0) {
+ throw new Exception("unexpected end of stream!");
+ }
+ position += n;
+ }
+
+ capabilities = SSLExplorer.explore(buffer, 0, recordLength);;
+ if (capabilities != null) {
+ System.out.println("Record version: " +
+ capabilities.getRecordVersion());
+ System.out.println("Hello version: " +
+ capabilities.getHelloVersion());
+ }
+
+ SSLSocketFactory sslsf =
+ (SSLSocketFactory) SSLSocketFactory.getDefault();
+ ByteArrayInputStream bais =
+ new ByteArrayInputStream(buffer, 0, position);
+ SSLSocket sslSocket = (SSLSocket)sslsf.createSocket(socket, bais, true);
+
+ SNIMatcher matcher = SNIHostName.createSNIMatcher(
+ serverAcceptableHostname);
+ Collection<SNIMatcher> matchers = new ArrayList<>(1);
+ matchers.add(matcher);
+ SSLParameters params = sslSocket.getSSLParameters();
+ params.setSNIMatchers(matchers);
+ sslSocket.setSSLParameters(params);
+
+ InputStream sslIS = sslSocket.getInputStream();
+ OutputStream sslOS = sslSocket.getOutputStream();
+
+ sslIS.read();
+ sslOS.write(85);
+ sslOS.flush();
+
+ ExtendedSSLSession session = (ExtendedSSLSession)sslSocket.getSession();
+ checkCapabilities(capabilities, session);
+
+ sslSocket.close();
+ serverSocket.close();
+ }
+
+
+ /*
+ * Define the client side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doClientSide() throws Exception {
+
+ /*
+ * Wait for server to get started.
+ */
+ while (!serverReady) {
+ Thread.sleep(50);
+ }
+
+ SSLSocketFactory sslsf =
+ (SSLSocketFactory) SSLSocketFactory.getDefault();
+ SSLSocket sslSocket = (SSLSocket)
+ sslsf.createSocket("localhost", serverPort);
+
+ InputStream sslIS = sslSocket.getInputStream();
+ OutputStream sslOS = sslSocket.getOutputStream();
+
+ sslOS.write(280);
+ sslOS.flush();
+ sslIS.read();
+
+ ExtendedSSLSession session = (ExtendedSSLSession)sslSocket.getSession();
+ checkSNIInSession(session);
+
+ sslSocket.close();
+ }
+
+ private static String clientRequestedHostname = "www.example.com";
+ private static String serverAcceptableHostname =
+ "www\\.example\\.(com|org)";
+
+ void checkCapabilities(SSLCapabilities capabilities,
+ ExtendedSSLSession session) throws Exception {
+
+ List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+ if (!sessionSNI.equals(capabilities.getServerNames())) {
+ for (SNIServerName sni : sessionSNI) {
+ System.out.println("SNI in session is " + sni);
+ }
+
+ List<SNIServerName> capaSNI = capabilities.getServerNames();
+ for (SNIServerName sni : capaSNI) {
+ System.out.println("SNI in session is " + sni);
+ }
+
+ throw new Exception(
+ "server name indication does not match capabilities");
+ }
+
+ checkSNIInSession(session);
+ }
+
+ void checkSNIInSession(ExtendedSSLSession session) throws Exception {
+ List<SNIServerName> sessionSNI = session.getRequestedServerNames();
+ if (!sessionSNI.isEmpty()) {
+ throw new Exception(
+ "should be empty request server name indication");
+ }
+ }
+
+ /*
+ * =============================================================
+ * The remainder is just support stuff
+ */
+
+ // use any free port by default
+ volatile int serverPort = 0;
+
+ volatile Exception serverException = null;
+ volatile Exception clientException = null;
+
+ public static void main(String[] args) throws Exception {
+ String keyFilename =
+ System.getProperty("test.src", ".") + "/" + pathToStores +
+ "/" + keyStoreFile;
+ String trustFilename =
+ System.getProperty("test.src", ".") + "/" + pathToStores +
+ "/" + trustStoreFile;
+
+ System.setProperty("javax.net.ssl.keyStore", keyFilename);
+ System.setProperty("javax.net.ssl.keyStorePassword", passwd);
+ System.setProperty("javax.net.ssl.trustStore", trustFilename);
+ System.setProperty("javax.net.ssl.trustStorePassword", passwd);
+
+ if (debug)
+ System.setProperty("javax.net.debug", "all");
+
+ /*
+ * Start the tests.
+ */
+ new SSLSocketExplorerWithSrvSNI();
+ }
+
+ Thread clientThread = null;
+ Thread serverThread = null;
+
+ /*
+ * Primary constructor, used to drive remainder of the test.
+ *
+ * Fork off the other side, then do your work.
+ */
+ SSLSocketExplorerWithSrvSNI() throws Exception {
+ try {
+ if (separateServerThread) {
+ startServer(true);
+ startClient(false);
+ } else {
+ startClient(true);
+ startServer(false);
+ }
+ } catch (Exception e) {
+ // swallow for now. Show later
+ }
+
+ /*
+ * Wait for other side to close down.
+ */
+ if (separateServerThread) {
+ serverThread.join();
+ } else {
+ clientThread.join();
+ }
+
+ /*
+ * When we get here, the test is pretty much over.
+ * Which side threw the error?
+ */
+ Exception local;
+ Exception remote;
+ String whichRemote;
+
+ if (separateServerThread) {
+ remote = serverException;
+ local = clientException;
+ whichRemote = "server";
+ } else {
+ remote = clientException;
+ local = serverException;
+ whichRemote = "client";
+ }
+
+ /*
+ * If both failed, return the curthread's exception, but also
+ * print the remote side Exception
+ */
+ if ((local != null) && (remote != null)) {
+ System.out.println(whichRemote + " also threw:");
+ remote.printStackTrace();
+ System.out.println();
+ throw local;
+ }
+
+ if (remote != null) {
+ throw remote;
+ }
+
+ if (local != null) {
+ throw local;
+ }
+ }
+
+ void startServer(boolean newThread) throws Exception {
+ if (newThread) {
+ serverThread = new Thread() {
+ public void run() {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ /*
+ * Our server thread just died.
+ *
+ * Release the client, if not active already...
+ */
+ System.err.println("Server died...");
+ serverReady = true;
+ serverException = e;
+ }
+ }
+ };
+ serverThread.start();
+ } else {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ serverException = e;
+ } finally {
+ serverReady = true;
+ }
+ }
+ }
+
+ void startClient(boolean newThread) throws Exception {
+ if (newThread) {
+ clientThread = new Thread() {
+ public void run() {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ /*
+ * Our client thread just died.
+ */
+ System.err.println("Client died...");
+ clientException = e;
+ }
+ }
+ };
+ clientThread.start();
+ } else {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ clientException = e;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/javax/net/ssl/ServerName/SSLSocketInconsistentSNI.java Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,340 @@
+/*
+ * 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.
+ */
+
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
+/**
+ * @test
+ * @bug 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @run main/othervm SSLSocketInconsistentSNI
+ */
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.net.*;
+import javax.net.ssl.*;
+
+public class SSLSocketInconsistentSNI {
+
+ /*
+ * =============================================================
+ * Set the various variables needed for the tests, then
+ * specify what tests to run on each side.
+ */
+
+ /*
+ * Should we run the client or server in a separate thread?
+ * Both sides can throw exceptions, but do you have a preference
+ * as to which side should be the main thread.
+ */
+ static boolean separateServerThread = true;
+
+ /*
+ * Where do we find the keystores?
+ */
+ static String pathToStores = "../../../../etc";
+ static String keyStoreFile = "keystore";
+ static String trustStoreFile = "truststore";
+ static String passwd = "passphrase";
+
+ /*
+ * Is the server ready to serve?
+ */
+ volatile static boolean serverReady = false;
+
+ /*
+ * Turn on SSL debugging?
+ */
+ static boolean debug = false;
+
+ /*
+ * If the client or server is doing some kind of object creation
+ * that the other side depends on, and that thread prematurely
+ * exits, you may experience a hang. The test harness will
+ * terminate all hung threads after its timeout has expired,
+ * currently 3 minutes by default, but you might try to be
+ * smart about it....
+ */
+
+ /*
+ * Define the server side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doServerSide() throws Exception {
+ SSLServerSocketFactory sslssf =
+ (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
+ SSLServerSocket sslServerSocket =
+ (SSLServerSocket) sslssf.createServerSocket(serverPort);
+
+ SNIMatcher matcher = SNIHostName.createSNIMatcher(
+ serverAcceptableHostname);
+ Collection<SNIMatcher> matchers = new ArrayList<>(1);
+ matchers.add(matcher);
+ SSLParameters params = sslServerSocket.getSSLParameters();
+ params.setSNIMatchers(matchers);
+ sslServerSocket.setSSLParameters(params);
+
+ serverPort = sslServerSocket.getLocalPort();
+
+ /*
+ * Signal Client, we're ready for his connect.
+ */
+ serverReady = true;
+
+ SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
+ try {
+ InputStream sslIS = sslSocket.getInputStream();
+ OutputStream sslOS = sslSocket.getOutputStream();
+
+ sslIS.read();
+ sslOS.write(85);
+ sslOS.flush();
+
+ throw new Exception(
+ "Mismatched server name indication was accepted");
+ } catch (SSLHandshakeException sslhe) {
+ // the expected unrecognized server name indication exception
+ } catch (IOException ioe) {
+ // the peer may have closed the socket because of the unmatched
+ // server name indication.
+ } finally {
+ sslSocket.close();
+ sslServerSocket.close();
+ }
+ }
+
+
+ /*
+ * Define the client side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doClientSide() throws Exception {
+
+ /*
+ * Wait for server to get started.
+ */
+ while (!serverReady) {
+ Thread.sleep(50);
+ }
+
+ SSLSocketFactory sslsf =
+ (SSLSocketFactory) SSLSocketFactory.getDefault();
+ SSLSocket sslSocket = (SSLSocket)
+ sslsf.createSocket("localhost", serverPort);
+
+ SNIHostName serverName = new SNIHostName(clientRequestedHostname);
+ List<SNIServerName> serverNames = new ArrayList<>(1);
+ serverNames.add(serverName);
+ SSLParameters params = sslSocket.getSSLParameters();
+ params.setServerNames(serverNames);
+ sslSocket.setSSLParameters(params);
+
+ try {
+ InputStream sslIS = sslSocket.getInputStream();
+ OutputStream sslOS = sslSocket.getOutputStream();
+
+ sslOS.write(280);
+ sslOS.flush();
+ sslIS.read();
+
+ throw new Exception(
+ "Mismatched server name indication was accepted");
+ } catch (SSLHandshakeException sslhe) {
+ // the expected unrecognized server name indication exception
+ } catch (IOException ioe) {
+ // the peer may have closed the socket because of the unmatched
+ // server name indication.
+ } finally {
+ sslSocket.close();
+ }
+ }
+
+ private static String clientRequestedHostname = "www.example.com";
+ private static String serverAcceptableHostname = "www\\.example\\.org";
+
+ /*
+ * =============================================================
+ * The remainder is just support stuff
+ */
+
+ // use any free port by default
+ volatile int serverPort = 0;
+
+ volatile Exception serverException = null;
+ volatile Exception clientException = null;
+
+ public static void main(String[] args) throws Exception {
+ String keyFilename =
+ System.getProperty("test.src", ".") + "/" + pathToStores +
+ "/" + keyStoreFile;
+ String trustFilename =
+ System.getProperty("test.src", ".") + "/" + pathToStores +
+ "/" + trustStoreFile;
+
+ System.setProperty("javax.net.ssl.keyStore", keyFilename);
+ System.setProperty("javax.net.ssl.keyStorePassword", passwd);
+ System.setProperty("javax.net.ssl.trustStore", trustFilename);
+ System.setProperty("javax.net.ssl.trustStorePassword", passwd);
+
+ if (debug)
+ System.setProperty("javax.net.debug", "all");
+
+ /*
+ * Start the tests.
+ */
+ new SSLSocketInconsistentSNI();
+ }
+
+ Thread clientThread = null;
+ Thread serverThread = null;
+
+ /*
+ * Primary constructor, used to drive remainder of the test.
+ *
+ * Fork off the other side, then do your work.
+ */
+ SSLSocketInconsistentSNI() throws Exception {
+ try {
+ if (separateServerThread) {
+ startServer(true);
+ startClient(false);
+ } else {
+ startClient(true);
+ startServer(false);
+ }
+ } catch (Exception e) {
+ // swallow for now. Show later
+ }
+
+ /*
+ * Wait for other side to close down.
+ */
+ if (separateServerThread) {
+ serverThread.join();
+ } else {
+ clientThread.join();
+ }
+
+ /*
+ * When we get here, the test is pretty much over.
+ * Which side threw the error?
+ */
+ Exception local;
+ Exception remote;
+ String whichRemote;
+
+ if (separateServerThread) {
+ remote = serverException;
+ local = clientException;
+ whichRemote = "server";
+ } else {
+ remote = clientException;
+ local = serverException;
+ whichRemote = "client";
+ }
+
+ /*
+ * If both failed, return the curthread's exception, but also
+ * print the remote side Exception
+ */
+ if ((local != null) && (remote != null)) {
+ System.out.println(whichRemote + " also threw:");
+ remote.printStackTrace();
+ System.out.println();
+ throw local;
+ }
+
+ if (remote != null) {
+ throw remote;
+ }
+
+ if (local != null) {
+ throw local;
+ }
+ }
+
+ void startServer(boolean newThread) throws Exception {
+ if (newThread) {
+ serverThread = new Thread() {
+ public void run() {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ /*
+ * Our server thread just died.
+ *
+ * Release the client, if not active already...
+ */
+ System.err.println("Server died...");
+ serverReady = true;
+ serverException = e;
+ }
+ }
+ };
+ serverThread.start();
+ } else {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ serverException = e;
+ } finally {
+ serverReady = true;
+ }
+ }
+ }
+
+ void startClient(boolean newThread) throws Exception {
+ if (newThread) {
+ clientThread = new Thread() {
+ public void run() {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ /*
+ * Our client thread just died.
+ */
+ System.err.println("Client died...");
+ clientException = e;
+ }
+ }
+ };
+ clientThread.start();
+ } else {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ clientException = e;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/javax/net/ssl/ServerName/SSLSocketSNISensitive.java Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,564 @@
+/*
+ * 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.
+ */
+
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
+/*
+ * @test
+ * @bug 7068321
+ * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
+ * @run main/othervm SSLSocketSNISensitive PKIX www.example.com
+ * @run main/othervm SSLSocketSNISensitive SunX509 www.example.com
+ * @run main/othervm SSLSocketSNISensitive PKIX www.example.net
+ * @run main/othervm SSLSocketSNISensitive SunX509 www.example.net
+ * @run main/othervm SSLSocketSNISensitive PKIX www.invalid.com
+ * @run main/othervm SSLSocketSNISensitive SunX509 www.invalid.com
+ */
+
+import java.net.*;
+import java.util.*;
+import java.io.*;
+import javax.net.ssl.*;
+import java.security.KeyStore;
+import java.security.KeyFactory;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.spec.*;
+import java.security.interfaces.*;
+import sun.misc.BASE64Decoder;
+
+
+public class SSLSocketSNISensitive {
+
+ /*
+ * =============================================================
+ * Set the various variables needed for the tests, then
+ * specify what tests to run on each side.
+ */
+
+ /*
+ * Should we run the client or server in a separate thread?
+ * Both sides can throw exceptions, but do you have a preference
+ * as to which side should be the main thread.
+ */
+ static boolean separateServerThread = false;
+
+ /*
+ * Where do we find the keystores?
+ */
+ // Certificates and key used in the test.
+ static String trustedCertStr =
+ "-----BEGIN CERTIFICATE-----\n" +
+ "MIICkjCCAfugAwIBAgIBADANBgkqhkiG9w0BAQQFADA7MQswCQYDVQQGEwJVUzEN\n" +
+ "MAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwHhcN\n" +
+ "MTIwNDE3MTIwNjA3WhcNMzMwMzI4MTIwNjA3WjA7MQswCQYDVQQGEwJVUzENMAsG\n" +
+ "A1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwgZ8wDQYJ\n" +
+ "KoZIhvcNAQEBBQADgY0AMIGJAoGBANY+7Enp+1S566kLcKk+qe4Ki6BxaHGZ+v7r\n" +
+ "vLksx9IQZCbAEf4YLbrZhKzKD3SPIJXyxPFwknAknIh3Knk8mViOZks7T8L3GnJr\n" +
+ "TBaVvDyTzDJum/QYiahfO2qpfN/Oya2UILmqsBAeLyWpzbQsAyWBXfoUtkOUgnzK\n" +
+ "fk6QAKYrAgMBAAGjgaUwgaIwHQYDVR0OBBYEFEtmQi7jT1ijXOafPsfkrLwSVu9e\n" +
+ "MGMGA1UdIwRcMFqAFEtmQi7jT1ijXOafPsfkrLwSVu9eoT+kPTA7MQswCQYDVQQG\n" +
+ "EwJVUzENMAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2\n" +
+ "Y2WCAQAwDwYDVR0TAQH/BAUwAwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQEE\n" +
+ "BQADgYEAkKWxMc4+ODk5WwLXXweB8/IKfVfrizNn0KLEgsZ6xNXFIXDpiPGAFcgl\n" +
+ "MzFO424JgyvUulsUc/X16Cnuwwntkk6KUG7vEV7h4o9sAV7Cax3gfQE/EZFb4ybn\n" +
+ "aBm1UsujMKd/ovqbbbxJbmOWzCeo0QfIGleDEyh3NBBZ0i11Kiw=\n" +
+ "-----END CERTIFICATE-----";
+
+ // web server certificate, www.example.com
+ static String targetCertStr_A =
+ "-----BEGIN CERTIFICATE-----\n" +
+ "MIICVTCCAb6gAwIBAgIBAjANBgkqhkiG9w0BAQQFADA7MQswCQYDVQQGEwJVUzEN\n" +
+ "MAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwHhcN\n" +
+ "MTIwNDE3MTIwNjA4WhcNMzIwMTAzMTIwNjA4WjBVMQswCQYDVQQGEwJVUzENMAsG\n" +
+ "A1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UxGDAWBgNV\n" +
+ "BAMTD3d3dy5leGFtcGxlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA\n" +
+ "4zFp3PZNzsd3ZwG6FNNWO9eSN+UBymlf8oCwpKJM2tIinmMWvWIXnlx/2UXIfSAq\n" +
+ "QEG3aXkAFyEiGGpQlBbqcfrESsHsiz2pnnm5dG2v/eS0Bwz1jmcuNmwnh3UQw2Vl\n" +
+ "+BLk8ukdrLjiCT8jARiHExYf1Xg+wUqQ9y8NV26hdaUCAwEAAaNPME0wCwYDVR0P\n" +
+ "BAQDAgPoMB0GA1UdDgQWBBQwtx+gqzn2w4y82brXlp7tqBYEZDAfBgNVHSMEGDAW\n" +
+ "gBRLZkIu409Yo1zmnz7H5Ky8ElbvXjANBgkqhkiG9w0BAQQFAAOBgQAJWo8B6Ud+\n" +
+ "/OU+UcZLihlfMX02OSlK2ZB7mfqpj2G3JT9yb0A+VbY3uuajmaYYIIxl3kXGz/n8\n" +
+ "M2Q/Ux/MDxG+IFKHC26Kuj4dAQgzjq2pILVPTE2QnaQTNCsgVZtTaC47SG9FRSoC\n" +
+ "qvnIvn/oTpKSqus76I1cR4joDtiV2OEuVw==\n" +
+ "-----END CERTIFICATE-----";
+
+ // Private key in the format of PKCS#8
+ static String targetPrivateKey_A =
+ "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAOMxadz2Tc7Hd2cB\n" +
+ "uhTTVjvXkjflAcppX/KAsKSiTNrSIp5jFr1iF55cf9lFyH0gKkBBt2l5ABchIhhq\n" +
+ "UJQW6nH6xErB7Is9qZ55uXRtr/3ktAcM9Y5nLjZsJ4d1EMNlZfgS5PLpHay44gk/\n" +
+ "IwEYhxMWH9V4PsFKkPcvDVduoXWlAgMBAAECgYAqX2nuIyXp3fvgA0twXOYlbRRB\n" +
+ "Rn3qAXM6qFPJsNeCrFR2k+aG1cev6nKR1FkLNTeMGnWZv06MAcr5IML8i7WXyG4C\n" +
+ "LY/C0gedn94FDKFlln+bTENwQTGjn4lKysDA+IuNpasTeMCajbic+dPByhIdTOjZ\n" +
+ "iMCyxbLfpk40zQopVQJBAPyfGmkeHB3GjdbdgujWCGKb2UxBa4O8dy3O4l2yizTn\n" +
+ "uUqMGcwGY4ciNSVvZQ7jKo4vDmkSuYib4/woPChaNfMCQQDmO0BQuSWYGNtSwV35\n" +
+ "lafZfX1dNCLKm1iNA6A12evXgvQiE9WT4mqionig0VZW16HtiY4/BkHOcos/K9Um\n" +
+ "ARQHAkA8mkaRtSF1my5nv1gqVz5Hua+VdZQ/VDUbDiiL5cszc+ulkJqXsWirAG/T\n" +
+ "fTe3LJQG7A7+8fkEZrF4yoY0AAA1AkEAotokezULj5N9iAL5SzL9wIzQYV4ggfny\n" +
+ "YATBjXXxKccakwQ+ndWZIiMUeoS4ssLialhTgucVI0fIkU2a/r/ifwJAc6e+5Pvh\n" +
+ "MghQj/U788Od/v6rgqz/NGsduZ7uilCMcWiwA73OR2MHMH/OIuoofuEPrfuV9isV\n" +
+ "xVXhgpKfP/pdOA==";
+
+ // web server certificate, www.example.net
+ static String targetCertStr_B =
+ "-----BEGIN CERTIFICATE-----\n" +
+ "MIICVTCCAb6gAwIBAgIBBDANBgkqhkiG9w0BAQQFADA7MQswCQYDVQQGEwJVUzEN\n" +
+ "MAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwHhcN\n" +
+ "MTIwNDE3MTIwNjA5WhcNMzIwMTAzMTIwNjA5WjBVMQswCQYDVQQGEwJVUzENMAsG\n" +
+ "A1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UxGDAWBgNV\n" +
+ "BAMTD3d3dy5leGFtcGxlLm5ldDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA\n" +
+ "2VlzF1fvWYczDChrUeJiLJ1M/dIShCaOTfYGiXfQGEZCAWTacUclwr+rVMnZ75/c\n" +
+ "wwg5pNdXRijxMil8DBTS1gFcIFQhosLHvzIAe6ULlg/xB+/L6KBz+NTWfo/2KF6t\n" +
+ "xatmcToNrCcwi7eUOfbzQje65Tizs56jJYem2m7Rk0ECAwEAAaNPME0wCwYDVR0P\n" +
+ "BAQDAgPoMB0GA1UdDgQWBBQT/FR0cAWcZQ7h0X79KGki34OSQjAfBgNVHSMEGDAW\n" +
+ "gBRLZkIu409Yo1zmnz7H5Ky8ElbvXjANBgkqhkiG9w0BAQQFAAOBgQB67cPIT6fz\n" +
+ "6Ws8fBpYgW2ad4ci66i1WduBD9CpGFE+jRK2feRj6hvYBXocKj0AMWUFIEB2E3hA\n" +
+ "oIjxcf1GxIpHVl9DjlhxqXbA0Ktl7/NGNRlDSLTizOTl3FB1mMTlOGvXDVmpcFhl\n" +
+ "HuoP1hYvhTsBwPx5igGNchuPtDIUzL2mXw==\n" +
+ "-----END CERTIFICATE-----";
+
+ static String targetPrivateKey_B =
+ "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBANlZcxdX71mHMwwo\n" +
+ "a1HiYiydTP3SEoQmjk32Bol30BhGQgFk2nFHJcK/q1TJ2e+f3MMIOaTXV0Yo8TIp\n" +
+ "fAwU0tYBXCBUIaLCx78yAHulC5YP8Qfvy+igc/jU1n6P9ihercWrZnE6DawnMIu3\n" +
+ "lDn280I3uuU4s7OeoyWHptpu0ZNBAgMBAAECgYEAl19H26sfhD+32rDPxZCgBShs\n" +
+ "dZ33zVe45i0Bcn4iTLWpxKTDyf7eGps4rO2DvfKdYqt40ggzvSZIjUH9JcDe8GmG\n" +
+ "d3m0ILB7pg4jsFlpyeHpTO8grPLxA1G9s3o0DoFpz/rooqgFfe/DrRDmRoOSkgfV\n" +
+ "/gseIbgJHRO/Ctyvdh0CQQD6uFd0HxhH1jl/JzvPzIH4LSnPcdEh9zsMEb6uzh75\n" +
+ "9qL+IHD5N2I/pYZTKqDFIwhJf701+LKag55AX/zrDt7rAkEA3e00AbnwanDMa6Wj\n" +
+ "+gFekUQveSVra38LiihzCkyVvQpFjbiF1rUhSNQ0dpU5/hmrYF0C6H9VXAesfkUY\n" +
+ "WhpDgwJAYjgZOop77piDycZK7isFt32p5XSHIzFBVocVFlH1XKM8UyXOXDNQL/Le\n" +
+ "XnJSrSf+NRzvuNcG0PVC56Ey6brXpQJAY4M4vcltt5zq3R5CQBmbGRJ1IyKXX3Vx\n" +
+ "bDslEqoyvri7ZYgnY5aG3UxiVgYmIf3KrgQnCLAIS6MZQumiuMxsFwJAK5pEG063\n" +
+ "9ngUof4fDMvZphqZjZR1zMKz/V/9ge0DWBINaqFgsgebNu+MyImsC8C6WKjGmV/2\n" +
+ "f1MY0D7sC2vU/Q==";
+
+ // web server certificate, www.invalid.com
+ static String targetCertStr_C =
+ "-----BEGIN CERTIFICATE-----\n" +
+ "MIICVTCCAb6gAwIBAgIBAzANBgkqhkiG9w0BAQQFADA7MQswCQYDVQQGEwJVUzEN\n" +
+ "MAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwHhcN\n" +
+ "MTIwNDE3MTIwNjA5WhcNMzIwMTAzMTIwNjA5WjBVMQswCQYDVQQGEwJVUzENMAsG\n" +
+ "A1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UxGDAWBgNV\n" +
+ "BAMTD3d3dy5pbnZhbGlkLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA\n" +
+ "q6MyQwzCr2nJ41l0frmHL0qULSyW51MhevBC+1W28i0LE/efrmpwV3LdnlQEGFak\n" +
+ "DLDwtnff3iru8dSMcA7KdWVkivsE7ZTP+qFDaWBAy7XXiSsv6yZ2Nh4jJb0YcD28\n" +
+ "45zk2nAl5Az1/PuoTi1vpQxzFZKuBm1HGgz3MEZvBvMCAwEAAaNPME0wCwYDVR0P\n" +
+ "BAQDAgPoMB0GA1UdDgQWBBRRMifrND015Nm8N6gV5X7cg1YjjjAfBgNVHSMEGDAW\n" +
+ "gBRLZkIu409Yo1zmnz7H5Ky8ElbvXjANBgkqhkiG9w0BAQQFAAOBgQBjkUO6Ri/B\n" +
+ "uDC2gDMIyL5+NTe/1dPPQYM4HhCNa/KQYvU5lzCKO9Vpa+i+nyrUNNXUu8Tkyq4Y\n" +
+ "A+aGSm6+FT/i9rFwkYUdorBtD3KfQiwTIWrVERXBkWI5iZNaVZhx0TFy4vUpf65d\n" +
+ "QtwkbHpC66fdKc2EdLXkuY9KkmtZZJJ7YA==\n" +
+ "-----END CERTIFICATE-----";
+
+ static String targetPrivateKey_C =
+ "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAKujMkMMwq9pyeNZ\n" +
+ "dH65hy9KlC0sludTIXrwQvtVtvItCxP3n65qcFdy3Z5UBBhWpAyw8LZ3394q7vHU\n" +
+ "jHAOynVlZIr7BO2Uz/qhQ2lgQMu114krL+smdjYeIyW9GHA9vOOc5NpwJeQM9fz7\n" +
+ "qE4tb6UMcxWSrgZtRxoM9zBGbwbzAgMBAAECgYASJDK40Y12Wvki1Z6xkkyOnBRj\n" +
+ "XfYpRykfxGtgA2RN3qLwHlk7Zzaul46DIKA6LlYynTUkJDF+Ww1cdDnP0lBlwcmM\n" +
+ "iD0ck3zYyYBLhQHuVbkK3SYE+ANRhM0icvvqANP2at/U4awQcPNEae/KCiecLNu3\n" +
+ "CJGqyhPDdrEAqPuJGQJBAN46pQC6l3yrcSYE2s53jSmsm2HVVOFlFXjU6k/RMTxG\n" +
+ "FfDJtGUAOQ37rPQ06ugr/gjLAmmPp+FXozaBdA32D80CQQDFuGRgv3WYqbglIcRL\n" +
+ "JRs6xlj9w1F97s/aiUenuwhIPNiUoRbV7mnNuZ/sGF0svOVE7SazRjuFX6UqL9Y9\n" +
+ "HzG/AkEA170pCI8cl4w8eUNHRB9trGKEKjMXhwVCFh7lJf2ZBcGodSzr8w2HVhrZ\n" +
+ "Ke7hiemDYffrbJ1oxmv05+o+x3r0lQJBAL6adVm2+FyFMFnLZXmzeb59O4jWY5bt\n" +
+ "Qz6/HG6bpO5OidMuP99YCHMkQQDOs/PO3Y5GuAoW6IY4n/Y9S2B80+0CQBl1/H9/\n" +
+ "0n/vrb6vW6Azds49tuS82RFAnOhtwTyBEajs08WF8rZQ3WD2RHJnH0+jjfL0anIp\n" +
+ "dQBSeNN7s7b6rRk=";
+
+ // This is a certificate for client
+ static String targetCertStr_D=
+ "-----BEGIN CERTIFICATE-----\n" +
+ "MIICVDCCAb2gAwIBAgIBBTANBgkqhkiG9w0BAQQFADA7MQswCQYDVQQGEwJVUzEN\n" +
+ "MAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwHhcN\n" +
+ "MTIwNDE3MTIwNjEwWhcNMzIwMTAzMTIwNjEwWjBUMQswCQYDVQQGEwJVUzENMAsG\n" +
+ "A1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UxFzAVBgNV\n" +
+ "BAMTDkludGVyT3AgVGVzdGVyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDo\n" +
+ "Q/KoAIAC2ljFfW2KwjnxTzi4NQJeUuk2seqKpsAY8x4O5dvixzUl6142zmljapqi\n" +
+ "bJloQVpfB+CEc5/l4h5gzGRVzkuqP1oPzDrpZ5GsvmvuHenV/TzCIgX1cLETzQVt\n" +
+ "6Rk06okoBPnw3hDJEJiEc1Rv7HCE8p/p+SaiHrskwwIDAQABo08wTTALBgNVHQ8E\n" +
+ "BAMCA+gwHQYDVR0OBBYEFPr91O33RIGfFSqza2AwQIgE4QswMB8GA1UdIwQYMBaA\n" +
+ "FEtmQi7jT1ijXOafPsfkrLwSVu9eMA0GCSqGSIb3DQEBBAUAA4GBANIDFYgAhoj3\n" +
+ "B8u1YpqeoEp2Lt9TwrYBshaIrbmBPCwCGio0JIsoov3n8BCSg5F+8MnOtPl+TjeO\n" +
+ "0Ug+7guPdCk/wg8YNxLHgSsQlpcNJDjWiErqmUPVrg5BPPQb65qMund6KTmMN0y6\n" +
+ "4EbSmxRpZO/N0/5oK4umTk0EeXKNekBj\n" +
+ "-----END CERTIFICATE-----";
+
+ static String targetPrivateKey_D =
+ "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOhD8qgAgALaWMV9\n" +
+ "bYrCOfFPOLg1Al5S6Tax6oqmwBjzHg7l2+LHNSXrXjbOaWNqmqJsmWhBWl8H4IRz\n" +
+ "n+XiHmDMZFXOS6o/Wg/MOulnkay+a+4d6dX9PMIiBfVwsRPNBW3pGTTqiSgE+fDe\n" +
+ "EMkQmIRzVG/scITyn+n5JqIeuyTDAgMBAAECgYBw37yIKp4LRONJLnhSq6sO+0n8\n" +
+ "Mz6waiiN/Q6XTQwj09pysQAYCGlqwSRrDAqpVsBJWO+Ae+oYLrLMi4hUZnwN75v3\n" +
+ "pe1nXlrD11RmPLXwBxqFxNSvAs2FgLHZEtwHI7Bn8KybT/8bGkQ8csLceInYtMDD\n" +
+ "MuTyy2KRk/pj60zIKQJBAPgebQiAH6viFQ88AwHaNvQhlUfwmSC1i6f8LVoeqaHC\n" +
+ "lnP0LJBwlyDeeEInhHrCR2ibnCB6I/Pig+49XQgabK8CQQDvpJwuGEbsOO+3rkJJ\n" +
+ "OpOw4toG0QJZdRnT6l8I6BlboQRZSfFh+lGGahvFXkxc4KdUpJ7QPtXU7HHk6Huk\n" +
+ "8RYtAkA9CW8VGj+wTuuTVdX/jKjcIa7RhbSFwWNbrcOSWdys+Gt+luCnn6rt4QyA\n" +
+ "aaxDbquWZkFgE+voQR7nap0KM0XtAkAznd0WAJymHM1lXt9gLoHJQ9N6TGKZKiPa\n" +
+ "BU1a+cMcfV4WbVrUo7oTnZ9Fr73681iXXq3mZOJh7lvJ1llreZIxAkBEnbiTgEf4\n" +
+ "tvku68jHcRbRPmdS7CBSWNEBaHLOm4pUSTcxVTKKMHw7vmM5/UYUxJ8QNKCYxn6O\n" +
+ "+vtiBwBawwzN";
+
+ static String[] serverCerts = {targetCertStr_A,
+ targetCertStr_B, targetCertStr_C};
+ static String[] serverKeys = {targetPrivateKey_A,
+ targetPrivateKey_B, targetPrivateKey_C};
+ static String[] clientCerts = {targetCertStr_D};
+ static String[] clientKeys = {targetPrivateKey_D};
+
+ static char passphrase[] = "passphrase".toCharArray();
+
+ /*
+ * Is the server ready to serve?
+ */
+ volatile static boolean serverReady = false;
+
+ /*
+ * Turn on SSL debugging?
+ */
+ static boolean debug = false;
+
+ /*
+ * Define the server side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doServerSide() throws Exception {
+ SSLContext context = generateSSLContext(false);
+ SSLServerSocketFactory sslssf = context.getServerSocketFactory();
+ SSLServerSocket sslServerSocket =
+ (SSLServerSocket)sslssf.createServerSocket(serverPort);
+ serverPort = sslServerSocket.getLocalPort();
+
+ /*
+ * Signal Client, we're ready for his connect.
+ */
+ serverReady = true;
+
+ SSLSocket sslSocket = (SSLSocket)sslServerSocket.accept();
+ try {
+ InputStream sslIS = sslSocket.getInputStream();
+ OutputStream sslOS = sslSocket.getOutputStream();
+
+ sslIS.read();
+ sslOS.write('A');
+ sslOS.flush();
+
+ SSLSession session = sslSocket.getSession();
+ checkCertificate(session.getLocalCertificates(),
+ clientRequestedHostname);
+ } finally {
+ sslSocket.close();
+ sslServerSocket.close();
+ }
+ }
+
+ /*
+ * Define the client side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doClientSide() throws Exception {
+
+ /*
+ * Wait for server to get started.
+ */
+ while (!serverReady) {
+ Thread.sleep(50);
+ }
+
+ SSLContext context = generateSSLContext(true);
+ SSLSocketFactory sslsf = context.getSocketFactory();
+
+ SSLSocket sslSocket =
+ (SSLSocket)sslsf.createSocket("localhost", serverPort);
+
+ SNIHostName serverName = new SNIHostName(clientRequestedHostname);
+ List<SNIServerName> serverNames = new ArrayList<>(1);
+ serverNames.add(serverName);
+ SSLParameters params = sslSocket.getSSLParameters();
+ params.setServerNames(serverNames);
+ sslSocket.setSSLParameters(params);
+
+ try {
+ InputStream sslIS = sslSocket.getInputStream();
+ OutputStream sslOS = sslSocket.getOutputStream();
+
+ sslOS.write('B');
+ sslOS.flush();
+ sslIS.read();
+
+ SSLSession session = sslSocket.getSession();
+ checkCertificate(session.getPeerCertificates(),
+ clientRequestedHostname);
+ } finally {
+ sslSocket.close();
+ }
+ }
+
+ private static void checkCertificate(Certificate[] certs,
+ String hostname) throws Exception {
+ if (certs != null && certs.length != 0) {
+ X509Certificate x509Cert = (X509Certificate)certs[0];
+
+ String subject = x509Cert.getSubjectX500Principal().getName();
+
+ if (!subject.contains(hostname)) {
+ throw new Exception(
+ "Not the expected certificate: " + subject);
+ }
+ }
+ }
+
+ /*
+ * =============================================================
+ * The remainder is just support stuff
+ */
+ private static String tmAlgorithm; // trust manager
+ private static String clientRequestedHostname; // server name indication
+
+ private static void parseArguments(String[] args) {
+ tmAlgorithm = args[0];
+ clientRequestedHostname = args[1];
+ }
+
+ private static SSLContext generateSSLContext(boolean isClient)
+ throws Exception {
+
+ // generate certificate from cert string
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+
+ // create a key store
+ KeyStore ks = KeyStore.getInstance("JKS");
+ ks.load(null, null);
+
+ // import the trused cert
+ ByteArrayInputStream is =
+ new ByteArrayInputStream(trustedCertStr.getBytes());
+ Certificate trusedCert = cf.generateCertificate(is);
+ is.close();
+
+ ks.setCertificateEntry("RSA Export Signer", trusedCert);
+
+ String[] certStrs = null;
+ String[] keyStrs = null;
+ if (isClient) {
+ certStrs = clientCerts;
+ keyStrs = clientKeys;
+ } else {
+ certStrs = serverCerts;
+ keyStrs = serverKeys;
+ }
+
+ for (int i = 0; i < certStrs.length; i++) {
+ // generate the private key.
+ String keySpecStr = keyStrs[i];
+ PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec(
+ new BASE64Decoder().decodeBuffer(keySpecStr));
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ RSAPrivateKey priKey =
+ (RSAPrivateKey)kf.generatePrivate(priKeySpec);
+
+ // generate certificate chain
+ String keyCertStr = certStrs[i];
+ is = new ByteArrayInputStream(keyCertStr.getBytes());
+ Certificate keyCert = cf.generateCertificate(is);
+ is.close();
+
+ Certificate[] chain = new Certificate[2];
+ chain[0] = keyCert;
+ chain[1] = trusedCert;
+
+ // import the key entry.
+ ks.setKeyEntry("key-entry-" + i, priKey, passphrase, chain);
+ }
+
+ // create SSL context
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmAlgorithm);
+ tmf.init(ks);
+
+ SSLContext ctx = SSLContext.getInstance("TLS");
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("NewSunX509");
+ kmf.init(ks, passphrase);
+
+ ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+ ks = null;
+
+ return ctx;
+ }
+
+ // use any free port by default
+ volatile int serverPort = 0;
+
+ volatile Exception serverException = null;
+ volatile Exception clientException = null;
+
+ public static void main(String[] args) throws Exception {
+ if (debug)
+ System.setProperty("javax.net.debug", "all");
+
+ /*
+ * Get the customized arguments.
+ */
+ parseArguments(args);
+
+ /*
+ * Start the tests.
+ */
+ new SSLSocketSNISensitive();
+ }
+
+ Thread clientThread = null;
+ Thread serverThread = null;
+
+ /*
+ * Primary constructor, used to drive remainder of the test.
+ *
+ * Fork off the other side, then do your work.
+ */
+ SSLSocketSNISensitive() throws Exception {
+ try {
+ if (separateServerThread) {
+ startServer(true);
+ startClient(false);
+ } else {
+ startClient(true);
+ startServer(false);
+ }
+ } catch (Exception e) {
+ // swallow for now. Show later
+ }
+
+ /*
+ * Wait for other side to close down.
+ */
+ if (separateServerThread) {
+ serverThread.join();
+ } else {
+ clientThread.join();
+ }
+
+ /*
+ * When we get here, the test is pretty much over.
+ * Which side threw the error?
+ */
+ Exception local;
+ Exception remote;
+ String whichRemote;
+
+ if (separateServerThread) {
+ remote = serverException;
+ local = clientException;
+ whichRemote = "server";
+ } else {
+ remote = clientException;
+ local = serverException;
+ whichRemote = "client";
+ }
+
+ /*
+ * If both failed, return the curthread's exception, but also
+ * print the remote side Exception
+ */
+ if ((local != null) && (remote != null)) {
+ System.out.println(whichRemote + " also threw:");
+ remote.printStackTrace();
+ System.out.println();
+ throw local;
+ }
+
+ if (remote != null) {
+ throw remote;
+ }
+
+ if (local != null) {
+ throw local;
+ }
+ }
+
+ void startServer(boolean newThread) throws Exception {
+ if (newThread) {
+ serverThread = new Thread() {
+ public void run() {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ /*
+ * Our server thread just died.
+ *
+ * Release the client, if not active already...
+ */
+ System.err.println("Server died...");
+ serverReady = true;
+ serverException = e;
+ }
+ }
+ };
+ serverThread.start();
+ } else {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ serverException = e;
+ } finally {
+ serverReady = true;
+ }
+ }
+ }
+
+ void startClient(boolean newThread) throws Exception {
+ if (newThread) {
+ clientThread = new Thread() {
+ public void run() {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ /*
+ * Our client thread just died.
+ */
+ System.err.println("Client died...");
+ clientException = e;
+ }
+ }
+ };
+ clientThread.start();
+ } else {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ clientException = e;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/templates/SSLCapabilities.java Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+import java.util.List;
+import javax.net.ssl.SNIServerName;
+
+/**
+ * Encapsulates the security capabilities of an SSL/TLS connection.
+ * <P>
+ * The security capabilities are the list of ciphersuites to be accepted in
+ * an SSL/TLS handshake, the record version, the hello version, and server
+ * name indication, etc., of an SSL/TLS connection.
+ * <P>
+ * <code>SSLCapabilities</code> can be retrieved by exploring the network
+ * data of an SSL/TLS connection via {@link SSLExplorer#explore(ByteBuffer)}
+ * or {@link SSLExplorer#explore(byte[], int, int)}.
+ *
+ * @see SSLExplorer
+ */
+public abstract class SSLCapabilities {
+
+ /**
+ * Returns the record version of an SSL/TLS connection
+ *
+ * @return a non-null record version
+ */
+ public abstract String getRecordVersion();
+
+ /**
+ * Returns the hello version of an SSL/TLS connection
+ *
+ * @return a non-null hello version
+ */
+ public abstract String getHelloVersion();
+
+ /**
+ * Returns a <code>List</code> containing all {@link SNIServerName}s
+ * of the server name indication.
+ *
+ * @return a non-null immutable list of {@link SNIServerName}s
+ * of the server name indication parameter, may be empty
+ * if no server name indication.
+ *
+ * @see SNIServerName
+ */
+ public abstract List<SNIServerName> getServerNames();
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/templates/SSLExplorer.java Thu Oct 18 01:14:00 2012 -0700
@@ -0,0 +1,615 @@
+/*
+ * 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.
+ */
+
+import java.nio.ByteBuffer;
+import java.nio.BufferUnderflowException;
+import java.io.IOException;
+import javax.net.ssl.*;
+import java.util.*;
+
+import sun.misc.HexDumpEncoder;
+
+/**
+ * Instances of this class acts as an explorer of the network data of an
+ * SSL/TLS connection.
+ */
+public final class SSLExplorer {
+
+ // Private constructor prevents construction outside this class.
+ private SSLExplorer() {
+ }
+
+ /**
+ * The header size of TLS/SSL records.
+ * <P>
+ * The value of this constant is {@value}.
+ */
+ public final static int RECORD_HEADER_SIZE = 0x05;
+
+ /**
+ * Returns the required number of bytes in the {@code source}
+ * {@link ByteBuffer} necessary to explore SSL/TLS connection.
+ * <P>
+ * This method tries to parse as few bytes as possible from
+ * {@code source} byte buffer to get the length of an
+ * SSL/TLS record.
+ * <P>
+ * This method accesses the {@code source} parameter in read-only
+ * mode, and does not update the buffer's properties such as capacity,
+ * limit, position, and mark values.
+ *
+ * @param source
+ * a {@link ByteBuffer} containing
+ * inbound or outbound network data for an SSL/TLS connection.
+ * @throws BufferUnderflowException if less than {@code RECORD_HEADER_SIZE}
+ * bytes remaining in {@code source}
+ * @return the required size in byte to explore an SSL/TLS connection
+ */
+ public final static int getRequiredSize(ByteBuffer source) {
+
+ ByteBuffer input = source.duplicate();
+
+ // Do we have a complete header?
+ if (input.remaining() < RECORD_HEADER_SIZE) {
+ throw new BufferUnderflowException();
+ }
+
+ // Is it a handshake message?
+ byte firstByte = input.get();
+ byte secondByte = input.get();
+ byte thirdByte = input.get();
+ if ((firstByte & 0x80) != 0 && thirdByte == 0x01) {
+ // looks like a V2ClientHello
+ // return (((firstByte & 0x7F) << 8) | (secondByte & 0xFF)) + 2;
+ return RECORD_HEADER_SIZE; // Only need the header fields
+ } else {
+ return (((input.get() & 0xFF) << 8) | (input.get() & 0xFF)) + 5;
+ }
+ }
+
+ /**
+ * Returns the required number of bytes in the {@code source} byte array
+ * necessary to explore SSL/TLS connection.
+ * <P>
+ * This method tries to parse as few bytes as possible from
+ * {@code source} byte array to get the length of an
+ * SSL/TLS record.
+ *
+ * @param source
+ * a byte array containing inbound or outbound network data for
+ * an SSL/TLS connection.
+ * @param offset
+ * the start offset in array {@code source} at which the
+ * network data is read from.
+ * @param length
+ * the maximum number of bytes to read.
+ *
+ * @throws BufferUnderflowException if less than {@code RECORD_HEADER_SIZE}
+ * bytes remaining in {@code source}
+ * @return the required size in byte to explore an SSL/TLS connection
+ */
+ public final static int getRequiredSize(byte[] source,
+ int offset, int length) throws IOException {
+
+ ByteBuffer byteBuffer =
+ ByteBuffer.wrap(source, offset, length).asReadOnlyBuffer();
+ return getRequiredSize(byteBuffer);
+ }
+
+ /**
+ * Launch and explore the security capabilities from byte buffer.
+ * <P>
+ * This method tries to parse as few records as possible from
+ * {@code source} byte buffer to get the {@link SSLCapabilities}
+ * of an SSL/TLS connection.
+ * <P>
+ * Please NOTE that this method must be called before any handshaking
+ * occurs. The behavior of this method is not defined in this release
+ * if the handshake has begun, or has completed.
+ * <P>
+ * This method accesses the {@code source} parameter in read-only
+ * mode, and does not update the buffer's properties such as capacity,
+ * limit, position, and mark values.
+ *
+ * @param source
+ * a {@link ByteBuffer} containing
+ * inbound or outbound network data for an SSL/TLS connection.
+ *
+ * @throws IOException on network data error
+ * @throws BufferUnderflowException if not enough source bytes available
+ * to make a complete exploration.
+ *
+ * @return the explored {@link SSLCapabilities} of the SSL/TLS
+ * connection
+ */
+ public final static SSLCapabilities explore(ByteBuffer source)
+ throws IOException {
+
+ ByteBuffer input = source.duplicate();
+
+ // Do we have a complete header?
+ if (input.remaining() < RECORD_HEADER_SIZE) {
+ throw new BufferUnderflowException();
+ }
+
+ // Is it a handshake message?
+ byte firstByte = input.get();
+ byte secondByte = input.get();
+ byte thirdByte = input.get();
+ if ((firstByte & 0x80) != 0 && thirdByte == 0x01) {
+ // looks like a V2ClientHello
+ return exploreV2HelloRecord(input,
+ firstByte, secondByte, thirdByte);
+ } else if (firstByte == 22) { // 22: handshake record
+ return exploreTLSRecord(input,
+ firstByte, secondByte, thirdByte);
+ } else {
+ throw new SSLException("Not handshake record");
+ }
+ }
+
+ /**
+ * Launch and explore the security capabilities from byte array.
+ * <P>
+ * Please NOTE that this method must be called before any handshaking
+ * occurs. The behavior of this method is not defined in this release
+ * if the handshake has begun, or has completed. Once handshake has
+ * begun, or has completed, the security capabilities can not and
+ * should not be launched with this method.
+ *
+ * @param source
+ * a byte array containing inbound or outbound network data for
+ * an SSL/TLS connection.
+ * @param offset
+ * the start offset in array {@code source} at which the
+ * network data is read from.
+ * @param length
+ * the maximum number of bytes to read.
+ *
+ * @throws IOException on network data error
+ * @throws BufferUnderflowException if not enough source bytes available
+ * to make a complete exploration.
+ * @return the explored {@link SSLCapabilities} of the SSL/TLS
+ * connection
+ *
+ * @see #explore(ByteBuffer)
+ */
+ public final static SSLCapabilities explore(byte[] source,
+ int offset, int length) throws IOException {
+ ByteBuffer byteBuffer =
+ ByteBuffer.wrap(source, offset, length).asReadOnlyBuffer();
+ return explore(byteBuffer);
+ }
+
+ /*
+ * uint8 V2CipherSpec[3];
+ * struct {
+ * uint16 msg_length; // The highest bit MUST be 1;
+ * // the remaining bits contain the length
+ * // of the following data in bytes.
+ * uint8 msg_type; // MUST be 1
+ * Version version;
+ * uint16 cipher_spec_length; // It cannot be zero and MUST be a
+ * // multiple of the V2CipherSpec length.
+ * uint16 session_id_length; // This field MUST be empty.
+ * uint16 challenge_length; // SHOULD use a 32-byte challenge
+ * V2CipherSpec cipher_specs[V2ClientHello.cipher_spec_length];
+ * opaque session_id[V2ClientHello.session_id_length];
+ * opaque challenge[V2ClientHello.challenge_length;
+ * } V2ClientHello;
+ */
+ private static SSLCapabilities exploreV2HelloRecord(
+ ByteBuffer input, byte firstByte, byte secondByte,
+ byte thirdByte) throws IOException {
+
+ // We only need the header. We have already had enough source bytes.
+ // int recordLength = (firstByte & 0x7F) << 8) | (secondByte & 0xFF);
+ try {
+ // Is it a V2ClientHello?
+ if (thirdByte != 0x01) {
+ throw new SSLException(
+ "Unsupported or Unrecognized SSL record");
+ }
+
+ // What's the hello version?
+ byte helloVersionMajor = input.get();
+ byte helloVersionMinor = input.get();
+
+ // 0x00: major version of SSLv20
+ // 0x02: minor version of SSLv20
+ //
+ // SNIServerName is an extension, SSLv20 doesn't support extension.
+ return new SSLCapabilitiesImpl((byte)0x00, (byte)0x02,
+ helloVersionMajor, helloVersionMinor,
+ Collections.<SNIServerName>emptyList());
+ } catch (BufferUnderflowException bufe) {
+ throw new SSLProtocolException(
+ "Invalid handshake record");
+ }
+ }
+
+ /*
+ * struct {
+ * uint8 major;
+ * uint8 minor;
+ * } ProtocolVersion;
+ *
+ * enum {
+ * change_cipher_spec(20), alert(21), handshake(22),
+ * application_data(23), (255)
+ * } ContentType;
+ *
+ * struct {
+ * ContentType type;
+ * ProtocolVersion version;
+ * uint16 length;
+ * opaque fragment[TLSPlaintext.length];
+ * } TLSPlaintext;
+ */
+ private static SSLCapabilities exploreTLSRecord(
+ ByteBuffer input, byte firstByte, byte secondByte,
+ byte thirdByte) throws IOException {
+
+ // Is it a handshake message?
+ if (firstByte != 22) { // 22: handshake record
+ throw new SSLException("Not handshake record");
+ }
+
+ // We need the record version to construct SSLCapabilities.
+ byte recordMajorVersion = secondByte;
+ byte recordMinorVersion = thirdByte;
+
+ // Is there enough data for a full record?
+ int recordLength = getInt16(input);
+ if (recordLength > input.remaining()) {
+ throw new BufferUnderflowException();
+ }
+
+ // We have already had enough source bytes.
+ try {
+ return exploreHandshake(input,
+ recordMajorVersion, recordMinorVersion, recordLength);
+ } catch (BufferUnderflowException bufe) {
+ throw new SSLProtocolException(
+ "Invalid handshake record");
+ }
+ }
+
+ /*
+ * enum {
+ * hello_request(0), client_hello(1), server_hello(2),
+ * certificate(11), server_key_exchange (12),
+ * certificate_request(13), server_hello_done(14),
+ * certificate_verify(15), client_key_exchange(16),
+ * finished(20)
+ * (255)
+ * } HandshakeType;
+ *
+ * struct {
+ * HandshakeType msg_type;
+ * uint24 length;
+ * select (HandshakeType) {
+ * case hello_request: HelloRequest;
+ * case client_hello: ClientHello;
+ * case server_hello: ServerHello;
+ * case certificate: Certificate;
+ * case server_key_exchange: ServerKeyExchange;
+ * case certificate_request: CertificateRequest;
+ * case server_hello_done: ServerHelloDone;
+ * case certificate_verify: CertificateVerify;
+ * case client_key_exchange: ClientKeyExchange;
+ * case finished: Finished;
+ * } body;
+ * } Handshake;
+ */
+ private static SSLCapabilities exploreHandshake(
+ ByteBuffer input, byte recordMajorVersion,
+ byte recordMinorVersion, int recordLength) throws IOException {
+
+ // What is the handshake type?
+ byte handshakeType = input.get();
+ if (handshakeType != 0x01) { // 0x01: client_hello message
+ throw new IllegalStateException("Not initial handshaking");
+ }
+
+ // What is the handshake body length?
+ int handshakeLength = getInt24(input);
+
+ // Theoretically, a single handshake message might span multiple
+ // records, but in practice this does not occur.
+ if (handshakeLength > (recordLength - 4)) { // 4: handshake header size
+ throw new SSLException("Handshake message spans multiple records");
+ }
+
+ input = input.duplicate();
+ input.limit(handshakeLength + input.position());
+ return exploreClientHello(input,
+ recordMajorVersion, recordMinorVersion);
+ }
+
+ /*
+ * struct {
+ * uint32 gmt_unix_time;
+ * opaque random_bytes[28];
+ * } Random;
+ *
+ * opaque SessionID<0..32>;
+ *
+ * uint8 CipherSuite[2];
+ *
+ * enum { null(0), (255) } CompressionMethod;
+ *
+ * struct {
+ * ProtocolVersion client_version;
+ * Random random;
+ * SessionID session_id;
+ * CipherSuite cipher_suites<2..2^16-2>;
+ * CompressionMethod compression_methods<1..2^8-1>;
+ * select (extensions_present) {
+ * case false:
+ * struct {};
+ * case true:
+ * Extension extensions<0..2^16-1>;
+ * };
+ * } ClientHello;
+ */
+ private static SSLCapabilities exploreClientHello(
+ ByteBuffer input,
+ byte recordMajorVersion,
+ byte recordMinorVersion) throws IOException {
+
+ List<SNIServerName> snList = Collections.<SNIServerName>emptyList();
+
+ // client version
+ byte helloMajorVersion = input.get();
+ byte helloMinorVersion = input.get();
+
+ // ignore random
+ int position = input.position();
+ input.position(position + 32); // 32: the length of Random
+
+ // ignore session id
+ ignoreByteVector8(input);
+
+ // ignore cipher_suites
+ ignoreByteVector16(input);
+
+ // ignore compression methods
+ ignoreByteVector8(input);
+
+ if (input.remaining() > 0) {
+ snList = exploreExtensions(input);
+ }
+
+ return new SSLCapabilitiesImpl(
+ recordMajorVersion, recordMinorVersion,
+ helloMajorVersion, helloMinorVersion, snList);
+ }
+
+ /*
+ * struct {
+ * ExtensionType extension_type;
+ * opaque extension_data<0..2^16-1>;
+ * } Extension;
+ *
+ * enum {
+ * server_name(0), max_fragment_length(1),
+ * client_certificate_url(2), trusted_ca_keys(3),
+ * truncated_hmac(4), status_request(5), (65535)
+ * } ExtensionType;
+ */
+ private static List<SNIServerName> exploreExtensions(ByteBuffer input)
+ throws IOException {
+
+ int length = getInt16(input); // length of extensions
+ while (length > 0) {
+ int extType = getInt16(input); // extenson type
+ int extLen = getInt16(input); // length of extension data
+
+ if (extType == 0x00) { // 0x00: type of server name indication
+ return exploreSNIExt(input, extLen);
+ } else { // ignore other extensions
+ ignoreByteVector(input, extLen);
+ }
+
+ length -= extLen + 4;
+ }
+
+ return Collections.<SNIServerName>emptyList();
+ }
+
+ /*
+ * struct {
+ * NameType name_type;
+ * select (name_type) {
+ * case host_name: HostName;
+ * } name;
+ * } ServerName;
+ *
+ * enum {
+ * host_name(0), (255)
+ * } NameType;
+ *
+ * opaque HostName<1..2^16-1>;
+ *
+ * struct {
+ * ServerName server_name_list<1..2^16-1>
+ * } ServerNameList;
+ */
+ private static List<SNIServerName> exploreSNIExt(ByteBuffer input,
+ int extLen) throws IOException {
+
+ Map<Integer, SNIServerName> sniMap = new LinkedHashMap<>();
+
+ int remains = extLen;
+ if (extLen >= 2) { // "server_name" extension in ClientHello
+ int listLen = getInt16(input); // length of server_name_list
+ if (listLen == 0 || listLen + 2 != extLen) {
+ throw new SSLProtocolException(
+ "Invalid server name indication extension");
+ }
+
+ remains -= 2; // 0x02: the length field of server_name_list
+ while (remains > 0) {
+ int code = getInt8(input); // name_type
+ int snLen = getInt16(input); // length field of server name
+ if (snLen > remains) {
+ throw new SSLProtocolException(
+ "Not enough data to fill declared vector size");
+ }
+ byte[] encoded = new byte[snLen];
+ input.get(encoded);
+
+ SNIServerName serverName;
+ switch (code) {
+ case StandardConstants.SNI_HOST_NAME:
+ if (encoded.length == 0) {
+ throw new SSLProtocolException(
+ "Empty HostName in server name indication");
+ }
+ serverName = new SNIHostName(encoded);
+ break;
+ default:
+ serverName = new UnknownServerName(code, encoded);
+ }
+ // check for duplicated server name type
+ if (sniMap.put(serverName.getType(), serverName) != null) {
+ throw new SSLProtocolException(
+ "Duplicated server name of type " +
+ serverName.getType());
+ }
+
+ remains -= encoded.length + 3; // NameType: 1 byte
+ // HostName length: 2 bytes
+ }
+ } else if (extLen == 0) { // "server_name" extension in ServerHello
+ throw new SSLProtocolException(
+ "Not server name indication extension in client");
+ }
+
+ if (remains != 0) {
+ throw new SSLProtocolException(
+ "Invalid server name indication extension");
+ }
+
+ return Collections.<SNIServerName>unmodifiableList(
+ new ArrayList<>(sniMap.values()));
+ }
+
+ private static int getInt8(ByteBuffer input) {
+ return input.get();
+ }
+
+ private static int getInt16(ByteBuffer input) {
+ return ((input.get() & 0xFF) << 8) | (input.get() & 0xFF);
+ }
+
+ private static int getInt24(ByteBuffer input) {
+ return ((input.get() & 0xFF) << 16) | ((input.get() & 0xFF) << 8) |
+ (input.get() & 0xFF);
+ }
+
+ private static void ignoreByteVector8(ByteBuffer input) {
+ ignoreByteVector(input, getInt8(input));
+ }
+
+ private static void ignoreByteVector16(ByteBuffer input) {
+ ignoreByteVector(input, getInt16(input));
+ }
+
+ private static void ignoreByteVector24(ByteBuffer input) {
+ ignoreByteVector(input, getInt24(input));
+ }
+
+ private static void ignoreByteVector(ByteBuffer input, int length) {
+ if (length != 0) {
+ int position = input.position();
+ input.position(position + length);
+ }
+ }
+
+ private static class UnknownServerName extends SNIServerName {
+ UnknownServerName(int code, byte[] encoded) {
+ super(code, encoded);
+ }
+ }
+
+ private static final class SSLCapabilitiesImpl extends SSLCapabilities {
+ private final static Map<Integer, String> versionMap = new HashMap<>(5);
+
+ private final String recordVersion;
+ private final String helloVersion;
+ List<SNIServerName> sniNames;
+
+ static {
+ versionMap.put(0x0002, "SSLv2Hello");
+ versionMap.put(0x0300, "SSLv3");
+ versionMap.put(0x0301, "TLSv1");
+ versionMap.put(0x0302, "TLSv1.1");
+ versionMap.put(0x0303, "TLSv1.2");
+ }
+
+ SSLCapabilitiesImpl(byte recordMajorVersion, byte recordMinorVersion,
+ byte helloMajorVersion, byte helloMinorVersion,
+ List<SNIServerName> sniNames) {
+
+ int version = (recordMajorVersion << 8) | recordMinorVersion;
+ this.recordVersion = versionMap.get(version) != null ?
+ versionMap.get(version) :
+ unknownVersion(recordMajorVersion, recordMinorVersion);
+
+ version = (helloMajorVersion << 8) | helloMinorVersion;
+ this.helloVersion = versionMap.get(version) != null ?
+ versionMap.get(version) :
+ unknownVersion(helloMajorVersion, helloMinorVersion);
+
+ this.sniNames = sniNames;
+ }
+
+ @Override
+ public String getRecordVersion() {
+ return recordVersion;
+ }
+
+ @Override
+ public String getHelloVersion() {
+ return helloVersion;
+ }
+
+ @Override
+ public List<SNIServerName> getServerNames() {
+ if (!sniNames.isEmpty()) {
+ return Collections.<SNIServerName>unmodifiableList(sniNames);
+ }
+
+ return sniNames;
+ }
+
+ private static String unknownVersion(byte major, byte minor) {
+ return "Unknown-" + ((int)major) + "." + ((int)minor);
+ }
+ }
+}
+