提交 b80ffe55 编写于 作者: X xuelei

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

Reviewed-by: mullan, weijun, wetmore
上级 b1886967
......@@ -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();
}
}
/*
* 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();
}
}
......
/*
* 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();
}
}
}
/*
* 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);
}
/*
* 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();
}
}
/*
* 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;
}
}
/*
* 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
......
/*
* 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
......
/*
* 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();
}
}
......
/*
* 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;
}
/*
* 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();
// we only allow FQDN
if (hostname != null && hostname.indexOf('.') > 0 &&
!IPAddressUtil.isIPv4LiteralAddress(hostname) &&
!IPAddressUtil.isIPv6LiteralAddress(hostname)) {
clientHelloMessage.addServerNameIndicationExtension(hostname);
if (session != null) {
requestedServerNames = session.getRequestedServerNames();
} else {
requestedServerNames = serverNames;
}
if (!requestedServerNames.isEmpty()) {
clientHelloMessage.addSNIExtension(requestedServerNames);
}
}
......
/*
* 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
}
......
/*
* 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.
......
/*
* 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;
sniMap = Collections.<Integer, SNIServerName>emptyMap();
}
// constructor for ClientHello
ServerNameExtension(List<SNIServerName> serverNames)
throws IOException {
super(ExtensionType.EXT_SERVER_NAME);
listLength = 0;
names = new ArrayList<ServerName>(hostnames.size());
for (String hostname : hostnames) {
if (hostname != null && hostname.length() != 0) {
// we only support DNS hostname now.
ServerName serverName =
new ServerName(NAME_HOST_NAME, hostname);
names.add(serverName);
listLength += serverName.length;
sniMap = new LinkedHashMap<>();
for (SNIServerName serverName : serverNames) {
// check for duplicated server name type
if (sniMap.put(serverName.getType(), serverName) != null) {
// unlikely to happen, but in case ...
throw new RuntimeException(
"Duplicated server name of type " + serverName.getType());
}
}
// 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");
listLength += serverName.getEncoded().length + NAME_HEADER_LENGTH;
}
// 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
// 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());
}
// we may need to check the duplicated ServerName type
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
}
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;
List<SNIServerName> getServerNames() {
if (sniMap != null && !sniMap.isEmpty()) {
return Collections.<SNIServerName>unmodifiableList(
new ArrayList<>(sniMap.values()));
}
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 {
......
/*
* 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();
......
/*
* 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());
......
/*
* 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
......
/*
* 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;
}
}
/*
* 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);
......
/*
* 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.
*
......
/*
* 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);
......
/*
* 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;
......
/*
* 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();
}
}
}
/*
* 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();
}
}
}
/*
* 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();
}
}
}
/*
* 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();
}
}
}
/*
* 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();
}
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册