7068321: Support TLS Server Name Indication (SNI) Extension in JSSE Server
Reviewed-by: mullan, weijun, wetmore
This commit is contained in:
parent
1fb9910146
commit
1892ebf911
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
394
jdk/src/share/classes/javax/net/ssl/SNIHostName.java
Normal file
394
jdk/src/share/classes/javax/net/ssl/SNIHostName.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
105
jdk/src/share/classes/javax/net/ssl/SNIMatcher.java
Normal file
105
jdk/src/share/classes/javax/net/ssl/SNIMatcher.java
Normal 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);
|
||||
}
|
213
jdk/src/share/classes/javax/net/ssl/SNIServerName.java
Normal file
213
jdk/src/share/classes/javax/net/ssl/SNIServerName.java
Normal 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();
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
56
jdk/src/share/classes/javax/net/ssl/StandardConstants.java
Normal file
56
jdk/src/share/classes/javax/net/ssl/StandardConstants.java
Normal 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;
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
115
jdk/src/share/classes/sun/security/ssl/Utilities.java
Normal file
115
jdk/src/share/classes/sun/security/ssl/Utilities.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
70
jdk/test/sun/security/ssl/templates/SSLCapabilities.java
Normal file
70
jdk/test/sun/security/ssl/templates/SSLCapabilities.java
Normal 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();
|
||||
}
|
||||
|
615
jdk/test/sun/security/ssl/templates/SSLExplorer.java
Normal file
615
jdk/test/sun/security/ssl/templates/SSLExplorer.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user