7068321: Support TLS Server Name Indication (SNI) Extension in JSSE Server

Reviewed-by: mullan, weijun, wetmore
This commit is contained in:
Xue-Lei Andrew Fan 2012-10-18 01:14:00 -07:00
parent 1fb9910146
commit 1892ebf911
46 changed files with 8169 additions and 288 deletions

View File

@ -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 @@ public abstract class ExtendedSSLSession implements SSLSession {
* @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();
}
}

View File

@ -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 @@ public class HandshakeCompletedEvent extends EventObject
// 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 @@ public class HandshakeCompletedEvent extends EventObject
// return the X500Principal of the end-entity cert.
Certificate[] certs = getLocalCertificates();
if (certs != null) {
principal = (X500Principal)
principal =
((X509Certificate)certs[0]).getSubjectX500Principal();
}
}

View File

@ -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();
}
}
}

View File

@ -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);
}

View File

@ -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();
}
}

View File

@ -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 @@ public abstract class SSLEngine {
*
* <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

View File

@ -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 @@ import java.security.AlgorithmConstraints;
* 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 @@ public class SSLParameters {
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 @@ public class SSLParameters {
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;
}
}

View File

@ -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 @@ public abstract class SSLServerSocket extends ServerSocket {
*
* <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

View File

@ -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 @@ public abstract class SSLSocket extends Socket
*
* <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

View File

@ -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 @@ package javax.net.ssl;
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 @@ public abstract class SSLSocketFactory extends SocketFactory
* @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();
}
}

View File

@ -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;
}

View File

@ -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 @@ abstract class BaseSSLSocketImpl extends SSLSocket {
* 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 @@ abstract class BaseSSLSocketImpl extends SSLSocket {
}
}
@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);
}
}

View File

@ -48,8 +48,6 @@ import sun.security.ssl.HandshakeMessage.*;
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 @@ final class ClientHandshaker extends Handshaker {
private final static boolean enableSNIExtension =
Debug.getBooleanProperty("jsse.enableSNIExtension", true);
private List<SNIServerName> requestedServerNames =
Collections.<SNIServerName>emptyList();
/*
* Constructors
*/
@ -579,6 +580,7 @@ final class ClientHandshaker extends Handshaker {
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 @@ final class ClientHandshaker extends Handshaker {
// 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);
}
}

View File

@ -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 @@ public class HandshakeInStream extends InputStream {
r.mark(readlimit);
}
public void reset() {
@Override
public void reset() throws IOException {
r.reset();
}

View File

@ -256,13 +256,9 @@ static final class ClientHello extends HandshakeMessage {
}
// 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
}

View File

@ -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 @@ abstract class Handshaker {
*/
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 @@ abstract class Handshaker {
}
}
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 @@ abstract class Handshaker {
}
}
// ONLY used by ServerHandshaker to setup the peer host in SSLSession.
String getHostAddressSE() {
if (conn != null) {
return conn.getInetAddress().getHostAddress();
@ -435,6 +435,22 @@ abstract class Handshaker {
this.identificationProtocol = protocol;
}
/**
* 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.

View File

@ -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 @@ package sun.security.ssl;
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 @@ final class UnknownExtension extends HelloExtension {
}
/*
* [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 UnknownExtension extends HelloExtension {
*/
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;
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 = Collections.<Integer, SNIServerName>emptyMap();
}
// constructor for ClientHello
ServerNameExtension(List<SNIServerName> serverNames)
throws IOException {
super(ExtensionType.EXT_SERVER_NAME);
listLength = 0;
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 @@ final class ServerNameExtension extends HelloExtension {
}
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 @@ final class ServerNameExtension extends HelloExtension {
}
}
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 @@ final class ServerNameExtension extends HelloExtension {
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 {

View File

@ -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 @@ final class ProtocolList {
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) {

View File

@ -27,6 +27,7 @@ package sun.security.ssl;
import java.io.*;
import java.nio.*;
import java.util.*;
import java.security.*;
import javax.crypto.BadPaddingException;
@ -34,7 +35,6 @@ import javax.crypto.BadPaddingException;
import javax.net.ssl.*;
import javax.net.ssl.SSLEngineResult.*;
/**
* Implementation of an non-blocking SSLEngine.
*
@ -253,6 +253,12 @@ final public class SSLEngineImpl extends SSLEngine {
// 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 @@ final public class SSLEngineImpl extends SSLEngine {
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 @@ final public class SSLEngineImpl extends SSLEngine {
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 @@ final public class SSLEngineImpl extends SSLEngine {
// 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 @@ final public class SSLEngineImpl extends SSLEngine {
* 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 @@ final public class SSLEngineImpl extends SSLEngine {
*/
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 @@ final public class SSLEngineImpl extends SSLEngine {
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 @@ final public class SSLEngineImpl extends SSLEngine {
* 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 @@ final public class SSLEngineImpl extends SSLEngine {
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 @@ final public class SSLEngineImpl extends SSLEngine {
* 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 @@ final public class SSLEngineImpl extends SSLEngine {
*/
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 @@ final public class SSLEngineImpl extends SSLEngine {
}
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 @@ final public class SSLEngineImpl extends SSLEngine {
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 @@ final public class SSLEngineImpl extends SSLEngine {
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 @@ final public class SSLEngineImpl extends SSLEngine {
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 @@ final public class SSLEngineImpl extends SSLEngine {
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 @@ final public class SSLEngineImpl extends SSLEngine {
// the super implementation does not handle the following parameters
params.setEndpointIdentificationAlgorithm(identificationProtocol);
params.setAlgorithmConstraints(algorithmConstraints);
params.setSNIMatchers(sniMatchers);
params.setServerNames(serverNames);
return params;
}
@ -2063,19 +2082,28 @@ final public class SSLEngineImpl extends SSLEngine {
// 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.
*/

View File

@ -39,6 +39,7 @@ import javax.net.ServerSocketFactory;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SNIMatcher;
/**
@ -92,6 +93,10 @@ class SSLServerSocketImpl extends SSLServerSocket
// 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 @@ class SSLServerSocketImpl extends SSLServerSocket
// the super implementation does not handle the following parameters
params.setEndpointIdentificationAlgorithm(identificationProtocol);
params.setAlgorithmConstraints(algorithmConstraints);
params.setSNIMatchers(sniMatchers);
return params;
}
@ -302,6 +308,10 @@ class SSLServerSocketImpl extends SSLServerSocket
// 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 @@ class SSLServerSocketImpl extends SSLServerSocket
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();

View File

@ -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.Hashtable;
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.SSLSession;
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 @@ final class SSLSessionImpl extends ExtendedSSLSession {
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 @@ final class SSLSessionImpl extends ExtendedSSLSession {
SignatureAndHashAlgorithm.getAlgorithmNames(algorithms);
}
void setRequestedServerNames(List<SNIServerName> requestedServerNames) {
this.requestedServerNames = new ArrayList<>(requestedServerNames);
}
/**
* Set the peer principal.
*/
@ -748,6 +758,7 @@ final class SSLSessionImpl extends ExtendedSSLSession {
* 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 @@ final class SSLSessionImpl extends ExtendedSSLSession {
* 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 @@ final class SSLSessionImpl extends ExtendedSSLSession {
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

View File

@ -109,6 +109,16 @@ final public class SSLSocketFactoryImpl extends SSLSocketFactory {
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

View File

@ -36,9 +36,9 @@ import java.security.AlgorithmConstraints;
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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
// 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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
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,12 +537,35 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
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.
*/
@ -604,7 +629,7 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
public void connect(SocketAddress endpoint, int timeout)
throws IOException {
if (self != this) {
if (isLayered()) {
throw new SocketException("Already connected");
}
@ -628,13 +653,8 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
* 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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
// 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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
// 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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
* 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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
*/
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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
protected void closeSocket() throws IOException {
if ((debug != null) && Debug.isOn("ssl")) {
System.out.println(threadName() + ", called closeSocket()");
}
if (self == this) {
super.close();
} else {
self.close();
System.out.println(Thread.currentThread().getName() +
", called closeSocket()");
}
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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
*/
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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
*/
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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
// 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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
}
}
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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
*/
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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
} 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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
*/
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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
// 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 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
// 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);
}
}
}
@ -2530,13 +2567,6 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
}
}
/**
* 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.
*/
@ -2548,11 +2578,7 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
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();

View File

@ -276,6 +276,18 @@ final class ServerHandshaker extends Handshaker {
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 @@ final class ServerHandshaker extends Handshaker {
}
}
// 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 @@ final class ServerHandshaker extends Handshaker {
// 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 @@ final class ServerHandshaker extends Handshaker {
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());

View File

@ -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 @@ public abstract class SunJSSE extends java.security.Provider {
// standard constructor
protected SunJSSE() {
super("SunJSSE", 1.7d, info);
super("SunJSSE", 1.8d, info);
subclassCheck();
if (Boolean.TRUE.equals(fips)) {
throw new ProviderException

View File

@ -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;
}
}

View File

@ -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 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager
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 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager
* 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 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager
*/
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 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager
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 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager
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 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager
// 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 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager
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 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager
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 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager
// 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 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager
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 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager
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);

View File

@ -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 @@ package sun.security.ssl;
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 @@ final class X509TrustManagerImpl extends X509ExtendedTrustManager
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 @@ final class X509TrustManagerImpl extends X509ExtendedTrustManager
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 @@ final class X509TrustManagerImpl extends X509ExtendedTrustManager
}
}
// 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.
*

View File

@ -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 @@ public class LargePacket extends SSLEngineService {
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 @@ public class LargePacket extends SSLEngineService {
}
// handshaking
handshaking(ssle, sc);
handshaking(ssle, sc, null);
// send out application data
deliver(ssle, sc);
@ -169,6 +174,8 @@ public class LargePacket extends SSLEngineService {
* Fork off the other side, then do your work.
*/
LargePacket() throws Exception {
super("../../../../../etc");
if (separateServerThread) {
startServer(true);
startClient(false);

View File

@ -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 @@ import java.nio.channels.*;
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 @@ public class SSLEngineService {
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 @@ public class SSLEngineService {
" bytes large packet ");
}
needToReadMore = (peerNetData.position() > 0) ? false : true;
break;
case BUFFER_OVERFLOW :
@ -206,6 +223,8 @@ public class SSLEngineService {
" bytes for BUFFER_UNDERFLOW");
peerNetData = enlargeBuffer(peerNetData, size);
}
needToReadMore = true;
break;
default : // CLOSED :
@ -215,8 +234,8 @@ public class SSLEngineService {
}
}
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 @@ public class SSLEngineService {
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 @@ public class SSLEngineService {
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 @@ public class SSLEngineService {
/*
* 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;

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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();
}

View File

@ -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);
}
}
}