8043758: Datagram Transport Layer Security (DTLS)

Reviewed-by: jnimeh, weijun, mullan, wetmore
This commit is contained in:
Xue-Lei Andrew Fan 2015-06-02 04:01:04 +00:00
parent ea68abc64e
commit 0f1698f906
76 changed files with 11196 additions and 4956 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2010, 2015, 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,7 +28,7 @@ package javax.net.ssl;
import java.util.List;
/**
* Extends the <code>SSLSession</code> interface to support additional
* Extends the {@code SSLSession} interface to support additional
* session attributes.
*
* @since 1.7
@ -39,8 +39,8 @@ public abstract class ExtendedSSLSession implements SSLSession {
* is willing to use.
* <p>
* Note: this method is used to indicate to the peer which signature
* algorithms may be used for digital signatures in TLS 1.2. It is
* not meaningful for TLS versions prior to 1.2.
* algorithms may be used for digital signatures in TLS/DTLS 1.2. It is
* not meaningful for TLS/DTLS versions prior to 1.2.
* <p>
* The signature algorithm name must be a standard Java Security
* name (such as "SHA1withRSA", "SHA256withECDSA", and so on).
@ -52,7 +52,7 @@ public abstract class ExtendedSSLSession implements SSLSession {
* Note: the local supported signature algorithms should conform to
* the algorithm constraints specified by
* {@link SSLParameters#getAlgorithmConstraints getAlgorithmConstraints()}
* method in <code>SSLParameters</code>.
* method in {@code SSLParameters}.
*
* @return An array of supported signature algorithms, in descending
* order of preference. The return value is an empty array if
@ -67,8 +67,8 @@ public abstract class ExtendedSSLSession implements SSLSession {
* able to use.
* <p>
* Note: this method is used to indicate to the local side which signature
* algorithms may be used for digital signatures in TLS 1.2. It is
* not meaningful for TLS versions prior to 1.2.
* algorithms may be used for digital signatures in TLS/DTLS 1.2. It is
* not meaningful for TLS/DTLS versions prior to 1.2.
* <p>
* The signature algorithm name must be a standard Java Security
* name (such as "SHA1withRSA", "SHA256withECDSA", and so on).

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2015, 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
@ -31,7 +31,7 @@ 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
* The SNI extension is a feature that extends the SSL/TLS/DTLS 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>.

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2015, 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
@ -32,12 +32,12 @@ import sun.security.jca.GetInstance;
/**
* Instances of this class represent a secure socket protocol
* implementation which acts as a factory for secure socket
* factories or <code>SSLEngine</code>s. This class is initialized
* factories or {@code SSLEngine}s. This class is initialized
* with an optional set of key and trust managers and source of
* secure random bytes.
*
* <p> Every implementation of the Java platform is required to support the
* following standard <code>SSLContext</code> protocol:
* following standard {@code SSLContext} protocol:
* <ul>
* <li><tt>TLSv1</tt></li>
* </ul>
@ -79,7 +79,7 @@ public class SSLContext {
* <p>If a default context was set using the {@link #setDefault
* SSLContext.setDefault()} method, it is returned. Otherwise, the first
* call of this method triggers the call
* <code>SSLContext.getInstance("Default")</code>.
* {@code SSLContext.getInstance("Default")}.
* If successful, that object is made the default SSL context and returned.
*
* <p>The default context is immediately
@ -106,8 +106,8 @@ public class SSLContext {
* @param context the SSLContext
* @throws NullPointerException if context is null
* @throws SecurityException if a security manager exists and its
* <code>checkPermission</code> method does not allow
* <code>SSLPermission("setDefaultSSLContext")</code>
* {@code checkPermission} method does not allow
* {@code SSLPermission("setDefaultSSLContext")}
* @since 1.6
*/
public static synchronized void setDefault(SSLContext context) {
@ -122,7 +122,7 @@ public class SSLContext {
}
/**
* Returns a <code>SSLContext</code> object that implements the
* Returns a {@code SSLContext} object that implements the
* specified secure socket protocol.
*
* <p> This method traverses the list of registered security Providers,
@ -141,7 +141,7 @@ public class SSLContext {
* Documentation</a>
* for information about standard protocol names.
*
* @return the new <code>SSLContext</code> object.
* @return the new {@code SSLContext} object.
*
* @exception NoSuchAlgorithmException if no Provider supports a
* SSLContextSpi implementation for the
@ -159,7 +159,7 @@ public class SSLContext {
}
/**
* Returns a <code>SSLContext</code> object that implements the
* Returns a {@code SSLContext} object that implements the
* specified secure socket protocol.
*
* <p> A new SSLContext object encapsulating the
@ -179,7 +179,7 @@ public class SSLContext {
*
* @param provider the name of the provider.
*
* @return the new <code>SSLContext</code> object.
* @return the new {@code SSLContext} object.
*
* @throws NoSuchAlgorithmException if a SSLContextSpi
* implementation for the specified protocol is not
@ -202,7 +202,7 @@ public class SSLContext {
}
/**
* Returns a <code>SSLContext</code> object that implements the
* Returns a {@code SSLContext} object that implements the
* specified secure socket protocol.
*
* <p> A new SSLContext object encapsulating the
@ -219,7 +219,7 @@ public class SSLContext {
*
* @param provider an instance of the provider.
*
* @return the new <code>SSLContext</code> object.
* @return the new {@code SSLContext} object.
*
* @throws NoSuchAlgorithmException if a SSLContextSpi
* implementation for the specified protocol is not available
@ -239,22 +239,22 @@ public class SSLContext {
}
/**
* Returns the protocol name of this <code>SSLContext</code> object.
* Returns the protocol name of this {@code SSLContext} object.
*
* <p>This is the same name that was specified in one of the
* <code>getInstance</code> calls that created this
* <code>SSLContext</code> object.
* {@code getInstance} calls that created this
* {@code SSLContext} object.
*
* @return the protocol name of this <code>SSLContext</code> object.
* @return the protocol name of this {@code SSLContext} object.
*/
public final String getProtocol() {
return this.protocol;
}
/**
* Returns the provider of this <code>SSLContext</code> object.
* Returns the provider of this {@code SSLContext} object.
*
* @return the provider of this <code>SSLContext</code> object
* @return the provider of this {@code SSLContext} object
*/
public final Provider getProvider() {
return this.provider;
@ -283,31 +283,35 @@ public class SSLContext {
}
/**
* Returns a <code>SocketFactory</code> object for this
* Returns a {@code SocketFactory} object for this
* context.
*
* @return the <code>SocketFactory</code> object
* @return the {@code SocketFactory} object
* @throws UnsupportedOperationException if the underlying provider
* does not implement the operation.
* @throws IllegalStateException if the SSLContextImpl requires
* initialization and the <code>init()</code> has not been called
* initialization and the {@code init()} has not been called
*/
public final SSLSocketFactory getSocketFactory() {
return contextSpi.engineGetSocketFactory();
}
/**
* Returns a <code>ServerSocketFactory</code> object for
* Returns a {@code ServerSocketFactory} object for
* this context.
*
* @return the <code>ServerSocketFactory</code> object
* @return the {@code ServerSocketFactory} object
* @throws UnsupportedOperationException if the underlying provider
* does not implement the operation.
* @throws IllegalStateException if the SSLContextImpl requires
* initialization and the <code>init()</code> has not been called
* initialization and the {@code init()} has not been called
*/
public final SSLServerSocketFactory getServerSocketFactory() {
return contextSpi.engineGetServerSocketFactory();
}
/**
* Creates a new <code>SSLEngine</code> using this context.
* Creates a new {@code SSLEngine} using this context.
* <P>
* Applications using this factory method are providing no hints
* for an internal session reuse strategy. If hints are desired,
@ -317,11 +321,11 @@ public class SSLContext {
* Some cipher suites (such as Kerberos) require remote hostname
* information, in which case this factory method should not be used.
*
* @return the <code>SSLEngine</code> object
* @return the {@code SSLEngine} object
* @throws UnsupportedOperationException if the underlying provider
* does not implement the operation.
* @throws IllegalStateException if the SSLContextImpl requires
* initialization and the <code>init()</code> has not been called
* initialization and the {@code init()} has not been called
* @since 1.5
*/
public final SSLEngine createSSLEngine() {
@ -338,7 +342,7 @@ public class SSLContext {
}
/**
* Creates a new <code>SSLEngine</code> using this context using
* Creates a new {@code SSLEngine} using this context using
* advisory peer information.
* <P>
* Applications using this factory method are providing hints
@ -349,11 +353,11 @@ public class SSLContext {
*
* @param peerHost the non-authoritative name of the host
* @param peerPort the non-authoritative port
* @return the new <code>SSLEngine</code> object
* @return the new {@code SSLEngine} object
* @throws UnsupportedOperationException if the underlying provider
* does not implement the operation.
* @throws IllegalStateException if the SSLContextImpl requires
* initialization and the <code>init()</code> has not been called
* initialization and the {@code init()} has not been called
* @since 1.5
*/
public final SSLEngine createSSLEngine(String peerHost, int peerPort) {

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2015, 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,7 +29,7 @@ import java.security.*;
/**
* This class defines the <i>Service Provider Interface</i> (<b>SPI</b>)
* for the <code>SSLContext</code> class.
* for the {@code SSLContext} class.
*
* <p> All the abstract methods in this class must be implemented by each
* cryptographic service provider who wishes to supply the implementation
@ -52,31 +52,35 @@ public abstract class SSLContextSpi {
SecureRandom sr) throws KeyManagementException;
/**
* Returns a <code>SocketFactory</code> object for this
* Returns a {@code SocketFactory} object for this
* context.
*
* @return the <code>SocketFactory</code> object
* @return the {@code SocketFactory} object
* @throws UnsupportedOperationException if the underlying provider
* does not implement the operation.
* @throws IllegalStateException if the SSLContextImpl requires
* initialization and the <code>engineInit()</code>
* initialization and the {@code engineInit()}
* has not been called
* @see javax.net.ssl.SSLContext#getSocketFactory()
*/
protected abstract SSLSocketFactory engineGetSocketFactory();
/**
* Returns a <code>ServerSocketFactory</code> object for
* Returns a {@code ServerSocketFactory} object for
* this context.
*
* @return the <code>ServerSocketFactory</code> object
* @return the {@code ServerSocketFactory} object
* @throws UnsupportedOperationException if the underlying provider
* does not implement the operation.
* @throws IllegalStateException if the SSLContextImpl requires
* initialization and the <code>engineInit()</code>
* initialization and the {@code engineInit()}
* has not been called
* @see javax.net.ssl.SSLContext#getServerSocketFactory()
*/
protected abstract SSLServerSocketFactory engineGetServerSocketFactory();
/**
* Creates a new <code>SSLEngine</code> using this context.
* Creates a new {@code SSLEngine} using this context.
* <P>
* Applications using this factory method are providing no hints
* for an internal session reuse strategy. If hints are desired,
@ -86,9 +90,9 @@ public abstract class SSLContextSpi {
* Some cipher suites (such as Kerberos) require remote hostname
* information, in which case this factory method should not be used.
*
* @return the <code>SSLEngine</code> Object
* @return the {@code SSLEngine} Object
* @throws IllegalStateException if the SSLContextImpl requires
* initialization and the <code>engineInit()</code>
* initialization and the {@code engineInit()}
* has not been called
*
* @see SSLContext#createSSLEngine()
@ -98,7 +102,7 @@ public abstract class SSLContextSpi {
protected abstract SSLEngine engineCreateSSLEngine();
/**
* Creates a <code>SSLEngine</code> using this context.
* Creates a {@code SSLEngine} using this context.
* <P>
* Applications using this factory method are providing hints
* for an internal session reuse strategy.
@ -108,9 +112,9 @@ public abstract class SSLContextSpi {
*
* @param host the non-authoritative name of the host
* @param port the non-authoritative port
* @return the <code>SSLEngine</code> Object
* @return the {@code SSLEngine} Object
* @throws IllegalStateException if the SSLContextImpl requires
* initialization and the <code>engineInit()</code>
* initialization and the {@code engineInit()}
* has not been called
*
* @see SSLContext#createSSLEngine(String, int)
@ -120,19 +124,19 @@ public abstract class SSLContextSpi {
protected abstract SSLEngine engineCreateSSLEngine(String host, int port);
/**
* Returns a server <code>SSLSessionContext</code> object for
* Returns a server {@code SSLSessionContext} object for
* this context.
*
* @return the <code>SSLSessionContext</code> object
* @return the {@code SSLSessionContext} object
* @see javax.net.ssl.SSLContext#getServerSessionContext()
*/
protected abstract SSLSessionContext engineGetServerSessionContext();
/**
* Returns a client <code>SSLSessionContext</code> object for
* Returns a client {@code SSLSessionContext} object for
* this context.
*
* @return the <code>SSLSessionContext</code> object
* @return the {@code SSLSessionContext} object
* @see javax.net.ssl.SSLContext#getClientSessionContext()
*/
protected abstract SSLSessionContext engineGetClientSessionContext();

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2015, 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
@ -37,15 +37,15 @@ import java.nio.ReadOnlyBufferException;
* <P>
* The secure communications modes include: <UL>
*
* <LI> <em>Integrity Protection</em>. SSL/TLS protects against
* <LI> <em>Integrity Protection</em>. SSL/TLS/DTLS protects against
* modification of messages by an active wiretapper.
*
* <LI> <em>Authentication</em>. In most modes, SSL/TLS provides
* <LI> <em>Authentication</em>. In most modes, SSL/TLS/DTLS provides
* peer authentication. Servers are usually authenticated, and
* clients may be authenticated as requested by servers.
*
* <LI> <em>Confidentiality (Privacy Protection)</em>. In most
* modes, SSL/TLS encrypts data being sent between client and
* modes, SSL/TLS/DTLS encrypts data being sent between client and
* server. This protects the confidentiality of data, so that
* passive wiretappers won't see sensitive data such as financial
* information or personal information of many kinds.
@ -65,19 +65,19 @@ import java.nio.ReadOnlyBufferException;
* handshaking has completed, you can access session attributes by
* using the {@link #getSession()} method.
* <P>
* The <code>SSLSocket</code> class provides much of the same security
* The {@code SSLSocket} class provides much of the same security
* functionality, but all of the inbound and outbound data is
* automatically transported using the underlying {@link
* java.net.Socket Socket}, which by design uses a blocking model.
* While this is appropriate for many applications, this model does not
* provide the scalability required by large servers.
* <P>
* The primary distinction of an <code>SSLEngine</code> is that it
* The primary distinction of an {@code SSLEngine} is that it
* operates on inbound and outbound byte streams, independent of the
* transport mechanism. It is the responsibility of the
* <code>SSLEngine</code> user to arrange for reliable I/O transport to
* the peer. By separating the SSL/TLS abstraction from the I/O
* transport mechanism, the <code>SSLEngine</code> can be used for a
* {@code SSLEngine} user to arrange for reliable I/O transport to
* the peer. By separating the SSL/TLS/DTLS abstraction from the I/O
* transport mechanism, the {@code SSLEngine} can be used for a
* wide variety of I/O types, such as {@link
* java.nio.channels.spi.AbstractSelectableChannel#configureBlocking(boolean)
* non-blocking I/O (polling)}, {@link java.nio.channels.Selector
@ -87,7 +87,7 @@ import java.nio.ReadOnlyBufferException;
* HREF="http://www.jcp.org/en/jsr/detail?id=203"> future asynchronous
* I/O models </A>, and so on.
* <P>
* At a high level, the <code>SSLEngine</code> appears thus:
* At a high level, the {@code SSLEngine} appears thus:
*
* <pre>
* app data
@ -115,18 +115,18 @@ import java.nio.ReadOnlyBufferException;
* mechanism. Inbound data is data which has been received from the
* peer, and outbound data is destined for the peer.
* <P>
* (In the context of an <code>SSLEngine</code>, the term "handshake
* (In the context of an {@code SSLEngine}, the term "handshake
* data" is taken to mean any data exchanged to establish and control a
* secure connection. Handshake data includes the SSL/TLS messages
* secure connection. Handshake data includes the SSL/TLS/DTLS messages
* "alert", "change_cipher_spec," and "handshake.")
* <P>
* There are five distinct phases to an <code>SSLEngine</code>.
* There are five distinct phases to an {@code SSLEngine}.
*
* <OL>
* <li> Creation - The <code>SSLEngine</code> has been created and
* <li> Creation - The {@code SSLEngine} has been created and
* initialized, but has not yet been used. During this phase, an
* application may set any <code>SSLEngine</code>-specific settings
* (enabled cipher suites, whether the <code>SSLEngine</code> should
* application may set any {@code SSLEngine}-specific settings
* (enabled cipher suites, whether the {@code SSLEngine} should
* handshake in client or server mode, and so on). Once
* handshaking has begun, though, any new settings (except
* client/server mode, see below) will be used for
@ -139,7 +139,7 @@ import java.nio.ReadOnlyBufferException;
*
* <li> Application Data - Once the communication parameters have
* been established and the handshake is complete, application data
* may flow through the <code>SSLEngine</code>. Outbound
* may flow through the {@code SSLEngine}. Outbound
* application messages are encrypted and integrity protected,
* and inbound messages reverse the process.
*
@ -147,50 +147,50 @@ import java.nio.ReadOnlyBufferException;
* the session at any time during the Application Data phase. New
* handshaking data can be intermixed among the application data.
* Before starting the rehandshake phase, the application may
* reset the SSL/TLS communication parameters such as the list of
* reset the SSL/TLS/DTLS communication parameters such as the list of
* enabled ciphersuites and whether to use client authentication,
* but can not change between client/server modes. As before, once
* handshaking has begun, any new <code>SSLEngine</code>
* handshaking has begun, any new {@code SSLEngine}
* configuration settings will not be used until the next
* handshake.
*
* <li> Closure - When the connection is no longer needed, the
* application should close the <code>SSLEngine</code> and should
* application should close the {@code SSLEngine} and should
* send/receive any remaining messages to the peer before
* closing the underlying transport mechanism. Once an engine is
* closed, it is not reusable: a new <code>SSLEngine</code> must
* closed, it is not reusable: a new {@code SSLEngine} must
* be created.
* </OL>
* An <code>SSLEngine</code> is created by calling {@link
* An {@code SSLEngine} is created by calling {@link
* SSLContext#createSSLEngine()} from an initialized
* <code>SSLContext</code>. Any configuration
* {@code SSLContext}. Any configuration
* parameters should be set before making the first call to
* <code>wrap()</code>, <code>unwrap()</code>, or
* <code>beginHandshake()</code>. These methods all trigger the
* {@code wrap()}, {@code unwrap()}, or
* {@code beginHandshake()}. These methods all trigger the
* initial handshake.
* <P>
* Data moves through the engine by calling {@link #wrap(ByteBuffer,
* ByteBuffer) wrap()} or {@link #unwrap(ByteBuffer, ByteBuffer)
* unwrap()} on outbound or inbound data, respectively. Depending on
* the state of the <code>SSLEngine</code>, a <code>wrap()</code> call
* the state of the {@code SSLEngine}, a {@code wrap()} call
* may consume application data from the source buffer and may produce
* network data in the destination buffer. The outbound data
* may contain application and/or handshake data. A call to
* <code>unwrap()</code> will examine the source buffer and may
* {@code unwrap()} will examine the source buffer and may
* advance the handshake if the data is handshaking information, or
* may place application data in the destination buffer if the data
* is application. The state of the underlying SSL/TLS algorithm
* is application. The state of the underlying SSL/TLS/DTLS algorithm
* will determine when data is consumed and produced.
* <P>
* Calls to <code>wrap()</code> and <code>unwrap()</code> return an
* <code>SSLEngineResult</code> which indicates the status of the
* Calls to {@code wrap()} and {@code unwrap()} return an
* {@code SSLEngineResult} which indicates the status of the
* operation, and (optionally) how to interact with the engine to make
* progress.
* <P>
* The <code>SSLEngine</code> produces/consumes complete SSL/TLS
* The {@code SSLEngine} produces/consumes complete SSL/TLS/DTLS
* packets only, and does not store application data internally between
* calls to <code>wrap()/unwrap()</code>. Thus input and output
* <code>ByteBuffer</code>s must be sized appropriately to hold the
* calls to {@code wrap()/unwrap()}. Thus input and output
* {@code ByteBuffer}s must be sized appropriately to hold the
* maximum record that can be produced. Calls to {@link
* SSLSession#getPacketBufferSize()} and {@link
* SSLSession#getApplicationBufferSize()} should be used to determine
@ -200,12 +200,12 @@ import java.nio.ReadOnlyBufferException;
* must determine (via {@link SSLEngineResult}) and correct the
* problem, and then try the call again.
* <P>
* For example, <code>unwrap()</code> will return a {@link
* For example, {@code unwrap()} will return a {@link
* SSLEngineResult.Status#BUFFER_OVERFLOW} result if the engine
* determines that there is not enough destination buffer space available.
* Applications should call {@link SSLSession#getApplicationBufferSize()}
* and compare that value with the space available in the destination buffer,
* enlarging the buffer if necessary. Similarly, if <code>unwrap()</code>
* enlarging the buffer if necessary. Similarly, if {@code unwrap()}
* were to return a {@link SSLEngineResult.Status#BUFFER_UNDERFLOW}, the
* application should call {@link SSLSession#getPacketBufferSize()} to ensure
* that the source buffer has enough room to hold a record (enlarging if
@ -241,8 +241,8 @@ import java.nio.ReadOnlyBufferException;
* }</pre>
*
* <P>
* Unlike <code>SSLSocket</code>, all methods of SSLEngine are
* non-blocking. <code>SSLEngine</code> implementations may
* Unlike {@code SSLSocket}, all methods of SSLEngine are
* non-blocking. {@code SSLEngine} implementations may
* require the results of tasks that may take an extended period of
* time to complete, or may even block. For example, a TrustManager
* may need to connect to a remote certificate validation service,
@ -252,8 +252,8 @@ import java.nio.ReadOnlyBufferException;
* seemingly blocking.
* <P>
* For any operation which may potentially block, the
* <code>SSLEngine</code> will create a {@link java.lang.Runnable}
* delegated task. When <code>SSLEngineResult</code> indicates that a
* {@code SSLEngine} will create a {@link java.lang.Runnable}
* delegated task. When {@code SSLEngineResult} indicates that a
* delegated task result is needed, the application must call {@link
* #getDelegatedTask()} to obtain an outstanding delegated task and
* call its {@link java.lang.Runnable#run() run()} method (possibly using
@ -262,16 +262,16 @@ import java.nio.ReadOnlyBufferException;
* exist, and try the original operation again.
* <P>
* At the end of a communication session, applications should properly
* close the SSL/TLS link. The SSL/TLS protocols have closure handshake
* messages, and these messages should be communicated to the peer
* before releasing the <code>SSLEngine</code> and closing the
* close the SSL/TLS/DTLS link. The SSL/TLS/DTLS protocols have closure
* handshake messages, and these messages should be communicated to the
* peer before releasing the {@code SSLEngine} and closing the
* underlying transport mechanism. A close can be initiated by one of:
* an SSLException, an inbound closure handshake message, or one of the
* close methods. In all cases, closure handshake messages are
* generated by the engine, and <code>wrap()</code> should be repeatedly
* called until the resulting <code>SSLEngineResult</code>'s status
* generated by the engine, and {@code wrap()} should be repeatedly
* called until the resulting {@code SSLEngineResult}'s status
* returns "CLOSED", or {@link #isOutboundDone()} returns true. All
* data obtained from the <code>wrap()</code> method should be sent to the
* data obtained from the {@code wrap()} method should be sent to the
* peer.
* <P>
* {@link #closeOutbound()} is used to signal the engine that the
@ -279,12 +279,12 @@ import java.nio.ReadOnlyBufferException;
* <P>
* A peer will signal its intent to close by sending its own closure
* handshake message. After this message has been received and
* processed by the local <code>SSLEngine</code>'s <code>unwrap()</code>
* processed by the local {@code SSLEngine}'s {@code unwrap()}
* call, the application can detect the close by calling
* <code>unwrap()</code> and looking for a <code>SSLEngineResult</code>
* {@code unwrap()} and looking for a {@code SSLEngineResult}
* with status "CLOSED", or if {@link #isInboundDone()} returns true.
* If for some reason the peer closes the communication link without
* sending the proper SSL/TLS closure message, the application can
* sending the proper SSL/TLS/DTLS closure message, the application can
* detect the end-of-stream and can signal the engine via {@link
* #closeInbound()} that there will no more inbound messages to
* process. Some applications might choose to require orderly shutdown
@ -315,16 +315,16 @@ import java.nio.ReadOnlyBufferException;
* and/or non-private (unencrypted) communications will such a
* cipher suite be selected.
* <P>
* Each SSL/TLS connection must have one client and one server, thus
* Each SSL/TLS/DTLS connection must have one client and one server, thus
* each endpoint must decide which role to assume. This choice determines
* who begins the handshaking process as well as which type of messages
* should be sent by each party. The method {@link
* #setUseClientMode(boolean)} configures the mode. Once the initial
* handshaking has started, an <code>SSLEngine</code> can not switch
* handshaking has started, an {@code SSLEngine} can not switch
* between client and server modes, even when performing renegotiations.
* <P>
* Applications might choose to process delegated tasks in different
* threads. When an <code>SSLEngine</code>
* threads. When an {@code SSLEngine}
* is created, the current {@link java.security.AccessControlContext}
* is saved. All future delegated tasks will be processed using this
* context: that is, all access control decisions will be made using the
@ -336,10 +336,10 @@ import java.nio.ReadOnlyBufferException;
* There are two concurrency issues to be aware of:
*
* <OL>
* <li>The <code>wrap()</code> and <code>unwrap()</code> methods
* <li>The {@code wrap()} and {@code unwrap()} methods
* may execute concurrently of each other.
*
* <li> The SSL/TLS protocols employ ordered packets.
* <li> The SSL/TLS/DTLS protocols employ ordered packets.
* Applications must take care to ensure that generated packets
* are delivered in sequence. If packets arrive
* out-of-order, unexpected or fatal results may occur.
@ -354,7 +354,7 @@ import java.nio.ReadOnlyBufferException;
* </pre>
*
* As a corollary, two threads must not attempt to call the same method
* (either <code>wrap()</code> or <code>unwrap()</code>) concurrently,
* (either {@code wrap()} or {@code unwrap()}) concurrently,
* because there is no way to guarantee the eventual packet ordering.
* </OL>
*
@ -374,7 +374,7 @@ public abstract class SSLEngine {
private int peerPort = -1;
/**
* Constructor for an <code>SSLEngine</code> providing no hints
* Constructor for an {@code SSLEngine} providing no hints
* for an internal session reuse strategy.
*
* @see SSLContext#createSSLEngine()
@ -384,10 +384,10 @@ public abstract class SSLEngine {
}
/**
* Constructor for an <code>SSLEngine</code>.
* Constructor for an {@code SSLEngine}.
* <P>
* <code>SSLEngine</code> implementations may use the
* <code>peerHost</code> and <code>peerPort</code> parameters as hints
* {@code SSLEngine} implementations may use the
* {@code peerHost} and {@code peerPort} parameters as hints
* for their internal session reuse strategy.
* <P>
* Some cipher suites (such as Kerberos) require remote hostname
@ -395,7 +395,7 @@ public abstract class SSLEngine {
* constructor to use Kerberos.
* <P>
* The parameters are not authenticated by the
* <code>SSLEngine</code>.
* {@code SSLEngine}.
*
* @param peerHost the name of the peer host
* @param peerPort the port number of the peer
@ -435,7 +435,7 @@ public abstract class SSLEngine {
/**
* Attempts to encode a buffer of plaintext application data into
* SSL/TLS network data.
* SSL/TLS/DTLS network data.
* <P>
* An invocation of this method behaves in exactly the same manner
* as the invocation:
@ -445,20 +445,20 @@ public abstract class SSLEngine {
* </pre></blockquote>
*
* @param src
* a <code>ByteBuffer</code> containing outbound application data
* a {@code ByteBuffer} containing outbound application data
* @param dst
* a <code>ByteBuffer</code> to hold outbound network data
* @return an <code>SSLEngineResult</code> describing the result
* a {@code ByteBuffer} to hold outbound network data
* @return an {@code SSLEngineResult} describing the result
* of this operation.
* @throws SSLException
* A problem was encountered while processing the
* data that caused the <code>SSLEngine</code> to abort.
* data that caused the {@code SSLEngine} to abort.
* See the class description for more information on
* engine closure.
* @throws ReadOnlyBufferException
* if the <code>dst</code> buffer is read-only.
* if the {@code dst} buffer is read-only.
* @throws IllegalArgumentException
* if either <code>src</code> or <code>dst</code>
* if either {@code src} or {@code dst}
* is null.
* @throws IllegalStateException if the client/server mode
* has not yet been set.
@ -471,7 +471,7 @@ public abstract class SSLEngine {
/**
* Attempts to encode plaintext bytes from a sequence of data
* buffers into SSL/TLS network data.
* buffers into SSL/TLS/DTLS network data.
* <P>
* An invocation of this method behaves in exactly the same manner
* as the invocation:
@ -481,22 +481,22 @@ public abstract class SSLEngine {
* </pre></blockquote>
*
* @param srcs
* an array of <code>ByteBuffers</code> containing the
* an array of {@code ByteBuffers} containing the
* outbound application data
* @param dst
* a <code>ByteBuffer</code> to hold outbound network data
* @return an <code>SSLEngineResult</code> describing the result
* a {@code ByteBuffer} to hold outbound network data
* @return an {@code SSLEngineResult} describing the result
* of this operation.
* @throws SSLException
* A problem was encountered while processing the
* data that caused the <code>SSLEngine</code> to abort.
* data that caused the {@code SSLEngine} to abort.
* See the class description for more information on
* engine closure.
* @throws ReadOnlyBufferException
* if the <code>dst</code> buffer is read-only.
* if the {@code dst} buffer is read-only.
* @throws IllegalArgumentException
* if either <code>srcs</code> or <code>dst</code>
* is null, or if any element in <code>srcs</code> is null.
* if either {@code srcs} or {@code dst}
* is null, or if any element in {@code srcs} is null.
* @throws IllegalStateException if the client/server mode
* has not yet been set.
* @see #wrap(ByteBuffer [], int, int, ByteBuffer)
@ -512,7 +512,7 @@ public abstract class SSLEngine {
/**
* Attempts to encode plaintext bytes from a subsequence of data
* buffers into SSL/TLS network data. This <i>"gathering"</i>
* buffers into SSL/TLS/DTLS network data. This <i>"gathering"</i>
* operation encodes, in a single invocation, a sequence of bytes
* from one or more of a given sequence of buffers. Gathering
* wraps are often useful when implementing network protocols or
@ -535,49 +535,49 @@ public abstract class SSLEngine {
* it was generated. The application must properly synchronize
* multiple calls to this method.
* <P>
* If this <code>SSLEngine</code> has not yet started its initial
* If this {@code SSLEngine} has not yet started its initial
* handshake, this method will automatically start the handshake.
* <P>
* This method will attempt to produce SSL/TLS records, and will
* This method will attempt to produce SSL/TLS/DTLS records, and will
* consume as much source data as possible, but will never consume
* more than the sum of the bytes remaining in each buffer. Each
* <code>ByteBuffer</code>'s position is updated to reflect the
* {@code ByteBuffer}'s position is updated to reflect the
* amount of data consumed or produced. The limits remain the
* same.
* <P>
* The underlying memory used by the <code>srcs</code> and
* <code>dst ByteBuffer</code>s must not be the same.
* The underlying memory used by the {@code srcs} and
* {@code dst ByteBuffer}s must not be the same.
* <P>
* See the class description for more information on engine closure.
*
* @param srcs
* an array of <code>ByteBuffers</code> containing the
* an array of {@code ByteBuffers} containing the
* outbound application data
* @param offset
* The offset within the buffer array of the first buffer from
* which bytes are to be retrieved; it must be non-negative
* and no larger than <code>srcs.length</code>
* and no larger than {@code srcs.length}
* @param length
* The maximum number of buffers to be accessed; it must be
* non-negative and no larger than
* <code>srcs.length</code>&nbsp;-&nbsp;<code>offset</code>
* {@code srcs.length}&nbsp;-&nbsp;{@code offset}
* @param dst
* a <code>ByteBuffer</code> to hold outbound network data
* @return an <code>SSLEngineResult</code> describing the result
* a {@code ByteBuffer} to hold outbound network data
* @return an {@code SSLEngineResult} describing the result
* of this operation.
* @throws SSLException
* A problem was encountered while processing the
* data that caused the <code>SSLEngine</code> to abort.
* data that caused the {@code SSLEngine} to abort.
* See the class description for more information on
* engine closure.
* @throws IndexOutOfBoundsException
* if the preconditions on the <code>offset</code> and
* <code>length</code> parameters do not hold.
* if the preconditions on the {@code offset} and
* {@code length} parameters do not hold.
* @throws ReadOnlyBufferException
* if the <code>dst</code> buffer is read-only.
* if the {@code dst} buffer is read-only.
* @throws IllegalArgumentException
* if either <code>srcs</code> or <code>dst</code>
* is null, or if any element in the <code>srcs</code>
* if either {@code srcs} or {@code dst}
* is null, or if any element in the {@code srcs}
* subsequence specified is null.
* @throws IllegalStateException if the client/server mode
* has not yet been set.
@ -589,7 +589,7 @@ public abstract class SSLEngine {
int length, ByteBuffer dst) throws SSLException;
/**
* Attempts to decode SSL/TLS network data into a plaintext
* Attempts to decode SSL/TLS/DTLS network data into a plaintext
* application data buffer.
* <P>
* An invocation of this method behaves in exactly the same manner
@ -600,20 +600,20 @@ public abstract class SSLEngine {
* </pre></blockquote>
*
* @param src
* a <code>ByteBuffer</code> containing inbound network data.
* a {@code ByteBuffer} containing inbound network data.
* @param dst
* a <code>ByteBuffer</code> to hold inbound application data.
* @return an <code>SSLEngineResult</code> describing the result
* a {@code ByteBuffer} to hold inbound application data.
* @return an {@code SSLEngineResult} describing the result
* of this operation.
* @throws SSLException
* A problem was encountered while processing the
* data that caused the <code>SSLEngine</code> to abort.
* data that caused the {@code SSLEngine} to abort.
* See the class description for more information on
* engine closure.
* @throws ReadOnlyBufferException
* if the <code>dst</code> buffer is read-only.
* if the {@code dst} buffer is read-only.
* @throws IllegalArgumentException
* if either <code>src</code> or <code>dst</code>
* if either {@code src} or {@code dst}
* is null.
* @throws IllegalStateException if the client/server mode
* has not yet been set.
@ -625,7 +625,7 @@ public abstract class SSLEngine {
}
/**
* Attempts to decode SSL/TLS network data into a sequence of plaintext
* Attempts to decode SSL/TLS/DTLS network data into a sequence of plaintext
* application data buffers.
* <P>
* An invocation of this method behaves in exactly the same manner
@ -636,22 +636,22 @@ public abstract class SSLEngine {
* </pre></blockquote>
*
* @param src
* a <code>ByteBuffer</code> containing inbound network data.
* a {@code ByteBuffer} containing inbound network data.
* @param dsts
* an array of <code>ByteBuffer</code>s to hold inbound
* an array of {@code ByteBuffer}s to hold inbound
* application data.
* @return an <code>SSLEngineResult</code> describing the result
* @return an {@code SSLEngineResult} describing the result
* of this operation.
* @throws SSLException
* A problem was encountered while processing the
* data that caused the <code>SSLEngine</code> to abort.
* data that caused the {@code SSLEngine} to abort.
* See the class description for more information on
* engine closure.
* @throws ReadOnlyBufferException
* if any of the <code>dst</code> buffers are read-only.
* if any of the {@code dst} buffers are read-only.
* @throws IllegalArgumentException
* if either <code>src</code> or <code>dsts</code>
* is null, or if any element in <code>dsts</code> is null.
* if either {@code src} or {@code dsts}
* is null, or if any element in {@code dsts} is null.
* @throws IllegalStateException if the client/server mode
* has not yet been set.
* @see #unwrap(ByteBuffer, ByteBuffer [], int, int)
@ -665,7 +665,7 @@ public abstract class SSLEngine {
}
/**
* Attempts to decode SSL/TLS network data into a subsequence of
* Attempts to decode SSL/TLS/DTLS network data into a subsequence of
* plaintext application data buffers. This <i>"scattering"</i>
* operation decodes, in a single invocation, a sequence of bytes
* into one or more of a given sequence of buffers. Scattering
@ -688,55 +688,55 @@ public abstract class SSLEngine {
* order it was received. The application must properly synchronize
* multiple calls to this method.
* <P>
* If this <code>SSLEngine</code> has not yet started its initial
* If this {@code SSLEngine} has not yet started its initial
* handshake, this method will automatically start the handshake.
* <P>
* This method will attempt to consume one complete SSL/TLS network
* This method will attempt to consume one complete SSL/TLS/DTLS network
* packet, but will never consume more than the sum of the bytes
* remaining in the buffers. Each <code>ByteBuffer</code>'s
* remaining in the buffers. Each {@code ByteBuffer}'s
* position is updated to reflect the amount of data consumed or
* produced. The limits remain the same.
* <P>
* The underlying memory used by the <code>src</code> and
* <code>dsts ByteBuffer</code>s must not be the same.
* The underlying memory used by the {@code src} and
* {@code dsts ByteBuffer}s must not be the same.
* <P>
* The inbound network buffer may be modified as a result of this
* call: therefore if the network data packet is required for some
* secondary purpose, the data should be duplicated before calling this
* method. Note: the network data will not be useful to a second
* SSLEngine, as each SSLEngine contains unique random state which
* influences the SSL/TLS messages.
* influences the SSL/TLS/DTLS messages.
* <P>
* See the class description for more information on engine closure.
*
* @param src
* a <code>ByteBuffer</code> containing inbound network data.
* a {@code ByteBuffer} containing inbound network data.
* @param dsts
* an array of <code>ByteBuffer</code>s to hold inbound
* an array of {@code ByteBuffer}s to hold inbound
* application data.
* @param offset
* The offset within the buffer array of the first buffer from
* which bytes are to be transferred; it must be non-negative
* and no larger than <code>dsts.length</code>.
* and no larger than {@code dsts.length}.
* @param length
* The maximum number of buffers to be accessed; it must be
* non-negative and no larger than
* <code>dsts.length</code>&nbsp;-&nbsp;<code>offset</code>.
* @return an <code>SSLEngineResult</code> describing the result
* {@code dsts.length}&nbsp;-&nbsp;{@code offset}.
* @return an {@code SSLEngineResult} describing the result
* of this operation.
* @throws SSLException
* A problem was encountered while processing the
* data that caused the <code>SSLEngine</code> to abort.
* data that caused the {@code SSLEngine} to abort.
* See the class description for more information on
* engine closure.
* @throws IndexOutOfBoundsException
* If the preconditions on the <code>offset</code> and
* <code>length</code> parameters do not hold.
* If the preconditions on the {@code offset} and
* {@code length} parameters do not hold.
* @throws ReadOnlyBufferException
* if any of the <code>dst</code> buffers are read-only.
* if any of the {@code dst} buffers are read-only.
* @throws IllegalArgumentException
* if either <code>src</code> or <code>dsts</code>
* is null, or if any element in the <code>dsts</code>
* if either {@code src} or {@code dsts}
* is null, or if any element in the {@code dsts}
* subsequence specified is null.
* @throws IllegalStateException if the client/server mode
* has not yet been set.
@ -749,19 +749,19 @@ public abstract class SSLEngine {
/**
* Returns a delegated <code>Runnable</code> task for
* this <code>SSLEngine</code>.
* Returns a delegated {@code Runnable} task for
* this {@code SSLEngine}.
* <P>
* <code>SSLEngine</code> operations may require the results of
* {@code SSLEngine} operations may require the results of
* operations that block, or may take an extended period of time to
* complete. This method is used to obtain an outstanding {@link
* java.lang.Runnable} operation (task). Each task must be assigned
* a thread (possibly the current) to perform the {@link
* java.lang.Runnable#run() run} operation. Once the
* <code>run</code> method returns, the <code>Runnable</code> object
* {@code run} method returns, the {@code Runnable} object
* is no longer needed and may be discarded.
* <P>
* Delegated tasks run in the <code>AccessControlContext</code>
* Delegated tasks run in the {@code AccessControlContext}
* in place when this object was created.
* <P>
* A call to this method will return each outstanding task
@ -769,7 +769,7 @@ public abstract class SSLEngine {
* <P>
* Multiple delegated tasks can be run in parallel.
*
* @return a delegated <code>Runnable</code> task, or null
* @return a delegated {@code Runnable} task, or null
* if none are available.
*/
public abstract Runnable getDelegatedTask();
@ -777,7 +777,7 @@ public abstract class SSLEngine {
/**
* Signals that no more inbound network data will be sent
* to this <code>SSLEngine</code>.
* to this {@code SSLEngine}.
* <P>
* If the application initiated the closing process by calling
* {@link #closeOutbound()}, under some circumstances it is not
@ -789,9 +789,9 @@ public abstract class SSLEngine {
* <P>
* But if the application did not initiate the closure process, or
* if the circumstances above do not apply, this method should be
* called whenever the end of the SSL/TLS data stream is reached.
* called whenever the end of the SSL/TLS/DTLS data stream is reached.
* This ensures closure of the inbound side, and checks that the
* peer followed the SSL/TLS close procedure properly, thus
* peer followed the SSL/TLS/DTLS close procedure properly, thus
* detecting possible truncation attacks.
* <P>
* This method is idempotent: if the inbound side has already
@ -801,7 +801,7 @@ public abstract class SSLEngine {
* called to flush any remaining handshake data.
*
* @throws SSLException
* if this engine has not received the proper SSL/TLS close
* if this engine has not received the proper SSL/TLS/DTLS close
* notification message from the peer.
*
* @see #isInboundDone()
@ -814,7 +814,7 @@ public abstract class SSLEngine {
* Returns whether {@link #unwrap(ByteBuffer, ByteBuffer)} will
* accept any more inbound data messages.
*
* @return true if the <code>SSLEngine</code> will not
* @return true if the {@code SSLEngine} will not
* consume anymore network data (and by implication,
* will not produce any more application data.)
* @see #closeInbound()
@ -824,7 +824,7 @@ public abstract class SSLEngine {
/**
* Signals that no more outbound application data will be sent
* on this <code>SSLEngine</code>.
* on this {@code SSLEngine}.
* <P>
* This method is idempotent: if the outbound side has already
* been closed, this method does not do anything.
@ -841,12 +841,12 @@ public abstract class SSLEngine {
* Returns whether {@link #wrap(ByteBuffer, ByteBuffer)} will
* produce any more outbound data messages.
* <P>
* Note that during the closure phase, a <code>SSLEngine</code> may
* Note that during the closure phase, a {@code SSLEngine} may
* generate handshake closure data that must be sent to the peer.
* <code>wrap()</code> must be called to generate this data. When
* {@code wrap()} must be called to generate this data. When
* this method returns true, no more outbound data will be created.
*
* @return true if the <code>SSLEngine</code> will not produce
* @return true if the {@code SSLEngine} will not produce
* any more network data
*
* @see #closeOutbound()
@ -890,10 +890,10 @@ public abstract class SSLEngine {
/**
* Sets the cipher suites enabled for use on this engine.
* <P>
* Each cipher suite in the <code>suites</code> parameter must have
* Each cipher suite in the {@code suites} parameter must have
* been listed by getSupportedCipherSuites(), or the method will
* fail. Following a successful call to this method, only suites
* listed in the <code>suites</code> parameter are enabled for use.
* listed in the {@code suites} parameter are enabled for use.
* <P>
* See {@link #getEnabledCipherSuites()} for more information
* on why a specific cipher suite may never be used on a engine.
@ -910,7 +910,7 @@ public abstract class SSLEngine {
/**
* Returns the names of the protocols which could be enabled for use
* with this <code>SSLEngine</code>.
* with this {@code SSLEngine}.
*
* @return an array of protocols supported
*/
@ -919,7 +919,7 @@ public abstract class SSLEngine {
/**
* Returns the names of the protocol versions which are currently
* enabled for use with this <code>SSLEngine</code>.
* enabled for use with this {@code SSLEngine}.
*
* @return an array of protocols
* @see #setEnabledProtocols(String [])
@ -932,7 +932,7 @@ public abstract class SSLEngine {
* <P>
* The protocols must have been listed by getSupportedProtocols()
* as being supported. Following a successful call to this method,
* only protocols listed in the <code>protocols</code> parameter
* only protocols listed in the {@code protocols} parameter
* are enabled for use.
*
* @param protocols Names of all the protocols to enable.
@ -945,8 +945,8 @@ public abstract class SSLEngine {
/**
* Returns the <code>SSLSession</code> in use in this
* <code>SSLEngine</code>.
* Returns the {@code SSLSession} in use in this
* {@code SSLEngine}.
* <P>
* These can be long lived, and frequently correspond to an entire
* login session for some user. The session specifies a particular
@ -961,22 +961,22 @@ public abstract class SSLEngine {
* a session object which reports an invalid cipher suite of
* "SSL_NULL_WITH_NULL_NULL".
*
* @return the <code>SSLSession</code> for this <code>SSLEngine</code>
* @return the {@code SSLSession} for this {@code SSLEngine}
* @see SSLSession
*/
public abstract SSLSession getSession();
/**
* Returns the {@code SSLSession} being constructed during a SSL/TLS
* Returns the {@code SSLSession} being constructed during a SSL/TLS/DTLS
* handshake.
* <p>
* TLS protocols may negotiate parameters that are needed when using
* TLS/DTLS protocols may negotiate parameters that are needed when using
* an instance of this class, but before the {@code SSLSession} has
* been completely initialized and made available via {@code getSession}.
* For example, the list of valid signature algorithms may restrict
* the type of certificates that can used during TrustManager
* decisions, or the maximum TLS fragment packet sizes can be
* decisions, or the maximum TLS/DTLS fragment packet sizes can be
* resized to better support the network environment.
* <p>
* This method provides early access to the {@code SSLSession} being
@ -1012,26 +1012,26 @@ public abstract class SSLEngine {
* Initiates handshaking (initial or renegotiation) on this SSLEngine.
* <P>
* This method is not needed for the initial handshake, as the
* <code>wrap()</code> and <code>unwrap()</code> methods will
* {@code wrap()} and {@code unwrap()} methods will
* implicitly call this method if handshaking has not already begun.
* <P>
* Note that the peer may also request a session renegotiation with
* this <code>SSLEngine</code> by sending the appropriate
* this {@code SSLEngine} by sending the appropriate
* session renegotiate handshake message.
* <P>
* Unlike the {@link SSLSocket#startHandshake()
* SSLSocket#startHandshake()} method, this method does not block
* until handshaking is completed.
* <P>
* To force a complete SSL/TLS session renegotiation, the current
* To force a complete SSL/TLS/DTLS session renegotiation, the current
* session should be invalidated prior to calling this method.
* <P>
* Some protocols may not support multiple handshakes on an existing
* engine and may throw an <code>SSLException</code>.
* engine and may throw an {@code SSLException}.
*
* @throws SSLException
* if a problem was encountered while signaling the
* <code>SSLEngine</code> to begin a new handshake.
* {@code SSLEngine} to begin a new handshake.
* See the class description for more information on
* engine closure.
* @throws IllegalStateException if the client/server mode
@ -1042,9 +1042,9 @@ public abstract class SSLEngine {
/**
* Returns the current handshake status for this <code>SSLEngine</code>.
* Returns the current handshake status for this {@code SSLEngine}.
*
* @return the current <code>SSLEngineResult.HandshakeStatus</code>.
* @return the current {@code SSLEngineResult.HandshakeStatus}.
*/
public abstract SSLEngineResult.HandshakeStatus getHandshakeStatus();

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2015, 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
@ -27,13 +27,13 @@ package javax.net.ssl;
/**
* An encapsulation of the result state produced by
* <code>SSLEngine</code> I/O calls.
* {@code SSLEngine} I/O calls.
*
* <p> A <code>SSLEngine</code> provides a means for establishing
* secure communication sessions between two peers. <code>SSLEngine</code>
* <p> A {@code SSLEngine} provides a means for establishing
* secure communication sessions between two peers. {@code SSLEngine}
* operations typically consume bytes from an input buffer and produce
* bytes in an output buffer. This class provides operational result
* values describing the state of the <code>SSLEngine</code>, including
* values describing the state of the {@code SSLEngine}, including
* indications of what operations are needed to finish an
* ongoing handshake. Lastly, it reports the number of bytes consumed
* and produced as a result of this operation.
@ -49,12 +49,12 @@ package javax.net.ssl;
public class SSLEngineResult {
/**
* An <code>SSLEngineResult</code> enum describing the overall result
* of the <code>SSLEngine</code> operation.
* An {@code SSLEngineResult} enum describing the overall result
* of the {@code SSLEngine} operation.
*
* The <code>Status</code> value does not reflect the
* state of a <code>SSLEngine</code> handshake currently
* in progress. The <code>SSLEngineResult's HandshakeStatus</code>
* The {@code Status} value does not reflect the
* state of a {@code SSLEngine} handshake currently
* in progress. The {@code SSLEngineResult's HandshakeStatus}
* should be consulted for that information.
*
* @author Brad R. Wetmore
@ -63,7 +63,7 @@ public class SSLEngineResult {
public static enum Status {
/**
* The <code>SSLEngine</code> was not able to unwrap the
* The {@code SSLEngine} was not able to unwrap the
* incoming data because there were not enough source bytes
* available to make a complete packet.
*
@ -73,7 +73,7 @@ public class SSLEngineResult {
BUFFER_UNDERFLOW,
/**
* The <code>SSLEngine</code> was not able to process the
* The {@code SSLEngine} was not able to process the
* operation because there are not enough bytes available in the
* destination buffer to hold the result.
* <P>
@ -85,22 +85,22 @@ public class SSLEngineResult {
BUFFER_OVERFLOW,
/**
* The <code>SSLEngine</code> completed the operation, and
* The {@code SSLEngine} completed the operation, and
* is available to process similar calls.
*/
OK,
/**
* The operation just closed this side of the
* <code>SSLEngine</code>, or the operation
* {@code SSLEngine}, or the operation
* could not be completed because it was already closed.
*/
CLOSED;
}
/**
* An <code>SSLEngineResult</code> enum describing the current
* handshaking state of this <code>SSLEngine</code>.
* An {@code SSLEngineResult} enum describing the current
* handshaking state of this {@code SSLEngine}.
*
* @author Brad R. Wetmore
* @since 1.5
@ -108,17 +108,17 @@ public class SSLEngineResult {
public static enum HandshakeStatus {
/**
* The <code>SSLEngine</code> is not currently handshaking.
* The {@code SSLEngine} is not currently handshaking.
*/
NOT_HANDSHAKING,
/**
* The <code>SSLEngine</code> has just finished handshaking.
* The {@code SSLEngine} has just finished handshaking.
* <P>
* This value is only generated by a call to
* <code>SSLEngine.wrap()/unwrap()</code> when that call
* {@code SSLEngine.wrap()/unwrap()} when that call
* finishes a handshake. It is never generated by
* <code>SSLEngine.getHandshakeStatus()</code>.
* {@code SSLEngine.getHandshakeStatus()}.
*
* @see SSLEngine#wrap(ByteBuffer, ByteBuffer)
* @see SSLEngine#unwrap(ByteBuffer, ByteBuffer)
@ -127,7 +127,7 @@ public class SSLEngineResult {
FINISHED,
/**
* The <code>SSLEngine</code> needs the results of one (or more)
* The {@code SSLEngine} needs the results of one (or more)
* delegated tasks before handshaking can continue.
*
* @see SSLEngine#getDelegatedTask()
@ -135,8 +135,8 @@ public class SSLEngineResult {
NEED_TASK,
/**
* The <code>SSLEngine</code> must send data to the remote side
* before handshaking can continue, so <code>SSLEngine.wrap()</code>
* The {@code SSLEngine} must send data to the remote side
* before handshaking can continue, so {@code SSLEngine.wrap()}
* should be called.
*
* @see SSLEngine#wrap(ByteBuffer, ByteBuffer)
@ -144,10 +144,22 @@ public class SSLEngineResult {
NEED_WRAP,
/**
* The <code>SSLEngine</code> needs to receive data from the
* The {@code SSLEngine} needs to receive data from the
* remote side before handshaking can continue.
*/
NEED_UNWRAP;
NEED_UNWRAP,
/**
* The {@code SSLEngine} needs to unwrap before handshaking can
* can continue.
* <P>
* This value is used to indicate that not-yet-interpreted data
* has been previously received from the remote side, and does
* not need to be received again.
*
* @since 1.9
*/
NEED_UNWRAP_AGAIN;
}
@ -155,6 +167,7 @@ public class SSLEngineResult {
private final HandshakeStatus handshakeStatus;
private final int bytesConsumed;
private final int bytesProduced;
private final long sequenceNumber;
/**
* Initializes a new instance of this class.
@ -172,12 +185,44 @@ public class SSLEngineResult {
* the number of bytes placed into the destination ByteBuffer
*
* @throws IllegalArgumentException
* if the <code>status</code> or <code>handshakeStatus</code>
* arguments are null, or if <code>bytesConsumed</code> or
* <code>bytesProduced</code> is negative.
* if the {@code status} or {@code handshakeStatus}
* arguments are null, or if {@code bytesConsumed} or
* {@code bytesProduced} is negative.
*/
public SSLEngineResult(Status status, HandshakeStatus handshakeStatus,
int bytesConsumed, int bytesProduced) {
this(status, handshakeStatus, bytesConsumed, bytesProduced, -1);
}
/**
* Initializes a new instance of this class.
*
* @param status
* the return value of the operation.
*
* @param handshakeStatus
* the current handshaking status.
*
* @param bytesConsumed
* the number of bytes consumed from the source ByteBuffer
*
* @param bytesProduced
* the number of bytes placed into the destination ByteBuffer
*
* @param sequenceNumber
* the sequence number (unsigned long) of the produced or
* consumed SSL/TLS/DTLS record, or ${@code -1L} if no record
* produced or consumed
*
* @throws IllegalArgumentException
* if the {@code status} or {@code handshakeStatus}
* arguments are null, or if {@code bytesConsumed} or
* {@code bytesProduced} is negative
*
* @since 1.9
*/
public SSLEngineResult(Status status, HandshakeStatus handshakeStatus,
int bytesConsumed, int bytesProduced, long sequenceNumber) {
if ((status == null) || (handshakeStatus == null) ||
(bytesConsumed < 0) || (bytesProduced < 0)) {
@ -188,10 +233,11 @@ public class SSLEngineResult {
this.handshakeStatus = handshakeStatus;
this.bytesConsumed = bytesConsumed;
this.bytesProduced = bytesProduced;
this.sequenceNumber = sequenceNumber;
}
/**
* Gets the return value of this <code>SSLEngine</code> operation.
* Gets the return value of this {@code SSLEngine} operation.
*
* @return the return value
*/
@ -200,7 +246,7 @@ public class SSLEngineResult {
}
/**
* Gets the handshake status of this <code>SSLEngine</code>
* Gets the handshake status of this {@code SSLEngine}
* operation.
*
* @return the handshake status
@ -227,6 +273,41 @@ public class SSLEngineResult {
return bytesProduced;
}
/**
* Returns the sequence number of the produced or consumed SSL/TLS/DTLS
* record (optional operation).
*
* @apiNote Note that sequence number is an unsigned long and cannot
* exceed {@code -1L}. It is desired to use the unsigned
* long comparing mode for comparison of unsigned long values
* (see also {@link java.lang.Long#compareUnsigned()
* Long.compareUnsigned()}).
* <P>
* For DTLS protocols, the first 16 bits of the sequence
* number is a counter value (epoch) that is incremented on
* every cipher state change. The remaining 48 bits on the
* right side of the sequence number represents the sequence
* of the record, which is maintained separately for each epoch.
*
* @implNote It is recommended that providers should never allow the
* sequence number incremented to {@code -1L}. If the sequence
* number is close to wrapping, renegotiate should be requested,
* otherwise the connection should be closed immediately.
* This should be carried on automatically by the underlying
* implementation.
*
* @return the sequence number of the produced or consumed SSL/TLS/DTLS
* record; or ${@code -1L} if no record is produced or consumed,
* or this operation is not supported by the underlying provider
*
* @see java.lang.Long#compareUnsigned(boolean, boolean)
*
* @since 1.9
*/
final public long sequenceNumber() {
return sequenceNumber;
}
/**
* Returns a String representation of this object.
*/
@ -235,6 +316,8 @@ public class SSLEngineResult {
return ("Status = " + status +
" HandshakeStatus = " + handshakeStatus +
"\nbytesConsumed = " + bytesConsumed +
" bytesProduced = " + bytesProduced);
" bytesProduced = " + bytesProduced +
(sequenceNumber == -1 ? "" :
" sequenceNumber = " + Long.toUnsignedString(sequenceNumber)));
}
}

View file

@ -35,22 +35,22 @@ import java.util.Collections;
import java.util.LinkedHashMap;
/**
* Encapsulates parameters for an SSL/TLS connection. The parameters
* are the list of ciphersuites to be accepted in an SSL/TLS handshake,
* Encapsulates parameters for an SSL/TLS/DTLS connection. The parameters
* are the list of ciphersuites to be accepted in an SSL/TLS/DTLS handshake,
* the list of protocols to be allowed, the endpoint identification
* algorithm during SSL/TLS handshaking, the Server Name Indication (SNI),
* the algorithm constraints and whether SSL/TLS servers should request
* or require client authentication, etc.
* algorithm during SSL/TLS/DTLS handshaking, the Server Name Indication (SNI),
* the maximum network packet size, the algorithm constraints and whether
* SSL/TLS/DTLS servers should request or require client authentication, etc.
* <p>
* SSLParameters can be created via the constructors in this class.
* Objects can also be obtained using the <code>getSSLParameters()</code>
* Objects can also be obtained using the {@code getSSLParameters()}
* methods in
* {@link SSLSocket#getSSLParameters SSLSocket} and
* {@link SSLServerSocket#getSSLParameters SSLServerSocket} and
* {@link SSLEngine#getSSLParameters SSLEngine} or the
* {@link SSLContext#getDefaultSSLParameters getDefaultSSLParameters()} and
* {@link SSLContext#getSupportedSSLParameters getSupportedSSLParameters()}
* methods in <code>SSLContext</code>.
* methods in {@code SSLContext}.
* <p>
* SSLParameters can be applied to a connection via the methods
* {@link SSLSocket#setSSLParameters SSLSocket.setSSLParameters()} and
@ -74,14 +74,18 @@ public class SSLParameters {
private Map<Integer, SNIServerName> sniNames = null;
private Map<Integer, SNIMatcher> sniMatchers = null;
private boolean preferLocalCipherSuites;
private boolean enableRetransmissions = true;
private int maximumPacketSize = 0;
/**
* Constructs SSLParameters.
* <p>
* The values of cipherSuites, protocols, cryptographic algorithm
* constraints, endpoint identification algorithm, server names and
* server name matchers are set to <code>null</code>, useCipherSuitesOrder,
* wantClientAuth and needClientAuth are set to <code>false</code>.
* server name matchers are set to {@code null}; useCipherSuitesOrder,
* wantClientAuth and needClientAuth are set to {@code false};
* enableRetransmissions is set to {@code true}; maximum network packet
* size is set to {@code 0}.
*/
public SSLParameters() {
// empty
@ -92,7 +96,7 @@ public class SSLParameters {
* <p>
* Calling this constructor is equivalent to calling the no-args
* constructor followed by
* <code>setCipherSuites(cipherSuites);</code>.
* {@code setCipherSuites(cipherSuites);}.
*
* @param cipherSuites the array of ciphersuites (or null)
*/
@ -106,7 +110,7 @@ public class SSLParameters {
* <p>
* Calling this constructor is equivalent to calling the no-args
* constructor followed by
* <code>setCipherSuites(cipherSuites); setProtocols(protocols);</code>.
* {@code setCipherSuites(cipherSuites); setProtocols(protocols);}.
*
* @param cipherSuites the array of ciphersuites (or null)
* @param protocols the array of protocols (or null)
@ -171,7 +175,7 @@ public class SSLParameters {
/**
* Sets whether client authentication should be requested. Calling
* this method clears the <code>needClientAuth</code> flag.
* this method clears the {@code needClientAuth} flag.
*
* @param wantClientAuth whether client authentication should be requested
*/
@ -191,7 +195,7 @@ public class SSLParameters {
/**
* Sets whether client authentication should be required. Calling
* this method clears the <code>wantClientAuth</code> flag.
* this method clears the {@code wantClientAuth} flag.
*
* @param needClientAuth whether client authentication should be required
*/
@ -218,9 +222,9 @@ public class SSLParameters {
* Sets the cryptographic algorithm constraints, which will be used
* in addition to any configured by the runtime environment.
* <p>
* If the <code>constraints</code> parameter is non-null, every
* If the {@code constraints} parameter is non-null, every
* cryptographic algorithm, key and algorithm parameters used in the
* SSL/TLS handshake must be permitted by the constraints.
* SSL/TLS/DTLS handshake must be permitted by the constraints.
*
* @param constraints the algorithm constraints (or null)
*
@ -249,9 +253,9 @@ public class SSLParameters {
/**
* Sets the endpoint identification algorithm.
* <p>
* If the <code>algorithm</code> parameter is non-null or non-empty, the
* If the {@code algorithm} parameter is non-null or non-empty, the
* endpoint identification/verification procedures must be handled during
* SSL/TLS handshaking. This is to prevent man-in-the-middle attacks.
* SSL/TLS/DTLS handshaking. This is to prevent man-in-the-middle attacks.
*
* @param algorithm The standard string name of the endpoint
* identification algorithm (or null). See Appendix A in the <a href=
@ -317,7 +321,7 @@ public class SSLParameters {
* 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
* For SSL/TLS/DTLS connections, the underlying SSL/TLS/DTLS 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
@ -440,7 +444,7 @@ public class SSLParameters {
*
* @param honorOrder whether local cipher suites order in
* {@code #getCipherSuites} should be honored during
* SSL/TLS handshaking.
* SSL/TLS/DTLS handshaking.
*
* @see #getUseCipherSuitesOrder()
*
@ -454,7 +458,7 @@ public class SSLParameters {
* Returns whether the local cipher suites preference should be honored.
*
* @return whether local cipher suites order in {@code #getCipherSuites}
* should be honored during SSL/TLS handshaking.
* should be honored during SSL/TLS/DTLS handshaking.
*
* @see #setUseCipherSuitesOrder(boolean)
*
@ -463,5 +467,107 @@ public class SSLParameters {
public final boolean getUseCipherSuitesOrder() {
return preferLocalCipherSuites;
}
/**
* Sets whether DTLS handshake retransmissions should be enabled.
*
* This method only applies to DTLS.
*
* @param enableRetransmissions
* {@code true} indicates that DTLS handshake retransmissions
* should be enabled; {@code false} indicates that DTLS handshake
* retransmissions should be disabled
*
* @see #getEnableRetransmissions()
*
* @since 1.9
*/
public void setEnableRetransmissions(boolean enableRetransmissions) {
this.enableRetransmissions = enableRetransmissions;
}
/**
* Returns whether DTLS handshake retransmissions should be enabled.
*
* This method only applies to DTLS.
*
* @return true, if DTLS handshake retransmissions should be enabled
*
* @see #setEnableRetransmissions(boolean)
*
* @since 1.9
*/
public boolean getEnableRetransmissions() {
return enableRetransmissions;
}
/**
* Sets the maximum expected network packet size in bytes for
* SSL/TLS/DTLS records.
*
* @apiNote It is recommended that if possible, the maximum packet size
* should not be less than 256 bytes so that small handshake
* messages, such as HelloVerifyRequests, are not fragmented.
*
* @implNote If the maximum packet size is too small to hold a minimal
* record, an implementation may attempt to generate as minimal
* records as possible. However, this may cause a generated
* packet to be larger than the maximum packet size.
*
* @param maximumPacketSize
* the maximum expected network packet size in bytes, or
* {@code 0} to use the implicit size that is automatically
* specified by the underlying implementation.
* @throws IllegalArgumentException
* if {@code maximumPacketSize} is negative.
*
* @see #getMaximumPacketSize()
*
* @since 1.9
*/
public void setMaximumPacketSize(int maximumPacketSize) {
if (maximumPacketSize < 0) {
throw new IllegalArgumentException(
"The maximum packet size cannot be negative");
}
this.maximumPacketSize = maximumPacketSize;
}
/**
* Returns the maximum expected network packet size in bytes for
* SSL/TLS/DTLS records.
*
* @apiNote The implicit size may not be a fixed value, especially
* for a DTLS protocols implementation.
*
* @implNote For SSL/TLS/DTLS connections, the underlying provider
* should calculate and specify the implicit value of the
* maximum expected network packet size if it is not
* configured explicitly. For any connection populated
* object, this method should never return {@code 0} so
* that applications can retrieve the actual implicit size
* of the underlying implementation.
* <P>
* An implementation should attempt to comply with the maximum
* packet size configuration. However, if the maximum packet
* size is too small to hold a minimal record, an implementation
* may try to generate as minimal records as possible. This
* may cause a generated packet to be larger than the maximum
* packet size.
*
* @return the maximum expected network packet size, or {@code 0} if
* use the implicit size that is automatically specified by
* the underlying implementation and this object has not been
* populated by any connection.
*
* @see #setMaximumPacketSize(int)
*
* @since 1.9
*/
public int getMaximumPacketSize() {
return maximumPacketSize;
}
}

View file

@ -35,7 +35,7 @@ import java.security.Principal;
* also be replaced by a different session. Sessions are created, or
* rejoined, as part of the SSL handshaking protocol. Sessions may be
* invalidated due to policies affecting security or resource usage,
* or by an application explicitly calling <code>invalidate</code>.
* or by an application explicitly calling {@code invalidate}.
* Session management policies are typically used to tune performance.
*
* <P> In addition to the standard session attributes, SSL sessions expose
@ -82,8 +82,8 @@ public interface SSLSession {
* security manager installed, the caller may require
* permission to access it or a security exception may be thrown.
* In a Java environment, the security manager's
* <code>checkPermission</code> method is called with a
* <code>SSLPermission("getSSLSessionContext")</code> permission.
* {@code checkPermission} method is called with a
* {@code SSLPermission("getSSLSessionContext")} permission.
*
* @throws SecurityException if the calling thread does not have
* permission to get SSL session context.
@ -148,14 +148,14 @@ public interface SSLSession {
/**
*
* Binds the specified <code>value</code> object into the
* Binds the specified {@code value} object into the
* session's application layer data
* with the given <code>name</code>.
* with the given {@code name}.
* <P>
* Any existing binding using the same <code>name</code> is
* replaced. If the new (or existing) <code>value</code> implements the
* <code>SSLSessionBindingListener</code> interface, the object
* represented by <code>value</code> is notified appropriately.
* Any existing binding using the same {@code name} is
* replaced. If the new (or existing) {@code value} implements the
* {@code SSLSessionBindingListener} interface, the object
* represented by {@code value} is notified appropriately.
* <p>
* For security reasons, the same named values may not be
* visible across different access control contexts.
@ -187,7 +187,7 @@ public interface SSLSession {
* Removes the object bound to the given name in the session's
* application layer data. Does nothing if there is no object
* bound to the given name. If the bound existing object
* implements the <code>SessionBindingListener</code> interface,
* implements the {@code SessionBindingListener} interface,
* it is notified appropriately.
* <p>
* For security reasons, the same named values may not be
@ -349,7 +349,7 @@ public interface SSLSession {
* by this method.
* <P>
* This value is not authenticated and should not be relied upon.
* It is mainly used as a hint for <code>SSLSession</code> caching
* It is mainly used as a hint for {@code SSLSession} caching
* strategies.
*
* @return the host name of the peer host, or null if no information
@ -364,7 +364,7 @@ public interface SSLSession {
* the client, it is the server's port number.
* <P>
* This value is not authenticated and should not be relied upon.
* It is mainly used as a hint for <code>SSLSession</code> caching
* It is mainly used as a hint for {@code SSLSession} caching
* strategies.
*
* @return the port number of the peer host, or -1 if no information
@ -375,14 +375,14 @@ public interface SSLSession {
public int getPeerPort();
/**
* Gets the current size of the largest SSL/TLS packet that is expected
* when using this session.
* Gets the current size of the largest SSL/TLS/DTLS packet that is
* expected when using this session.
* <P>
* A <code>SSLEngine</code> using this session may generate SSL/TLS
* An {@code SSLEngine} using this session may generate SSL/TLS/DTLS
* packets of any size up to and including the value returned by this
* method. All <code>SSLEngine</code> network buffers should be sized
* method. All {@code SSLEngine} network buffers should be sized
* at least this large to avoid insufficient space problems when
* performing <code>wrap</code> and <code>unwrap</code> calls.
* performing {@code wrap} and {@code unwrap} calls.
*
* @return the current maximum expected network packet size
*
@ -398,7 +398,7 @@ public interface SSLSession {
* Gets the current size of the largest application data that is
* expected when using this session.
* <P>
* <code>SSLEngine</code> application data buffers must be large
* {@code SSLEngine} application data buffers must be large
* enough to hold the application data from any inbound network
* application data packet received. Typically, outbound
* application data buffers can be of any size.

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2010, 2015, 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
@ -32,16 +32,17 @@ import java.security.cert.X509Certificate;
import java.security.cert.CertificateException;
/**
* Extensions to the <code>X509TrustManager</code> interface to support
* SSL/TLS connection sensitive trust management.
* Extensions to the {@code X509TrustManager} interface to support
* SSL/TLS/DTLS connection sensitive trust management.
* <p>
* To prevent man-in-the-middle attacks, hostname checks can be done
* to verify that the hostname in an end-entity certificate matches the
* targeted hostname. TLS does not require such checks, but some protocols
* over TLS (such as HTTPS) do. In earlier versions of the JDK, the
* certificate chain checks were done at the SSL/TLS layer, and the hostname
* verification checks were done at the layer over TLS. This class allows
* for the checking to be done during a single call to this class.
* targeted hostname. TLS/DTLS does not require such checks, but some
* protocols over TLS/DTLS (such as HTTPS) do. In earlier versions of the
* JDK, the certificate chain checks were done at the SSL/TLS/DTLS layer,
* and the hostname verification checks were done at the layer over TLS/DTLS.
* This class allows for the checking to be done during a single call to
* this class.
* <p>
* RFC 2830 defines the server identification specification for the "LDAPS"
* algorithm. RFC 2818 defines both the server identification and the
@ -62,17 +63,17 @@ public abstract class X509ExtendedTrustManager implements X509TrustManager {
* used. For instance, if RSAPublicKey is used, the authType
* should be "RSA". Checking is case-sensitive.
* <p>
* If the <code>socket</code> parameter is an instance of
* If the {@code socket} parameter is an instance of
* {@link javax.net.ssl.SSLSocket}, and the endpoint identification
* algorithm of the <code>SSLParameters</code> is non-empty, to prevent
* man-in-the-middle attacks, the address that the <code>socket</code>
* algorithm of the {@code SSLParameters} is non-empty, to prevent
* man-in-the-middle attacks, the address that the {@code socket}
* connected to should be checked against the peer's identity presented
* in the end-entity X509 certificate, as specified in the endpoint
* identification algorithm.
* <p>
* If the <code>socket</code> parameter is an instance of
* If the {@code socket} parameter is an instance of
* {@link javax.net.ssl.SSLSocket}, and the algorithm constraints of the
* <code>SSLParameters</code> is non-null, for every certificate in the
* {@code SSLParameters} is non-null, for every certificate in the
* certification path, fields such as subject public key, the signature
* algorithm, key usage, extended key usage, etc. need to conform to the
* algorithm constraints in place on this socket.
@ -83,8 +84,8 @@ public abstract class X509ExtendedTrustManager implements X509TrustManager {
* can be null, which indicates that implementations need not check
* the ssl parameters
* @throws IllegalArgumentException if null or zero-length array is passed
* in for the <code>chain</code> parameter or if null or zero-length
* string is passed in for the <code>authType</code> parameter
* in for the {@code chain} parameter or if null or zero-length
* string is passed in for the {@code authType} parameter
* @throws CertificateException if the certificate chain is not trusted
* by this TrustManager
*
@ -110,17 +111,17 @@ public abstract class X509ExtendedTrustManager implements X509TrustManager {
* used for the key exchange, and RSA when the key from the server
* certificate is used. Checking is case-sensitive.
* <p>
* If the <code>socket</code> parameter is an instance of
* If the {@code socket} parameter is an instance of
* {@link javax.net.ssl.SSLSocket}, and the endpoint identification
* algorithm of the <code>SSLParameters</code> is non-empty, to prevent
* man-in-the-middle attacks, the address that the <code>socket</code>
* algorithm of the {@code SSLParameters} is non-empty, to prevent
* man-in-the-middle attacks, the address that the {@code socket}
* connected to should be checked against the peer's identity presented
* in the end-entity X509 certificate, as specified in the endpoint
* identification algorithm.
* <p>
* If the <code>socket</code> parameter is an instance of
* If the {@code socket} parameter is an instance of
* {@link javax.net.ssl.SSLSocket}, and the algorithm constraints of the
* <code>SSLParameters</code> is non-null, for every certificate in the
* {@code SSLParameters} is non-null, for every certificate in the
* certification path, fields such as subject public key, the signature
* algorithm, key usage, extended key usage, etc. need to conform to the
* algorithm constraints in place on this socket.
@ -131,8 +132,8 @@ public abstract class X509ExtendedTrustManager implements X509TrustManager {
* can be null, which indicates that implementations need not check
* the ssl parameters
* @throws IllegalArgumentException if null or zero-length array is passed
* in for the <code>chain</code> parameter or if null or zero-length
* string is passed in for the <code>authType</code> parameter
* in for the {@code chain} parameter or if null or zero-length
* string is passed in for the {@code authType} parameter
* @throws CertificateException if the certificate chain is not trusted
* by this TrustManager
*
@ -153,15 +154,15 @@ public abstract class X509ExtendedTrustManager implements X509TrustManager {
* used. For instance, if RSAPublicKey is used, the authType
* should be "RSA". Checking is case-sensitive.
* <p>
* If the <code>engine</code> parameter is available, and the endpoint
* identification algorithm of the <code>SSLParameters</code> is
* If the {@code engine} parameter is available, and the endpoint
* identification algorithm of the {@code SSLParameters} is
* non-empty, to prevent man-in-the-middle attacks, the address that
* the <code>engine</code> connected to should be checked against
* the {@code engine} connected to should be checked against
* the peer's identity presented in the end-entity X509 certificate,
* as specified in the endpoint identification algorithm.
* <p>
* If the <code>engine</code> parameter is available, and the algorithm
* constraints of the <code>SSLParameters</code> is non-null, for every
* If the {@code engine} parameter is available, and the algorithm
* constraints of the {@code SSLParameters} is non-null, for every
* certificate in the certification path, fields such as subject public
* key, the signature algorithm, key usage, extended key usage, etc.
* need to conform to the algorithm constraints in place on this engine.
@ -172,8 +173,8 @@ public abstract class X509ExtendedTrustManager implements X509TrustManager {
* can be null, which indicates that implementations need not check
* the ssl parameters
* @throws IllegalArgumentException if null or zero-length array is passed
* in for the <code>chain</code> parameter or if null or zero-length
* string is passed in for the <code>authType</code> parameter
* in for the {@code chain} parameter or if null or zero-length
* string is passed in for the {@code authType} parameter
* @throws CertificateException if the certificate chain is not trusted
* by this TrustManager
*
@ -199,15 +200,15 @@ public abstract class X509ExtendedTrustManager implements X509TrustManager {
* used for the key exchange, and RSA when the key from the server
* certificate is used. Checking is case-sensitive.
* <p>
* If the <code>engine</code> parameter is available, and the endpoint
* identification algorithm of the <code>SSLParameters</code> is
* If the {@code engine} parameter is available, and the endpoint
* identification algorithm of the {@code SSLParameters} is
* non-empty, to prevent man-in-the-middle attacks, the address that
* the <code>engine</code> connected to should be checked against
* the {@code engine} connected to should be checked against
* the peer's identity presented in the end-entity X509 certificate,
* as specified in the endpoint identification algorithm.
* <p>
* If the <code>engine</code> parameter is available, and the algorithm
* constraints of the <code>SSLParameters</code> is non-null, for every
* If the {@code engine} parameter is available, and the algorithm
* constraints of the {@code SSLParameters} is non-null, for every
* certificate in the certification path, fields such as subject public
* key, the signature algorithm, key usage, extended key usage, etc.
* need to conform to the algorithm constraints in place on this engine.
@ -218,8 +219,8 @@ public abstract class X509ExtendedTrustManager implements X509TrustManager {
* can be null, which indicates that implementations need not check
* the ssl parameters
* @throws IllegalArgumentException if null or zero-length array is passed
* in for the <code>chain</code> parameter or if null or zero-length
* string is passed in for the <code>authType</code> parameter
* in for the {@code chain} parameter or if null or zero-length
* string is passed in for the {@code authType} parameter
* @throws CertificateException if the certificate chain is not trusted
* by this TrustManager
*

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2015, 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
@ -26,41 +26,54 @@
package sun.security.ssl;
import java.io.*;
import java.io.InputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import javax.net.ssl.SSLProtocolException;
/**
* InputStream for application data as returned by SSLSocket.getInputStream().
* It uses an InputRecord as internal buffer that is refilled on demand
* whenever it runs out of data.
*
* @author David Brownell
*/
class AppInputStream extends InputStream {
final class AppInputStream extends InputStream {
// the buffer size for each read of network data
private static final int READ_BUFFER_SIZE = 4096;
// static dummy array we use to implement skip()
private final static byte[] SKIP_ARRAY = new byte[1024];
private static final byte[] SKIP_ARRAY = new byte[256];
private SSLSocketImpl c;
InputRecord r;
// the related socket of the input stream
private final SSLSocketImpl socket;
// the temporary buffer used to read network
private ByteBuffer buffer;
// Is application data available in the stream?
private boolean appDataIsAvailable;
// One element array used to implement the single byte read() method
private final byte[] oneByte = new byte[1];
AppInputStream(SSLSocketImpl conn) {
r = new InputRecord();
c = conn;
this.buffer = ByteBuffer.allocate(READ_BUFFER_SIZE);
this.socket = conn;
this.appDataIsAvailable = false;
}
/**
* Return the minimum number of bytes that can be read without blocking.
*
* Currently not synchronized.
*/
@Override
public int available() throws IOException {
if (c.checkEOF() || (r.isAppDataValid() == false)) {
if ((!appDataIsAvailable) || socket.checkEOF()) {
return 0;
}
return r.available();
return buffer.remaining();
}
/**
@ -72,17 +85,21 @@ class AppInputStream extends InputStream {
if (n <= 0) { // EOF
return -1;
}
return oneByte[0] & 0xff;
return oneByte[0] & 0xFF;
}
/**
* Read up to "len" bytes into this buffer, starting at "off".
* Reads up to {@code len} bytes of data from the input stream into an
* array of bytes. An attempt is made to read as many as {@code len} bytes,
* but a smaller number may be read. The number of bytes actually read
* is returned as an integer.
*
* If the layer above needs more data, it asks for more, so we
* are responsible only for blocking to fill at most one buffer,
* and returning "-1" on non-fault EOF status.
*/
@Override
public synchronized int read(byte b[], int off, int len)
public synchronized int read(byte[] b, int off, int len)
throws IOException {
if (b == null) {
throw new NullPointerException();
@ -92,28 +109,69 @@ class AppInputStream extends InputStream {
return 0;
}
if (c.checkEOF()) {
if (socket.checkEOF()) {
return -1;
}
// Read the available bytes at first.
int remains = available();
if (remains > 0) {
int howmany = Math.min(remains, len);
buffer.get(b, off, howmany);
return howmany;
}
appDataIsAvailable = false;
int volume = 0;
try {
/*
* Read data if needed ... notice that the connection guarantees
* that handshake, alert, and change cipher spec data streams are
* handled as they arrive, so we never see them here.
*/
while (r.available() == 0) {
c.readDataRecord(r);
if (c.checkEOF()) {
while(volume == 0) {
// Clear the buffer for a new record reading.
buffer.clear();
//
// grow the buffer if needed
//
// Read the header of a record into the buffer, and return
// the packet size.
int packetLen = socket.bytesInCompletePacket();
if (packetLen < 0) { // EOF
return -1;
}
// Is this packet bigger than SSL/TLS normally allows?
if (packetLen > SSLRecord.maxLargeRecordSize) {
throw new SSLProtocolException(
"Illegal packet size: " + packetLen);
}
if (packetLen > buffer.remaining()) {
buffer = ByteBuffer.allocate(packetLen);
}
volume = socket.readRecord(buffer);
if (volume < 0) { // EOF
return -1;
} else if (volume > 0) {
appDataIsAvailable = true;
break;
}
}
int howmany = Math.min(len, r.available());
howmany = r.read(b, off, howmany);
int howmany = Math.min(len, volume);
buffer.get(b, off, howmany);
return howmany;
} catch (Exception e) {
// shutdown and rethrow (wrapped) exception as appropriate
c.handleException(e);
socket.handleException(e);
// dummy for compiler
return -1;
}
@ -147,9 +205,8 @@ class AppInputStream extends InputStream {
*/
@Override
public void close() throws IOException {
c.close();
socket.close();
}
// inherit default mark/reset behavior (throw Exceptions) from InputStream
}

View file

@ -23,41 +23,33 @@
* questions.
*/
package sun.security.ssl;
import java.io.OutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
/*
* Output stream for application data. This is the kind of stream
* that's handed out via SSLSocket.getOutputStream(). It's all the application
* ever sees.
*
* Once the initial handshake has completed, application data may be
* interleaved with handshake data. That is handled internally and remains
* transparent to the application.
* OutputStream for application data as returned by SSLSocket.getOutputStream().
*
* @author David Brownell
*/
class AppOutputStream extends OutputStream {
private SSLSocketImpl c;
OutputRecord r;
private SSLSocketImpl socket;
// One element array used to implement the write(byte) method
private final byte[] oneByte = new byte[1];
AppOutputStream(SSLSocketImpl conn) {
r = new OutputRecord(Record.ct_application_data);
c = conn;
this.socket = conn;
}
/**
* Write the data out, NOW.
*/
@Override
synchronized public void write(byte b[], int off, int len)
synchronized public void write(byte[] b, int off, int len)
throws IOException {
if (b == null) {
throw new NullPointerException();
@ -68,64 +60,15 @@ class AppOutputStream extends OutputStream {
}
// check if the Socket is invalid (error or closed)
c.checkWrite();
socket.checkWrite();
/*
* By default, we counter chosen plaintext issues on CBC mode
* ciphersuites in SSLv3/TLS1.0 by sending one byte of application
* data in the first record of every payload, and the rest in
* subsequent record(s). Note that the issues have been solved in
* TLS 1.1 or later.
*
* It is not necessary to split the very first application record of
* a freshly negotiated TLS session, as there is no previous
* application data to guess. To improve compatibility, we will not
* split such records.
*
* This avoids issues in the outbound direction. For a full fix,
* the peer must have similar protections.
*/
boolean isFirstRecordOfThePayload = true;
// Always flush at the end of each application level record.
// This lets application synchronize read and write streams
// however they like; if we buffered here, they couldn't.
// Delegate the writing to the underlying socket.
try {
do {
boolean holdRecord = false;
int howmuch;
if (isFirstRecordOfThePayload && c.needToSplitPayload()) {
howmuch = Math.min(0x01, r.availableDataBytes());
/*
* Nagle's algorithm (TCP_NODELAY) was coming into
* play here when writing short (split) packets.
* Signal to the OutputRecord code to internally
* buffer this small packet until the next outbound
* packet (of any type) is written.
*/
if ((len != 1) && (howmuch == 1)) {
holdRecord = true;
}
} else {
howmuch = Math.min(len, r.availableDataBytes());
}
if (isFirstRecordOfThePayload && howmuch != 0) {
isFirstRecordOfThePayload = false;
}
// NOTE: *must* call c.writeRecord() even for howmuch == 0
if (howmuch > 0) {
r.write(b, off, howmuch);
off += howmuch;
len -= howmuch;
}
c.writeRecord(r, holdRecord);
c.checkWrite();
} while (len > 0);
socket.writeRecord(b, off, len);
socket.checkWrite();
} catch (Exception e) {
// shutdown and rethrow (wrapped) exception as appropriate
c.handleException(e);
socket.handleException(e);
}
}
@ -143,7 +86,7 @@ class AppOutputStream extends OutputStream {
*/
@Override
public void close() throws IOException {
c.close();
socket.close();
}
// inherit no-op flush()

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2015, 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,19 +28,26 @@ package sun.security.ssl;
import java.util.Arrays;
/**
* This class represents an SSL/TLS message authentication token,
* This class represents an SSL/TLS/DTLS message authentication token,
* which encapsulates a sequence number and ensures that attempts to
* delete or reorder messages can be detected.
*
* Each SSL/TLS connection state contains a sequence number, which
* is maintained separately for read and write states. The sequence
* number MUST be set to zero whenever a connection state is made the
* active state. Sequence numbers are of type uint64 and may not
* exceed 2^64-1. Sequence numbers do not wrap. If a SSL/TLS
* implementation would need to wrap a sequence number, it must
* renegotiate instead. A sequence number is incremented after each
* record: specifically, the first record transmitted under a
* particular connection state MUST use sequence number 0.
* Each connection state contains a sequence number, which is maintained
* separately for read and write states.
*
* For SSL/TLS protocols, the sequence number MUST be set to zero
* whenever a connection state is made the active state.
*
* DTLS uses an explicit sequence number, rather than an implicit one.
* Sequence numbers are maintained separately for each epoch, with
* each sequence number initially being 0 for each epoch. The sequence
* number used to compute the DTLS MAC is the 64-bit value formed by
* concatenating the epoch and the sequence number.
*
* Sequence numbers do not wrap. If an implementation would need to wrap
* a sequence number, it must renegotiate instead. A sequence number is
* incremented after each record: specifically, the first record transmitted
* under a particular connection state MUST use sequence number 0.
*/
class Authenticator {
@ -56,13 +63,30 @@ class Authenticator {
// sequence number + record type + protocol version + record length
private static final int BLOCK_SIZE_TLS = 8 + 1 + 2 + 2;
// the block size of DTLS v1.0 and later:
// epoch + sequence number + record type + protocol version + record length
private static final int BLOCK_SIZE_DTLS = 2 + 6 + 1 + 2 + 2;
private final boolean isDTLS;
/**
* Default construct, no message authentication token is initialized.
*
* Note that this construct can only be called for null MAC
*/
Authenticator() {
block = new byte[0];
protected Authenticator(boolean isDTLS) {
if (isDTLS) {
// For DTLS protocols, plaintexts use explicit epoch and
// sequence number in each record. The first 8 byte of
// the block is initialized for null MAC so that the
// epoch and sequence number can be acquired to generate
// plaintext records.
block = new byte[8];
} else {
block = new byte[0];
}
this.isDTLS = isDTLS;
}
/**
@ -70,12 +94,22 @@ class Authenticator {
* SSL/TLS protocol.
*/
Authenticator(ProtocolVersion protocolVersion) {
if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
if (protocolVersion.isDTLSProtocol()) {
block = new byte[BLOCK_SIZE_DTLS];
block[9] = protocolVersion.major;
block[10] = protocolVersion.minor;
this.isDTLS = true;
} else if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
block = new byte[BLOCK_SIZE_TLS];
block[9] = protocolVersion.major;
block[10] = protocolVersion.minor;
this.isDTLS = false;
} else {
block = new byte[BLOCK_SIZE_SSL];
this.isDTLS = false;
}
}
@ -93,11 +127,19 @@ class Authenticator {
* Conservatively, we don't allow more records to be generated
* when there are only 2^8 sequence numbers left.
*/
return (block.length != 0 &&
if (isDTLS) {
return (block.length != 0 &&
// no epoch bytes, block[0] and block[1]
block[2] == (byte)0xFF && block[3] == (byte)0xFF &&
block[4] == (byte)0xFF && block[5] == (byte)0xFF &&
block[6] == (byte)0xFF);
} else {
return (block.length != 0 &&
block[0] == (byte)0xFF && block[1] == (byte)0xFF &&
block[2] == (byte)0xFF && block[3] == (byte)0xFF &&
block[4] == (byte)0xFF && block[5] == (byte)0xFF &&
block[6] == (byte)0xFF);
}
}
/**
@ -113,14 +155,22 @@ class Authenticator {
final boolean seqNumIsHuge() {
/*
* Conservatively, we should ask for renegotiation when there are
* only 2^48 sequence numbers left.
* only 2^32 sequence numbers left.
*/
return (block.length != 0 &&
block[0] == (byte)0xFF && block[1] == (byte)0xFF);
if (isDTLS) {
return (block.length != 0 &&
// no epoch bytes, block[0] and block[1]
block[2] == (byte)0xFF && block[3] == (byte)0xFF);
} else {
return (block.length != 0 &&
block[0] == (byte)0xFF && block[1] == (byte)0xFF &&
block[2] == (byte)0xFF && block[3] == (byte)0xFF);
}
}
/**
* Gets the current sequence number.
* Gets the current sequence number, including the epoch number for
* DTLS protocols.
*
* @return the byte array of the current sequence number
*/
@ -128,6 +178,33 @@ class Authenticator {
return Arrays.copyOf(block, 8);
}
/**
* Sets the epoch number (only apply to DTLS protocols).
*/
final void setEpochNumber(int epoch) {
if (!isDTLS) {
throw new RuntimeException(
"Epoch numbers apply to DTLS protocols only");
}
block[0] = (byte)((epoch >> 8) & 0xFF);
block[1] = (byte)(epoch & 0xFF);
}
/**
* Increase the sequence number.
*/
final void increaseSequenceNumber() {
/*
* The sequence number in the block array is a 64-bit
* number stored in big-endian format.
*/
int k = 7;
while ((k >= 0) && (++block[k] == 0)) {
k--;
}
}
/**
* Acquires the current message authentication information with the
* specified record type and fragment length, and then increases the
@ -135,27 +212,50 @@ class Authenticator {
*
* @param type the record type
* @param length the fragment of the record
* @param sequence the explicit sequence number of the record
*
* @return the byte array of the current message authentication information
*/
final byte[] acquireAuthenticationBytes(byte type, int length) {
final byte[] acquireAuthenticationBytes(
byte type, int length, byte[] sequence) {
byte[] copy = block.clone();
if (sequence != null) {
if (sequence.length != 8) {
throw new RuntimeException(
"Insufficient explicit sequence number bytes");
}
System.arraycopy(sequence, 0, copy, 0, sequence.length);
} // Otherwise, use the implicit sequence number.
if (block.length != 0) {
copy[8] = type;
copy[copy.length - 2] = (byte)(length >> 8);
copy[copy.length - 1] = (byte)(length);
/*
* Increase the sequence number in the block array
* it is a 64-bit number stored in big-endian format
*/
int k = 7;
while ((k >= 0) && (++block[k] == 0)) {
k--;
if (sequence == null || sequence.length != 0) {
// Increase the implicit sequence number in the block array.
increaseSequenceNumber();
}
}
return copy;
}
final static long toLong(byte[] recordEnS) {
if (recordEnS != null && recordEnS.length == 8) {
return ((recordEnS[0] & 0xFFL) << 56) |
((recordEnS[1] & 0xFFL) << 48) |
((recordEnS[2] & 0xFFL) << 40) |
((recordEnS[3] & 0xFFL) << 32) |
((recordEnS[4] & 0xFFL) << 24) |
((recordEnS[5] & 0xFFL) << 16) |
((recordEnS[6] & 0xFFL) << 8) |
(recordEnS[7] & 0xFFL);
}
return -1L;
}
}

View file

@ -154,9 +154,9 @@ final class CipherBox {
* NULL cipherbox. Identity operation, no encryption.
*/
private CipherBox() {
this.protocolVersion = ProtocolVersion.DEFAULT;
this.protocolVersion = ProtocolVersion.DEFAULT_TLS;
this.cipher = null;
this.cipherType = STREAM_CIPHER;
this.cipherType = NULL_CIPHER;
this.fixedIv = new byte[0];
this.key = null;
this.mode = Cipher.ENCRYPT_MODE; // choose at random
@ -197,7 +197,7 @@ final class CipherBox {
*/
if (iv == null && bulkCipher.ivSize != 0 &&
mode == Cipher.DECRYPT_MODE &&
protocolVersion.v >= ProtocolVersion.TLS11.v) {
protocolVersion.useTLS11PlusSpec()) {
iv = getFixedMask(bulkCipher.ivSize);
}
@ -491,7 +491,7 @@ final class CipherBox {
newLen = removePadding(
buf, offset, newLen, tagLen, blockSize, protocolVersion);
if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
if (protocolVersion.useTLS11PlusSpec()) {
if (newLen < blockSize) {
throw new BadPaddingException("invalid explicit IV");
}
@ -573,7 +573,7 @@ final class CipherBox {
newLen = removePadding(bb, tagLen, blockSize, protocolVersion);
// check the explicit IV of TLS v1.1 or later
if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
if (protocolVersion.useTLS11PlusSpec()) {
if (newLen < blockSize) {
throw new BadPaddingException("invalid explicit IV");
}
@ -746,7 +746,7 @@ final class CipherBox {
// The padding data should be filled with the padding length value.
int[] results = checkPadding(buf, offset + newLen,
padLen + 1, (byte)(padLen & 0xFF));
if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
if (protocolVersion.useTLS10PlusSpec()) {
if (results[0] != 0) { // padding data has invalid bytes
throw new BadPaddingException("Invalid TLS padding data");
}
@ -792,7 +792,7 @@ final class CipherBox {
int[] results = checkPadding(
bb.duplicate().position(offset + newLen),
(byte)(padLen & 0xFF));
if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
if (protocolVersion.useTLS10PlusSpec()) {
if (results[0] != 0) { // padding data has invalid bytes
throw new BadPaddingException("Invalid TLS padding data");
}
@ -873,7 +873,7 @@ final class CipherBox {
// For block ciphers, the explicit IV length is of length
// SecurityParameters.record_iv_length, which is equal to
// the SecurityParameters.block_size.
if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
if (protocolVersion.useTLS11PlusSpec()) {
return cipher.getBlockSize();
}
break;
@ -902,7 +902,7 @@ final class CipherBox {
* @return the explicit nonce size of the cipher.
*/
int applyExplicitNonce(Authenticator authenticator, byte contentType,
ByteBuffer bb) throws BadPaddingException {
ByteBuffer bb, byte[] sequence) throws BadPaddingException {
switch (cipherType) {
case BLOCK_CIPHER:
// sanity check length of the ciphertext
@ -918,7 +918,7 @@ final class CipherBox {
// For block ciphers, the explicit IV length is of length
// SecurityParameters.record_iv_length, which is equal to
// the SecurityParameters.block_size.
if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
if (protocolVersion.useTLS11PlusSpec()) {
return cipher.getBlockSize();
}
break;
@ -945,7 +945,8 @@ final class CipherBox {
// update the additional authentication data
byte[] aad = authenticator.acquireAuthenticationBytes(
contentType, bb.remaining() - recordIvSize - tagSize);
contentType, bb.remaining() - recordIvSize - tagSize,
sequence);
cipher.updateAAD(aad);
return recordIvSize;
@ -956,33 +957,6 @@ final class CipherBox {
return 0;
}
/*
* Applies the explicit nonce/IV to this cipher. This method is used to
* decrypt an SSL/TLS input record.
*
* The returned value is the SecurityParameters.record_iv_length in
* RFC 4346/5246. It is the size of explicit IV for CBC mode, and the
* size of explicit nonce for AEAD mode.
*
* @param authenticator the authenticator to get the additional
* authentication data
* @param contentType the content type of the input record
* @param buf the byte array to get the explicit nonce from
* @param offset the offset of the byte buffer
* @param cipheredLength the ciphered fragment length of the output
* record, it is the TLSCiphertext.length in RFC 4346/5246.
*
* @return the explicit nonce size of the cipher.
*/
int applyExplicitNonce(Authenticator authenticator,
byte contentType, byte[] buf, int offset,
int cipheredLength) throws BadPaddingException {
ByteBuffer bb = ByteBuffer.wrap(buf, offset, cipheredLength);
return applyExplicitNonce(authenticator, contentType, bb);
}
/*
* Creates the explicit nonce/IV to this cipher. This method is used to
* encrypt an SSL/TLS output record.
@ -1005,7 +979,7 @@ final class CipherBox {
byte[] nonce = new byte[0];
switch (cipherType) {
case BLOCK_CIPHER:
if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
if (protocolVersion.useTLS11PlusSpec()) {
// For block ciphers, the explicit IV length is of length
// SecurityParameters.record_iv_length, which is equal to
// the SecurityParameters.block_size.
@ -1034,9 +1008,10 @@ final class CipherBox {
"invalid key or spec in GCM mode", ikae);
}
// update the additional authentication data
// Update the additional authentication data, using the
// implicit sequence number of the authenticator.
byte[] aad = authenticator.acquireAuthenticationBytes(
contentType, fragmentLength);
contentType, fragmentLength, null);
cipher.updateAAD(aad);
break;
}
@ -1044,6 +1019,93 @@ final class CipherBox {
return nonce;
}
// See also CipherSuite.calculatePacketSize().
int calculatePacketSize(int fragmentSize, int macLen, int headerSize) {
int packetSize = fragmentSize;
if (cipher != null) {
int blockSize = cipher.getBlockSize();
switch (cipherType) {
case BLOCK_CIPHER:
packetSize += macLen;
packetSize += 1; // 1 byte padding length field
packetSize += // use the minimal padding
(blockSize - (packetSize % blockSize)) % blockSize;
if (protocolVersion.useTLS11PlusSpec()) {
packetSize += blockSize; // explicit IV
}
break;
case AEAD_CIPHER:
packetSize += recordIvSize;
packetSize += tagSize;
break;
default: // NULL_CIPHER or STREAM_CIPHER
packetSize += macLen;
}
}
return packetSize + headerSize;
}
// See also CipherSuite.calculateFragSize().
int calculateFragmentSize(int packetLimit, int macLen, int headerSize) {
int fragLen = packetLimit - headerSize;
if (cipher != null) {
int blockSize = cipher.getBlockSize();
switch (cipherType) {
case BLOCK_CIPHER:
if (protocolVersion.useTLS11PlusSpec()) {
fragLen -= blockSize; // explicit IV
}
fragLen -= (fragLen % blockSize); // cannot hold a block
// No padding for a maximum fragment.
fragLen -= 1; // 1 byte padding length field: 0x00
fragLen -= macLen;
break;
case AEAD_CIPHER:
fragLen -= recordIvSize;
fragLen -= tagSize;
break;
default: // NULL_CIPHER or STREAM_CIPHER
fragLen -= macLen;
}
}
return fragLen;
}
// Estimate the maximum fragment size of a received packet.
int estimateFragmentSize(int packetSize, int macLen, int headerSize) {
int fragLen = packetSize - headerSize;
if (cipher != null) {
int blockSize = cipher.getBlockSize();
switch (cipherType) {
case BLOCK_CIPHER:
if (protocolVersion.useTLS11PlusSpec()) {
fragLen -= blockSize; // explicit IV
}
// No padding for a maximum fragment.
fragLen -= 1; // 1 byte padding length field: 0x00
fragLen -= macLen;
break;
case AEAD_CIPHER:
fragLen -= recordIvSize;
fragLen -= tagSize;
break;
default: // NULL_CIPHER or STREAM_CIPHER
fragLen -= macLen;
}
}
return fragLen;
}
/*
* Is this cipher available?
*
@ -1100,7 +1162,7 @@ final class CipherBox {
if ((fragmentLen % blockSize) == 0) {
int minimal = tagLen + 1;
minimal = (minimal >= blockSize) ? minimal : blockSize;
if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
if (protocolVersion.useTLS11PlusSpec()) {
minimal += blockSize; // plus the size of the explicit IV
}

View file

@ -122,9 +122,15 @@ final class CipherSuite implements Comparable<CipherSuite> {
final boolean allowed;
// obsoleted since protocol version
//
// TLS version is used. If checking DTLS versions, please map to
// TLS version firstly. See ProtocolVersion.mapToTLSProtocol().
final int obsoleted;
// supported since protocol version
// supported since protocol version (TLS version is used)
//
// TLS version is used. If checking DTLS versions, please map to
// TLS version firstly. See ProtocolVersion.mapToTLSProtocol().
final int supported;
/**
@ -182,6 +188,70 @@ final class CipherSuite implements Comparable<CipherSuite> {
return this != C_SCSV && isAvailable();
}
// See also CipherBox.calculatePacketSize().
int calculatePacketSize(int fragmentSize,
ProtocolVersion protocolVersion, boolean isDTLS) {
int packetSize = fragmentSize;
if (cipher != B_NULL) {
int blockSize = cipher.ivSize;
switch (cipher.cipherType) {
case BLOCK_CIPHER:
packetSize += macAlg.size;
packetSize += 1; // 1 byte padding length field
packetSize += // use the minimal padding
(blockSize - (packetSize % blockSize)) % blockSize;
if (protocolVersion.useTLS11PlusSpec()) {
packetSize += blockSize; // explicit IV
}
break;
case AEAD_CIPHER:
packetSize += cipher.ivSize - cipher.fixedIvSize; // record IV
packetSize += cipher.tagSize;
break;
default: // NULL_CIPHER or STREAM_CIPHER
packetSize += macAlg.size;
}
}
return packetSize +
(isDTLS ? DTLSRecord.headerSize : SSLRecord.headerSize);
}
// See also CipherBox.calculateFragmentSize().
int calculateFragSize(int packetLimit,
ProtocolVersion protocolVersion, boolean isDTLS) {
int fragSize = packetLimit -
(isDTLS ? DTLSRecord.headerSize : SSLRecord.headerSize);
if (cipher != B_NULL) {
int blockSize = cipher.ivSize;
switch (cipher.cipherType) {
case BLOCK_CIPHER:
if (protocolVersion.useTLS11PlusSpec()) {
fragSize -= blockSize; // explicit IV
}
fragSize -= (fragSize % blockSize); // cannot hold a block
// No padding for a maximum fragment.
fragSize -= 1; // 1 byte padding length field: 0x00
fragSize -= macAlg.size;
break;
case AEAD_CIPHER:
fragSize -= cipher.tagSize;
fragSize -= cipher.ivSize - cipher.fixedIvSize; // record IV
break;
default: // NULL_CIPHER or STREAM_CIPHER
fragSize -= macAlg.size;
}
}
return fragSize;
}
/**
* Compares CipherSuites based on their priority. Has the effect of
* sorting CipherSuites when put in a sorted collection, which is
@ -242,7 +312,7 @@ final class CipherSuite implements Comparable<CipherSuite> {
return c;
}
// for use by CipherSuiteList only
// for use by SSLContextImpl only
static Collection<CipherSuite> allowedCipherSuites() {
return nameMap.values();
}
@ -372,7 +442,8 @@ final class CipherSuite implements Comparable<CipherSuite> {
}
static enum CipherType {
STREAM_CIPHER, // null or stream cipher
NULL_CIPHER, // null cipher
STREAM_CIPHER, // stream cipher
BLOCK_CIPHER, // block cipher in CBC mode
AEAD_CIPHER // AEAD cipher
}
@ -387,7 +458,7 @@ final class CipherSuite implements Comparable<CipherSuite> {
static enum BulkCipher {
// export strength ciphers
B_NULL("NULL", STREAM_CIPHER, 0, 0, 0, 0, true),
B_NULL("NULL", NULL_CIPHER, 0, 0, 0, 0, true),
B_RC4_40(CIPHER_RC4, STREAM_CIPHER, 5, 16, 0, 0, true),
B_RC2_40("RC2", BLOCK_CIPHER, 5, 16, 8, 0, false),
B_DES_40(CIPHER_DES, BLOCK_CIPHER, 5, 8, 8, 0, true),
@ -568,7 +639,7 @@ final class CipherSuite implements Comparable<CipherSuite> {
iv = new IvParameterSpec(new byte[cipher.ivSize]);
}
temporary = cipher.newCipher(
ProtocolVersion.DEFAULT,
ProtocolVersion.DEFAULT_TLS,
key, iv, secureRandom, true);
b = temporary.isAvailable();
} catch (NoSuchAlgorithmException e) {

View file

@ -0,0 +1,145 @@
/*
* Copyright (c) 2015, 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.SSLEngineResult.HandshakeStatus;
import static sun.security.ssl.HandshakeMessage.*;
/*
* enumeration of record type
*/
final class Ciphertext {
final static Ciphertext CIPHERTEXT_NULL = new Ciphertext();
RecordType recordType;
long recordSN;
HandshakeStatus handshakeStatus; // null if not used or not handshaking
Ciphertext() {
this.recordType = null;
this.recordSN = -1L;
this.handshakeStatus = null;
}
Ciphertext(RecordType recordType, long recordSN) {
this.recordType = recordType;
this.recordSN = recordSN;
this.handshakeStatus = null;
}
static enum RecordType {
RECORD_CHANGE_CIPHER_SPEC (
Record.ct_change_cipher_spec, ht_not_applicable),
RECORD_ALERT (
Record.ct_alert, ht_not_applicable),
RECORD_HELLO_REQUEST (
Record.ct_handshake, ht_hello_request),
RECORD_CLIENT_HELLO (
Record.ct_handshake, ht_client_hello),
RECORD_SERVER_HELLO (
Record.ct_handshake, ht_server_hello),
RECORD_HELLO_VERIFY_REQUEST (
Record.ct_handshake, ht_hello_verify_request),
RECORD_NEW_SESSION_TICKET (
Record.ct_handshake, ht_new_session_ticket),
RECORD_CERTIFICATE (
Record.ct_handshake, ht_certificate),
RECORD_SERVER_KEY_EXCHANGE (
Record.ct_handshake, ht_server_key_exchange),
RECORD_CERTIFICATE_REQUEST (
Record.ct_handshake, ht_certificate_request),
RECORD_SERVER_HELLO_DONE (
Record.ct_handshake, ht_server_hello_done),
RECORD_CERTIFICATE_VERIFY (
Record.ct_handshake, ht_certificate_verify),
RECORD_CLIENT_KEY_EXCHANGE (
Record.ct_handshake, ht_client_key_exchange),
RECORD_FINISHED (
Record.ct_handshake, ht_finished),
RECORD_CERTIFICATE_URL (
Record.ct_handshake, ht_certificate_url),
RECORD_CERTIFICATE_STATUS (
Record.ct_handshake, ht_certificate_status),
RECORD_SUPPLIEMENTAL_DATA (
Record.ct_handshake, ht_supplemental_data),
RECORD_APPLICATION_DATA (
Record.ct_application_data, ht_not_applicable);
byte contentType;
byte handshakeType;
private RecordType(byte contentType, byte handshakeType) {
this.contentType = contentType;
this.handshakeType = handshakeType;
}
static RecordType valueOf(byte contentType, byte handshakeType) {
if (contentType == Record.ct_change_cipher_spec) {
return RECORD_CHANGE_CIPHER_SPEC;
} else if (contentType == Record.ct_alert) {
return RECORD_ALERT;
} else if (contentType == Record.ct_application_data) {
return RECORD_APPLICATION_DATA;
} else if (handshakeType == ht_hello_request) {
return RECORD_HELLO_REQUEST;
} else if (handshakeType == ht_client_hello) {
return RECORD_CLIENT_HELLO;
} else if (handshakeType == ht_server_hello) {
return RECORD_SERVER_HELLO;
} else if (handshakeType == ht_hello_verify_request) {
return RECORD_HELLO_VERIFY_REQUEST;
} else if (handshakeType == ht_new_session_ticket) {
return RECORD_NEW_SESSION_TICKET;
} else if (handshakeType == ht_certificate) {
return RECORD_CERTIFICATE;
} else if (handshakeType == ht_server_key_exchange) {
return RECORD_SERVER_KEY_EXCHANGE;
} else if (handshakeType == ht_certificate_request) {
return RECORD_CERTIFICATE_REQUEST;
} else if (handshakeType == ht_server_hello_done) {
return RECORD_SERVER_HELLO_DONE;
} else if (handshakeType == ht_certificate_verify) {
return RECORD_CERTIFICATE_VERIFY;
} else if (handshakeType == ht_client_key_exchange) {
return RECORD_CLIENT_KEY_EXCHANGE;
} else if (handshakeType == ht_finished) {
return RECORD_FINISHED;
} else if (handshakeType == ht_certificate_url) {
return RECORD_CERTIFICATE_URL;
} else if (handshakeType == ht_certificate_status) {
return RECORD_CERTIFICATE_STATUS;
} else if (handshakeType == ht_supplemental_data) {
return RECORD_SUPPLIEMENTAL_DATA;
}
// otherwise, invalid record type
throw new IllegalArgumentException(
"Invalid record type (ContentType:" + contentType +
", HandshakeType:" + handshakeType + ")");
}
}
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2015, 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;
/**
* Client authentication type.
*/
enum ClientAuthType {
CLIENT_AUTH_NONE, // turn off client authentication
CLIENT_AUTH_REQUESTED, // need to request client authentication
CLIENT_AUTH_REQUIRED // require client authentication
}

View file

@ -141,11 +141,20 @@ final class ClientHandshaker extends Handshaker {
private final static boolean allowUnsafeServerCertChange =
Debug.getBooleanProperty("jdk.tls.allowUnsafeServerCertChange", false);
// To switch off the max_fragment_length extension.
private final static boolean enableMFLExtension =
Debug.getBooleanProperty("jsse.enableMFLExtension", false);
private List<SNIServerName> requestedServerNames =
Collections.<SNIServerName>emptyList();
// maximum fragment length
private int requestedMFLength = -1; // -1: no fragment length limit
private boolean serverNamesAccepted = false;
private ClientHello initialClientHelloMsg = null; // DTLS only
/*
* the reserved server certificate chain in previous handshaking
*
@ -172,11 +181,12 @@ final class ClientHandshaker extends Handshaker {
ProtocolList enabledProtocols,
ProtocolVersion activeProtocolVersion,
boolean isInitialHandshake, boolean secureRenegotiation,
byte[] clientVerifyData, byte[] serverVerifyData) {
byte[] clientVerifyData, byte[] serverVerifyData,
boolean isDTLS) {
super(engine, context, enabledProtocols, true, true,
activeProtocolVersion, isInitialHandshake, secureRenegotiation,
clientVerifyData, serverVerifyData);
clientVerifyData, serverVerifyData, isDTLS);
}
/*
@ -191,19 +201,35 @@ final class ClientHandshaker extends Handshaker {
*/
@Override
void processMessage(byte type, int messageLen) throws IOException {
if (state >= type
&& (type != HandshakeMessage.ht_hello_request)) {
throw new SSLProtocolException(
"Handshake message sequence violation, " + type);
}
// check the handshake state
handshakeState.check(type);
switch (type) {
case HandshakeMessage.ht_hello_request:
this.serverHelloRequest(new HelloRequest(input));
HelloRequest helloRequest = new HelloRequest(input);
handshakeState.update(helloRequest, resumingSession);
this.serverHelloRequest(helloRequest);
break;
case HandshakeMessage.ht_hello_verify_request:
if (!isDTLS) {
throw new SSLProtocolException(
"hello_verify_request is not a SSL/TLS handshake message");
}
HelloVerifyRequest helloVerifyRequest =
new HelloVerifyRequest(input, messageLen);
handshakeState.update(helloVerifyRequest, resumingSession);
this.helloVerifyRequest(helloVerifyRequest);
break;
case HandshakeMessage.ht_server_hello:
this.serverHello(new ServerHello(input, messageLen));
ServerHello serverHello = new ServerHello(input, messageLen);
this.serverHello(serverHello);
// This handshake state update needs the resumingSession value
// set by serverHello().
handshakeState.update(serverHello, resumingSession);
break;
case HandshakeMessage.ht_certificate:
@ -213,7 +239,9 @@ final class ClientHandshaker extends Handshaker {
"unexpected server cert chain");
// NOTREACHED
}
this.serverCertificate(new CertificateMsg(input));
CertificateMsg certificateMsg = new CertificateMsg(input);
handshakeState.update(certificateMsg, resumingSession);
this.serverCertificate(certificateMsg);
serverKey =
session.getPeerCertificates()[0].getPublicKey();
break;
@ -249,41 +277,52 @@ final class ClientHandshaker extends Handshaker {
}
try {
this.serverKeyExchange(new RSA_ServerKeyExchange(input));
RSA_ServerKeyExchange rsaSrvKeyExchange =
new RSA_ServerKeyExchange(input);
handshakeState.update(rsaSrvKeyExchange, resumingSession);
this.serverKeyExchange(rsaSrvKeyExchange);
} catch (GeneralSecurityException e) {
throwSSLException("Server key", e);
throw new SSLException("Server key", e);
}
break;
case K_DH_ANON:
try {
this.serverKeyExchange(new DH_ServerKeyExchange(
input, protocolVersion));
DH_ServerKeyExchange dhSrvKeyExchange =
new DH_ServerKeyExchange(input, protocolVersion);
handshakeState.update(dhSrvKeyExchange, resumingSession);
this.serverKeyExchange(dhSrvKeyExchange);
} catch (GeneralSecurityException e) {
throwSSLException("Server key", e);
throw new SSLException("Server key", e);
}
break;
case K_DHE_DSS:
case K_DHE_RSA:
try {
this.serverKeyExchange(new DH_ServerKeyExchange(
input, serverKey,
clnt_random.random_bytes, svr_random.random_bytes,
messageLen,
localSupportedSignAlgs, protocolVersion));
DH_ServerKeyExchange dhSrvKeyExchange =
new DH_ServerKeyExchange(
input, serverKey,
clnt_random.random_bytes, svr_random.random_bytes,
messageLen,
localSupportedSignAlgs, protocolVersion);
handshakeState.update(dhSrvKeyExchange, resumingSession);
this.serverKeyExchange(dhSrvKeyExchange);
} catch (GeneralSecurityException e) {
throwSSLException("Server key", e);
throw new SSLException("Server key", e);
}
break;
case K_ECDHE_ECDSA:
case K_ECDHE_RSA:
case K_ECDH_ANON:
try {
this.serverKeyExchange(new ECDH_ServerKeyExchange
(input, serverKey, clnt_random.random_bytes,
svr_random.random_bytes,
localSupportedSignAlgs, protocolVersion));
ECDH_ServerKeyExchange ecdhSrvKeyExchange =
new ECDH_ServerKeyExchange
(input, serverKey, clnt_random.random_bytes,
svr_random.random_bytes,
localSupportedSignAlgs, protocolVersion);
handshakeState.update(ecdhSrvKeyExchange, resumingSession);
this.serverKeyExchange(ecdhSrvKeyExchange);
} catch (GeneralSecurityException e) {
throwSSLException("Server key", e);
throw new SSLException("Server key", e);
}
break;
case K_RSA:
@ -320,8 +359,9 @@ final class ClientHandshaker extends Handshaker {
if (debug != null && Debug.isOn("handshake")) {
certRequest.print(System.out);
}
handshakeState.update(certRequest, resumingSession);
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
Collection<SignatureAndHashAlgorithm> peerSignAlgs =
certRequest.getSignAlgorithms();
if (peerSignAlgs == null || peerSignAlgs.isEmpty()) {
@ -345,33 +385,24 @@ final class ClientHandshaker extends Handshaker {
break;
case HandshakeMessage.ht_server_hello_done:
this.serverHelloDone(new ServerHelloDone(input));
ServerHelloDone serverHelloDone = new ServerHelloDone(input);
handshakeState.update(serverHelloDone, resumingSession);
this.serverHelloDone(serverHelloDone);
break;
case HandshakeMessage.ht_finished:
// A ChangeCipherSpec record must have been received prior to
// reception of the Finished message (RFC 5246, 7.4.9).
if (!receivedChangeCipherSpec()) {
fatalSE(Alerts.alert_handshake_failure,
"Received Finished message before ChangeCipherSpec");
}
Finished serverFinished =
new Finished(protocolVersion, input, cipherSuite);
handshakeState.update(serverFinished, resumingSession);
this.serverFinished(serverFinished);
this.serverFinished(
new Finished(protocolVersion, input, cipherSuite));
break;
default:
throw new SSLProtocolException(
"Illegal client handshake msg, " + type);
}
//
// Move state machine forward if the message handling
// code didn't already do so
//
if (state < type) {
state = type;
}
}
/*
@ -389,10 +420,10 @@ final class ClientHandshaker extends Handshaker {
// Could be (e.g. at connection setup) that we already
// sent the "client hello" but the server's not seen it.
//
if (state < HandshakeMessage.ht_client_hello) {
if (!clientHelloDelivered) {
if (!secureRenegotiation && !allowUnsafeRenegotiation) {
// renegotiation is not allowed.
if (activeProtocolVersion.v >= ProtocolVersion.TLS10.v) {
if (activeProtocolVersion.useTLS10PlusSpec()) {
// response with a no_renegotiation warning,
warningSE(Alerts.alert_no_renegotiation);
@ -428,6 +459,29 @@ final class ClientHandshaker extends Handshaker {
}
}
private void helloVerifyRequest(
HelloVerifyRequest mesg) throws IOException {
if (debug != null && Debug.isOn("handshake")) {
mesg.print(System.out);
}
//
// Note that HelloVerifyRequest.server_version is used solely to
// indicate packet formatting, and not as part of version negotiation.
// Need not to check version values match for HelloVerifyRequest
// message.
//
initialClientHelloMsg.cookie = mesg.cookie.clone();
if (debug != null && Debug.isOn("handshake")) {
initialClientHelloMsg.print(System.out);
}
// deliver the ClientHello message with cookie
initialClientHelloMsg.write(output);
handshakeState.update(initialClientHelloMsg, resumingSession);
}
/*
* Server chooses session parameters given options created by the
@ -441,6 +495,9 @@ final class ClientHandshaker extends Handshaker {
* probably authentication getting done.
*/
private void serverHello(ServerHello mesg) throws IOException {
// Dispose the reserved ClientHello message (if exists).
initialClientHelloMsg = null;
serverKeyExchangeReceived = false;
if (debug != null && Debug.isOn("handshake")) {
mesg.print(System.out);
@ -536,7 +593,7 @@ final class ClientHandshaker extends Handshaker {
}
setCipherSuite(mesg.cipherSuite);
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
handshakeHash.setFinishedAlg(cipherSuite.prfAlg.getPRFHashAlg());
}
@ -611,9 +668,8 @@ final class ClientHandshaker extends Handshaker {
}
}
// looks fine; resume it, and update the state machine.
// looks fine; resume it.
resumingSession = true;
state = HandshakeMessage.ht_finished - 1;
calculateConnectionKeys(session.getMasterSecret());
if (debug != null && Debug.isOn("session")) {
System.out.println("%% Server resumed " + session);
@ -627,6 +683,24 @@ final class ClientHandshaker extends Handshaker {
}
}
// check the "max_fragment_length" extension
MaxFragmentLengthExtension maxFragLenExt = (MaxFragmentLengthExtension)
mesg.extensions.get(ExtensionType.EXT_MAX_FRAGMENT_LENGTH);
if (maxFragLenExt != null) {
if ((requestedMFLength == -1) ||
maxFragLenExt.getMaxFragLen() != requestedMFLength) {
// If the client did not request this extension, or the
// response value is different from the length it requested,
// abort the handshake with a fatal illegal_parameter alert.
fatalSE(Alerts.alert_illegal_parameter,
"Failed to negotiate the max_fragment_length");
}
} else if (!resumingSession) {
// no "max_fragment_length" extension
requestedMFLength = -1;
} // Otherwise, using the value negotiated during the original
// session initiation
if (resumingSession && session != null) {
setHandshakeSessionSE(session);
// Reserve the handshake state if this is a session-resumption
@ -657,6 +731,8 @@ final class ClientHandshaker extends Handshaker {
getLocalSupportedSignAlgs(),
mesg.sessionId, getHostSE(), getPortSE());
session.setRequestedServerNames(requestedServerNames);
session.setNegotiatedMaxFragSize(requestedMFLength);
session.setMaximumPacketSize(maximumPacketSize);
setHandshakeSessionSE(session);
if (debug != null && Debug.isOn("handshake")) {
System.out.println("** " + cipherSuite);
@ -681,7 +757,6 @@ final class ClientHandshaker extends Handshaker {
ephemeralServerKey = mesg.getPublicKey();
}
/*
* Diffie-Hellman key exchange. We save the server public key and
* our own D-H algorithm object so we can defer key calculations
@ -716,13 +791,6 @@ final class ClientHandshaker extends Handshaker {
if (debug != null && Debug.isOn("handshake")) {
mesg.print(System.out);
}
/*
* Always make sure the input has been digested before we
* start emitting data, to ensure the hashes are correctly
* computed for the Finished and CertificateVerify messages
* which we send (here).
*/
input.digestNow();
/*
* FIRST ... if requested, send an appropriate Certificate chain
@ -817,7 +885,7 @@ final class ClientHandshaker extends Handshaker {
// server. For SSLv3, send the no_certificate alert;
// TLS uses an empty cert chain instead.
//
if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
if (protocolVersion.useTLS10PlusSpec()) {
m1 = new CertificateMsg(new X509Certificate [0]);
} else {
warningSE(Alerts.alert_no_certificate);
@ -837,6 +905,7 @@ final class ClientHandshaker extends Handshaker {
m1.print(System.out);
}
m1.write(output);
handshakeState.update(m1, resumingSession);
}
}
@ -1000,7 +1069,7 @@ final class ClientHandshaker extends Handshaker {
m2.print(System.out);
}
m2.write(output);
handshakeState.update(m2, resumingSession);
/*
* THIRD, send a "change_cipher_spec" record followed by the
@ -1010,8 +1079,6 @@ final class ClientHandshaker extends Handshaker {
* to compute the "Finished" message, and to compute the keys used
* to protect all records following the change_cipher_spec.
*/
output.doHashes();
output.flush();
/*
@ -1069,7 +1136,7 @@ final class ClientHandshaker extends Handshaker {
CertificateVerify m3;
try {
SignatureAndHashAlgorithm preferableSignatureAlgorithm = null;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
preferableSignatureAlgorithm =
SignatureAndHashAlgorithm.getPreferableAlgorithm(
peerSupportedSignAlgs, signingKey.getAlgorithm(),
@ -1103,13 +1170,17 @@ final class ClientHandshaker extends Handshaker {
m3.print(System.out);
}
m3.write(output);
output.doHashes();
handshakeState.update(m3, resumingSession);
output.flush();
}
/*
* OK, that's that!
*/
sendChangeCipherAndFinish(false);
// expecting the final ChangeCipherSpec and Finished messages
expectingFinishFlightSE();
}
@ -1158,8 +1229,9 @@ final class ClientHandshaker extends Handshaker {
* completed handshakes.
*/
if (resumingSession) {
input.digestNow();
sendChangeCipherAndFinish(true);
} else {
handshakeFinished = true;
}
session.setLastAccessedTime(System.currentTimeMillis());
@ -1188,6 +1260,10 @@ final class ClientHandshaker extends Handshaker {
*/
private void sendChangeCipherAndFinish(boolean finishedTag)
throws IOException {
// Reload if this message has been reserved.
handshakeHash.reload();
Finished mesg = new Finished(protocolVersion, handshakeHash,
Finished.CLIENT, session.getMasterSecret(), cipherSuite);
@ -1205,13 +1281,6 @@ final class ClientHandshaker extends Handshaker {
if (secureRenegotiation) {
clientVerifyData = mesg.getVerifyData();
}
/*
* Update state machine so server MUST send 'finished' next.
* (In "long" handshake case; in short case, we're responding
* to its message.)
*/
state = HandshakeMessage.ht_finished - 1;
}
@ -1361,10 +1430,10 @@ final class ClientHandshaker extends Handshaker {
// create the ClientHello message
ClientHello clientHelloMessage = new ClientHello(
sslContext.getSecureRandom(), maxProtocolVersion,
sessionId, cipherSuites);
sessionId, cipherSuites, isDTLS);
// add signature_algorithm extension
if (maxProtocolVersion.v >= ProtocolVersion.TLS12.v) {
if (maxProtocolVersion.useTLS12PlusSpec()) {
// we will always send the signature_algorithm extension
Collection<SignatureAndHashAlgorithm> localSignAlgs =
getLocalSupportedSignAlgs();
@ -1389,6 +1458,37 @@ final class ClientHandshaker extends Handshaker {
}
}
// add max_fragment_length extension
if (enableMFLExtension) {
if (session != null) {
// The same extension should be sent for resumption.
requestedMFLength = session.getNegotiatedMaxFragSize();
} else if (maximumPacketSize != 0) {
// Maybe we can calculate the fragment size more accurate
// by condering the enabled cipher suites in the future.
requestedMFLength = maximumPacketSize;
if (isDTLS) {
requestedMFLength -= DTLSRecord.maxPlaintextPlusSize;
} else {
requestedMFLength -= SSLRecord.maxPlaintextPlusSize;
}
} else {
// Need no max_fragment_length extension.
requestedMFLength = -1;
}
if ((requestedMFLength > 0) &&
MaxFragmentLengthExtension.needFragLenNego(requestedMFLength)) {
requestedMFLength =
MaxFragmentLengthExtension.getValidMaxFragLen(
requestedMFLength);
clientHelloMessage.addMFLExtension(requestedMFLength);
} else {
requestedMFLength = -1;
}
}
// reset the client random cookie
clnt_random = clientHelloMessage.clnt_random;
@ -1403,6 +1503,11 @@ final class ClientHandshaker extends Handshaker {
clientHelloMessage.addRenegotiationInfoExtension(clientVerifyData);
}
if (isDTLS) {
// Cookie exchange need to reserve the initial ClientHello message.
initialClientHelloMsg = clientHelloMessage;
}
return clientHelloMessage;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,597 @@
/*
* Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ssl;
import java.io.*;
import java.nio.*;
import java.util.*;
import javax.crypto.BadPaddingException;
import javax.net.ssl.*;
import sun.misc.HexDumpEncoder;
import static sun.security.ssl.Ciphertext.RecordType;
/**
* DTLS {@code OutputRecord} implementation for {@code SSLEngine}.
*/
final class DTLSOutputRecord extends OutputRecord implements DTLSRecord {
private DTLSFragmenter fragmenter = null;
int writeEpoch;
int prevWriteEpoch;
Authenticator prevWriteAuthenticator;
CipherBox prevWriteCipher;
private LinkedList<RecordMemo> alertMemos = new LinkedList<>();
DTLSOutputRecord() {
this.writeAuthenticator = new MAC(true);
this.writeEpoch = 0;
this.prevWriteEpoch = 0;
this.prevWriteCipher = CipherBox.NULL;
this.prevWriteAuthenticator = new MAC(true);
this.packetSize = DTLSRecord.maxRecordSize;
this.protocolVersion = ProtocolVersion.DEFAULT_DTLS;
}
@Override
void changeWriteCiphers(Authenticator writeAuthenticator,
CipherBox writeCipher) throws IOException {
encodeChangeCipherSpec();
prevWriteCipher.dispose();
this.prevWriteAuthenticator = this.writeAuthenticator;
this.prevWriteCipher = this.writeCipher;
this.prevWriteEpoch = this.writeEpoch;
this.writeAuthenticator = writeAuthenticator;
this.writeCipher = writeCipher;
this.writeEpoch++;
this.isFirstAppOutputRecord = true;
// set the epoch number
this.writeAuthenticator.setEpochNumber(this.writeEpoch);
}
@Override
void encodeAlert(byte level, byte description) throws IOException {
RecordMemo memo = new RecordMemo();
memo.contentType = Record.ct_alert;
memo.majorVersion = protocolVersion.major;
memo.minorVersion = protocolVersion.minor;
memo.encodeEpoch = writeEpoch;
memo.encodeCipher = writeCipher;
memo.encodeAuthenticator = writeAuthenticator;
memo.fragment = new byte[2];
memo.fragment[0] = level;
memo.fragment[1] = description;
alertMemos.add(memo);
}
@Override
void encodeChangeCipherSpec() throws IOException {
if (fragmenter == null) {
fragmenter = new DTLSFragmenter();
}
fragmenter.queueUpChangeCipherSpec();
}
@Override
void encodeHandshake(byte[] source,
int offset, int length) throws IOException {
if (firstMessage) {
firstMessage = false;
}
if (fragmenter == null) {
fragmenter = new DTLSFragmenter();
}
fragmenter.queueUpHandshake(source, offset, length);
}
@Override
Ciphertext encode(ByteBuffer[] sources, int offset, int length,
ByteBuffer destination) throws IOException {
if (writeAuthenticator.seqNumOverflow()) {
if (debug != null && Debug.isOn("ssl")) {
System.out.println(Thread.currentThread().getName() +
", sequence number extremely close to overflow " +
"(2^64-1 packets). Closing connection.");
}
throw new SSLHandshakeException("sequence number overflow");
}
// not apply to handshake message
int macLen = 0;
if (writeAuthenticator instanceof MAC) {
macLen = ((MAC)writeAuthenticator).MAClen();
}
int fragLen;
if (packetSize > 0) {
fragLen = Math.min(maxRecordSize, packetSize);
fragLen = writeCipher.calculateFragmentSize(
fragLen, macLen, headerSize);
fragLen = Math.min(fragLen, Record.maxDataSize);
} else {
fragLen = Record.maxDataSize;
}
if (fragmentSize > 0) {
fragLen = Math.min(fragLen, fragmentSize);
}
int dstPos = destination.position();
int dstLim = destination.limit();
int dstContent = dstPos + headerSize +
writeCipher.getExplicitNonceSize();
destination.position(dstContent);
int remains = Math.min(fragLen, destination.remaining());
fragLen = 0;
int srcsLen = offset + length;
for (int i = offset; (i < srcsLen) && (remains > 0); i++) {
int amount = Math.min(sources[i].remaining(), remains);
int srcLimit = sources[i].limit();
sources[i].limit(sources[i].position() + amount);
destination.put(sources[i]);
sources[i].limit(srcLimit); // restore the limit
remains -= amount;
fragLen += amount;
}
destination.limit(destination.position());
destination.position(dstContent);
if ((debug != null) && Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
", WRITE: " + protocolVersion + " " +
Record.contentName(Record.ct_application_data) +
", length = " + destination.remaining());
}
// Encrypt the fragment and wrap up a record.
long recordSN = encrypt(writeAuthenticator, writeCipher,
Record.ct_application_data, destination,
dstPos, dstLim, headerSize,
protocolVersion, true);
if ((debug != null) && Debug.isOn("packet")) {
ByteBuffer temporary = destination.duplicate();
temporary.limit(temporary.position());
temporary.position(dstPos);
Debug.printHex(
"[Raw write]: length = " + temporary.remaining(),
temporary);
}
// remain the limit unchanged
destination.limit(dstLim);
return new Ciphertext(RecordType.RECORD_APPLICATION_DATA, recordSN);
}
@Override
Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException {
if (alertMemos != null && !alertMemos.isEmpty()) {
RecordMemo memo = alertMemos.pop();
int macLen = 0;
if (memo.encodeAuthenticator instanceof MAC) {
macLen = ((MAC)memo.encodeAuthenticator).MAClen();
}
int dstPos = destination.position();
int dstLim = destination.limit();
int dstContent = dstPos + headerSize +
writeCipher.getExplicitNonceSize();
destination.position(dstContent);
destination.put(memo.fragment);
destination.limit(destination.position());
destination.position(dstContent);
if ((debug != null) && Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
", WRITE: " + protocolVersion + " " +
Record.contentName(Record.ct_alert) +
", length = " + destination.remaining());
}
// Encrypt the fragment and wrap up a record.
long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher,
Record.ct_alert, destination, dstPos, dstLim, headerSize,
ProtocolVersion.valueOf(memo.majorVersion,
memo.minorVersion), true);
if ((debug != null) && Debug.isOn("packet")) {
ByteBuffer temporary = destination.duplicate();
temporary.limit(temporary.position());
temporary.position(dstPos);
Debug.printHex(
"[Raw write]: length = " + temporary.remaining(),
temporary);
}
// remain the limit unchanged
destination.limit(dstLim);
return new Ciphertext(RecordType.RECORD_ALERT, recordSN);
}
if (fragmenter != null) {
return fragmenter.acquireCiphertext(destination);
}
return null;
}
@Override
boolean isEmpty() {
return ((fragmenter == null) || fragmenter.isEmpty()) &&
((alertMemos == null) || alertMemos.isEmpty());
}
@Override
void initHandshaker() {
// clean up
fragmenter = null;
}
// buffered record fragment
private static class RecordMemo {
byte contentType;
byte majorVersion;
byte minorVersion;
int encodeEpoch;
CipherBox encodeCipher;
Authenticator encodeAuthenticator;
byte[] fragment;
}
private static class HandshakeMemo extends RecordMemo {
byte handshakeType;
int messageSequence;
int acquireOffset;
}
private final class DTLSFragmenter {
private LinkedList<RecordMemo> handshakeMemos = new LinkedList<>();
private int acquireIndex = 0;
private int messageSequence = 0;
private boolean flightIsReady = false;
// Per section 4.1.1, RFC 6347:
//
// If repeated retransmissions do not result in a response, and the
// PMTU is unknown, subsequent retransmissions SHOULD back off to a
// smaller record size, fragmenting the handshake message as
// appropriate.
//
// In this implementation, two times of retransmits would be attempted
// before backing off. The back off is supported only if the packet
// size is bigger than 256 bytes.
private int retransmits = 2; // attemps of retransmits
void queueUpChangeCipherSpec() {
// Cleanup if a new flight starts.
if (flightIsReady) {
handshakeMemos.clear();
acquireIndex = 0;
flightIsReady = false;
}
RecordMemo memo = new RecordMemo();
memo.contentType = Record.ct_change_cipher_spec;
memo.majorVersion = protocolVersion.major;
memo.minorVersion = protocolVersion.minor;
memo.encodeEpoch = writeEpoch;
memo.encodeCipher = writeCipher;
memo.encodeAuthenticator = writeAuthenticator;
memo.fragment = new byte[1];
memo.fragment[0] = 1;
handshakeMemos.add(memo);
}
void queueUpHandshake(byte[] buf,
int offset, int length) throws IOException {
// Cleanup if a new flight starts.
if (flightIsReady) {
handshakeMemos.clear();
acquireIndex = 0;
flightIsReady = false;
}
HandshakeMemo memo = new HandshakeMemo();
memo.contentType = Record.ct_handshake;
memo.majorVersion = protocolVersion.major;
memo.minorVersion = protocolVersion.minor;
memo.encodeEpoch = writeEpoch;
memo.encodeCipher = writeCipher;
memo.encodeAuthenticator = writeAuthenticator;
memo.handshakeType = buf[offset];
memo.messageSequence = messageSequence++;
memo.acquireOffset = 0;
memo.fragment = new byte[length - 4]; // 4: header size
// 1: HandshakeType
// 3: message length
System.arraycopy(buf, offset + 4, memo.fragment, 0, length - 4);
handshakeHashing(memo, memo.fragment);
handshakeMemos.add(memo);
if ((memo.handshakeType == HandshakeMessage.ht_client_hello) ||
(memo.handshakeType == HandshakeMessage.ht_hello_request) ||
(memo.handshakeType ==
HandshakeMessage.ht_hello_verify_request) ||
(memo.handshakeType == HandshakeMessage.ht_server_hello_done) ||
(memo.handshakeType == HandshakeMessage.ht_finished)) {
flightIsReady = true;
}
}
Ciphertext acquireCiphertext(ByteBuffer dstBuf) throws IOException {
if (isEmpty()) {
if (isRetransmittable()) {
setRetransmission(); // configure for retransmission
} else {
return null;
}
}
RecordMemo memo = handshakeMemos.get(acquireIndex);
HandshakeMemo hsMemo = null;
if (memo.contentType == Record.ct_handshake) {
hsMemo = (HandshakeMemo)memo;
}
int macLen = 0;
if (memo.encodeAuthenticator instanceof MAC) {
macLen = ((MAC)memo.encodeAuthenticator).MAClen();
}
// ChangeCipherSpec message is pretty small. Don't worry about
// the fragmentation of ChangeCipherSpec record.
int fragLen;
if (packetSize > 0) {
fragLen = Math.min(maxRecordSize, packetSize);
fragLen = memo.encodeCipher.calculateFragmentSize(
fragLen, macLen, 25); // 25: header size
// 13: DTLS record
// 12: DTLS handshake message
fragLen = Math.min(fragLen, Record.maxDataSize);
} else {
fragLen = Record.maxDataSize;
}
if (fragmentSize > 0) {
fragLen = Math.min(fragLen, fragmentSize);
}
int dstPos = dstBuf.position();
int dstLim = dstBuf.limit();
int dstContent = dstPos + headerSize +
memo.encodeCipher.getExplicitNonceSize();
dstBuf.position(dstContent);
if (hsMemo != null) {
fragLen = Math.min(fragLen,
(hsMemo.fragment.length - hsMemo.acquireOffset));
dstBuf.put(hsMemo.handshakeType);
dstBuf.put((byte)((hsMemo.fragment.length >> 16) & 0xFF));
dstBuf.put((byte)((hsMemo.fragment.length >> 8) & 0xFF));
dstBuf.put((byte)(hsMemo.fragment.length & 0xFF));
dstBuf.put((byte)((hsMemo.messageSequence >> 8) & 0xFF));
dstBuf.put((byte)(hsMemo.messageSequence & 0xFF));
dstBuf.put((byte)((hsMemo.acquireOffset >> 16) & 0xFF));
dstBuf.put((byte)((hsMemo.acquireOffset >> 8) & 0xFF));
dstBuf.put((byte)(hsMemo.acquireOffset & 0xFF));
dstBuf.put((byte)((fragLen >> 16) & 0xFF));
dstBuf.put((byte)((fragLen >> 8) & 0xFF));
dstBuf.put((byte)(fragLen & 0xFF));
dstBuf.put(hsMemo.fragment, hsMemo.acquireOffset, fragLen);
} else {
fragLen = Math.min(fragLen, memo.fragment.length);
dstBuf.put(memo.fragment, 0, fragLen);
}
dstBuf.limit(dstBuf.position());
dstBuf.position(dstContent);
if ((debug != null) && Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
", WRITE: " + protocolVersion + " " +
Record.contentName(memo.contentType) +
", length = " + dstBuf.remaining());
}
// Encrypt the fragment and wrap up a record.
long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher,
memo.contentType, dstBuf,
dstPos, dstLim, headerSize,
ProtocolVersion.valueOf(memo.majorVersion,
memo.minorVersion), true);
if ((debug != null) && Debug.isOn("packet")) {
ByteBuffer temporary = dstBuf.duplicate();
temporary.limit(temporary.position());
temporary.position(dstPos);
Debug.printHex(
"[Raw write]: length = " + temporary.remaining(),
temporary);
}
// remain the limit unchanged
dstBuf.limit(dstLim);
// Reset the fragmentation offset.
if (hsMemo != null) {
hsMemo.acquireOffset += fragLen;
if (hsMemo.acquireOffset == hsMemo.fragment.length) {
acquireIndex++;
}
return new Ciphertext(RecordType.valueOf(
hsMemo.contentType, hsMemo.handshakeType), recordSN);
} else {
acquireIndex++;
return new Ciphertext(
RecordType.RECORD_CHANGE_CIPHER_SPEC, recordSN);
}
}
private void handshakeHashing(HandshakeMemo hsFrag, byte[] hsBody) {
byte hsType = hsFrag.handshakeType;
if ((hsType == HandshakeMessage.ht_hello_request) ||
(hsType == HandshakeMessage.ht_hello_verify_request)) {
// omitted from handshake hash computation
return;
}
if ((hsFrag.messageSequence == 0) &&
(hsType == HandshakeMessage.ht_client_hello)) {
// omit initial ClientHello message
//
// 2: ClientHello.client_version
// 32: ClientHello.random
int sidLen = hsBody[34];
if (sidLen == 0) { // empty session_id, initial handshake
return;
}
}
// calculate the DTLS header
byte[] temporary = new byte[12]; // 12: handshake header size
// Handshake.msg_type
temporary[0] = hsFrag.handshakeType;
// Handshake.length
temporary[1] = (byte)((hsBody.length >> 16) & 0xFF);
temporary[2] = (byte)((hsBody.length >> 8) & 0xFF);
temporary[3] = (byte)(hsBody.length & 0xFF);
// Handshake.message_seq
temporary[4] = (byte)((hsFrag.messageSequence >> 8) & 0xFF);
temporary[5] = (byte)(hsFrag.messageSequence & 0xFF);
// Handshake.fragment_offset
temporary[6] = 0;
temporary[7] = 0;
temporary[8] = 0;
// Handshake.fragment_length
temporary[9] = temporary[1];
temporary[10] = temporary[2];
temporary[11] = temporary[3];
if ((hsType != HandshakeMessage.ht_finished) &&
(hsType != HandshakeMessage.ht_certificate_verify)) {
handshakeHash.update(temporary, 0, 12);
handshakeHash.update(hsBody, 0, hsBody.length);
} else {
// Reserve until this handshake message has been processed.
handshakeHash.reserve(temporary, 0, 12);
handshakeHash.reserve(hsBody, 0, hsBody.length);
}
}
boolean isEmpty() {
if (!flightIsReady || handshakeMemos.isEmpty() ||
acquireIndex >= handshakeMemos.size()) {
return true;
}
return false;
}
boolean isRetransmittable() {
return (flightIsReady && !handshakeMemos.isEmpty() &&
(acquireIndex >= handshakeMemos.size()));
}
private void setRetransmission() {
acquireIndex = 0;
for (RecordMemo memo : handshakeMemos) {
if (memo instanceof HandshakeMemo) {
HandshakeMemo hmemo = (HandshakeMemo)memo;
hmemo.acquireOffset = 0;
}
}
// Shrink packet size if:
// 1. maximum fragment size is allowed, in which case the packet
// size is configured bigger than maxRecordSize;
// 2. maximum packet is bigger than 256 bytes;
// 3. two times of retransmits have been attempted.
if ((packetSize <= maxRecordSize) &&
(packetSize > 256) && ((retransmits--) <= 0)) {
// shrink packet size
shrinkPacketSize();
retransmits = 2; // attemps of retransmits
}
}
private void shrinkPacketSize() {
packetSize = Math.max(256, packetSize / 2);
}
}
}

View file

@ -0,0 +1,87 @@
/*
* Copyright (c) 1996, 2015, 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;
/**
* DTLS record
*/
interface DTLSRecord extends Record {
static final int headerSize = 13; // DTLS record header
static final int handshakeHeaderSize = 12; // DTLS handshake header
/*
* The size of the header plus the max IV length
*/
static final int headerPlusMaxIVSize =
headerSize // header
+ maxIVLength; // iv
/*
* The maximum size that may be increased when translating plaintext to
* ciphertext fragment.
*/
static final int maxPlaintextPlusSize =
headerSize // header
+ maxIVLength // iv
+ maxMacSize // MAC or AEAD tag
+ maxPadding; // block cipher padding
/*
* the maximum record size
*/
static final int maxRecordSize =
headerPlusMaxIVSize // header + iv
+ maxDataSize // data
+ maxPadding // padding
+ maxMacSize; // MAC or AEAD tag
/*
* For CBC protection in SSL3/TLS1, we break some plaintext into two
* packets. Max application data size for the second packet.
*/
static final int maxDataSizeMinusOneByteRecord =
maxDataSize // max data size
- ( // max one byte record size
headerPlusMaxIVSize // header + iv
+ 1 // one byte data
+ maxPadding // padding
+ maxMacSize // MAC
);
/*
* Maximum record size for alert and change cipher spec records.
* They only contain 2 and 1 bytes of data, respectively.
* Allocate a smaller array.
*/
static final int maxAlertRecordSize =
headerPlusMaxIVSize // header + iv
+ 2 // alert
+ maxPadding // padding
+ maxMacSize; // MAC
}

View file

@ -29,6 +29,9 @@ import java.io.PrintStream;
import java.security.AccessController;
import java.util.Locale;
import sun.misc.HexDumpEncoder;
import java.nio.ByteBuffer;
import sun.security.action.GetPropertyAction;
/**
@ -198,4 +201,47 @@ public class Debug {
static String toString(byte[] b) {
return sun.security.util.Debug.toString(b);
}
static void printHex(String prefix, byte[] bytes) {
HexDumpEncoder dump = new HexDumpEncoder();
synchronized (System.out) {
System.out.println(prefix);
try {
dump.encodeBuffer(bytes, System.out);
} catch (Exception e) {
// ignore
}
System.out.flush();
}
}
static void printHex(String prefix, ByteBuffer bb) {
HexDumpEncoder dump = new HexDumpEncoder();
synchronized (System.out) {
System.out.println(prefix);
try {
dump.encodeBuffer(bb.slice(), System.out);
} catch (Exception e) {
// ignore
}
System.out.flush();
}
}
static void printHex(String prefix, byte[] bytes, int offset, int length) {
HexDumpEncoder dump = new HexDumpEncoder();
synchronized (System.out) {
System.out.println(prefix);
try {
ByteBuffer bb = ByteBuffer.wrap(bytes, offset, length);
dump.encodeBuffer(bb, System.out);
} catch (Exception e) {
// ignore
}
System.out.flush();
}
}
}

View file

@ -1,238 +0,0 @@
/*
* 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ssl;
import java.nio.*;
/*
* A multi-purpose class which handles all of the SSLEngine arguments.
* It validates arguments, checks for RO conditions, does space
* calculations, performs scatter/gather, etc.
*
* @author Brad R. Wetmore
*/
class EngineArgs {
/*
* Keep track of the input parameters.
*/
ByteBuffer netData;
ByteBuffer [] appData;
private int offset; // offset/len for the appData array.
private int len;
/*
* The initial pos/limit conditions. This is useful because we can
* quickly calculate the amount consumed/produced in successful
* operations, or easily return the buffers to their pre-error
* conditions.
*/
private int netPos;
private int netLim;
private int [] appPoss;
private int [] appLims;
/*
* Sum total of the space remaining in all of the appData buffers
*/
private int appRemaining = 0;
private boolean wrapMethod;
/*
* Called by the SSLEngine.wrap() method.
*/
EngineArgs(ByteBuffer [] appData, int offset, int len,
ByteBuffer netData) {
this.wrapMethod = true;
init(netData, appData, offset, len);
}
/*
* Called by the SSLEngine.unwrap() method.
*/
EngineArgs(ByteBuffer netData, ByteBuffer [] appData, int offset,
int len) {
this.wrapMethod = false;
init(netData, appData, offset, len);
}
/*
* The main initialization method for the arguments. Most
* of them are pretty obvious as to what they do.
*
* Since we're already iterating over appData array for validity
* checking, we also keep track of how much remainging space is
* available. Info is used in both unwrap (to see if there is
* enough space available in the destination), and in wrap (to
* determine how much more we can copy into the outgoing data
* buffer.
*/
private void init(ByteBuffer netData, ByteBuffer [] appData,
int offset, int len) {
if ((netData == null) || (appData == null)) {
throw new IllegalArgumentException("src/dst is null");
}
if ((offset < 0) || (len < 0) || (offset > appData.length - len)) {
throw new IndexOutOfBoundsException();
}
if (wrapMethod && netData.isReadOnly()) {
throw new ReadOnlyBufferException();
}
netPos = netData.position();
netLim = netData.limit();
appPoss = new int [appData.length];
appLims = new int [appData.length];
for (int i = offset; i < offset + len; i++) {
if (appData[i] == null) {
throw new IllegalArgumentException(
"appData[" + i + "] == null");
}
/*
* If we're unwrapping, then check to make sure our
* destination bufffers are writable.
*/
if (!wrapMethod && appData[i].isReadOnly()) {
throw new ReadOnlyBufferException();
}
appRemaining += appData[i].remaining();
appPoss[i] = appData[i].position();
appLims[i] = appData[i].limit();
}
/*
* Ok, looks like we have a good set of args, let's
* store the rest of this stuff.
*/
this.netData = netData;
this.appData = appData;
this.offset = offset;
this.len = len;
}
/*
* Given spaceLeft bytes to transfer, gather up that much data
* from the appData buffers (starting at offset in the array),
* and transfer it into the netData buffer.
*
* The user has already ensured there is enough room.
*/
void gather(int spaceLeft) {
for (int i = offset; (i < (offset + len)) && (spaceLeft > 0); i++) {
int amount = Math.min(appData[i].remaining(), spaceLeft);
appData[i].limit(appData[i].position() + amount);
netData.put(appData[i]);
appRemaining -= amount;
spaceLeft -= amount;
}
}
/*
* Using the supplied buffer, scatter the data into the appData buffers
* (starting at offset in the array).
*
* The user has already ensured there is enough room.
*/
void scatter(ByteBuffer readyData) {
int amountLeft = readyData.remaining();
for (int i = offset; (i < (offset + len)) && (amountLeft > 0);
i++) {
int amount = Math.min(appData[i].remaining(), amountLeft);
readyData.limit(readyData.position() + amount);
appData[i].put(readyData);
amountLeft -= amount;
}
assert(readyData.remaining() == 0);
}
int getAppRemaining() {
return appRemaining;
}
/*
* Calculate the bytesConsumed/byteProduced. Aren't you glad
* we saved this off earlier?
*/
int deltaNet() {
return (netData.position() - netPos);
}
/*
* Calculate the bytesConsumed/byteProduced. Aren't you glad
* we saved this off earlier?
*/
int deltaApp() {
int sum = 0; // Only calculating 2^14 here, don't need a long.
for (int i = offset; i < offset + len; i++) {
sum += appData[i].position() - appPoss[i];
}
return sum;
}
/*
* In the case of Exception, we want to reset the positions
* to appear as though no data has been consumed or produced.
*
* Currently, this method is only called as we are preparing to
* fail out, and thus we don't need to actually recalculate
* appRemaining. If that assumption changes, that variable should
* be updated here.
*/
void resetPos() {
netData.position(netPos);
for (int i = offset; i < offset + len; i++) {
// See comment above about recalculating appRemaining.
appData[i].position(appPoss[i]);
}
}
/*
* We are doing lots of ByteBuffer manipulations, in which case
* we need to make sure that the limits get set back correctly.
* This is one of the last things to get done before returning to
* the user.
*/
void resetLim() {
netData.limit(netLim);
for (int i = offset; i < offset + len; i++) {
appData[i].limit(appLims[i]);
}
}
}

View file

@ -1,427 +0,0 @@
/*
* Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ssl;
import java.io.*;
import java.nio.*;
import javax.net.ssl.*;
import javax.crypto.BadPaddingException;
import sun.misc.HexDumpEncoder;
/**
* Wrapper class around InputRecord.
*
* Application data is kept external to the InputRecord,
* but handshake data (alert/change_cipher_spec/handshake) will
* be kept internally in the ByteArrayInputStream.
*
* @author Brad Wetmore
*/
final class EngineInputRecord extends InputRecord {
private SSLEngineImpl engine;
/*
* A dummy ByteBuffer we'll pass back even when the data
* is stored internally. It'll never actually be used.
*/
static private ByteBuffer tmpBB = ByteBuffer.allocate(0);
/*
* Flag to tell whether the last read/parsed data resides
* internal in the ByteArrayInputStream, or in the external
* buffers.
*/
private boolean internalData;
EngineInputRecord(SSLEngineImpl engine) {
super();
this.engine = engine;
}
@Override
byte contentType() {
if (internalData) {
return super.contentType();
} else {
return ct_application_data;
}
}
/*
* Check if there is enough inbound data in the ByteBuffer
* to make a inbound packet. Look for both SSLv2 and SSLv3.
*
* @return -1 if there are not enough bytes to tell (small header),
*/
int bytesInCompletePacket(ByteBuffer buf) throws SSLException {
/*
* SSLv2 length field is in bytes 0/1
* SSLv3/TLS length field is in bytes 3/4
*/
if (buf.remaining() < 5) {
return -1;
}
int pos = buf.position();
byte byteZero = buf.get(pos);
int len = 0;
/*
* If we have already verified previous packets, we can
* ignore the verifications steps, and jump right to the
* determination. Otherwise, try one last hueristic to
* see if it's SSL/TLS.
*/
if (formatVerified ||
(byteZero == ct_handshake) ||
(byteZero == ct_alert)) {
/*
* Last sanity check that it's not a wild record
*/
ProtocolVersion recordVersion =
ProtocolVersion.valueOf(buf.get(pos + 1), buf.get(pos + 2));
// check the record version
checkRecordVersion(recordVersion, false);
/*
* Reasonably sure this is a V3, disable further checks.
* We can't do the same in the v2 check below, because
* read still needs to parse/handle the v2 clientHello.
*/
formatVerified = true;
/*
* One of the SSLv3/TLS message types.
*/
len = ((buf.get(pos + 3) & 0xff) << 8) +
(buf.get(pos + 4) & 0xff) + headerSize;
} else {
/*
* Must be SSLv2 or something unknown.
* Check if it's short (2 bytes) or
* long (3) header.
*
* Internals can warn about unsupported SSLv2
*/
boolean isShort = ((byteZero & 0x80) != 0);
if (isShort &&
((buf.get(pos + 2) == 1) || buf.get(pos + 2) == 4)) {
ProtocolVersion recordVersion =
ProtocolVersion.valueOf(buf.get(pos + 3), buf.get(pos + 4));
// check the record version
checkRecordVersion(recordVersion, true);
/*
* Client or Server Hello
*/
int mask = (isShort ? 0x7f : 0x3f);
len = ((byteZero & mask) << 8) + (buf.get(pos + 1) & 0xff) +
(isShort ? 2 : 3);
} else {
// Gobblygook!
throw new SSLException(
"Unrecognized SSL message, plaintext connection?");
}
}
return len;
}
/*
* Pass the data down if it's internally cached, otherwise
* do it here.
*
* If internal data, data is decrypted internally.
*
* If external data(app), return a new ByteBuffer with data to
* process.
*/
ByteBuffer decrypt(Authenticator authenticator,
CipherBox box, ByteBuffer bb) throws BadPaddingException {
if (internalData) {
decrypt(authenticator, box); // MAC is checked during decryption
return tmpBB;
}
BadPaddingException reservedBPE = null;
int tagLen =
(authenticator instanceof MAC) ? ((MAC)authenticator).MAClen() : 0;
int cipheredLength = bb.remaining();
if (!box.isNullCipher()) {
try {
// apply explicit nonce for AEAD/CBC cipher suites if needed
int nonceSize =
box.applyExplicitNonce(authenticator, contentType(), bb);
// decrypt the content
if (box.isAEADMode()) {
// DON'T encrypt the nonce_explicit for AEAD mode
bb.position(bb.position() + nonceSize);
} // The explicit IV for CBC mode can be decrypted.
// Note that the CipherBox.decrypt() does not change
// the capacity of the buffer.
box.decrypt(bb, tagLen);
bb.position(nonceSize); // We don't actually remove the nonce.
} catch (BadPaddingException bpe) {
// RFC 2246 states that decryption_failed should be used
// for this purpose. However, that allows certain attacks,
// so we just send bad record MAC. We also need to make
// sure to always check the MAC to avoid a timing attack
// for the same issue. See paper by Vaudenay et al and the
// update in RFC 4346/5246.
//
// Failover to message authentication code checking.
reservedBPE = bpe;
}
}
// Requires message authentication code for null, stream and block
// cipher suites.
if ((authenticator instanceof MAC) && (tagLen != 0)) {
MAC signer = (MAC)authenticator;
int macOffset = bb.limit() - tagLen;
// Note that although it is not necessary, we run the same MAC
// computation and comparison on the payload for both stream
// cipher and CBC block cipher.
if (bb.remaining() < tagLen) {
// negative data length, something is wrong
if (reservedBPE == null) {
reservedBPE = new BadPaddingException("bad record");
}
// set offset of the dummy MAC
macOffset = cipheredLength - tagLen;
bb.limit(cipheredLength);
}
// Run MAC computation and comparison on the payload.
if (checkMacTags(contentType(), bb, signer, false)) {
if (reservedBPE == null) {
reservedBPE = new BadPaddingException("bad record MAC");
}
}
// Run MAC computation and comparison on the remainder.
//
// It is only necessary for CBC block cipher. It is used to get a
// constant time of MAC computation and comparison on each record.
if (box.isCBCMode()) {
int remainingLen = calculateRemainingLen(
signer, cipheredLength, macOffset);
// NOTE: here we use the InputRecord.buf because I did not find
// an effective way to work on ByteBuffer when its capacity is
// less than remainingLen.
// NOTE: remainingLen may be bigger (less than 1 block of the
// hash algorithm of the MAC) than the cipheredLength. However,
// We won't need to worry about it because we always use a
// maximum buffer for every record. We need a change here if
// we use small buffer size in the future.
if (remainingLen > buf.length) {
// unlikely to happen, just a placehold
throw new RuntimeException(
"Internal buffer capacity error");
}
// Won't need to worry about the result on the remainder. And
// then we won't need to worry about what's actual data to
// check MAC tag on. We start the check from the header of the
// buffer so that we don't need to construct a new byte buffer.
checkMacTags(contentType(), buf, 0, remainingLen, signer, true);
}
bb.limit(macOffset);
}
// Is it a failover?
if (reservedBPE != null) {
throw reservedBPE;
}
return bb.slice();
}
/*
* Run MAC computation and comparison
*
* Please DON'T change the content of the ByteBuffer parameter!
*/
private static boolean checkMacTags(byte contentType, ByteBuffer bb,
MAC signer, boolean isSimulated) {
int position = bb.position();
int tagLen = signer.MAClen();
int lim = bb.limit();
int macData = lim - tagLen;
bb.limit(macData);
byte[] hash = signer.compute(contentType, bb, isSimulated);
if (hash == null || tagLen != hash.length) {
// Something is wrong with MAC implementation.
throw new RuntimeException("Internal MAC error");
}
bb.position(macData);
bb.limit(lim);
try {
int[] results = compareMacTags(bb, hash);
return (results[0] != 0);
} finally {
// reset to the data
bb.position(position);
bb.limit(macData);
}
}
/*
* A constant-time comparison of the MAC tags.
*
* Please DON'T change the content of the ByteBuffer parameter!
*/
private static int[] compareMacTags(ByteBuffer bb, byte[] tag) {
// An array of hits is used to prevent Hotspot optimization for
// the purpose of a constant-time check.
int[] results = {0, 0}; // {missed #, matched #}
// The caller ensures there are enough bytes available in the buffer.
// So we won't need to check the remaining of the buffer.
for (int i = 0; i < tag.length; i++) {
if (bb.get() != tag[i]) {
results[0]++; // mismatched bytes
} else {
results[1]++; // matched bytes
}
}
return results;
}
/*
* Override the actual write below. We do things this way to be
* consistent with InputRecord. InputRecord may try to write out
* data to the peer, and *then* throw an Exception. This forces
* data to be generated/output before the exception is ever
* generated.
*/
@Override
void writeBuffer(OutputStream s, byte [] buf, int off, int len)
throws IOException {
/*
* Copy data out of buffer, it's ready to go.
*/
ByteBuffer netBB = ByteBuffer.allocate(len).put(buf, 0, len).flip();
engine.writer.putOutboundDataSync(netBB);
}
/*
* Delineate or read a complete packet from src.
*
* If internal data (hs, alert, ccs), the data is read and
* stored internally.
*
* If external data (app), return a new ByteBuffer which points
* to the data to process.
*/
ByteBuffer read(ByteBuffer srcBB) throws IOException {
/*
* Could have a src == null/dst == null check here,
* but that was already checked by SSLEngine.unwrap before
* ever attempting to read.
*/
/*
* If we have anything besides application data,
* or if we haven't even done the initial v2 verification,
* we send this down to be processed by the underlying
* internal cache.
*/
if (!formatVerified ||
(srcBB.get(srcBB.position()) != ct_application_data)) {
internalData = true;
read(new ByteBufferInputStream(srcBB), (OutputStream) null);
return tmpBB;
}
internalData = false;
int srcPos = srcBB.position();
int srcLim = srcBB.limit();
ProtocolVersion recordVersion = ProtocolVersion.valueOf(
srcBB.get(srcPos + 1), srcBB.get(srcPos + 2));
// check the record version
checkRecordVersion(recordVersion, false);
/*
* It's really application data. How much to consume?
* Jump over the header.
*/
int len = bytesInCompletePacket(srcBB);
assert(len > 0);
if (debug != null && Debug.isOn("packet")) {
try {
HexDumpEncoder hd = new HexDumpEncoder();
ByteBuffer bb = srcBB.duplicate(); // Use copy of BB
bb.limit(srcPos + len);
System.out.println("[Raw read (bb)]: length = " + len);
hd.encodeBuffer(bb, System.out);
} catch (IOException e) { }
}
// Demarcate past header to end of packet.
srcBB.position(srcPos + headerSize);
srcBB.limit(srcPos + len);
// Protect remainder of buffer, create slice to actually
// operate on.
ByteBuffer bb = srcBB.slice();
srcBB.position(srcBB.limit());
srcBB.limit(srcLim);
return bb;
}
}

View file

@ -1,329 +0,0 @@
/*
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ssl;
import java.io.*;
import java.nio.*;
/**
* A OutputRecord class extension which uses external ByteBuffers
* or the internal ByteArrayOutputStream for data manipulations.
* <P>
* Instead of rewriting this entire class
* to use ByteBuffers, we leave things intact, so handshake, CCS,
* and alerts will continue to use the internal buffers, but application
* data will use external buffers.
*
* @author Brad Wetmore
*/
final class EngineOutputRecord extends OutputRecord {
private SSLEngineImpl engine;
private EngineWriter writer;
private boolean finishedMsg = false;
/*
* All handshake hashing is done by the superclass
*/
/*
* Default constructor makes a record supporting the maximum
* SSL record size. It allocates the header bytes directly.
*
* @param type the content type for the record
*/
EngineOutputRecord(byte type, SSLEngineImpl engine) {
super(type, recordSize(type));
this.engine = engine;
writer = engine.writer;
}
/**
* Get the size of the buffer we need for records of the specified
* type.
* <P>
* Application data buffers will provide their own byte buffers,
* and will not use the internal byte caching.
*/
private static int recordSize(byte type) {
switch (type) {
case ct_change_cipher_spec:
case ct_alert:
return maxAlertRecordSize;
case ct_handshake:
return maxRecordSize;
case ct_application_data:
return 0;
}
throw new RuntimeException("Unknown record type: " + type);
}
void setFinishedMsg() {
finishedMsg = true;
}
@Override
public void flush() throws IOException {
finishedMsg = false;
}
boolean isFinishedMsg() {
return finishedMsg;
}
/*
* Override the actual write below. We do things this way to be
* consistent with InputRecord. InputRecord may try to write out
* data to the peer, and *then* throw an Exception. This forces
* data to be generated/output before the exception is ever
* generated.
*/
@Override
void writeBuffer(OutputStream s, byte [] buf, int off, int len,
int debugOffset) throws IOException {
/*
* Copy data out of buffer, it's ready to go.
*/
ByteBuffer netBB = ByteBuffer.allocate(len).put(buf, off, len).flip();
writer.putOutboundData(netBB);
}
/*
* Main method for writing non-application data.
* We MAC/encrypt, then send down for processing.
*/
void write(Authenticator authenticator, CipherBox writeCipher)
throws IOException {
/*
* Sanity check.
*/
switch (contentType()) {
case ct_change_cipher_spec:
case ct_alert:
case ct_handshake:
break;
default:
throw new RuntimeException("unexpected byte buffers");
}
/*
* Don't bother to really write empty records. We went this
* far to drive the handshake machinery, for correctness; not
* writing empty records improves performance by cutting CPU
* time and network resource usage. Also, some protocol
* implementations are fragile and don't like to see empty
* records, so this increases robustness.
*
* (Even change cipher spec messages have a byte of data!)
*/
if (!isEmpty()) {
// compress(); // eventually
encrypt(authenticator, writeCipher);
// send down for processing
write((OutputStream)null, false, (ByteArrayOutputStream)null);
}
return;
}
/**
* Main wrap/write driver.
*/
void write(EngineArgs ea, Authenticator authenticator,
CipherBox writeCipher) throws IOException {
/*
* sanity check to make sure someone didn't inadvertantly
* send us an impossible combination we don't know how
* to process.
*/
assert(contentType() == ct_application_data);
/*
* Have we set the MAC's yet? If not, we're not ready
* to process application data yet.
*/
if (authenticator == MAC.NULL) {
return;
}
/*
* Don't bother to really write empty records. We went this
* far to drive the handshake machinery, for correctness; not
* writing empty records improves performance by cutting CPU
* time and network resource usage. Also, some protocol
* implementations are fragile and don't like to see empty
* records, so this increases robustness.
*/
if (ea.getAppRemaining() == 0) {
return;
}
/*
* By default, we counter chosen plaintext issues on CBC mode
* ciphersuites in SSLv3/TLS1.0 by sending one byte of application
* data in the first record of every payload, and the rest in
* subsequent record(s). Note that the issues have been solved in
* TLS 1.1 or later.
*
* It is not necessary to split the very first application record of
* a freshly negotiated TLS session, as there is no previous
* application data to guess. To improve compatibility, we will not
* split such records.
*
* Because of the compatibility, we'd better produce no more than
* SSLSession.getPacketBufferSize() net data for each wrap. As we
* need a one-byte record at first, the 2nd record size should be
* equal to or less than Record.maxDataSizeMinusOneByteRecord.
*
* This avoids issues in the outbound direction. For a full fix,
* the peer must have similar protections.
*/
int length;
if (engine.needToSplitPayload(writeCipher, protocolVersion)) {
write(ea, authenticator, writeCipher, 0x01);
ea.resetLim(); // reset application data buffer limit
length = Math.min(ea.getAppRemaining(),
maxDataSizeMinusOneByteRecord);
} else {
length = Math.min(ea.getAppRemaining(), maxDataSize);
}
// Don't bother to really write empty records.
if (length > 0) {
write(ea, authenticator, writeCipher, length);
}
return;
}
void write(EngineArgs ea, Authenticator authenticator,
CipherBox writeCipher, int length) throws IOException {
/*
* Copy out existing buffer values.
*/
ByteBuffer dstBB = ea.netData;
int dstPos = dstBB.position();
int dstLim = dstBB.limit();
/*
* Where to put the data. Jump over the header.
*
* Don't need to worry about SSLv2 rewrites, if we're here,
* that's long since done.
*/
int dstData = dstPos + headerSize + writeCipher.getExplicitNonceSize();
dstBB.position(dstData);
/*
* transfer application data into the network data buffer
*/
ea.gather(length);
dstBB.limit(dstBB.position());
dstBB.position(dstData);
/*
* "flip" but skip over header again, add MAC & encrypt
*/
if (authenticator instanceof MAC) {
MAC signer = (MAC)authenticator;
if (signer.MAClen() != 0) {
byte[] hash = signer.compute(contentType(), dstBB, false);
/*
* position was advanced to limit in compute above.
*
* Mark next area as writable (above layers should have
* established that we have plenty of room), then write
* out the hash.
*/
dstBB.limit(dstBB.limit() + hash.length);
dstBB.put(hash);
// reset the position and limit
dstBB.limit(dstBB.position());
dstBB.position(dstData);
}
}
if (!writeCipher.isNullCipher()) {
/*
* Requires explicit IV/nonce for CBC/AEAD cipher suites for TLS 1.1
* or later.
*/
if (protocolVersion.v >= ProtocolVersion.TLS11.v &&
(writeCipher.isCBCMode() || writeCipher.isAEADMode())) {
byte[] nonce = writeCipher.createExplicitNonce(
authenticator, contentType(), dstBB.remaining());
dstBB.position(dstPos + headerSize);
dstBB.put(nonce);
if (!writeCipher.isAEADMode()) {
// The explicit IV in TLS 1.1 and later can be encrypted.
dstBB.position(dstPos + headerSize);
} // Otherwise, DON'T encrypt the nonce_explicit for AEAD mode
}
/*
* Encrypt may pad, so again the limit may have changed.
*/
writeCipher.encrypt(dstBB, dstLim);
if ((debug != null) && (Debug.isOn("record") ||
(Debug.isOn("handshake") &&
(contentType() == ct_change_cipher_spec)))) {
System.out.println(Thread.currentThread().getName()
// v3.0/v3.1 ...
+ ", WRITE: " + protocolVersion
+ " " + InputRecord.contentName(contentType())
+ ", length = " + length);
}
} else {
dstBB.position(dstBB.limit());
}
int packetLength = dstBB.limit() - dstPos - headerSize;
/*
* Finish out the record header.
*/
dstBB.put(dstPos, contentType());
dstBB.put(dstPos + 1, protocolVersion.major);
dstBB.put(dstPos + 2, protocolVersion.minor);
dstBB.put(dstPos + 3, (byte)(packetLength >> 8));
dstBB.put(dstPos + 4, (byte)packetLength);
/*
* Position was already set by encrypt() above.
*/
dstBB.limit(dstLim);
}
}

View file

@ -1,244 +0,0 @@
/*
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ssl;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.LinkedList;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import sun.misc.HexDumpEncoder;
/**
* A class to help abstract away SSLEngine writing synchronization.
*/
final class EngineWriter {
/*
* Outgoing handshake Data waiting for a ride is stored here.
* Normal application data is written directly into the outbound
* buffer, but handshake data can be written out at any time,
* so we have buffer it somewhere.
*
* When wrap is called, we first check to see if there is
* any data waiting, then if we're in a data transfer state,
* we try to write app data.
*
* This will contain either ByteBuffers, or the marker
* HandshakeStatus.FINISHED to signify that a handshake just completed.
*/
private LinkedList<Object> outboundList;
private boolean outboundClosed = false;
/* Class and subclass dynamic debugging support */
private static final Debug debug = Debug.getInstance("ssl");
EngineWriter() {
outboundList = new LinkedList<Object>();
}
/*
* Upper levels assured us we had room for at least one packet of data.
* As per the SSLEngine spec, we only return one SSL packets worth of
* data.
*/
private HandshakeStatus getOutboundData(ByteBuffer dstBB) {
Object msg = outboundList.removeFirst();
assert(msg instanceof ByteBuffer);
ByteBuffer bbIn = (ByteBuffer) msg;
assert(dstBB.remaining() >= bbIn.remaining());
dstBB.put(bbIn);
/*
* If we have more data in the queue, it's either
* a finished message, or an indication that we need
* to call wrap again.
*/
if (hasOutboundDataInternal()) {
msg = outboundList.getFirst();
if (msg == HandshakeStatus.FINISHED) {
outboundList.removeFirst(); // consume the message
return HandshakeStatus.FINISHED;
} else {
return HandshakeStatus.NEED_WRAP;
}
} else {
return null;
}
}
/*
* Properly orders the output of the data written to the wrap call.
* This is only handshake data, application data goes through the
* other writeRecord.
*/
synchronized void writeRecord(EngineOutputRecord outputRecord,
Authenticator authenticator,
CipherBox writeCipher) throws IOException {
/*
* Only output if we're still open.
*/
if (outboundClosed) {
throw new IOException("writer side was already closed.");
}
outputRecord.write(authenticator, writeCipher);
/*
* Did our handshakers notify that we just sent the
* Finished message?
*
* Add an "I'm finished" message to the queue.
*/
if (outputRecord.isFinishedMsg()) {
outboundList.addLast(HandshakeStatus.FINISHED);
}
}
/*
* Output the packet info.
*/
private void dumpPacket(EngineArgs ea, boolean hsData) {
try {
HexDumpEncoder hd = new HexDumpEncoder();
ByteBuffer bb = ea.netData.duplicate();
int pos = bb.position();
bb.position(pos - ea.deltaNet());
bb.limit(pos);
System.out.println("[Raw write" +
(hsData ? "" : " (bb)") + "]: length = " +
bb.remaining());
hd.encodeBuffer(bb, System.out);
} catch (IOException e) { }
}
/*
* Properly orders the output of the data written to the wrap call.
* Only app data goes through here, handshake data goes through
* the other writeRecord.
*
* Shouldn't expect to have an IOException here.
*
* Return any determined status.
*/
synchronized HandshakeStatus writeRecord(
EngineOutputRecord outputRecord, EngineArgs ea,
Authenticator authenticator,
CipherBox writeCipher) throws IOException {
/*
* If we have data ready to go, output this first before
* trying to consume app data.
*/
if (hasOutboundDataInternal()) {
HandshakeStatus hss = getOutboundData(ea.netData);
if (debug != null && Debug.isOn("packet")) {
/*
* We could have put the dump in
* OutputRecord.write(OutputStream), but let's actually
* output when it's actually output by the SSLEngine.
*/
dumpPacket(ea, true);
}
return hss;
}
/*
* If we are closed, no more app data can be output.
* Only existing handshake data (above) can be obtained.
*/
if (outboundClosed) {
throw new IOException("The write side was already closed");
}
outputRecord.write(ea, authenticator, writeCipher);
if (debug != null && Debug.isOn("packet")) {
dumpPacket(ea, false);
}
/*
* No way new outbound handshake data got here if we're
* locked properly.
*
* We don't have any status we can return.
*/
return null;
}
/*
* We already hold "this" lock, this is the callback from the
* outputRecord.write() above. We already know this
* writer can accept more data (outboundClosed == false),
* and the closure is sync'd.
*/
void putOutboundData(ByteBuffer bytes) {
outboundList.addLast(bytes);
}
/*
* This is for the really rare case that someone is writing from
* the *InputRecord* before we know what to do with it.
*/
synchronized void putOutboundDataSync(ByteBuffer bytes)
throws IOException {
if (outboundClosed) {
throw new IOException("Write side already closed");
}
outboundList.addLast(bytes);
}
/*
* Non-synch'd version of this method, called by internals
*/
private boolean hasOutboundDataInternal() {
return (outboundList.size() != 0);
}
synchronized boolean hasOutboundData() {
return hasOutboundDataInternal();
}
synchronized boolean isOutboundDone() {
return outboundClosed && !hasOutboundDataInternal();
}
synchronized void closeOutbound() {
outboundClosed = true;
}
}

View file

@ -29,6 +29,7 @@ package sun.security.ssl;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.util.Locale;
import java.nio.ByteBuffer;
/**
* Abstraction for the SSL/TLS hash of all handshake messages that is
@ -99,6 +100,9 @@ final class HandshakeHash {
// For TLS 1.2
private MessageDigest finMD;
// Cache for input record handshake hash computation
private ByteArrayOutputStream reserve = new ByteArrayOutputStream();
/**
* Create a new HandshakeHash. needCertificateVerify indicates whether
* a hash for the certificate verify message is required.
@ -107,7 +111,106 @@ final class HandshakeHash {
clonesNeeded = needCertificateVerify ? 3 : 2;
}
void reserve(ByteBuffer input) {
if (input.hasArray()) {
reserve.write(input.array(),
input.position() + input.arrayOffset(), input.remaining());
} else {
int inPos = input.position();
byte[] holder = new byte[input.remaining()];
input.get(holder);
input.position(inPos);
reserve.write(holder, 0, holder.length);
}
}
void reserve(byte[] b, int offset, int len) {
reserve.write(b, offset, len);
}
void reload() {
if (reserve.size() != 0) {
byte[] bytes = reserve.toByteArray();
reserve.reset();
update(bytes, 0, bytes.length);
}
}
void update(ByteBuffer input) {
// reload if there are reserved messages.
reload();
int inPos = input.position();
switch (version) {
case 1:
md5.update(input);
input.position(inPos);
sha.update(input);
input.position(inPos);
break;
default:
if (finMD != null) {
finMD.update(input);
input.position(inPos);
}
if (input.hasArray()) {
data.write(input.array(),
inPos + input.arrayOffset(), input.remaining());
} else {
byte[] holder = new byte[input.remaining()];
input.get(holder);
input.position(inPos);
data.write(holder, 0, holder.length);
}
break;
}
}
void update(byte handshakeType, byte[] handshakeBody) {
// reload if there are reserved messages.
reload();
switch (version) {
case 1:
md5.update(handshakeType);
sha.update(handshakeType);
md5.update((byte)((handshakeBody.length >> 16) & 0xFF));
sha.update((byte)((handshakeBody.length >> 16) & 0xFF));
md5.update((byte)((handshakeBody.length >> 8) & 0xFF));
sha.update((byte)((handshakeBody.length >> 8) & 0xFF));
md5.update((byte)(handshakeBody.length & 0xFF));
sha.update((byte)(handshakeBody.length & 0xFF));
md5.update(handshakeBody);
sha.update(handshakeBody);
break;
default:
if (finMD != null) {
finMD.update(handshakeType);
finMD.update((byte)((handshakeBody.length >> 16) & 0xFF));
finMD.update((byte)((handshakeBody.length >> 8) & 0xFF));
finMD.update((byte)(handshakeBody.length & 0xFF));
finMD.update(handshakeBody);
}
data.write(handshakeType);
data.write((byte)((handshakeBody.length >> 16) & 0xFF));
data.write((byte)((handshakeBody.length >> 8) & 0xFF));
data.write((byte)(handshakeBody.length & 0xFF));
data.write(handshakeBody, 0, handshakeBody.length);
break;
}
}
void update(byte[] b, int offset, int len) {
// reload if there are reserved messages.
reload();
switch (version) {
case 1:
md5.update(b, offset, len);
@ -139,9 +242,15 @@ final class HandshakeHash {
void protocolDetermined(ProtocolVersion pv) {
// Do not set again, will ignore
if (version != -1) return;
if (version != -1) {
return;
}
version = pv.compareTo(ProtocolVersion.TLS12) >= 0 ? 2 : 1;
if (pv.maybeDTLSProtocol()) {
version = pv.compareTo(ProtocolVersion.DTLS12) >= 0 ? 2 : 1;
} else {
version = pv.compareTo(ProtocolVersion.TLS12) >= 0 ? 2 : 1;
}
switch (version) {
case 1:
// initiate md5, sha and call update on saved array

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2015, 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,11 +23,11 @@
* questions.
*/
package sun.security.ssl;
import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import javax.net.ssl.SSLException;
@ -38,154 +38,104 @@ import javax.net.ssl.SSLException;
* Once a new handshake record arrives, it is buffered in this class until
* processed by the Handshaker. The buffer may also contain incomplete
* handshake messages in case the message is split across multiple records.
* Handshaker.process_record deals with all that. It may also contain
* Handshaker.processRecord deals with all that. It may also contain
* handshake messages larger than the default buffer size (e.g. large
* certificate messages). The buffer is grown dynamically to handle that
* (see InputRecord.queueHandshake()).
* certificate messages). The buffer is grown dynamically to handle that.
*
* Note that the InputRecord used as a buffer here is separate from the
* AppInStream.r, which is where data from the socket is initially read
* into. This is because once the initial handshake has been completed,
* handshake and application data messages may be interleaved arbitrarily
* and must be processed independently.
* Note that this class only handles Handshake messages in TLS format.
* DTLS Handshake messages should be converted into TLS format before
* calling into this method.
*
* @author David Brownell
*/
public class HandshakeInStream extends InputStream {
InputRecord r;
// This class is used to handle plain text handshake messages.
//
public final class HandshakeInStream extends ByteArrayInputStream {
/*
* Construct the stream; we'll be accumulating hashes of the
* input records using two sets of digests.
*/
HandshakeInStream(HandshakeHash handshakeHash) {
r = new InputRecord();
r.setHandshakeHash(handshakeHash);
HandshakeInStream() {
super(new byte[0]); // lazy to alloacte the internal buffer
}
//
// overridden ByteArrayInputStream methods
//
// overridden InputStream methods
/*
* Return the number of bytes available for read().
*
* Note that this returns the bytes remaining in the buffer, not
* the bytes remaining in the current handshake message.
*/
@Override
public int available() {
return r.available();
}
/*
* Get a byte of handshake data.
*/
@Override
public int read() throws IOException {
int n = r.read();
if (n == -1) {
public int read(byte[] b) throws IOException {
if (super.read(b) != b.length) {
throw new SSLException("Unexpected end of handshake data");
}
return n;
return b.length;
}
/*
* Get a bunch of bytes of handshake data.
*/
@Override
public int read(byte b [], int off, int len) throws IOException {
// we read from a ByteArrayInputStream, it always returns the
// data in a single read if enough is available
int n = r.read(b, off, len);
if (n != len) {
throw new SSLException("Unexpected end of handshake data");
}
return n;
}
/*
* Skip some handshake data.
*/
@Override
public long skip(long n) throws IOException {
return r.skip(n);
}
/*
* Mark/ reset code, implemented using InputRecord mark/ reset.
*
* Note that it currently provides only a limited mark functionality
* and should be used with care (once a new handshake record has been
* read, data that has already been consumed is lost even if marked).
*/
@Override
public void mark(int readlimit) {
r.mark(readlimit);
}
@Override
public void reset() throws IOException {
r.reset();
}
@Override
public boolean markSupported() {
return true;
}
// handshake management functions
//
// handshake input stream management functions
//
/*
* Here's an incoming record with handshake data. Queue the contents;
* it might be one or more entire messages, complete a message that's
* partly queued, or both.
*/
void incomingRecord(InputRecord in) throws IOException {
r.queueHandshake(in);
void incomingRecord(ByteBuffer in) throws IOException {
int len;
// Move any unread data to the front of the buffer.
if (pos != 0) {
len = count - pos;
if (len != 0) {
System.arraycopy(buf, pos, buf, 0, len);
}
pos = 0;
count = len;
}
// Grow buffer if needed.
len = in.remaining() + count;
if (buf.length < len) {
byte[] newbuf = new byte[len];
if (count != 0) {
System.arraycopy(buf, 0, newbuf, 0, count);
}
buf = newbuf;
}
// Append the incoming record to the buffer
in.get(buf, count, in.remaining());
count = len;
}
/*
* Hash any data we've consumed but not yet hashed. Useful mostly
* for processing client certificate messages (so we can check the
* immediately following cert verify message) and finished messages
* (so we can compute our own finished message).
*/
void digestNow() {
r.doHashes();
}
/*
* Do more than skip that handshake data ... totally ignore it.
* The difference is that the data does not get hashed.
*/
void ignore(int n) {
r.ignore(n);
}
//
// Message parsing methods
//
/*
* Read 8, 16, 24, and 32 bit SSL integer data types, encoded
* in standard big-endian form.
*/
int getInt8() throws IOException {
verifyLength(1);
return read();
}
int getInt16() throws IOException {
verifyLength(2);
return (getInt8() << 8) | getInt8();
}
int getInt24() throws IOException {
verifyLength(3);
return (getInt8() << 16) | (getInt8() << 8) | getInt8();
}
int getInt32() throws IOException {
verifyLength(4);
return (getInt8() << 24) | (getInt8() << 16)
| (getInt8() << 8) | getInt8();
}
@ -193,13 +143,12 @@ public class HandshakeInStream extends InputStream {
/*
* Read byte vectors with 8, 16, and 24 bit length encodings.
*/
byte[] getBytes8() throws IOException {
int len = getInt8();
verifyLength(len);
byte b[] = new byte[len];
read(b, 0, len);
read(b);
return b;
}
@ -208,7 +157,7 @@ public class HandshakeInStream extends InputStream {
verifyLength(len);
byte b[] = new byte[len];
read(b, 0, len);
read(b);
return b;
}
@ -217,16 +166,14 @@ public class HandshakeInStream extends InputStream {
verifyLength(len);
byte b[] = new byte[len];
read(b, 0, len);
read(b);
return b;
}
// Is a length greater than available bytes in the record?
private void verifyLength(int len) throws SSLException {
if (len > available()) {
throw new SSLException(
"Not enough data to fill declared vector size");
throw new SSLException("Unexpected end of handshake data");
}
}
}

View file

@ -73,25 +73,44 @@ import sun.security.util.KeyUtil;
*/
public abstract class HandshakeMessage {
HandshakeMessage() { }
// enum HandshakeType:
static final byte ht_hello_request = 0;
static final byte ht_client_hello = 1;
static final byte ht_server_hello = 2;
static final byte ht_certificate = 11;
static final byte ht_server_key_exchange = 12;
static final byte ht_certificate_request = 13;
static final byte ht_server_hello_done = 14;
static final byte ht_certificate_verify = 15;
static final byte ht_client_key_exchange = 16;
static final byte ht_finished = 20;
/* Class and subclass dynamic debugging support */
public static final Debug debug = Debug.getInstance("ssl");
// enum HandshakeType:
static final byte ht_hello_request = 0; // RFC 5246
static final byte ht_client_hello = 1; // RFC 5246
static final byte ht_server_hello = 2; // RFC 5246
static final byte ht_hello_verify_request = 3; // RFC 6347
static final byte ht_new_session_ticket = 4; // RFC 4507
static final byte ht_certificate = 11; // RFC 5246
static final byte ht_server_key_exchange = 12; // RFC 5246
static final byte ht_certificate_request = 13; // RFC 5246
static final byte ht_server_hello_done = 14; // RFC 5246
static final byte ht_certificate_verify = 15; // RFC 5246
static final byte ht_client_key_exchange = 16; // RFC 5246
static final byte ht_finished = 20; // RFC 5246
static final byte ht_certificate_url = 21; // RFC 6066
static final byte ht_certificate_status = 22; // RFC 6066
static final byte ht_supplemental_data = 23; // RFC 4680
static final byte ht_not_applicable = -1; // N/A
/*
* SSL 3.0 MAC padding constants.
* Also used by CertificateVerify and Finished during the handshake.
*/
static final byte[] MD5_pad1 = genPad(0x36, 48);
static final byte[] MD5_pad2 = genPad(0x5c, 48);
static final byte[] SHA_pad1 = genPad(0x36, 40);
static final byte[] SHA_pad2 = genPad(0x5c, 40);
// default constructor
HandshakeMessage() {
}
/**
* Utility method to convert a BigInteger to a byte array in unsigned
* format as needed in the handshake messages. BigInteger uses
@ -109,16 +128,6 @@ public abstract class HandshakeMessage {
return b;
}
/*
* SSL 3.0 MAC padding constants.
* Also used by CertificateVerify and Finished during the handshake.
*/
static final byte[] MD5_pad1 = genPad(0x36, 48);
static final byte[] MD5_pad2 = genPad(0x5c, 48);
static final byte[] SHA_pad1 = genPad(0x36, 40);
static final byte[] SHA_pad2 = genPad(0x5c, 40);
private static byte[] genPad(int b, int count) {
byte[] padding = new byte[count];
Arrays.fill(padding, (byte)b);
@ -141,6 +150,7 @@ public abstract class HandshakeMessage {
s.write(messageType());
s.putInt24(len);
send(s);
s.complete();
}
/*
@ -199,6 +209,69 @@ static final class HelloRequest extends HandshakeMessage {
}
/*
* HelloVerifyRequest ... SERVER --> CLIENT [DTLS only]
*
* The definition of HelloVerifyRequest is as follows:
*
* struct {
* ProtocolVersion server_version;
* opaque cookie<0..2^8-1>;
* } HelloVerifyRequest;
*
* For DTLS protocols, once the client has transmitted the ClientHello message,
* it expects to see a HelloVerifyRequest from the server. However, if the
* server's message is lost, the client knows that either the ClientHello or
* the HelloVerifyRequest has been lost and retransmits. [RFC 6347]
*/
static final class HelloVerifyRequest extends HandshakeMessage {
ProtocolVersion protocolVersion;
byte[] cookie; // 1 to 2^8 - 1 bytes
HelloVerifyRequest(HelloCookieManager helloCookieManager,
ClientHello clientHelloMsg) {
this.protocolVersion = clientHelloMsg.protocolVersion;
this.cookie = helloCookieManager.getCookie(clientHelloMsg);
}
HelloVerifyRequest(
HandshakeInStream input, int messageLength) throws IOException {
this.protocolVersion =
ProtocolVersion.valueOf(input.getInt8(), input.getInt8());
this.cookie = input.getBytes8();
// Is it a valid cookie?
HelloCookieManager.checkCookie(protocolVersion, cookie);
}
@Override
int messageType() {
return ht_hello_verify_request;
}
@Override
int messageLength() {
return 2 + cookie.length; // 2: the length of protocolVersion
}
@Override
void send(HandshakeOutStream hos) throws IOException {
hos.putInt8(protocolVersion.major);
hos.putInt8(protocolVersion.minor);
hos.putBytes8(cookie);
}
@Override
void print(PrintStream out) throws IOException {
out.println("*** HelloVerifyRequest");
if (debug != null && Debug.isOn("verbose")) {
out.println("server_version: " + protocolVersion);
Debug.println(out, "cookie", cookie);
}
}
}
/*
* ClientHello ... CLIENT --> SERVER
@ -213,22 +286,31 @@ static final class HelloRequest extends HandshakeMessage {
*/
static final class ClientHello extends HandshakeMessage {
ProtocolVersion protocolVersion;
RandomCookie clnt_random;
SessionId sessionId;
private CipherSuiteList cipherSuites;
byte[] compression_methods;
ProtocolVersion protocolVersion;
RandomCookie clnt_random;
SessionId sessionId;
byte[] cookie; // DTLS only
private CipherSuiteList cipherSuites;
private final boolean isDTLS;
byte[] compression_methods;
HelloExtensions extensions = new HelloExtensions();
private final static byte[] NULL_COMPRESSION = new byte[] {0};
ClientHello(SecureRandom generator, ProtocolVersion protocolVersion,
SessionId sessionId, CipherSuiteList cipherSuites) {
SessionId sessionId, CipherSuiteList cipherSuites,
boolean isDTLS) {
this.isDTLS = isDTLS;
this.protocolVersion = protocolVersion;
this.sessionId = sessionId;
this.cipherSuites = cipherSuites;
if (isDTLS) {
this.cookie = new byte[0];
} else {
this.cookie = null;
}
if (cipherSuites.containsEC()) {
extensions.add(SupportedEllipticCurvesExtension.DEFAULT);
@ -239,11 +321,21 @@ static final class ClientHello extends HandshakeMessage {
compression_methods = NULL_COMPRESSION;
}
ClientHello(HandshakeInStream s, int messageLength) throws IOException {
ClientHello(HandshakeInStream s,
int messageLength, boolean isDTLS) throws IOException {
this.isDTLS = isDTLS;
protocolVersion = ProtocolVersion.valueOf(s.getInt8(), s.getInt8());
clnt_random = new RandomCookie(s);
sessionId = new SessionId(s.getBytes8());
sessionId.checkLength(protocolVersion);
if (isDTLS) {
cookie = s.getBytes8();
} else {
cookie = null;
}
cipherSuites = new CipherSuiteList(s);
compression_methods = s.getBytes8();
if (messageLength() != messageLength) {
@ -279,6 +371,28 @@ static final class ClientHello extends HandshakeMessage {
extensions.add(signatureAlgorithm);
}
void addMFLExtension(int maximumPacketSize) {
HelloExtension maxFragmentLength =
new MaxFragmentLengthExtension(maximumPacketSize);
extensions.add(maxFragmentLength);
}
void updateHelloCookie(MessageDigest cookieDigest) {
//
// Just use HandshakeOutStream to compute the hello verify cookie.
// Not actually used to output handshake message records.
//
HandshakeOutStream hos = new HandshakeOutStream(null);
try {
send(hos, false); // Do not count hello verify cookie.
} catch (IOException ioe) {
// unlikely to happen
}
cookieDigest.update(hos.toByteArray());
}
@Override
int messageType() { return ht_client_hello; }
@ -290,6 +404,7 @@ static final class ClientHello extends HandshakeMessage {
*/
return (2 + 32 + 1 + 2 + 1
+ sessionId.length() /* ... + variable parts */
+ (isDTLS ? (1 + cookie.length) : 0)
+ (cipherSuites.size() * 2)
+ compression_methods.length)
+ extensions.length();
@ -297,13 +412,7 @@ static final class ClientHello extends HandshakeMessage {
@Override
void send(HandshakeOutStream s) throws IOException {
s.putInt8(protocolVersion.major);
s.putInt8(protocolVersion.minor);
clnt_random.send(s);
s.putBytes8(sessionId.getId());
cipherSuites.send(s);
s.putBytes8(compression_methods);
extensions.send(s);
send(s, true); // Count hello verify cookie.
}
@Override
@ -317,6 +426,10 @@ static final class ClientHello extends HandshakeMessage {
s.print("Session ID: ");
s.println(sessionId);
if (isDTLS) {
Debug.println(s, "cookie", cookie);
}
s.println("Cipher Suites: " + cipherSuites);
Debug.println(s, "Compression Methods", compression_methods);
@ -324,6 +437,21 @@ static final class ClientHello extends HandshakeMessage {
s.println("***");
}
}
private void send(HandshakeOutStream s,
boolean computeCookie) throws IOException {
s.putInt8(protocolVersion.major);
s.putInt8(protocolVersion.minor);
clnt_random.send(s);
s.putBytes8(sessionId.getId());
if (isDTLS && computeCookie) {
s.putBytes8(cookie);
}
cipherSuites.send(s);
s.putBytes8(compression_methods);
extensions.send(s);
}
}
/*
@ -740,7 +868,7 @@ class DH_ServerKeyExchange extends ServerKeyExchange
setValues(obj);
Signature sig;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
this.preferableSignatureAlgorithm = signAlgorithm;
sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName());
} else {
@ -801,7 +929,7 @@ class DH_ServerKeyExchange extends ServerKeyExchange
new BigInteger(1, dh_g)));
// read the signature and hash algorithm
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
int hash = input.getInt8(); // hash algorithm
int signature = input.getInt8(); // signature algorithm
@ -834,7 +962,7 @@ class DH_ServerKeyExchange extends ServerKeyExchange
Signature sig;
String algorithm = publicKey.getAlgorithm();
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
sig = JsseJce.getSignature(
preferableSignatureAlgorithm.getAlgorithmName());
} else {
@ -914,7 +1042,7 @@ class DH_ServerKeyExchange extends ServerKeyExchange
temp += dh_Ys.length;
if (signature != null) {
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
temp += SignatureAndHashAlgorithm.sizeInRecord();
}
@ -934,7 +1062,7 @@ class DH_ServerKeyExchange extends ServerKeyExchange
s.putBytes16(dh_Ys);
if (signature != null) {
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
s.putInt8(preferableSignatureAlgorithm.getHashValue());
s.putInt8(preferableSignatureAlgorithm.getSignatureValue());
}
@ -959,7 +1087,7 @@ class DH_ServerKeyExchange extends ServerKeyExchange
if (signature == null) {
s.println("Anonymous");
} else {
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
s.println("Signature Algorithm " +
preferableSignatureAlgorithm.getAlgorithmName());
}
@ -1021,7 +1149,7 @@ class ECDH_ServerKeyExchange extends ServerKeyExchange {
}
Signature sig;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
this.preferableSignatureAlgorithm = signAlgorithm;
sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName());
} else {
@ -1084,7 +1212,7 @@ class ECDH_ServerKeyExchange extends ServerKeyExchange {
}
// read the signature and hash algorithm
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
int hash = input.getInt8(); // hash algorithm
int signature = input.getInt8(); // signature algorithm
@ -1105,7 +1233,7 @@ class ECDH_ServerKeyExchange extends ServerKeyExchange {
// verify the signature
Signature sig;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
sig = JsseJce.getSignature(
preferableSignatureAlgorithm.getAlgorithmName());
} else {
@ -1157,7 +1285,7 @@ class ECDH_ServerKeyExchange extends ServerKeyExchange {
int sigLen = 0;
if (signatureBytes != null) {
sigLen = 2 + signatureBytes.length;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
sigLen += SignatureAndHashAlgorithm.sizeInRecord();
}
}
@ -1172,7 +1300,7 @@ class ECDH_ServerKeyExchange extends ServerKeyExchange {
s.putBytes8(pointBytes);
if (signatureBytes != null) {
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
s.putInt8(preferableSignatureAlgorithm.getHashValue());
s.putInt8(preferableSignatureAlgorithm.getSignatureValue());
}
@ -1189,7 +1317,7 @@ class ECDH_ServerKeyExchange extends ServerKeyExchange {
if (signatureBytes == null) {
s.println("Anonymous");
} else {
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
s.println("Signature Algorithm " +
preferableSignatureAlgorithm.getAlgorithmName());
}
@ -1315,7 +1443,7 @@ class CertificateRequest extends HandshakeMessage
this.types = JsseJce.isEcAvailable() ? TYPES_ECC : TYPES_NO_ECC;
// Use supported_signature_algorithms for TLS 1.2 or later.
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
if (signAlgs == null || signAlgs.isEmpty()) {
throw new SSLProtocolException(
"No supported signature algorithms");
@ -1339,7 +1467,7 @@ class CertificateRequest extends HandshakeMessage
types = input.getBytes8();
// Read the supported_signature_algorithms for TLS 1.2 or later.
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
algorithmsLen = input.getInt16();
if (algorithmsLen < 2) {
throw new SSLProtocolException(
@ -1406,7 +1534,7 @@ class CertificateRequest extends HandshakeMessage
int messageLength() {
int len = 1 + types.length + 2;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
len += algorithmsLen + 2;
}
@ -1423,7 +1551,7 @@ class CertificateRequest extends HandshakeMessage
output.putBytes8(types);
// put supported_signature_algorithms
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
output.putInt16(algorithmsLen);
for (SignatureAndHashAlgorithm algorithm : algorithms) {
output.putInt8(algorithm.getHashValue()); // hash
@ -1478,7 +1606,7 @@ class CertificateRequest extends HandshakeMessage
}
s.println();
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
StringBuilder sb = new StringBuilder();
boolean opened = false;
for (SignatureAndHashAlgorithm signAlg : algorithms) {
@ -1576,7 +1704,7 @@ static final class CertificateVerify extends HandshakeMessage {
String algorithm = privateKey.getAlgorithm();
Signature sig = null;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
this.preferableSignatureAlgorithm = signAlgorithm;
sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName());
} else {
@ -1598,7 +1726,7 @@ static final class CertificateVerify extends HandshakeMessage {
this.protocolVersion = protocolVersion;
// read the signature and hash algorithm
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
int hashAlg = input.getInt8(); // hash algorithm
int signAlg = input.getInt8(); // signature algorithm
@ -1634,7 +1762,7 @@ static final class CertificateVerify extends HandshakeMessage {
SecretKey masterSecret) throws GeneralSecurityException {
String algorithm = publicKey.getAlgorithm();
Signature sig = null;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
sig = JsseJce.getSignature(
preferableSignatureAlgorithm.getAlgorithmName());
} else {
@ -1676,11 +1804,11 @@ static final class CertificateVerify extends HandshakeMessage {
throws SignatureException {
if (algorithm.equals("RSA")) {
if (protocolVersion.v < ProtocolVersion.TLS12.v) { // TLS1.1-
if (!protocolVersion.useTLS12PlusSpec()) { // TLS1.1-
MessageDigest md5Clone = handshakeHash.getMD5Clone();
MessageDigest shaClone = handshakeHash.getSHAClone();
if (protocolVersion.v < ProtocolVersion.TLS10.v) { // SSLv3
if (!protocolVersion.useTLS10PlusSpec()) { // SSLv3
updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterKey);
updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey);
}
@ -1692,10 +1820,10 @@ static final class CertificateVerify extends HandshakeMessage {
sig.update(handshakeHash.getAllHandshakeMessages());
}
} else { // DSA, ECDSA
if (protocolVersion.v < ProtocolVersion.TLS12.v) { // TLS1.1-
if (!protocolVersion.useTLS12PlusSpec()) { // TLS1.1-
MessageDigest shaClone = handshakeHash.getSHAClone();
if (protocolVersion.v < ProtocolVersion.TLS10.v) { // SSLv3
if (!protocolVersion.useTLS10PlusSpec()) { // SSLv3
updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey);
}
@ -1811,7 +1939,7 @@ static final class CertificateVerify extends HandshakeMessage {
int messageLength() {
int temp = 2;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
temp += SignatureAndHashAlgorithm.sizeInRecord();
}
@ -1820,7 +1948,7 @@ static final class CertificateVerify extends HandshakeMessage {
@Override
void send(HandshakeOutStream s) throws IOException {
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
s.putInt8(preferableSignatureAlgorithm.getHashValue());
s.putInt8(preferableSignatureAlgorithm.getSignatureValue());
}
@ -1833,7 +1961,7 @@ static final class CertificateVerify extends HandshakeMessage {
s.println("*** CertificateVerify");
if (debug != null && Debug.isOn("verbose")) {
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
s.println("Signature Algorithm " +
preferableSignatureAlgorithm.getAlgorithmName());
}
@ -1899,7 +2027,7 @@ static final class Finished extends HandshakeMessage {
CipherSuite cipherSuite) throws IOException {
this.protocolVersion = protocolVersion;
this.cipherSuite = cipherSuite;
int msgLen = (protocolVersion.v >= ProtocolVersion.TLS10.v) ? 12 : 36;
int msgLen = protocolVersion.useTLS10PlusSpec() ? 12 : 36;
verifyData = new byte[msgLen];
input.read(verifyData);
}
@ -1932,7 +2060,7 @@ static final class Finished extends HandshakeMessage {
throw new RuntimeException("Invalid sender: " + sender);
}
if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
if (protocolVersion.useTLS10PlusSpec()) {
// TLS 1.0+
try {
byte [] seed;
@ -1940,14 +2068,14 @@ static final class Finished extends HandshakeMessage {
PRF prf;
// Get the KeyGenerator alg and calculate the seed.
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
// TLS 1.2
if (protocolVersion.useTLS12PlusSpec()) {
// TLS 1.2+ or DTLS 1.2+
seed = handshakeHash.getFinishedHash();
prfAlg = "SunTls12Prf";
prf = cipherSuite.prfAlg;
} else {
// TLS 1.0/1.1
// TLS 1.0/1.1, DTLS 1.0
MessageDigest md5Clone = handshakeHash.getMD5Clone();
MessageDigest shaClone = handshakeHash.getSHAClone();
seed = new byte[36];

View file

@ -23,10 +23,9 @@
* questions.
*/
package sun.security.ssl;
import java.io.OutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
/**
@ -40,197 +39,113 @@ import java.io.IOException;
*
* @author David Brownell
*/
public class HandshakeOutStream extends OutputStream {
public class HandshakeOutStream extends ByteArrayOutputStream {
private SSLSocketImpl socket;
private SSLEngineImpl engine;
OutputRecord outputRecord; // May be null if not actually used to
// output handshake message records.
OutputRecord r;
HandshakeOutStream(ProtocolVersion protocolVersion,
ProtocolVersion helloVersion, HandshakeHash handshakeHash,
SSLSocketImpl socket) {
this.socket = socket;
r = new OutputRecord(Record.ct_handshake);
init(protocolVersion, helloVersion, handshakeHash);
HandshakeOutStream(OutputRecord outputRecord) {
super();
this.outputRecord = outputRecord;
}
HandshakeOutStream(ProtocolVersion protocolVersion,
ProtocolVersion helloVersion, HandshakeHash handshakeHash,
SSLEngineImpl engine) {
this.engine = engine;
r = new EngineOutputRecord(Record.ct_handshake, engine);
init(protocolVersion, helloVersion, handshakeHash);
}
private void init(ProtocolVersion protocolVersion,
ProtocolVersion helloVersion, HandshakeHash handshakeHash) {
r.setVersion(protocolVersion);
r.setHelloVersion(helloVersion);
r.setHandshakeHash(handshakeHash);
}
/*
* Update the handshake data hashes ... mostly for use after a
* client cert has been sent, so the cert verify message can be
* constructed correctly yet without forcing extra I/O. In all
* other cases, automatic hash calculation suffices.
*/
void doHashes() {
r.doHashes();
}
/*
* Write some data out onto the stream ... buffers as much as possible.
* Hashes are updated automatically if something gets flushed to the
* network (e.g. a big cert message etc).
*/
@Override
public void write(byte buf[], int off, int len) throws IOException {
while (len > 0) {
int howmuch = Math.min(len, r.availableDataBytes());
if (howmuch == 0) {
flush();
} else {
r.write(buf, off, howmuch);
off += howmuch;
len -= howmuch;
}
// Complete a handshakin message writing. Called by HandshakeMessage.
void complete() throws IOException {
if (size() < 4) { // 4: handshake message header size
// internal_error alert will be triggered
throw new RuntimeException("handshake message is not available");
}
// outputRecord cannot be null
outputRecord.encodeHandshake(buf, 0, count);
// reset the byte array output stream
reset();
}
/*
* write-a-byte
*/
//
// overridden ByteArrayOutputStream methods
//
@Override
public void write(int i) throws IOException {
if (r.availableDataBytes() < 1) {
flush();
}
r.write(i);
public void write(byte[] b, int off, int len) {
// The maximum fragment size is 24 bytes.
checkOverflow(len, Record.OVERFLOW_OF_INT24);
super.write(b, off, len);
}
@Override
public void flush() throws IOException {
if (socket != null) {
try {
socket.writeRecord(r);
} catch (IOException e) {
// Had problems writing; check if there was an
// alert from peer. If alert received, waitForClose
// will throw an exception for the alert
socket.waitForClose(true);
// No alert was received, just rethrow exception
throw e;
}
} else { // engine != null
/*
* Even if record might be empty, flush anyway in case
* there is a finished handshake message that we need
* to queue.
*/
engine.writeRecord((EngineOutputRecord)r);
}
outputRecord.flush();
}
/*
* Tell the OutputRecord that a finished message was
* contained either in this record or the one immeiately
* preceding it. We need to reliably pass back notifications
* that a finish message occurred.
*/
void setFinishedMsg() {
assert(socket == null);
((EngineOutputRecord)r).setFinishedMsg();
}
//
// handshake output stream management functions
//
/*
* Put integers encoded in standard 8, 16, 24, and 32 bit
* big endian formats. Note that OutputStream.write(int) only
* writes the least significant 8 bits and ignores the rest.
*/
void putInt8(int i) throws IOException {
checkOverflow(i, Record.OVERFLOW_OF_INT08);
r.write(i);
super.write(i);
}
void putInt16(int i) throws IOException {
checkOverflow(i, Record.OVERFLOW_OF_INT16);
if (r.availableDataBytes() < 2) {
flush();
}
r.write(i >> 8);
r.write(i);
super.write(i >> 8);
super.write(i);
}
void putInt24(int i) throws IOException {
checkOverflow(i, Record.OVERFLOW_OF_INT24);
if (r.availableDataBytes() < 3) {
flush();
}
r.write(i >> 16);
r.write(i >> 8);
r.write(i);
}
void putInt32(int i) throws IOException {
if (r.availableDataBytes() < 4) {
flush();
}
r.write(i >> 24);
r.write(i >> 16);
r.write(i >> 8);
r.write(i);
super.write(i >> 16);
super.write(i >> 8);
super.write(i);
}
/*
* Put byte arrays with length encoded as 8, 16, 24 bit
* integers in big-endian format.
*/
void putBytes8(byte b[]) throws IOException {
void putBytes8(byte[] b) throws IOException {
if (b == null) {
putInt8(0);
return;
} else {
checkOverflow(b.length, Record.OVERFLOW_OF_INT08);
putInt8(b.length);
super.write(b, 0, b.length);
}
putInt8(b.length);
write(b, 0, b.length);
}
public void putBytes16(byte b[]) throws IOException {
if (b == null) {
putInt16(0);
return;
} else {
checkOverflow(b.length, Record.OVERFLOW_OF_INT16);
putInt16(b.length);
super.write(b, 0, b.length);
}
putInt16(b.length);
write(b, 0, b.length);
}
void putBytes24(byte b[]) throws IOException {
if (b == null) {
putInt24(0);
return;
} else {
checkOverflow(b.length, Record.OVERFLOW_OF_INT24);
putInt24(b.length);
super.write(b, 0, b.length);
}
putInt24(b.length);
write(b, 0, b.length);
}
private void checkOverflow(int length, int overflow) {
if (length >= overflow) {
/*
* Does the specified length overflow the limitation?
*/
private static void checkOverflow(int length, int limit) {
if (length >= limit) {
// internal_error alert will be triggered
throw new RuntimeException(
"Field length overflow, the field length (" +
length + ") should be less than " + overflow);
length + ") should be less than " + limit);
}
}
}

View file

@ -0,0 +1,925 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ssl;
import java.util.LinkedList;
import java.util.HashMap;
import javax.net.ssl.SSLProtocolException;
import sun.security.ssl.HandshakeMessage.*;
import static sun.security.ssl.CipherSuite.KeyExchange;
import static sun.security.ssl.CipherSuite.KeyExchange.*;
import static sun.security.ssl.HandshakeStateManager.HandshakeState.*;
import static sun.security.ssl.HandshakeMessage.*;
/*
* Handshake state manager.
*
* Messages flow for a full handshake:
*
* - -
* | HelloRequest (No.0, RFC 5246) [*] |
* | <-------------------------------------------- |
* | |
* | ClientHello (No.1, RFC 5246) |
* | --------------------------------------------> |
* | |
* | - HelloVerifyRequest (No.3, RFC 6347) - |
* | D | <-------------------------------------------- | D |
* | T | | T |
* | L | ClientHello (No.1, RFC 5246) | L |
* | S | --------------------------------------------> | S |
* | - - |
* | |
* C | ServerHello (No.2, RFC 5246) | S
* L | SupplementalData (No.23, RFC4680) [*] | E
* I | Certificate (No.11, RFC 5246) [*] | R
* E | CertificateStatus (No.22, RFC 6066) [*] | V
* N | ServerKeyExchange (No.12, RFC 5246) [*] | E
* T | CertificateRequest (No.13, RFC 5246) [*] | R
* | ServerHelloDone (No.14, RFC 5246) |
* | <-------------------------------------------- |
* | |
* | SupplementalData (No.23, RFC4680) [*] |
* | Certificate (No.11, RFC 5246) [*] Or |
* | CertificateURL (No.21, RFC6066) [*] |
* | ClientKeyExchange (No.16, RFC 5246) |
* | CertificateVerify (No.15, RFC 5246) [*] |
* | [ChangeCipherSpec] (RFC 5246) |
* | Finished (No.20, RFC 5246) |
* | --------------------------------------------> |
* | |
* | NewSessionTicket (No.4, RFC4507) [*] |
* | [ChangeCipherSpec] (RFC 5246) |
* | Finished (No.20, RFC 5246) |
* | <-------------------------------------------- |
* - -
* [*] Indicates optional or situation-dependent messages that are not
* always sent.
*
* Message flow for an abbreviated handshake:
* - -
* | ClientHello (No.1, RFC 5246) |
* | --------------------------------------------> |
* | |
* C | ServerHello (No.2, RFC 5246) | S
* L | NewSessionTicket (No.4, RFC4507) [*] | E
* I | [ChangeCipherSpec] (RFC 5246) | R
* E | Finished (No.20, RFC 5246) | V
* N | <-------------------------------------------- | E
* T | | R
* | [ChangeCipherSpec] (RFC 5246) |
* | Finished (No.20, RFC 5246) |
* | --------------------------------------------> |
* - -
*
*
* State machine of handshake states:
*
* +--------------+
* START -----> | HelloRequest |
* | +--------------+
* | |
* v v
* +---------------------+ --> +---------------------+
* | ClientHello | | HelloVerifyRequest |
* +---------------------+ <-- +---------------------+
* |
* |
* =========================================================================
* |
* v
* +---------------------+
* | ServerHello | ----------------------------------+------+
* +---------------------+ --> +-------------------------+ | |
* | | Server SupplementalData | | |
* | +-------------------------+ | |
* | | | |
* v v | |
* +---------------------+ | |
* +---- | Server Certificate | | |
* | +---------------------+ | |
* | | | |
* | | +--------------------+ | |
* | +-> | CertificateStatus | | |
* | | +--------------------+ v |
* | | | | +--------------------+ |
* | v v +--> | ServerKeyExchange | |
* | +---------------------+ | +--------------------+ |
* | | CertificateRequest | | | |
* | +---------------------+ <-+---------+ |
* | | | | |
* v v | | |
* +---------------------+ <-------+ | |
* | ServerHelloDone | <-----------------+ |
* +---------------------+ |
* | | |
* | | |
* | | |
* =========================================================================
* | | |
* | v |
* | +-------------------------+ |
* | | Client SupplementalData | --------------+ |
* | +-------------------------+ | |
* | | | |
* | v | |
* | +--------------------+ | |
* +-> | Client Certificate | ALT. | |
* | +--------------------+----------------+ | |
* | | CertificateURL | | |
* | +----------------+ | |
* v | |
* +-------------------+ <------------------------+ |
* | ClientKeyExchange | |
* +-------------------+ |
* | | |
* | v |
* | +-------------------+ |
* | | CertificateVerify | |
* | +-------------------+ |
* | | |
* v v |
* +-------------------------+ |
* | Client ChangeCipherSpec | <---------------+ |
* +-------------------------+ | |
* | | |
* v | |
* +-----------------+ (abbreviated) | |
* | Client Finished | -------------> END | |
* +-----------------+ (Abbreviated handshake) | |
* | | |
* | (full) | |
* | | |
* ================================ | |
* | | |
* | ================================
* | | |
* v | |
* +------------------+ | (abbreviated) |
* | NewSessionTicket | <--------------------------------+
* +------------------+ | |
* | | |
* v | |
* +-------------------------+ | (abbreviated) |
* | Server ChangeCipherSpec | <-------------------------------------+
* +-------------------------+ |
* | |
* v |
* +-----------------+ (abbreviated) |
* | Server Finished | -------------------------+
* +-----------------+
* | (full)
* v
* END (Full handshake)
*
*
* The scenarios of the use of this class:
* 1. Create an instance of HandshakeStateManager during the initializtion
* handshake.
* 2. If receiving a handshake message, call HandshakeStateManager.check()
* to make sure that the message is of the expected handshake type. And
* then call HandshakeStateManager.update() in case handshake states may
* be impacted by this new incoming handshake message.
* 3. On delivering a handshake message, call HandshakeStateManager.update()
* in case handshake states may by thie new outgoing handshake message.
* 4. On receiving and delivering ChangeCipherSpec message, call
* HandshakeStateManager.changeCipherSpec() to check the present sequence
* of this message, and update the states if necessary.
*/
final class HandshakeStateManager {
// upcoming handshake states.
private LinkedList<HandshakeState> upcomingStates;
private LinkedList<HandshakeState> alternatives;
private boolean isDTLS;
private final static boolean debugIsOn;
private final static HashMap<Byte, String> handshakeTypes;
static {
debugIsOn = (Handshaker.debug != null) &&
Debug.isOn("handshake") && Debug.isOn("verbose");
handshakeTypes = new HashMap<>(15);
handshakeTypes.put(ht_hello_request, "hello_request");
handshakeTypes.put(ht_client_hello, "client_hello");
handshakeTypes.put(ht_server_hello, "server_hello");
handshakeTypes.put(ht_hello_verify_request, "hello_verify_request");
handshakeTypes.put(ht_new_session_ticket, "session_ticket");
handshakeTypes.put(ht_certificate, "certificate");
handshakeTypes.put(ht_server_key_exchange, "server_key_exchange");
handshakeTypes.put(ht_certificate_request, "certificate_request");
handshakeTypes.put(ht_server_hello_done, "server_hello_done");
handshakeTypes.put(ht_certificate_verify, "certificate_verify");
handshakeTypes.put(ht_client_key_exchange, "client_key_exchange");
handshakeTypes.put(ht_finished, "finished");
handshakeTypes.put(ht_certificate_url, "certificate_url");
handshakeTypes.put(ht_certificate_status, "certificate_status");
handshakeTypes.put(ht_supplemental_data, "supplemental_data");
}
HandshakeStateManager(boolean isDTLS) {
this.upcomingStates = new LinkedList<>();
this.alternatives = new LinkedList<>();
this.isDTLS = isDTLS;
}
//
// enumation of handshake type
//
static enum HandshakeState {
HS_HELLO_REQUEST(
"hello_request",
HandshakeMessage.ht_hello_request),
HS_CLIENT_HELLO(
"client_hello",
HandshakeMessage.ht_client_hello),
HS_HELLO_VERIFY_REQUEST(
"hello_verify_request",
HandshakeMessage.ht_hello_verify_request),
HS_SERVER_HELLO(
"server_hello",
HandshakeMessage.ht_server_hello),
HS_SERVER_SUPPLEMENTAL_DATA(
"server supplemental_data",
HandshakeMessage.ht_supplemental_data, true),
HS_SERVER_CERTIFICATE(
"server certificate",
HandshakeMessage.ht_certificate),
HS_CERTIFICATE_STATUS(
"certificate_status",
HandshakeMessage.ht_certificate_status, true),
HS_SERVER_KEY_EXCHANGE(
"server_key_exchange",
HandshakeMessage.ht_server_key_exchange, true),
HS_CERTIFICATE_REQUEST(
"certificate_request",
HandshakeMessage.ht_certificate_request, true),
HS_SERVER_HELLO_DONE(
"server_hello_done",
HandshakeMessage.ht_server_hello_done),
HS_CLIENT_SUPPLEMENTAL_DATA(
"client supplemental_data",
HandshakeMessage.ht_supplemental_data, true),
HS_CLIENT_CERTIFICATE(
"client certificate",
HandshakeMessage.ht_certificate, true),
HS_CERTIFICATE_URL(
"certificate_url",
HandshakeMessage.ht_certificate_url, true),
HS_CLIENT_KEY_EXCHANGE(
"client_key_exchange",
HandshakeMessage.ht_client_key_exchange),
HS_CERTIFICATE_VERIFY(
"certificate_verify",
HandshakeMessage.ht_certificate_verify, true),
HS_CLIENT_CHANGE_CIPHER_SPEC(
"client change_cipher_spec",
HandshakeMessage.ht_not_applicable),
HS_CLEINT_FINISHED(
"client finished",
HandshakeMessage.ht_finished),
HS_NEW_SESSION_TICKET(
"session_ticket",
HandshakeMessage.ht_new_session_ticket),
HS_SERVER_CHANGE_CIPHER_SPEC(
"server change_cipher_spec",
HandshakeMessage.ht_not_applicable),
HS_SERVER_FINISHDE(
"server finished",
HandshakeMessage.ht_finished);
final String description;
final byte handshakeType;
final boolean isOptional;
HandshakeState(String description, byte handshakeType) {
this.description = description;
this.handshakeType = handshakeType;
this.isOptional = false;
}
HandshakeState(String description,
byte handshakeType, boolean isOptional) {
this.description = description;
this.handshakeType = handshakeType;
this.isOptional = isOptional;
}
public String toString() {
return description + "[" + handshakeType + "]" +
(isOptional ? "(optional)" : "");
}
}
boolean isEmpty() {
return upcomingStates.isEmpty();
}
void check(byte handshakeType) throws SSLProtocolException {
String exceptionMsg =
"Handshake message sequence violation, " + handshakeType;
if (debugIsOn) {
System.out.println(
"check handshake state: " + toString(handshakeType));
}
if (upcomingStates.isEmpty()) {
// Is it a kickstart message?
if ((handshakeType != HandshakeMessage.ht_hello_request) &&
(handshakeType != HandshakeMessage.ht_client_hello)) {
throw new SSLProtocolException(
"Handshake message sequence violation, " + handshakeType);
}
// It is a kickstart message.
return;
}
// Ignore the checking for HelloRequest messages as they are
// may be sent by the server at any time.
if (handshakeType == HandshakeMessage.ht_hello_request) {
return;
}
for (HandshakeState handshakeState : upcomingStates) {
if (handshakeState.handshakeType == handshakeType) {
// It's the expected next handshake type.
return;
}
if (handshakeState.isOptional) {
continue;
} else {
for (HandshakeState alternative : alternatives) {
if (alternative.handshakeType == handshakeType) {
return;
}
if (alternative.isOptional) {
continue;
} else {
throw new SSLProtocolException(exceptionMsg);
}
}
}
throw new SSLProtocolException(exceptionMsg);
}
// Not an expected Handshake message.
throw new SSLProtocolException(
"Handshake message sequence violation, " + handshakeType);
}
void update(HandshakeMessage handshakeMessage,
boolean isAbbreviated) throws SSLProtocolException {
byte handshakeType = (byte)handshakeMessage.messageType();
String exceptionMsg =
"Handshake message sequence violation, " + handshakeType;
if (debugIsOn) {
System.out.println(
"update handshake state: " + toString(handshakeType));
}
boolean hasPresentState = false;
switch (handshakeType) {
case HandshakeMessage.ht_hello_request:
//
// State machine:
// PRESENT: START
// TO : ClientHello
//
// No old state to update.
// Add the upcoming states.
if (!upcomingStates.isEmpty()) {
// A ClientHello message should be followed.
upcomingStates.add(HS_CLIENT_HELLO);
} // Otherwise, ignore this HelloRequest message.
break;
case HandshakeMessage.ht_client_hello:
//
// State machine:
// PRESENT: START
// HS_CLIENT_HELLO
// TO : HS_HELLO_VERIFY_REQUEST (DTLS)
// HS_SERVER_HELLO
//
// Check and update the present state.
if (!upcomingStates.isEmpty()) {
// The current state should be HS_CLIENT_HELLO.
HandshakeState handshakeState = upcomingStates.pop();
if (handshakeState != HS_CLIENT_HELLO) {
throw new SSLProtocolException(exceptionMsg);
}
}
// Add the upcoming states.
ClientHello clientHello = (ClientHello)handshakeMessage;
if (isDTLS) {
// Is it an initial ClientHello message?
if (clientHello.cookie == null ||
clientHello.cookie.length == 0) {
// Is it an abbreviated handshake?
if (clientHello.sessionId.length() != 0) {
// A HelloVerifyRequest message or a ServerHello
// message may follow the abbreviated session
// resuming handshake request.
upcomingStates.add(HS_HELLO_VERIFY_REQUEST);
alternatives.add(HS_SERVER_HELLO);
} else {
// A HelloVerifyRequest message should follow
// the initial ClientHello message.
upcomingStates.add(HS_HELLO_VERIFY_REQUEST);
}
} else {
// A HelloVerifyRequest may be followed if the cookie
// cannot be verified.
upcomingStates.add(HS_SERVER_HELLO);
alternatives.add(HS_HELLO_VERIFY_REQUEST);
}
} else {
upcomingStates.add(HS_SERVER_HELLO);
}
break;
case HandshakeMessage.ht_hello_verify_request:
//
// State machine:
// PRESENT: HS_HELLO_VERIFY_REQUEST
// TO : HS_CLIENT_HELLO
//
// Note that this state may have an alternative option.
// Check and update the present state.
if (!upcomingStates.isEmpty()) {
// The current state should be HS_HELLO_VERIFY_REQUEST.
HandshakeState handshakeState = upcomingStates.pop();
HandshakeState alternative = null;
if (!alternatives.isEmpty()) {
alternative = alternatives.pop();
}
if ((handshakeState != HS_HELLO_VERIFY_REQUEST) &&
(alternative != HS_HELLO_VERIFY_REQUEST)) {
throw new SSLProtocolException(exceptionMsg);
}
} else {
// No present state.
throw new SSLProtocolException(exceptionMsg);
}
// Add the upcoming states.
upcomingStates.add(HS_CLIENT_HELLO);
break;
case HandshakeMessage.ht_server_hello:
//
// State machine:
// PRESENT: HS_SERVER_HELLO
// TO :
// Full handshake state stacks
// (ServerHello Flight)
// HS_SERVER_SUPPLEMENTAL_DATA [optional]
// --> HS_SERVER_CERTIFICATE [optional]
// --> HS_CERTIFICATE_STATUS [optional]
// --> HS_SERVER_KEY_EXCHANGE [optional]
// --> HS_CERTIFICATE_REQUEST [optional]
// --> HS_SERVER_HELLO_DONE
// (Client ClientKeyExchange Flight)
// --> HS_CLIENT_SUPPLEMENTAL_DATA [optional]
// --> HS_CLIENT_CERTIFICATE or
// HS_CERTIFICATE_URL
// --> HS_CLIENT_KEY_EXCHANGE
// --> HS_CERTIFICATE_VERIFY [optional]
// --> HS_CLIENT_CHANGE_CIPHER_SPEC
// --> HS_CLEINT_FINISHED
// (Server Finished Flight)
// --> HS_CLIENT_SUPPLEMENTAL_DATA [optional]
//
// Abbreviated handshake state stacks
// (Server Finished Flight)
// HS_NEW_SESSION_TICKET
// --> HS_SERVER_CHANGE_CIPHER_SPEC
// --> HS_SERVER_FINISHDE
// (Client Finished Flight)
// --> HS_CLIENT_CHANGE_CIPHER_SPEC
// --> HS_CLEINT_FINISHED
//
// Note that this state may have an alternative option.
// Check and update the present state.
if (!upcomingStates.isEmpty()) {
// The current state should be HS_SERVER_HELLO
HandshakeState handshakeState = upcomingStates.pop();
HandshakeState alternative = null;
if (!alternatives.isEmpty()) {
alternative = alternatives.pop();
}
if ((handshakeState != HS_SERVER_HELLO) &&
(alternative != HS_SERVER_HELLO)) {
throw new SSLProtocolException(exceptionMsg);
}
} else {
// No present state.
throw new SSLProtocolException(exceptionMsg);
}
// Add the upcoming states.
ServerHello serverHello = (ServerHello)handshakeMessage;
HelloExtensions hes = serverHello.extensions;
// Not support SessionTicket extension yet.
//
// boolean hasSessionTicketExt =
// (hes.get(HandshakeMessage.ht_new_session_ticket) != null);
if (isAbbreviated) {
// Not support SessionTicket extension yet.
//
// // Mandatory NewSessionTicket message
// if (hasSessionTicketExt) {
// upcomingStates.add(HS_NEW_SESSION_TICKET);
// }
// Mandatory server ChangeCipherSpec and Finished messages
upcomingStates.add(HS_SERVER_CHANGE_CIPHER_SPEC);
upcomingStates.add(HS_SERVER_FINISHDE);
// Mandatory client ChangeCipherSpec and Finished messages
upcomingStates.add(HS_CLIENT_CHANGE_CIPHER_SPEC);
upcomingStates.add(HS_CLEINT_FINISHED);
} else {
// Not support SupplementalData extension yet.
//
// boolean hasSupplementalDataExt =
// (hes.get(HandshakeMessage.ht_supplemental_data) != null);
// Not support CertificateStatus extension yet.
//
// boolean hasCertificateStatusExt =
// (hes.get(HandshakeMessage.ht_certificate_status) != null);
// Not support CertificateURL extension yet.
//
// boolean hasCertificateUrlExt =
// (hes.get(HandshakeMessage.ht_certificate_url) != null);
// Not support SupplementalData extension yet.
//
// // Optional SupplementalData message
// if (hasSupplementalDataExt) {
// upcomingStates.add(HS_SERVER_SUPPLEMENTAL_DATA);
// }
// Need server Certificate message or not?
KeyExchange keyExchange = serverHello.cipherSuite.keyExchange;
if ((keyExchange != K_KRB5) &&
(keyExchange != K_KRB5_EXPORT) &&
(keyExchange != K_DH_ANON) &&
(keyExchange != K_ECDH_ANON)) {
// Mandatory Certificate message
upcomingStates.add(HS_SERVER_CERTIFICATE);
}
// Not support CertificateStatus extension yet.
//
// // Optional CertificateStatus message
// if (hasCertificateStatusExt) {
// upcomingStates.add(HS_CERTIFICATE_STATUS);
// }
// Need ServerKeyExchange message or not?
if ((keyExchange == K_RSA_EXPORT) ||
(keyExchange == K_DHE_RSA) ||
(keyExchange == K_DHE_DSS) ||
(keyExchange == K_DH_ANON) ||
(keyExchange == K_ECDHE_RSA) ||
(keyExchange == K_ECDHE_ECDSA) ||
(keyExchange == K_ECDH_ANON)) {
// Optional ServerKeyExchange message
upcomingStates.add(HS_SERVER_KEY_EXCHANGE);
}
// Optional CertificateRequest message
upcomingStates.add(HS_CERTIFICATE_REQUEST);
// Mandatory ServerHelloDone message
upcomingStates.add(HS_SERVER_HELLO_DONE);
// Not support SupplementalData extension yet.
//
// // Optional SupplementalData message
// if (hasSupplementalDataExt) {
// upcomingStates.add(HS_CLIENT_SUPPLEMENTAL_DATA);
// }
// Optional client Certificate message
upcomingStates.add(HS_CLIENT_CERTIFICATE);
// Not support CertificateURL extension yet.
//
// // Alternative CertificateURL message, optional too.
// //
// // Please put CertificateURL rather than Certificate
// // message in the alternatives list. So that we can
// // simplify the process of this alternative pair later.
// if (hasCertificateUrlExt) {
// alternatives.add(HS_CERTIFICATE_URL);
// }
// Mandatory ClientKeyExchange message
upcomingStates.add(HS_CLIENT_KEY_EXCHANGE);
// Optional CertificateVerify message
upcomingStates.add(HS_CERTIFICATE_VERIFY);
// Mandatory client ChangeCipherSpec and Finished messages
upcomingStates.add(HS_CLIENT_CHANGE_CIPHER_SPEC);
upcomingStates.add(HS_CLEINT_FINISHED);
// Not support SessionTicket extension yet.
//
// // Mandatory NewSessionTicket message
// if (hasSessionTicketExt) {
// upcomingStates.add(HS_NEW_SESSION_TICKET);
// }
// Mandatory server ChangeCipherSpec and Finished messages
upcomingStates.add(HS_SERVER_CHANGE_CIPHER_SPEC);
upcomingStates.add(HS_SERVER_FINISHDE);
}
break;
case HandshakeMessage.ht_certificate:
//
// State machine:
// PRESENT: HS_CERTIFICATE_URL or
// HS_CLIENT_CERTIFICATE
// TO : HS_CLIENT_KEY_EXCHANGE
//
// Or
//
// PRESENT: HS_SERVER_CERTIFICATE
// TO : HS_CERTIFICATE_STATUS [optional]
// HS_SERVER_KEY_EXCHANGE [optional]
// HS_CERTIFICATE_REQUEST [optional]
// HS_SERVER_HELLO_DONE
//
// Note that this state may have an alternative option.
// Check and update the present state.
while (!upcomingStates.isEmpty()) {
HandshakeState handshakeState = upcomingStates.pop();
if (handshakeState.handshakeType == handshakeType) {
hasPresentState = true;
// The current state should be HS_CLIENT_CERTIFICATE or
// HS_SERVER_CERTIFICATE.
//
// Note that we won't put HS_CLIENT_CERTIFICATE into
// the alternative list.
if ((handshakeState != HS_CLIENT_CERTIFICATE) &&
(handshakeState != HS_SERVER_CERTIFICATE)) {
throw new SSLProtocolException(exceptionMsg);
}
// Is it an expected client Certificate message?
boolean isClientMessage = false;
if (!upcomingStates.isEmpty()) {
// If the next expected message is ClientKeyExchange,
// this one should be an expected client Certificate
// message.
HandshakeState nextState = upcomingStates.getFirst();
if (nextState == HS_CLIENT_KEY_EXCHANGE) {
isClientMessage = true;
}
}
if (isClientMessage) {
if (handshakeState != HS_CLIENT_CERTIFICATE) {
throw new SSLProtocolException(exceptionMsg);
}
// Not support CertificateURL extension yet.
/*******************************************
// clear up the alternatives list
if (!alternatives.isEmpty()) {
HandshakeState alternative = alternatives.pop();
if (alternative != HS_CERTIFICATE_URL) {
throw new SSLProtocolException(exceptionMsg);
}
}
********************************************/
} else {
if ((handshakeState != HS_SERVER_CERTIFICATE)) {
throw new SSLProtocolException(exceptionMsg);
}
}
break;
} else if (!handshakeState.isOptional) {
throw new SSLProtocolException(exceptionMsg);
} // Otherwise, looking for next state track.
}
// No present state.
if (!hasPresentState) {
throw new SSLProtocolException(exceptionMsg);
}
// no new upcoming states.
break;
// Not support CertificateURL extension yet.
/*************************************************/
case HandshakeMessage.ht_certificate_url:
//
// State machine:
// PRESENT: HS_CERTIFICATE_URL or
// HS_CLIENT_CERTIFICATE
// TO : HS_CLIENT_KEY_EXCHANGE
//
// Note that this state may have an alternative option.
// Check and update the present state.
while (!upcomingStates.isEmpty()) {
// The current state should be HS_CLIENT_CERTIFICATE.
//
// Note that we won't put HS_CLIENT_CERTIFICATE into
// the alternative list.
HandshakeState handshakeState = upcomingStates.pop();
if (handshakeState.handshakeType ==
HS_CLIENT_CERTIFICATE.handshakeType) {
hasPresentState = true;
// Look for HS_CERTIFICATE_URL state track.
if (!alternatives.isEmpty()) {
HandshakeState alternative = alternatives.pop();
if (alternative != HS_CERTIFICATE_URL) {
throw new SSLProtocolException(exceptionMsg);
}
} else {
// No alternative CertificateUR state track.
throw new SSLProtocolException(exceptionMsg);
}
if ((handshakeState != HS_CLIENT_CERTIFICATE)) {
throw new SSLProtocolException(exceptionMsg);
}
break;
} else if (!handshakeState.isOptional) {
throw new SSLProtocolException(exceptionMsg);
} // Otherwise, looking for next state track.
}
// No present state.
if (!hasPresentState) {
// No present state.
throw new SSLProtocolException(exceptionMsg);
}
// no new upcoming states.
break;
/*************************************************/
default:
// Check and update the present state.
while (!upcomingStates.isEmpty()) {
HandshakeState handshakeState = upcomingStates.pop();
if (handshakeState.handshakeType == handshakeType) {
hasPresentState = true;
break;
} else if (!handshakeState.isOptional) {
throw new SSLProtocolException(exceptionMsg);
} // Otherwise, looking for next state track.
}
// No present state.
if (!hasPresentState) {
throw new SSLProtocolException(exceptionMsg);
}
// no new upcoming states.
}
if (debugIsOn) {
for (HandshakeState handshakeState : upcomingStates) {
System.out.println(
"upcoming handshake states: " + handshakeState);
}
for (HandshakeState handshakeState : alternatives) {
System.out.println(
"upcoming handshake alternative state: " + handshakeState);
}
}
}
void changeCipherSpec(boolean isInput,
boolean isClient) throws SSLProtocolException {
if (debugIsOn) {
System.out.println(
"update handshake state: change_cipher_spec");
}
String exceptionMsg = "ChangeCipherSpec message sequence violation";
HandshakeState expectedState;
if ((isClient && isInput) || (!isClient && !isInput)) {
expectedState = HS_SERVER_CHANGE_CIPHER_SPEC;
} else {
expectedState = HS_CLIENT_CHANGE_CIPHER_SPEC;
}
boolean hasPresentState = false;
// Check and update the present state.
while (!upcomingStates.isEmpty()) {
HandshakeState handshakeState = upcomingStates.pop();
if (handshakeState == expectedState) {
hasPresentState = true;
break;
} else if (!handshakeState.isOptional) {
throw new SSLProtocolException(exceptionMsg);
} // Otherwise, looking for next state track.
}
// No present state.
if (!hasPresentState) {
throw new SSLProtocolException(exceptionMsg);
}
// no new upcoming states.
if (debugIsOn) {
for (HandshakeState handshakeState : upcomingStates) {
System.out.println(
"upcoming handshake states: " + handshakeState);
}
for (HandshakeState handshakeState : alternatives) {
System.out.println(
"upcoming handshake alternative state: " + handshakeState);
}
}
}
private static String toString(byte handshakeType) {
String s = handshakeTypes.get(handshakeType);
if (s == null) {
s = "unknown";
}
return (s + "[" + handshakeType + "]");
}
}

View file

@ -29,6 +29,7 @@ package sun.security.ssl;
import java.io.*;
import java.util.*;
import java.security.*;
import java.nio.ByteBuffer;
import java.security.NoSuchAlgorithmException;
import java.security.AccessController;
import java.security.AlgorithmConstraints;
@ -83,7 +84,7 @@ abstract class Handshaker {
private CipherSuiteList enabledCipherSuites;
// The endpoint identification protocol
String identificationProtocol;
String identificationProtocol;
// The cryptographic algorithm constraints
private AlgorithmConstraints algorithmConstraints = null;
@ -109,12 +110,15 @@ abstract class Handshaker {
* Active cipher suites is a subset of enabled cipher suites, and will
* contain only those cipher suites available for the active protocols.
*/
private CipherSuiteList activeCipherSuites;
private CipherSuiteList activeCipherSuites;
// The server name indication and matchers
List<SNIServerName> serverNames = Collections.<SNIServerName>emptyList();
Collection<SNIMatcher> sniMatchers = Collections.<SNIMatcher>emptyList();
// The maximum expected network packet size for SSL/TLS/DTLS records.
int maximumPacketSize = 0;
private boolean isClient;
private boolean needCertVerify;
@ -124,11 +128,16 @@ abstract class Handshaker {
HandshakeHash handshakeHash;
HandshakeInStream input;
HandshakeOutStream output;
int state;
SSLContextImpl sslContext;
RandomCookie clnt_random, svr_random;
SSLSessionImpl session;
HandshakeStateManager handshakeState;
boolean clientHelloDelivered;
boolean serverHelloRequested;
boolean handshakeActivated;
boolean handshakeFinished;
// current CipherSuite. Never null, initially SSL_NULL_WITH_NULL_NULL
CipherSuite cipherSuite;
@ -141,10 +150,6 @@ abstract class Handshaker {
// True if it's OK to start a new SSL session
boolean enableNewSession;
// True if session keys have been calculated and the caller may receive
// and process a ChangeCipherSpec message
private boolean sessKeysCalculated;
// Whether local cipher suites preference should be honored during
// handshaking?
//
@ -207,12 +212,18 @@ abstract class Handshaker {
// need to dispose the object when it is invalidated
boolean invalidated;
/*
* Is this an instance for Datagram Transport Layer Security (DTLS)?
*/
final boolean isDTLS;
Handshaker(SSLSocketImpl c, SSLContextImpl context,
ProtocolList enabledProtocols, boolean needCertVerify,
boolean isClient, ProtocolVersion activeProtocolVersion,
boolean isInitialHandshake, boolean secureRenegotiation,
byte[] clientVerifyData, byte[] serverVerifyData) {
this.conn = c;
this.isDTLS = false;
init(context, enabledProtocols, needCertVerify, isClient,
activeProtocolVersion, isInitialHandshake, secureRenegotiation,
clientVerifyData, serverVerifyData);
@ -222,8 +233,10 @@ abstract class Handshaker {
ProtocolList enabledProtocols, boolean needCertVerify,
boolean isClient, ProtocolVersion activeProtocolVersion,
boolean isInitialHandshake, boolean secureRenegotiation,
byte[] clientVerifyData, byte[] serverVerifyData) {
byte[] clientVerifyData, byte[] serverVerifyData,
boolean isDTLS) {
this.engine = engine;
this.isDTLS = isDTLS;
init(context, enabledProtocols, needCertVerify, isClient,
activeProtocolVersion, isInitialHandshake, secureRenegotiation,
clientVerifyData, serverVerifyData);
@ -251,9 +264,13 @@ abstract class Handshaker {
this.secureRenegotiation = secureRenegotiation;
this.clientVerifyData = clientVerifyData;
this.serverVerifyData = serverVerifyData;
enableNewSession = true;
invalidated = false;
sessKeysCalculated = false;
this.enableNewSession = true;
this.invalidated = false;
this.handshakeState = new HandshakeStateManager(isDTLS);
this.clientHelloDelivered = false;
this.serverHelloRequested = false;
this.handshakeActivated = false;
this.handshakeFinished = false;
setCipherSuite(CipherSuite.C_NULL);
setEnabledProtocols(enabledProtocols);
@ -263,22 +280,6 @@ abstract class Handshaker {
} else { // engine != null
algorithmConstraints = new SSLAlgorithmConstraints(engine, true);
}
//
// In addition to the connection state machine, controlling
// how the connection deals with the different sorts of records
// that get sent (notably handshake transitions!), there's
// also a handshaking state machine that controls message
// sequencing.
//
// It's a convenient artifact of the protocol that this can,
// with only a couple of minor exceptions, be driven by the
// type constant for the last message seen: except for the
// client's cert verify, those constants are in a convenient
// order to drastically simplify state machine checking.
//
state = -2; // initialized but not activated
}
/*
@ -360,14 +361,6 @@ abstract class Handshaker {
}
}
final boolean receivedChangeCipherSpec() {
if (conn != null) {
return conn.receivedChangeCipherSpec();
} else {
return engine.receivedChangeCipherSpec();
}
}
String getEndpointIdentificationAlgorithmSE() {
SSLParameters paras;
if (conn != null) {
@ -395,8 +388,6 @@ abstract class Handshaker {
void setVersion(ProtocolVersion protocolVersion) {
this.protocolVersion = protocolVersion;
setVersionSE(protocolVersion);
output.r.setVersion(protocolVersion);
}
/**
@ -482,6 +473,13 @@ abstract class Handshaker {
this.sniMatchers = sniMatchers;
}
/**
* Sets the maximum packet size of the handshaking.
*/
void setMaximumPacketSize(int maximumPacketSize) {
this.maximumPacketSize = maximumPacketSize;
}
/**
* Sets the cipher suites preference.
*/
@ -532,23 +530,29 @@ abstract class Handshaker {
handshakeHash = new HandshakeHash(needCertVerify);
// Generate handshake input/output stream.
input = new HandshakeInStream(handshakeHash);
if (conn != null) {
output = new HandshakeOutStream(protocolVersion, helloVersion,
handshakeHash, conn);
conn.getAppInputStream().r.setHandshakeHash(handshakeHash);
conn.getAppInputStream().r.setHelloVersion(helloVersion);
conn.getAppOutputStream().r.setHelloVersion(helloVersion);
} else {
output = new HandshakeOutStream(protocolVersion, helloVersion,
handshakeHash, engine);
input = new HandshakeInStream();
output = new HandshakeOutStream(conn.outputRecord);
conn.inputRecord.setHandshakeHash(handshakeHash);
conn.inputRecord.setHelloVersion(helloVersion);
conn.outputRecord.setHandshakeHash(handshakeHash);
conn.outputRecord.setHelloVersion(helloVersion);
conn.outputRecord.setVersion(protocolVersion);
} else if (engine != null) {
input = new HandshakeInStream();
output = new HandshakeOutStream(engine.outputRecord);
engine.inputRecord.setHandshakeHash(handshakeHash);
engine.inputRecord.setHelloVersion(helloVersion);
engine.outputRecord.setHandshakeHash(handshakeHash);
engine.outputRecord.setHelloVersion(helloVersion);
engine.outputRecord.setVersion(protocolVersion);
}
// move state to activated
state = -1;
handshakeActivated = true;
}
/**
@ -637,15 +641,15 @@ abstract class Handshaker {
if (!(activeProtocols.collection().isEmpty()) &&
activeProtocols.min.v != ProtocolVersion.NONE.v) {
for (CipherSuite suite : enabledCipherSuites.collection()) {
if (suite.obsoleted > activeProtocols.min.v &&
suite.supported <= activeProtocols.max.v) {
if (!activeProtocols.min.obsoletes(suite) &&
activeProtocols.max.supports(suite)) {
if (algorithmConstraints.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
suite.name, null)) {
suites.add(suite);
}
} else if (debug != null && Debug.isOn("verbose")) {
if (suite.obsoleted <= activeProtocols.min.v) {
if (activeProtocols.min.obsoletes(suite)) {
System.out.println(
"Ignoring obsoleted cipher suite: " + suite);
} else {
@ -700,8 +704,8 @@ abstract class Handshaker {
boolean found = false;
for (CipherSuite suite : enabledCipherSuites.collection()) {
if (suite.isAvailable() && suite.obsoleted > protocol.v &&
suite.supported <= protocol.v) {
if (suite.isAvailable() && (!protocol.obsoletes(suite)) &&
protocol.supports(suite)) {
if (algorithmConstraints.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
suite.name, null)) {
@ -837,7 +841,7 @@ abstract class Handshaker {
* this freshly created session can become the current one.
*/
boolean isDone() {
return state == HandshakeMessage.ht_finished;
return started() && handshakeState.isEmpty() && handshakeFinished;
}
@ -861,6 +865,14 @@ abstract class Handshaker {
}
}
void expectingFinishFlightSE() {
if (conn != null) {
conn.expectingFinishFlight();
} else {
engine.expectingFinishFlight();
}
}
/*
* Returns true if renegotiation is in use for this connection.
*/
@ -886,8 +898,8 @@ abstract class Handshaker {
* This routine is fed SSL handshake records when they become available,
* and processes messages found therein.
*/
void process_record(InputRecord r, boolean expectingFinished)
throws IOException {
void processRecord(ByteBuffer record,
boolean expectingFinished) throws IOException {
checkThrown();
@ -895,7 +907,7 @@ abstract class Handshaker {
* Store the incoming handshake data, then see if we can
* now process any completed handshake messages
*/
input.incomingRecord(r);
input.incomingRecord(record);
/*
* We don't need to create a separate delegatable task
@ -946,6 +958,13 @@ abstract class Handshaker {
return;
}
// Set the flags in the message receiving side.
if (messageType == HandshakeMessage.ht_client_hello) {
clientHelloDelivered = true;
} else if (messageType == HandshakeMessage.ht_hello_request) {
serverHelloRequested = true;
}
/*
* Process the message. We require
* that processMessage() consumes the entire message. In
@ -961,14 +980,16 @@ abstract class Handshaker {
* Also, note that hello request messages are never hashed;
* that includes the hello request header, too.
*/
if (messageType == HandshakeMessage.ht_hello_request) {
input.reset();
processMessage(messageType, messageLen);
input.ignore(4 + messageLen);
} else {
input.mark(messageLen);
processMessage(messageType, messageLen);
input.digestNow();
processMessage(messageType, messageLen);
// Reload if this message has been reserved.
//
// Note: in the implementation, only certificate_verify and
// finished messages are reserved.
if ((messageType == HandshakeMessage.ht_finished) ||
(messageType == HandshakeMessage.ht_certificate_verify)) {
handshakeHash.reload();
}
}
}
@ -980,29 +1001,29 @@ abstract class Handshaker {
* In activated state, the handshaker may not send any messages out.
*/
boolean activated() {
return state >= -1;
return handshakeActivated;
}
/**
* Returns true iff the handshaker has sent any messages.
*/
boolean started() {
return state >= 0; // 0: HandshakeMessage.ht_hello_request
// 1: HandshakeMessage.ht_client_hello
return (serverHelloRequested || clientHelloDelivered);
}
/*
* Used to kickstart the negotiation ... either writing a
* ClientHello or a HelloRequest as appropriate, whichever
* the subclass returns. NOP if handshaking's already started.
*/
void kickstart() throws IOException {
if (state >= 0) {
if ((isClient && clientHelloDelivered) ||
(!isClient && serverHelloRequested)) {
return;
}
HandshakeMessage m = getKickstartMessage();
handshakeState.update(m, resumingSession);
if (debug != null && Debug.isOn("handshake")) {
m.print(System.out);
@ -1010,7 +1031,13 @@ abstract class Handshaker {
m.write(output);
output.flush();
state = m.messageType();
// Set the flags in the message delivering side.
int handshakeType = m.messageType();
if (handshakeType == HandshakeMessage.ht_hello_request) {
serverHelloRequested = true;
} else { // HandshakeMessage.ht_client_hello
clientHelloDelivered = true;
}
}
/**
@ -1052,24 +1079,16 @@ abstract class Handshaker {
* We already hold SSLEngine/SSLSocket "this" by virtue
* of this being called from the readRecord code.
*/
OutputRecord r;
if (conn != null) {
r = new OutputRecord(Record.ct_change_cipher_spec);
} else {
r = new EngineOutputRecord(Record.ct_change_cipher_spec, engine);
}
r.setVersion(protocolVersion);
r.write(1); // single byte of data
if (conn != null) {
conn.writeLock.lock();
try {
conn.writeRecord(r);
handshakeState.changeCipherSpec(false, isClient);
conn.changeWriteCiphers();
if (debug != null && Debug.isOn("handshake")) {
mesg.print(System.out);
}
handshakeState.update(mesg, resumingSession);
mesg.write(output);
output.flush();
} finally {
@ -1077,19 +1096,25 @@ abstract class Handshaker {
}
} else {
synchronized (engine.writeLock) {
engine.writeRecord((EngineOutputRecord)r);
handshakeState.changeCipherSpec(false, isClient);
engine.changeWriteCiphers();
if (debug != null && Debug.isOn("handshake")) {
mesg.print(System.out);
}
mesg.write(output);
if (lastMessage) {
output.setFinishedMsg();
}
handshakeState.update(mesg, resumingSession);
mesg.write(output);
output.flush();
}
}
if (lastMessage) {
handshakeFinished = true;
}
}
void receiveChangeCipherSpec() throws IOException {
handshakeState.changeCipherSpec(true, isClient);
}
/*
@ -1131,12 +1156,31 @@ abstract class Handshaker {
String masterAlg;
PRF prf;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
masterAlg = "SunTls12MasterSecret";
prf = cipherSuite.prfAlg;
byte majorVersion = protocolVersion.major;
byte minorVersion = protocolVersion.minor;
if (protocolVersion.isDTLSProtocol()) {
// Use TLS version number for DTLS key calculation
if (protocolVersion.v == ProtocolVersion.DTLS10.v) {
majorVersion = ProtocolVersion.TLS11.major;
minorVersion = ProtocolVersion.TLS11.minor;
masterAlg = "SunTlsMasterSecret";
prf = P_NONE;
} else { // DTLS 1.2
majorVersion = ProtocolVersion.TLS12.major;
minorVersion = ProtocolVersion.TLS12.minor;
masterAlg = "SunTls12MasterSecret";
prf = cipherSuite.prfAlg;
}
} else {
masterAlg = "SunTlsMasterSecret";
prf = P_NONE;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
masterAlg = "SunTls12MasterSecret";
prf = cipherSuite.prfAlg;
} else {
masterAlg = "SunTlsMasterSecret";
prf = P_NONE;
}
}
String prfHashAlg = prf.getPRFHashAlg();
@ -1145,7 +1189,7 @@ abstract class Handshaker {
@SuppressWarnings("deprecation")
TlsMasterSecretParameterSpec spec = new TlsMasterSecretParameterSpec(
preMasterSecret, protocolVersion.major, protocolVersion.minor,
preMasterSecret, (majorVersion & 0xFF), (minorVersion & 0xFF),
clnt_random.random_bytes, svr_random.random_bytes,
prfHashAlg, prfHashLength, prfBlockSize);
@ -1196,36 +1240,55 @@ abstract class Handshaker {
String keyMaterialAlg;
PRF prf;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
keyMaterialAlg = "SunTls12KeyMaterial";
prf = cipherSuite.prfAlg;
byte majorVersion = protocolVersion.major;
byte minorVersion = protocolVersion.minor;
if (protocolVersion.isDTLSProtocol()) {
// Use TLS version number for DTLS key calculation
if (protocolVersion.v == ProtocolVersion.DTLS10.v) {
majorVersion = ProtocolVersion.TLS11.major;
minorVersion = ProtocolVersion.TLS11.minor;
keyMaterialAlg = "SunTlsKeyMaterial";
prf = P_NONE;
} else { // DTLS 1.2+
majorVersion = ProtocolVersion.TLS12.major;
minorVersion = ProtocolVersion.TLS12.minor;
keyMaterialAlg = "SunTls12KeyMaterial";
prf = cipherSuite.prfAlg;
}
} else {
keyMaterialAlg = "SunTlsKeyMaterial";
prf = P_NONE;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
keyMaterialAlg = "SunTls12KeyMaterial";
prf = cipherSuite.prfAlg;
} else {
keyMaterialAlg = "SunTlsKeyMaterial";
prf = P_NONE;
}
}
String prfHashAlg = prf.getPRFHashAlg();
int prfHashLength = prf.getPRFHashLength();
int prfBlockSize = prf.getPRFBlockSize();
// TLS v1.1 or later uses an explicit IV in CBC cipher suites to
// TLS v1.1+ and DTLS use an explicit IV in CBC cipher suites to
// protect against the CBC attacks. AEAD/GCM cipher suites in TLS
// v1.2 or later use a fixed IV as the implicit part of the partially
// implicit nonce technique described in RFC 5116.
int ivSize = cipher.ivSize;
if (cipher.cipherType == AEAD_CIPHER) {
ivSize = cipher.fixedIvSize;
} else if (protocolVersion.v >= ProtocolVersion.TLS11.v &&
cipher.cipherType == BLOCK_CIPHER) {
} else if ((cipher.cipherType == BLOCK_CIPHER) &&
protocolVersion.useTLS11PlusSpec()) {
ivSize = 0;
}
TlsKeyMaterialParameterSpec spec = new TlsKeyMaterialParameterSpec(
masterKey, protocolVersion.major, protocolVersion.minor,
clnt_random.random_bytes, svr_random.random_bytes,
cipher.algorithm, cipher.keySize, expandedKeySize,
ivSize, hashSize,
prfHashAlg, prfHashLength, prfBlockSize);
masterKey, (majorVersion & 0xFF), (minorVersion & 0xFF),
clnt_random.random_bytes, svr_random.random_bytes,
cipher.algorithm, cipher.keySize, expandedKeySize,
ivSize, hashSize,
prfHashAlg, prfHashLength, prfBlockSize);
try {
KeyGenerator kg = JsseJce.getKeyGenerator(keyMaterialAlg);
@ -1247,10 +1310,6 @@ abstract class Handshaker {
throw new ProviderException(e);
}
// Mark a flag that allows outside entities (like SSLSocket/SSLEngine)
// determine if a ChangeCipherSpec message could be processed.
sessKeysCalculated = true;
//
// Dump the connection keys as they're generated.
//
@ -1293,7 +1352,7 @@ abstract class Handshaker {
System.out.println("Server write IV:");
printHex(dump, svrWriteIV.getIV());
} else {
if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
if (protocolVersion.useTLS11PlusSpec()) {
System.out.println(
"... no IV derived for this protocol");
} else {
@ -1305,15 +1364,6 @@ abstract class Handshaker {
}
}
/**
* Return whether or not the Handshaker has derived session keys for
* this handshake. This is used for determining readiness to process
* an incoming ChangeCipherSpec message.
*/
boolean sessionKeysCalculated() {
return sessKeysCalculated;
}
private static void printHex(HexDumpEncoder dump, byte[] bytes) {
if (bytes == null) {
System.out.println("(key bytes not available)");
@ -1326,19 +1376,6 @@ abstract class Handshaker {
}
}
/**
* Throw an SSLException with the specified message and cause.
* Shorthand until a new SSLException constructor is added.
* This method never returns.
*/
static void throwSSLException(String msg, Throwable cause)
throws SSLException {
SSLException e = new SSLException(msg);
e.initCause(cause);
throw e;
}
/*
* Implement a simple task delegator.
*

View file

@ -0,0 +1,144 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ssl;
import java.io.IOException;
import javax.net.ssl.SSLProtocolException;
import java.security.MessageDigest;
import java.security.SecureRandom;
import sun.security.ssl.HandshakeMessage.ClientHello;
/*
* HelloVerifyRequest cookie manager
*/
final class HelloCookieManager {
// the cookie secret life time
private static long COOKIE_TIMING_WINDOW = 3600000; // in milliseconds
private static int COOKIE_MAX_LENGTH_DTLS10 = 32; // 32 bytes
private static int COOKIE_MAX_LENGTH_DTLS12 = 0xFF; // 2^8 -1 bytes
private final SecureRandom secureRandom;
private final MessageDigest cookieDigest;
private int cookieVersion; // allow to wrap
private long secretLifetime;
private byte[] cookieSecret;
private int prevCookieVersion;
private byte[] prevCookieSecret;
HelloCookieManager(SecureRandom secureRandom) {
this.secureRandom = secureRandom;
this.cookieDigest = JsseJce.getMessageDigest("SHA-256");
this.cookieVersion = secureRandom.nextInt();
this.secretLifetime = 0;
this.cookieSecret = null;
this.prevCookieVersion = 0;
this.prevCookieSecret = null;
}
// Used by server side to generate cookies in HelloVerifyRequest message.
synchronized byte[] getCookie(ClientHello clientHelloMsg) {
if (secretLifetime < System.currentTimeMillis()) {
if (cookieSecret != null) {
prevCookieVersion = cookieVersion;
prevCookieSecret = cookieSecret.clone();
} else {
cookieSecret = new byte[32];
}
cookieVersion++;
secureRandom.nextBytes(cookieSecret);
secretLifetime = System.currentTimeMillis() + COOKIE_TIMING_WINDOW;
}
clientHelloMsg.updateHelloCookie(cookieDigest);
byte[] cookie = cookieDigest.digest(cookieSecret); // 32 bytes
cookie[0] = (byte)((cookieVersion >> 24) & 0xFF);
cookie[1] = (byte)((cookieVersion >> 16) & 0xFF);
cookie[2] = (byte)((cookieVersion >> 8) & 0xFF);
cookie[3] = (byte)(cookieVersion & 0xFF);
return cookie;
}
// Used by server side to check the cookie in ClientHello message.
synchronized boolean isValid(ClientHello clientHelloMsg) {
byte[] cookie = clientHelloMsg.cookie;
// no cookie exchange or not a valid cookie length
if ((cookie == null) || (cookie.length != 32)) {
return false;
}
int version = ((cookie[0] & 0xFF) << 24) |
((cookie[1] & 0xFF) << 16) |
((cookie[2] & 0xFF) << 8) |
(cookie[3] & 0xFF);
byte[] secret;
if (version == cookieVersion) {
secret = cookieSecret;
} else if (version == prevCookieVersion) {
secret = prevCookieSecret;
} else {
return false; // may be out of the timing window
}
clientHelloMsg.updateHelloCookie(cookieDigest);
byte[] target = cookieDigest.digest(secret); // 32 bytes
for (int i = 4; i < 32; i++) {
if (cookie[i] != target[i]) {
return false;
}
}
return true;
}
// Used by client side to check the cookie in HelloVerifyRequest message.
static void checkCookie(ProtocolVersion protocolVersion,
byte[] cookie) throws IOException {
if (cookie != null && cookie.length != 0) {
int limit = COOKIE_MAX_LENGTH_DTLS12;
if (protocolVersion.v == ProtocolVersion.DTLS10.v) {
limit = COOKIE_MAX_LENGTH_DTLS10;
}
if (cookie.length > COOKIE_MAX_LENGTH_DTLS10) {
throw new SSLProtocolException(
"Invalid HelloVerifyRequest.cookie (length = " +
cookie.length + " bytes)");
}
}
// Otherwise, no cookie exchange.
}
}

View file

@ -85,6 +85,8 @@ final class HelloExtensions {
new SupportedEllipticPointFormatsExtension(s, extlen);
} else if (extType == ExtensionType.EXT_RENEGOTIATION_INFO) {
extension = new RenegotiationInfoExtension(s, extlen);
} else if (extType == ExtensionType.EXT_MAX_FRAGMENT_LENGTH) {
extension = new MaxFragmentLengthExtension(s, extlen);
} else {
extension = new UnknownExtension(s, extlen, extType);
}

View file

@ -23,7 +23,6 @@
* questions.
*/
package sun.security.ssl;
import java.security.InvalidKeyException;
@ -50,24 +49,26 @@ import static sun.security.ssl.CipherSuite.MacAlg.*;
*/
final class MAC extends Authenticator {
final static MAC NULL = new MAC();
final static MAC TLS_NULL = new MAC(false);
// Value of the null MAC is fixed
private static final byte nullMAC[] = new byte[0];
// internal identifier for the MAC algorithm
private final MacAlg macAlg;
private final MacAlg macAlg;
// JCE Mac object
private final Mac mac;
private MAC() {
MAC(boolean isDTLS) {
super(isDTLS);
macAlg = M_NULL;
mac = null;
}
/**
* Set up, configured for the given SSL/TLS MAC type and version.
* Set up, configured for the given MAC type and version.
*/
MAC(MacAlg macAlg, ProtocolVersion protocolVersion, SecretKey key)
throws NoSuchAlgorithmException, InvalidKeyException {
@ -75,12 +76,14 @@ final class MAC extends Authenticator {
this.macAlg = macAlg;
String algorithm;
boolean tls = (protocolVersion.v >= ProtocolVersion.TLS10.v);
// using SSL MAC computation?
boolean useSSLMac = (protocolVersion.v < ProtocolVersion.TLS10.v);
if (macAlg == M_MD5) {
algorithm = tls ? "HmacMD5" : "SslMacMD5";
algorithm = useSSLMac ? "SslMacMD5" : "HmacMD5";
} else if (macAlg == M_SHA) {
algorithm = tls ? "HmacSHA1" : "SslMacSHA1";
algorithm = useSSLMac ? "SslMacSHA1" : "HmacSHA1";
} else if (macAlg == M_SHA256) {
algorithm = "HmacSHA256"; // TLS 1.2+
} else if (macAlg == M_SHA384) {
@ -122,6 +125,8 @@ final class MAC extends Authenticator {
* @param offset start of compressed record data
* @param len the size of the compressed record
* @param isSimulated if true, simulate the MAC computation
*
* @return the MAC result
*/
final byte[] compute(byte type, byte buf[],
int offset, int len, boolean isSimulated) {
@ -130,7 +135,8 @@ final class MAC extends Authenticator {
}
if (!isSimulated) {
byte[] additional = acquireAuthenticationBytes(type, len);
// Uses the implicit sequence number for the computation.
byte[] additional = acquireAuthenticationBytes(type, len, null);
mac.update(additional);
}
mac.update(buf, offset, len);
@ -149,15 +155,22 @@ final class MAC extends Authenticator {
* @param bb a ByteBuffer in which the position and limit
* demarcate the data to be MAC'd.
* @param isSimulated if true, simulate the MAC computation
* @param sequence the explicit sequence number, or null if using
* the implicit sequence number for the computation
*
* @return the MAC result
*/
final byte[] compute(byte type, ByteBuffer bb, boolean isSimulated) {
final byte[] compute(byte type, ByteBuffer bb,
byte[] sequence, boolean isSimulated) {
if (macAlg.size == 0) {
return nullMAC;
}
if (!isSimulated) {
// Uses the explicit sequence number for the computation.
byte[] additional =
acquireAuthenticationBytes(type, bb.remaining());
acquireAuthenticationBytes(type, bb.remaining(), sequence);
mac.update(additional);
}
mac.update(bb);
@ -165,5 +178,22 @@ final class MAC extends Authenticator {
return mac.doFinal();
}
/**
* Compute and returns the MAC for the remaining data
* in this ByteBuffer.
*
* On return, the bb position == limit, and limit will
* have not changed.
*
* @param type record type
* @param bb a ByteBuffer in which the position and limit
* demarcate the data to be MAC'd.
* @param isSimulated if true, simulate the the MAC computation
*
* @return the MAC result
*/
final byte[] compute(byte type, ByteBuffer bb, boolean isSimulated) {
// Uses the implicit sequence number for the computation.
return compute(type, bb, null, isSimulated);
}
}

View file

@ -0,0 +1,139 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ssl;
import java.io.IOException;
import javax.net.ssl.SSLProtocolException;
/*
* [RFC6066] TLS specifies a fixed maximum plaintext fragment length of
* 2^14 bytes. It may be desirable for constrained clients to negotiate
* a smaller maximum fragment length due to memory limitations or bandwidth
* limitations.
*
* In order to negotiate smaller maximum fragment lengths, clients MAY
* include an extension of type "max_fragment_length" in the (extended)
* client hello. The "extension_data" field of this extension SHALL
* contain:
*
* enum{
* 2^9(1), 2^10(2), 2^11(3), 2^12(4), (255)
* } MaxFragmentLength;
*
* whose value is the desired maximum fragment length.
*/
final class MaxFragmentLengthExtension extends HelloExtension {
private static final int MAX_FRAGMENT_LENGTH_512 = 1; // 2^9
private static final int MAX_FRAGMENT_LENGTH_1024 = 2; // 2^10
private static final int MAX_FRAGMENT_LENGTH_2048 = 3; // 2^11
private static final int MAX_FRAGMENT_LENGTH_4096 = 4; // 2^12
final int maxFragmentLength;
MaxFragmentLengthExtension(int fragmentSize) {
super(ExtensionType.EXT_MAX_FRAGMENT_LENGTH);
if (fragmentSize < 1024) {
maxFragmentLength = MAX_FRAGMENT_LENGTH_512;
} else if (fragmentSize < 2048) {
maxFragmentLength = MAX_FRAGMENT_LENGTH_1024;
} else if (fragmentSize < 4096) {
maxFragmentLength = MAX_FRAGMENT_LENGTH_2048;
} else {
maxFragmentLength = MAX_FRAGMENT_LENGTH_4096;
}
}
MaxFragmentLengthExtension(HandshakeInStream s, int len)
throws IOException {
super(ExtensionType.EXT_MAX_FRAGMENT_LENGTH);
// check the extension length
if (len != 1) {
throw new SSLProtocolException("Invalid " + type + " extension");
}
maxFragmentLength = s.getInt8();
if ((maxFragmentLength > 4) || (maxFragmentLength < 1)) {
throw new SSLProtocolException("Invalid " + type + " extension");
}
}
// Length of the encoded extension, including the type and length fields
@Override
int length() {
return 5; // 4: extension type and length fields
// 1: MaxFragmentLength field
}
@Override
void send(HandshakeOutStream s) throws IOException {
s.putInt16(type.id);
s.putInt16(1);
s.putInt8(maxFragmentLength);
}
int getMaxFragLen() {
switch (maxFragmentLength) {
case MAX_FRAGMENT_LENGTH_512:
return 512;
case MAX_FRAGMENT_LENGTH_1024:
return 1024;
case MAX_FRAGMENT_LENGTH_2048:
return 2048;
case MAX_FRAGMENT_LENGTH_4096:
return 4096;
}
// unlikely to happen
return -1;
}
static boolean needFragLenNego(int fragmentSize) {
return (fragmentSize > 0) && (fragmentSize <= 4096);
}
static int getValidMaxFragLen(int fragmentSize) {
if (fragmentSize < 1024) {
return 512;
} else if (fragmentSize < 2048) {
return 1024;
} else if (fragmentSize < 4096) {
return 2048;
} else if (fragmentSize == 4096) {
return 4096;
} else {
return 16384;
}
}
@Override
public String toString() {
return "Extension " + type + ", max_fragment_length: " +
"(2^" + (maxFragmentLength + 8) + ")";
}
}

View file

@ -23,7 +23,6 @@
* questions.
*/
package sun.security.ssl;
import java.io.*;
@ -35,92 +34,61 @@ import sun.misc.HexDumpEncoder;
/**
* SSL 3.0 records, as written to a TCP stream.
*
* Each record has a message area that starts out with data supplied by the
* application. It may grow/shrink due to compression and will be modified
* in place for mac-ing and encryption.
*
* Handshake records have additional needs, notably accumulation of a set
* of hashes which are used to establish that handshaking was done right.
* Handshake records usually have several handshake messages each, and we
* need message-level control over what's hashed.
* {@code OutputRecord} takes care of the management of SSL/TLS/DTLS output
* records, including buffering, encryption, handshake messages marshal, etc.
*
* @author David Brownell
*/
class OutputRecord extends ByteArrayOutputStream implements Record {
abstract class OutputRecord extends ByteArrayOutputStream
implements Record, Closeable {
private HandshakeHash handshakeHash;
private int lastHashed;
private boolean firstMessage;
final private byte contentType;
private int headerOffset;
/* Class and subclass dynamic debugging support */
static final Debug debug = Debug.getInstance("ssl");
Authenticator writeAuthenticator;
CipherBox writeCipher;
HandshakeHash handshakeHash;
boolean firstMessage;
// current protocol version, sent as record version
ProtocolVersion protocolVersion;
ProtocolVersion protocolVersion;
// version for the ClientHello message. Only relevant if this is a
// client handshake record. If set to ProtocolVersion.SSL20Hello,
// the V3 client hello is converted to V2 format.
private ProtocolVersion helloVersion;
ProtocolVersion helloVersion;
/* Class and subclass dynamic debugging support */
static final Debug debug = Debug.getInstance("ssl");
// Is it the first application record to write?
boolean isFirstAppOutputRecord = true;
// packet size
int packetSize;
// fragment size
int fragmentSize;
// closed or not?
boolean isClosed;
/*
* Default constructor makes a record supporting the maximum
* SSL record size. It allocates the header bytes directly.
*
* The structure of the byte buffer looks like:
*
* |---------+--------+-------+---------------------------------|
* | unused | header | IV | content, MAC/TAG, padding, etc. |
* | headerPlusMaxIVSize |
*
* unused: unused part of the buffer of size
*
* headerPlusMaxIVSize - header size - IV size
*
* When this object is created, we don't know the protocol
* version number, IV length, etc., so reserve space in front
* to avoid extra data movement (copies).
* header: the header of an SSL record
* IV: the optional IV/nonce field, it is only required for block
* (TLS 1.1 or later) and AEAD cipher suites.
*
* @param type the content type for the record
* Mappings from V3 cipher suite encodings to their pure V2 equivalents.
* This is taken from the SSL V3 specification, Appendix E.
*/
OutputRecord(byte type, int size) {
super(size);
this.protocolVersion = ProtocolVersion.DEFAULT;
this.helloVersion = ProtocolVersion.DEFAULT_HELLO;
firstMessage = true;
count = headerPlusMaxIVSize;
contentType = type;
lastHashed = count;
headerOffset = headerPlusMaxIVSize - headerSize;
private static int[] V3toV2CipherMap1 =
{-1, -1, -1, 0x02, 0x01, -1, 0x04, 0x05, -1, 0x06, 0x07};
private static int[] V3toV2CipherMap3 =
{-1, -1, -1, 0x80, 0x80, -1, 0x80, 0x80, -1, 0x40, 0xC0};
OutputRecord() {
this.writeCipher = CipherBox.NULL;
this.firstMessage = true;
this.fragmentSize = Record.maxDataSize;
// Please set packetSize and protocolVersion in the implementation.
}
OutputRecord(byte type) {
this(type, recordSize(type));
}
/**
* Get the size of the buffer we need for records of the specified
* type.
*/
private static int recordSize(byte type) {
if ((type == ct_change_cipher_spec) || (type == ct_alert)) {
return maxAlertRecordSize;
} else {
return maxRecordSize;
}
}
/*
* Updates the SSL version of this record.
*/
synchronized void setVersion(ProtocolVersion protocolVersion) {
void setVersion(ProtocolVersion protocolVersion) {
this.protocolVersion = protocolVersion;
}
@ -131,442 +99,365 @@ class OutputRecord extends ByteArrayOutputStream implements Record {
this.helloVersion = helloVersion;
}
/*
* Reset the record so that it can be refilled, starting
* immediately after the header.
*/
@Override
public synchronized void reset() {
super.reset();
count = headerPlusMaxIVSize;
lastHashed = count;
headerOffset = headerPlusMaxIVSize - headerSize;
}
/*
* For handshaking, we need to be able to hash every byte above the
* record marking layer. This is where we're guaranteed to see those
* bytes, so this is where we can hash them.
*/
void setHandshakeHash(HandshakeHash handshakeHash) {
assert(contentType == ct_handshake);
this.handshakeHash = handshakeHash;
}
/*
* We hash (the plaintext) on demand. There is one place where
* we want to access the hash in the middle of a record: client
* cert message gets hashed, and part of the same record is the
* client cert verify message which uses that hash. So we track
* how much of each record we've hashed so far.
*/
void doHashes() {
int len = count - lastHashed;
if (len > 0) {
hashInternal(buf, lastHashed, len);
lastHashed = count;
}
}
/*
* Need a helper function so we can hash the V2 hello correctly
*/
private void hashInternal(byte buf [], int offset, int len) {
if (debug != null && Debug.isOn("data")) {
try {
HexDumpEncoder hd = new HexDumpEncoder();
System.out.println("[write] MD5 and SHA1 hashes: len = "
+ len);
hd.encodeBuffer(new ByteArrayInputStream(buf,
lastHashed, len), System.out);
} catch (IOException e) { }
}
handshakeHash.update(buf, lastHashed, len);
lastHashed = count;
}
/*
* Return true iff the record is empty -- to avoid doing the work
* of sending empty records over the network.
*/
boolean isEmpty() {
return count == headerPlusMaxIVSize;
}
/*
* Return true if the record is of an alert of the given description.
*
* Per SSL/TLS specifications, alert messages convey the severity of the
* message (warning or fatal) and a description of the alert. An alert
* is defined with a two bytes struct, {byte level, byte description},
* following after the header bytes.
*/
boolean isAlert(byte description) {
if ((count > (headerPlusMaxIVSize + 1)) && (contentType == ct_alert)) {
return buf[headerPlusMaxIVSize + 1] == description;
}
return false;
}
/*
* Encrypt ... length may grow due to block cipher padding, or
* message authentication code or tag.
*/
void encrypt(Authenticator authenticator, CipherBox box)
throws IOException {
boolean seqNumIsHuge() {
return (writeAuthenticator != null) &&
writeAuthenticator.seqNumIsHuge();
}
// In case we are automatically flushing a handshake stream, make
// sure we have hashed the message first.
//
// when we support compression, hashing can't go here
// since it'll need to be done on the uncompressed data,
// and the MAC applies to the compressed data.
if (contentType == ct_handshake) {
doHashes();
// SSLEngine and SSLSocket
abstract void encodeAlert(byte level, byte description) throws IOException;
// SSLEngine and SSLSocket
abstract void encodeHandshake(byte[] buffer,
int offset, int length) throws IOException;
// SSLEngine and SSLSocket
abstract void encodeChangeCipherSpec() throws IOException;
// apply to SSLEngine only
Ciphertext encode(ByteBuffer[] sources, int offset, int length,
ByteBuffer destination) throws IOException {
throw new UnsupportedOperationException();
}
// apply to SSLEngine only
void encodeV2NoCipher() throws IOException {
throw new UnsupportedOperationException();
}
// apply to SSLSocket only
void deliver(byte[] source, int offset, int length) throws IOException {
throw new UnsupportedOperationException();
}
// apply to SSLSocket only
void setDeliverStream(OutputStream outputStream) {
throw new UnsupportedOperationException();
}
// apply to SSLEngine only
Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException {
throw new UnsupportedOperationException();
}
void changeWriteCiphers(Authenticator writeAuthenticator,
CipherBox writeCipher) throws IOException {
encodeChangeCipherSpec();
/*
* Dispose of any intermediate state in the underlying cipher.
* For PKCS11 ciphers, this will release any attached sessions,
* and thus make finalization faster.
*
* Since MAC's doFinal() is called for every SSL/TLS packet, it's
* not necessary to do the same with MAC's.
*/
writeCipher.dispose();
this.writeAuthenticator = writeAuthenticator;
this.writeCipher = writeCipher;
this.isFirstAppOutputRecord = true;
}
void changePacketSize(int packetSize) {
this.packetSize = packetSize;
}
void changeFragmentSize(int fragmentSize) {
this.fragmentSize = fragmentSize;
}
int getMaxPacketSize() {
return packetSize;
}
// apply to DTLS SSLEngine
void initHandshaker() {
// blank
}
@Override
synchronized public void close() throws IOException {
if (!isClosed) {
isClosed = true;
writeCipher.dispose();
}
}
//
// shared helpers
//
// Encrypt a fragment and wrap up a record.
//
// To be consistent with the spec of SSLEngine.wrap() methods, the
// destination ByteBuffer's position is updated to reflect the amount
// of data produced. The limit remains the same.
static long encrypt(Authenticator authenticator,
CipherBox encCipher, byte contentType, ByteBuffer destination,
int headerOffset, int dstLim, int headerSize,
ProtocolVersion protocolVersion, boolean isDTLS) {
byte[] sequenceNumber = null;
int dstContent = destination.position();
// Acquire the current sequence number before using.
if (isDTLS) {
sequenceNumber = authenticator.sequenceNumber();
}
// Requires message authentication code for stream and block
// cipher suites.
// "flip" but skip over header again, add MAC & encrypt
if (authenticator instanceof MAC) {
MAC signer = (MAC)authenticator;
if (signer.MAClen() != 0) {
byte[] hash = signer.compute(contentType, buf,
headerPlusMaxIVSize, count - headerPlusMaxIVSize, false);
write(hash);
byte[] hash = signer.compute(contentType, destination, false);
/*
* position was advanced to limit in MAC compute above.
*
* Mark next area as writable (above layers should have
* established that we have plenty of room), then write
* out the hash.
*/
destination.limit(destination.limit() + hash.length);
destination.put(hash);
// reset the position and limit
destination.limit(destination.position());
destination.position(dstContent);
}
}
if (!box.isNullCipher()) {
// Requires explicit IV/nonce for CBC/AEAD cipher suites for
// TLS 1.1 or later.
if ((protocolVersion.v >= ProtocolVersion.TLS11.v) &&
(box.isCBCMode() || box.isAEADMode())) {
byte[] nonce = box.createExplicitNonce(authenticator,
contentType, count - headerPlusMaxIVSize);
int offset = headerPlusMaxIVSize - nonce.length;
System.arraycopy(nonce, 0, buf, offset, nonce.length);
headerOffset = offset - headerSize;
} else {
headerOffset = headerPlusMaxIVSize - headerSize;
if (!encCipher.isNullCipher()) {
if (protocolVersion.useTLS11PlusSpec() &&
(encCipher.isCBCMode() || encCipher.isAEADMode())) {
byte[] nonce = encCipher.createExplicitNonce(
authenticator, contentType, destination.remaining());
destination.position(headerOffset + headerSize);
destination.put(nonce);
}
// encrypt the content
int offset = headerPlusMaxIVSize;
if (!box.isAEADMode()) {
// The explicit IV can be encrypted.
offset = headerOffset + headerSize;
if (!encCipher.isAEADMode()) {
// The explicit IV in TLS 1.1 and later can be encrypted.
destination.position(headerOffset + headerSize);
} // Otherwise, DON'T encrypt the nonce_explicit for AEAD mode
count = offset + box.encrypt(buf, offset, count - offset);
}
}
/*
* Tell how full the buffer is ... for filling it with application or
* handshake data.
*/
final int availableDataBytes() {
int dataSize = count - headerPlusMaxIVSize;
return maxDataSize - dataSize;
}
/*
* Increases the capacity if necessary to ensure that it can hold
* at least the number of elements specified by the minimum
* capacity argument.
*
* Note that the increased capacity is only can be used for held
* record buffer. Please DO NOT update the availableDataBytes()
* according to the expended buffer capacity.
*
* @see availableDataBytes()
*/
private void ensureCapacity(int minCapacity) {
// overflow-conscious code
if (minCapacity > buf.length) {
buf = Arrays.copyOf(buf, minCapacity);
}
}
/*
* Return the type of SSL record that's buffered here.
*/
final byte contentType() {
return contentType;
}
/*
* Write the record out on the stream. Note that you must have (in
* order) compressed the data, appended the MAC, and encrypted it in
* order for the record to be understood by the other end. (Some of
* those steps will be null early in handshaking.)
*
* Note that this does no locking for the connection, it's required
* that synchronization be done elsewhere. Also, this does its work
* in a single low level write, for efficiency.
*/
void write(OutputStream s, boolean holdRecord,
ByteArrayOutputStream heldRecordBuffer) throws IOException {
/*
* Don't emit content-free records. (Even change cipher spec
* messages have a byte of data!)
*/
if (count == headerPlusMaxIVSize) {
return;
}
int length = count - headerOffset - headerSize;
// "should" really never write more than about 14 Kb...
if (length < 0) {
throw new SSLException("output record size too small: "
+ length);
}
if (debug != null
&& (Debug.isOn("record") || Debug.isOn("handshake"))) {
if ((debug != null && Debug.isOn("record"))
|| contentType() == ct_change_cipher_spec)
System.out.println(Thread.currentThread().getName()
// v3.0/v3.1 ...
+ ", WRITE: " + protocolVersion
+ " " + InputRecord.contentName(contentType())
+ ", length = " + length);
}
/*
* If this is the initial ClientHello on this connection and
* we're not trying to resume a (V3) session then send a V2
* ClientHello instead so we can detect V2 servers cleanly.
*/
if (firstMessage && useV2Hello()) {
byte[] v3Msg = new byte[length - 4];
System.arraycopy(buf, headerPlusMaxIVSize + 4,
v3Msg, 0, v3Msg.length);
headerOffset = 0; // reset the header offset
V3toV2ClientHello(v3Msg);
handshakeHash.reset();
lastHashed = 2;
doHashes();
if (debug != null && Debug.isOn("record")) {
System.out.println(
Thread.currentThread().getName()
+ ", WRITE: SSLv2 client hello message"
+ ", length = " + (count - 2)); // 2 byte SSLv2 header
}
// Encrypt may pad, so again the limit may be changed.
encCipher.encrypt(destination, dstLim);
} else {
/*
* Fill out the header, write it and the message.
*/
buf[headerOffset + 0] = contentType;
buf[headerOffset + 1] = protocolVersion.major;
buf[headerOffset + 2] = protocolVersion.minor;
buf[headerOffset + 3] = (byte)(length >> 8);
buf[headerOffset + 4] = (byte)(length);
destination.position(destination.limit());
}
firstMessage = false;
/*
* The upper levels may want us to delay sending this packet so
* multiple TLS Records can be sent in one (or more) TCP packets.
* If so, add this packet to the heldRecordBuffer.
*
* NOTE: all writes have been synchronized by upper levels.
*/
int debugOffset = 0;
if (holdRecord) {
/*
* If holdRecord is true, we must have a heldRecordBuffer.
*
* Don't worry about the override of writeBuffer(), because
* when holdRecord is true, the implementation in this class
* will be used.
*/
writeBuffer(heldRecordBuffer,
buf, headerOffset, count - headerOffset, debugOffset);
// Finish out the record header.
int fragLen = destination.limit() - headerOffset - headerSize;
destination.put(headerOffset, contentType); // content type
destination.put(headerOffset + 1, protocolVersion.major);
destination.put(headerOffset + 2, protocolVersion.minor);
if (!isDTLS) {
// fragment length
destination.put(headerOffset + 3, (byte)(fragLen >> 8));
destination.put(headerOffset + 4, (byte)fragLen);
} else {
// It's time to send, do we have buffered data?
// May or may not have a heldRecordBuffer.
if (heldRecordBuffer != null && heldRecordBuffer.size() > 0) {
int heldLen = heldRecordBuffer.size();
// epoch and sequence_number
destination.put(headerOffset + 3, sequenceNumber[0]);
destination.put(headerOffset + 4, sequenceNumber[1]);
destination.put(headerOffset + 5, sequenceNumber[2]);
destination.put(headerOffset + 6, sequenceNumber[3]);
destination.put(headerOffset + 7, sequenceNumber[4]);
destination.put(headerOffset + 8, sequenceNumber[5]);
destination.put(headerOffset + 9, sequenceNumber[6]);
destination.put(headerOffset + 10, sequenceNumber[7]);
// Ensure the capacity of this buffer.
int newCount = count + heldLen - headerOffset;
ensureCapacity(newCount);
// fragment length
destination.put(headerOffset + 11, (byte)(fragLen >> 8));
destination.put(headerOffset + 12, (byte)fragLen);
// Slide everything in the buffer to the right.
System.arraycopy(buf, headerOffset,
buf, heldLen, count - headerOffset);
// Increase the sequence number for next use.
authenticator.increaseSequenceNumber();
}
// Prepend the held record to the buffer.
System.arraycopy(
heldRecordBuffer.toByteArray(), 0, buf, 0, heldLen);
count = newCount;
headerOffset = 0;
// Update destination position to reflect the amount of data produced.
destination.position(destination.limit());
// Clear the held buffer.
heldRecordBuffer.reset();
return Authenticator.toLong(sequenceNumber);
}
// The held buffer has been dumped, set the debug dump offset.
debugOffset = heldLen;
// Encrypt a fragment and wrap up a record.
//
// Uses the internal expandable buf variable and the current
// protocolVersion variable.
void encrypt(Authenticator authenticator,
CipherBox encCipher, byte contentType, int headerSize) {
int position = headerSize + writeCipher.getExplicitNonceSize();
// "flip" but skip over header again, add MAC & encrypt
int macLen = 0;
if (authenticator instanceof MAC) {
MAC signer = (MAC)authenticator;
macLen = signer.MAClen();
if (macLen != 0) {
byte[] hash = signer.compute(contentType,
buf, position, (count - position), false);
write(hash, 0, hash.length);
}
writeBuffer(s, buf, headerOffset,
count - headerOffset, debugOffset);
}
reset();
}
if (!encCipher.isNullCipher()) {
// Requires explicit IV/nonce for CBC/AEAD cipher suites for
// TLS 1.1 or later.
if (protocolVersion.useTLS11PlusSpec() &&
(encCipher.isCBCMode() || encCipher.isAEADMode())) {
/*
* Actually do the write here. For SSLEngine's HS data,
* we'll override this method and let it take the appropriate
* action.
*/
void writeBuffer(OutputStream s, byte [] buf, int off, int len,
int debugOffset) throws IOException {
s.write(buf, off, len);
s.flush();
byte[] nonce = encCipher.createExplicitNonce(
authenticator, contentType, (count - position));
int noncePos = position - nonce.length;
System.arraycopy(nonce, 0, buf, noncePos, nonce.length);
}
// Output only the record from the specified debug offset.
if (debug != null && Debug.isOn("packet")) {
try {
HexDumpEncoder hd = new HexDumpEncoder();
if (!encCipher.isAEADMode()) {
// The explicit IV in TLS 1.1 and later can be encrypted.
position = headerSize;
} // Otherwise, DON'T encrypt the nonce_explicit for AEAD mode
System.out.println("[Raw write]: length = " +
(len - debugOffset));
hd.encodeBuffer(new ByteArrayInputStream(buf,
off + debugOffset, len - debugOffset), System.out);
} catch (IOException e) { }
// increase buf capacity if necessary
int fragSize = count - position;
int packetSize =
encCipher.calculatePacketSize(fragSize, macLen, headerSize);
if (packetSize > (buf.length - position)) {
byte[] newBuf = new byte[position + packetSize];
System.arraycopy(buf, 0, newBuf, 0, count);
buf = newBuf;
}
// Encrypt may pad, so again the count may be changed.
count = position +
encCipher.encrypt(buf, position, (count - position));
}
// Fill out the header, write it and the message.
int fragLen = count - headerSize;
buf[0] = contentType;
buf[1] = protocolVersion.major;
buf[2] = protocolVersion.minor;
buf[3] = (byte)((fragLen >> 8) & 0xFF);
buf[4] = (byte)(fragLen & 0xFF);
}
/*
* Return whether the buffer contains a ClientHello message that should
* be converted to V2 format.
*/
private boolean useV2Hello() {
return firstMessage
&& (helloVersion == ProtocolVersion.SSL20Hello)
&& (contentType == ct_handshake)
&& (buf[headerOffset + 5] == HandshakeMessage.ht_client_hello)
// 5: recode header size
&& (buf[headerPlusMaxIVSize + 4 + 2 + 32] == 0);
// V3 session ID is empty
// 4: handshake header size
// 2: client_version in ClientHello
// 32: random in ClientHello
}
static ByteBuffer encodeV2ClientHello(
byte[] fragment, int offset, int length) throws IOException {
/*
* Detect "old" servers which are capable of SSL V2.0 protocol ... for
* example, Netscape Commerce 1.0 servers. The V3 message is in the
* header and the bytes passed as parameter. This routine translates
* the V3 message into an equivalent V2 one.
*
* Note that the translation will strip off all hello extensions as
* SSL V2.0 does not support hello extension.
*/
private void V3toV2ClientHello(byte v3Msg []) throws SSLException {
int v3SessionIdLenOffset = 2 + 32; // version + nonce
int v3SessionIdLen = v3Msg[v3SessionIdLenOffset];
int v3CipherSpecLenOffset = v3SessionIdLenOffset + 1 + v3SessionIdLen;
int v3CipherSpecLen = ((v3Msg[v3CipherSpecLenOffset] & 0xff) << 8) +
(v3Msg[v3CipherSpecLenOffset + 1] & 0xff);
int cipherSpecs = v3CipherSpecLen / 2; // 2 bytes each in V3
int v3SessIdLenOffset = offset + 34; // 2: client_version
// 32: random
int v3SessIdLen = fragment[v3SessIdLenOffset];
int v3CSLenOffset = v3SessIdLenOffset + 1 + v3SessIdLen;
int v3CSLen = ((fragment[v3CSLenOffset] & 0xff) << 8) +
(fragment[v3CSLenOffset + 1] & 0xff);
int cipherSpecs = v3CSLen / 2; // 2: cipher spec size
// Estimate the max V2ClientHello message length
//
// 11: header size
// (cipherSpecs * 6): cipher_specs
// 6: one cipher suite may need 6 bytes, see V3toV2CipherSuite.
// 3: placeholder for the TLS_EMPTY_RENEGOTIATION_INFO_SCSV
// signaling cipher suite
// 32: challenge size
int v2MaxMsgLen = 11 + (cipherSpecs * 6) + 3 + 32;
// Create a ByteBuffer backed by an accessible byte array.
byte[] dstBytes = new byte[v2MaxMsgLen];
ByteBuffer dstBuf = ByteBuffer.wrap(dstBytes);
/*
* Copy over the cipher specs. We don't care about actually translating
* them for use with an actual V2 server since we only talk V3.
* Therefore, just copy over the V3 cipher spec values with a leading
* 0.
* Copy over the cipher specs. We don't care about actually
* translating them for use with an actual V2 server since
* we only talk V3. Therefore, just copy over the V3 cipher
* spec values with a leading 0.
*/
int v3CipherSpecOffset = v3CipherSpecLenOffset + 2; // skip length
int v2CipherSpecLen = 0;
count = 11;
int v3CSOffset = v3CSLenOffset + 2; // skip length field
int v2CSLen = 0;
dstBuf.position(11);
boolean containsRenegoInfoSCSV = false;
for (int i = 0; i < cipherSpecs; i++) {
byte byte1, byte2;
byte1 = v3Msg[v3CipherSpecOffset++];
byte2 = v3Msg[v3CipherSpecOffset++];
v2CipherSpecLen += V3toV2CipherSuite(byte1, byte2);
byte1 = fragment[v3CSOffset++];
byte2 = fragment[v3CSOffset++];
v2CSLen += V3toV2CipherSuite(dstBuf, byte1, byte2);
if (!containsRenegoInfoSCSV &&
byte1 == (byte)0x00 && byte2 == (byte)0xFF) {
byte1 == (byte)0x00 && byte2 == (byte)0xFF) {
containsRenegoInfoSCSV = true;
}
}
if (!containsRenegoInfoSCSV) {
v2CipherSpecLen += V3toV2CipherSuite((byte)0x00, (byte)0xFF);
v2CSLen += V3toV2CipherSuite(dstBuf, (byte)0x00, (byte)0xFF);
}
/*
* Copy in the nonce.
*/
dstBuf.put(fragment, (offset + 2), 32);
/*
* Build the first part of the V3 record header from the V2 one
* that's now buffered up. (Lengths are fixed up later).
*/
buf[2] = HandshakeMessage.ht_client_hello;
buf[3] = v3Msg[0]; // major version
buf[4] = v3Msg[1]; // minor version
buf[5] = (byte)(v2CipherSpecLen >>> 8);
buf[6] = (byte)v2CipherSpecLen;
buf[7] = 0;
buf[8] = 0; // always no session
buf[9] = 0;
buf[10] = 32; // nonce length (always 32 in V3)
int msgLen = dstBuf.position() - 2; // Exclude the legth field itself
dstBuf.position(0);
dstBuf.put((byte)(0x80 | ((msgLen >>> 8) & 0xFF))); // pos: 0
dstBuf.put((byte)(msgLen & 0xFF)); // pos: 1
dstBuf.put(HandshakeMessage.ht_client_hello); // pos: 2
dstBuf.put(fragment[offset]); // major version, pos: 3
dstBuf.put(fragment[offset + 1]); // minor version, pos: 4
dstBuf.put((byte)(v2CSLen >>> 8)); // pos: 5
dstBuf.put((byte)(v2CSLen & 0xFF)); // pos: 6
dstBuf.put((byte)0x00); // session_id_length, pos: 7
dstBuf.put((byte)0x00); // pos: 8
dstBuf.put((byte)0x00); // challenge_length, pos: 9
dstBuf.put((byte)32); // pos: 10
/*
* Copy in the nonce.
*/
System.arraycopy(v3Msg, 2, buf, count, 32);
count += 32;
dstBuf.position(0);
dstBuf.limit(msgLen + 2);
/*
* Set the length of the message.
*/
count -= 2; // don't include length field itself
buf[0] = (byte)(count >>> 8);
buf[0] |= 0x80;
buf[1] = (byte)(count);
count += 2;
return dstBuf;
}
/*
* Mappings from V3 cipher suite encodings to their pure V2 equivalents.
* This is taken from the SSL V3 specification, Appendix E.
*/
private static int[] V3toV2CipherMap1 =
{-1, -1, -1, 0x02, 0x01, -1, 0x04, 0x05, -1, 0x06, 0x07};
private static int[] V3toV2CipherMap3 =
{-1, -1, -1, 0x80, 0x80, -1, 0x80, 0x80, -1, 0x40, 0xC0};
private static int V3toV2CipherSuite(ByteBuffer dstBuf,
byte byte1, byte byte2) {
dstBuf.put((byte)0);
dstBuf.put(byte1);
dstBuf.put(byte2);
/*
* See which matching pure-V2 cipher specs we need to include.
* We are including these not because we are actually prepared
* to talk V2 but because the Oracle Web Server insists on receiving
* at least 1 "pure V2" cipher suite that it supports and returns an
* illegal_parameter alert unless one is present. Rather than mindlessly
* claiming to implement all documented pure V2 cipher suites the code below
* just claims to implement the V2 cipher suite that is "equivalent"
* in terms of cipher algorithm & exportability with the actual V3 cipher
* suite that we do support.
*/
private int V3toV2CipherSuite(byte byte1, byte byte2) {
buf[count++] = 0;
buf[count++] = byte1;
buf[count++] = byte2;
if (((byte2 & 0xff) > 0xA) ||
(V3toV2CipherMap1[byte2] == -1)) {
if (((byte2 & 0xff) > 0xA) || (V3toV2CipherMap1[byte2] == -1)) {
return 3;
}
buf[count++] = (byte)V3toV2CipherMap1[byte2];
buf[count++] = 0;
buf[count++] = (byte)V3toV2CipherMap3[byte2];
dstBuf.put((byte)V3toV2CipherMap1[byte2]);
dstBuf.put((byte)0);
dstBuf.put((byte)V3toV2CipherMap3[byte2]);
return 6;
}

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ssl;
import java.nio.ByteBuffer;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
/*
* Plaintext
*/
final class Plaintext {
final static Plaintext PLAINTEXT_NULL = new Plaintext();
byte contentType;
byte majorVersion;
byte minorVersion;
int recordEpoch; // incremented on every cipher state change
long recordSN;
ByteBuffer fragment; // null if need to be reassembled
HandshakeStatus handshakeStatus; // null if not used or not handshaking
Plaintext() {
this.contentType = 0;
this.majorVersion = 0;
this.minorVersion = 0;
this.recordEpoch = -1;
this.recordSN = -1;
this.fragment = null;
this.handshakeStatus = null;
}
Plaintext(byte contentType, byte majorVersion, byte minorVersion,
int recordEpoch, long recordSN, ByteBuffer fragment) {
this.contentType = contentType;
this.majorVersion = majorVersion;
this.minorVersion = minorVersion;
this.recordEpoch = recordEpoch;
this.recordSN = recordSN;
this.fragment = fragment;
this.handshakeStatus = null;
}
public String toString() {
return "contentType: " + contentType + "/" +
"majorVersion: " + majorVersion + "/" +
"minorVersion: " + minorVersion + "/" +
"recordEpoch: " + recordEpoch + "/" +
"recordSN: 0x" + Long.toHexString(recordSN) + "/" +
"fragment: " + fragment;
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2015, 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
@ -124,8 +124,9 @@ final class ProtocolList {
ProtocolVersion selectProtocolVersion(ProtocolVersion protocolVersion) {
ProtocolVersion selectedVersion = null;
for (ProtocolVersion pv : protocols) {
if (pv.v > protocolVersion.v) {
break; // Safe to break here as this.protocols is sorted
if (pv.compareTo(protocolVersion) > 0) {
break; // Safe to break here as this.protocols is sorted,
// and DTLS and SSL/TLS protocols are not mixed.
}
selectedVersion = pv;
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2015, 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
@ -27,6 +27,7 @@ package sun.security.ssl;
import java.util.*;
import java.security.CryptoPrimitive;
import sun.security.ssl.CipherSuite.*;
/**
* Type safe enum for an SSL/TLS protocol version. Instances are obtained
@ -61,9 +62,9 @@ public final class ProtocolVersion implements Comparable<ProtocolVersion> {
// Dummy protocol version value for invalid SSLSession
final static ProtocolVersion NONE = new ProtocolVersion(-1, "NONE");
// If enabled, send/ accept SSLv2 hello messages
final static ProtocolVersion SSL20Hello = new ProtocolVersion(0x0002,
"SSLv2Hello");
// If enabled, send/accept SSLv2 hello messages
final static ProtocolVersion SSL20Hello =
new ProtocolVersion(0x0002, "SSLv2Hello");
// SSL 3.0
final static ProtocolVersion SSL30 = new ProtocolVersion(0x0300, "SSLv3");
@ -77,6 +78,19 @@ public final class ProtocolVersion implements Comparable<ProtocolVersion> {
// TLS 1.2
final static ProtocolVersion TLS12 = new ProtocolVersion(0x0303, "TLSv1.2");
// DTLS 1.0
// {254, 255}, the version value of DTLS 1.0.
final static ProtocolVersion DTLS10 =
new ProtocolVersion(0xFEFF, "DTLSv1.0");
// No DTLS 1.1, that version number was skipped in order to harmonize
// version numbers with TLS.
// DTLS 1.2
// {254, 253}, the version value of DTLS 1.2.
final static ProtocolVersion DTLS12 =
new ProtocolVersion(0xFEFD, "DTLSv1.2");
private static final boolean FIPS = SunJSSE.isFIPS();
// minimum version we implement (SSL 3.0)
@ -85,8 +99,11 @@ public final class ProtocolVersion implements Comparable<ProtocolVersion> {
// maximum version we implement (TLS 1.2)
final static ProtocolVersion MAX = TLS12;
// ProtocolVersion to use by default (TLS 1.2)
final static ProtocolVersion DEFAULT = TLS12;
// SSL/TLS ProtocolVersion to use by default (TLS 1.2)
final static ProtocolVersion DEFAULT_TLS = TLS12;
// DTLS ProtocolVersion to use by default (TLS 1.2)
final static ProtocolVersion DEFAULT_DTLS = DTLS12;
// Default version for hello messages (SSLv2Hello)
final static ProtocolVersion DEFAULT_HELLO = FIPS ? TLS10 : SSL30;
@ -108,10 +125,10 @@ public final class ProtocolVersion implements Comparable<ProtocolVersion> {
// Initialize the available protocols.
static {
Set<ProtocolVersion> protocols = new HashSet<>(5);
Set<ProtocolVersion> protocols = new HashSet<>(7);
ProtocolVersion[] pvs = new ProtocolVersion[] {
SSL20Hello, SSL30, TLS10, TLS11, TLS12};
SSL20Hello, SSL30, TLS10, TLS11, TLS12, DTLS10, DTLS12};
EnumSet<CryptoPrimitive> cryptoPrimitives =
EnumSet.<CryptoPrimitive>of(CryptoPrimitive.KEY_AGREEMENT);
for (ProtocolVersion p : pvs) {
@ -145,6 +162,10 @@ public final class ProtocolVersion implements Comparable<ProtocolVersion> {
return TLS12;
} else if (v == SSL20Hello.v) {
return SSL20Hello;
} else if (v == DTLS10.v) {
return DTLS10;
} else if (v == DTLS12.v) {
return DTLS12;
} else {
int major = (v >>> 8) & 0xFF;
int minor = v & 0xFF;
@ -172,8 +193,8 @@ public final class ProtocolVersion implements Comparable<ProtocolVersion> {
}
if (FIPS && (name.equals(SSL30.name) || name.equals(SSL20Hello.name))) {
throw new IllegalArgumentException
("Only TLS 1.0 or later allowed in FIPS mode");
throw new IllegalArgumentException(
"Only TLS 1.0 or later allowed in FIPS mode");
}
if (name.equals(SSL30.name)) {
@ -186,6 +207,10 @@ public final class ProtocolVersion implements Comparable<ProtocolVersion> {
return TLS12;
} else if (name.equals(SSL20Hello.name)) {
return SSL20Hello;
} else if (name.equals(DTLS10.name)) {
return DTLS10;
} else if (name.equals(DTLS12.name)) {
return DTLS12;
} else {
throw new IllegalArgumentException(name);
}
@ -201,6 +226,90 @@ public final class ProtocolVersion implements Comparable<ProtocolVersion> {
*/
@Override
public int compareTo(ProtocolVersion protocolVersion) {
return this.v - protocolVersion.v;
if (maybeDTLSProtocol()) {
if (!protocolVersion.maybeDTLSProtocol()) {
throw new IllegalArgumentException("Not DTLS protocol");
}
return protocolVersion.v - this.v;
} else {
if (protocolVersion.maybeDTLSProtocol()) {
throw new IllegalArgumentException("Not TLS protocol");
}
return this.v - protocolVersion.v;
}
}
/**
* Returns true if a ProtocolVersion represents a DTLS protocol.
*/
boolean isDTLSProtocol() {
return this.v == DTLS12.v || this.v == DTLS10.v;
}
/**
* Returns true if a ProtocolVersion may represent a DTLS protocol.
*/
boolean maybeDTLSProtocol() {
return (this.major & 0x80) != 0;
}
boolean useTLS12PlusSpec() {
return maybeDTLSProtocol() ? (this.v <= DTLS12.v) : (this.v >= TLS12.v);
}
boolean useTLS11PlusSpec() {
return maybeDTLSProtocol() ? true : (this.v >= TLS11.v);
}
boolean useTLS10PlusSpec() {
return maybeDTLSProtocol() ? true : (this.v >= TLS10.v);
}
boolean obsoletes(CipherSuite suite) {
ProtocolVersion proto = this;
if (proto.isDTLSProtocol()) {
// DTLS bans stream ciphers.
if (suite.cipher.cipherType == CipherType.STREAM_CIPHER) {
return true;
}
proto = mapToTLSProtocol(this);
}
return (proto.v >= suite.obsoleted);
}
boolean supports(CipherSuite suite) {
ProtocolVersion proto = this;
if (proto.isDTLSProtocol()) {
// DTLS bans stream ciphers.
if (suite.cipher.cipherType == CipherType.STREAM_CIPHER) {
return false;
}
proto = mapToTLSProtocol(this);
}
return (proto.v >= suite.supported);
}
// Map a specified protocol to the corresponding TLS version.
//
// DTLS 1.2 -> TLS 1.2
// DTLS 1.0 -> TLS 1.1
private static ProtocolVersion mapToTLSProtocol(
ProtocolVersion protocolVersion) {
if (protocolVersion.isDTLSProtocol()) {
if (protocolVersion.v == DTLS10.v) {
protocolVersion = TLS11;
} else { // DTLS12
protocolVersion = TLS12;
}
}
return protocolVersion;
}
}

View file

@ -73,8 +73,8 @@ final class RSAClientKeyExchange extends HandshakeMessage {
this.protocolVersion = protocolVersion;
try {
String s = ((protocolVersion.v >= ProtocolVersion.TLS12.v) ?
"SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret");
String s = protocolVersion.useTLS12PlusSpec() ?
"SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret";
KeyGenerator kg = JsseJce.getKeyGenerator(s);
kg.init(new TlsRsaPremasterSecretParameterSpec(
maxVersion.v, protocolVersion.v), generator);
@ -103,7 +103,7 @@ final class RSAClientKeyExchange extends HandshakeMessage {
throw new SSLKeyException("Private key not of type RSA");
}
if (currentVersion.v >= ProtocolVersion.TLS10.v) {
if (currentVersion.useTLS10PlusSpec()) {
encrypted = input.getBytes16();
} else {
encrypted = new byte [messageSize];
@ -142,7 +142,7 @@ final class RSAClientKeyExchange extends HandshakeMessage {
@Override
int messageLength() {
if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
if (protocolVersion.useTLS10PlusSpec()) {
return encrypted.length + 2;
} else {
return encrypted.length;
@ -151,7 +151,7 @@ final class RSAClientKeyExchange extends HandshakeMessage {
@Override
void send(HandshakeOutStream s) throws IOException {
if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
if (protocolVersion.useTLS10PlusSpec()) {
s.putBytes16(encrypted);
} else {
s.write(encrypted);

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2007, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2015, 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
@ -70,10 +70,10 @@ final class RandomCookie {
void print(PrintStream s) {
int i, gmt_unix_time;
gmt_unix_time = random_bytes[0] << 24;
gmt_unix_time += random_bytes[1] << 16;
gmt_unix_time += random_bytes[2] << 8;
gmt_unix_time += random_bytes[3];
gmt_unix_time = ((random_bytes[0] & 0xFF) << 24) |
((random_bytes[1] & 0xFF) << 16) |
((random_bytes[2] & 0xFF) << 8) |
(random_bytes[3] & 0xFF);
s.print("GMT: " + gmt_unix_time + " ");
s.print("bytes = { ");

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2015, 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,21 +23,20 @@
* questions.
*/
package sun.security.ssl;
/**
* SSL/TLS records, as pulled off (and put onto) a TCP stream. This is
* SSL/TLS/DTLS records, as pulled off (and put onto) a TCP stream. This is
* the base interface, which defines common information and interfaces
* used by both Input and Output records.
*
* @author David Brownell
*/
interface Record {
/*
* There are four SSL record types, which are part of the interface
* to this level (along with the maximum record size)
* There are four record types, which are part of the interface
* to this level (along with the maximum record size).
*
* enum { change_cipher_spec(20), alert(21), handshake(22),
* application_data(23), (255) } ContentType;
@ -47,80 +46,48 @@ interface Record {
static final byte ct_handshake = 22;
static final byte ct_application_data = 23;
static final int headerSize = 5; // SSLv3 record header
static final int maxExpansion = 1024; // for bad compression
static final int trailerSize = 20; // SHA1 hash size
static final int maxMacSize = 48; // the max supported MAC or
// AEAD tag size
static final int maxDataSize = 16384; // 2^14 bytes of data
static final int maxPadding = 256; // block cipher padding
static final int maxIVLength = 256; // IV length
static final int maxIVLength = 16; // the max supported IV length
static final int maxFragmentSize = 18432; // the max fragment size
// 2^14 + 2048
/*
* The size of the header plus the max IV length
* System property to enable/disable CBC protection in SSL3/TLS1.
*/
static final int headerPlusMaxIVSize =
headerSize // header
+ maxIVLength; // iv
/*
* SSL has a maximum record size. It's header, (compressed) data,
* padding, and a trailer for the message authentication information (MAC
* for block and stream ciphers, and message authentication tag for AEAD
* ciphers).
*
* Some compression algorithms have rare cases where they expand the data.
* As we don't support compression at this time, leave that out.
*/
static final int maxRecordSize =
headerPlusMaxIVSize // header + iv
+ maxDataSize // data
+ maxPadding // padding
+ trailerSize; // MAC or AEAD tag
static final boolean enableCBCProtection =
Debug.getBooleanProperty("jsse.enableCBCProtection", true);
/*
* For CBC protection in SSL3/TLS1, we break some plaintext into two
* packets. Max application data size for the second packet.
*/
static final int maxDataSizeMinusOneByteRecord =
maxDataSize // max data size
- ( // max one byte record size
headerPlusMaxIVSize // header + iv
+ 1 // one byte data
+ maxPadding // padding
+ trailerSize // MAC
);
/*
* The maximum large record size.
*
* Some SSL/TLS implementations support large fragment upto 2^15 bytes,
* such as Microsoft. We support large incoming fragments.
*
* The maximum large record size is defined as maxRecordSize plus 2^14,
* this is the amount OpenSSL is using.
*/
static final int maxLargeRecordSize =
maxRecordSize // Max size with a conforming implementation
+ maxDataSize; // extra 2^14 bytes for large data packets.
/*
* Maximum record size for alert and change cipher spec records.
* They only contain 2 and 1 bytes of data, respectively.
* Allocate a smaller array.
*/
static final int maxAlertRecordSize =
headerPlusMaxIVSize // header + iv
+ 2 // alert
+ maxPadding // padding
+ trailerSize; // MAC
/*
* The overflow values of integers of 8, 16 and 24 bits.
*/
static final int OVERFLOW_OF_INT08 = (1 << 8);
static final int OVERFLOW_OF_INT16 = (1 << 16);
static final int OVERFLOW_OF_INT24 = (1 << 24);
/**
* Return a description for the given content type.
*/
static String contentName(byte contentType) {
switch (contentType) {
case ct_change_cipher_spec:
return "Change Cipher Spec";
case ct_alert:
return "Alert";
case ct_handshake:
return "Handshake";
case ct_application_data:
return "Application Data";
default:
return "contentType = " + contentType;
}
}
static boolean isValidContentType(byte contentType) {
return (contentType == 20) || (contentType == 21) ||
(contentType == 22) || (contentType == 23);
}
}

View file

@ -0,0 +1,122 @@
/*
* Copyright (c) 2015, 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;
/*
* enumation of record type
*/
enum RecordType {
RECORD_CHANGE_CIPHER_SPEC (Record.ct_change_cipher_spec,
HandshakeMessage.ht_not_applicable),
RECORD_ALERT (Record.ct_alert,
HandshakeMessage.ht_not_applicable),
RECORD_HELLO_REQUEST (Record.ct_handshake,
HandshakeMessage.ht_hello_request),
RECORD_CLIENT_HELLO (Record.ct_handshake,
HandshakeMessage.ht_client_hello),
RECORD_SERVER_HELLO (Record.ct_handshake,
HandshakeMessage.ht_server_hello),
RECORD_HELLO_VERIFY_REQUEST (Record.ct_handshake,
HandshakeMessage.ht_hello_verify_request),
RECORD_NEW_SESSION_TICKET (Record.ct_handshake,
HandshakeMessage.ht_new_session_ticket),
RECORD_CERTIFICATE (Record.ct_handshake,
HandshakeMessage.ht_certificate),
RECORD_SERVER_KEY_EXCHANGE (Record.ct_handshake,
HandshakeMessage.ht_server_key_exchange),
RECORD_CERTIFICATE_REQUEST (Record.ct_handshake,
HandshakeMessage.ht_certificate_request),
RECORD_SERVER_HELLO_DONE (Record.ct_handshake,
HandshakeMessage.ht_server_hello_done),
RECORD_CERTIFICATE_VERIFY (Record.ct_handshake,
HandshakeMessage.ht_certificate_verify),
RECORD_CLIENT_KEY_EXCHANGE (Record.ct_handshake,
HandshakeMessage.ht_client_key_exchange),
RECORD_FINISHED (Record.ct_handshake,
HandshakeMessage.ht_finished),
RECORD_CERTIFICATE_URL (Record.ct_handshake,
HandshakeMessage.ht_certificate_url),
RECORD_CERTIFICATE_STATUS (Record.ct_handshake,
HandshakeMessage.ht_certificate_status),
RECORD_SUPPLIEMENTAL_DATA (Record.ct_handshake,
HandshakeMessage.ht_supplemental_data),
RECORD_APPLICATION_DATA (Record.ct_application_data,
HandshakeMessage.ht_not_applicable);
byte contentType;
byte handshakeType;
private RecordType(byte contentType, byte handshakeType) {
this.contentType = contentType;
this.handshakeType = handshakeType;
}
static RecordType valueOf(byte contentType, byte handshakeType) {
if (contentType == Record.ct_change_cipher_spec) {
return RECORD_CHANGE_CIPHER_SPEC;
} else if (contentType == Record.ct_alert) {
return RECORD_ALERT;
} else if (contentType == Record.ct_application_data) {
return RECORD_APPLICATION_DATA;
} else if (handshakeType == HandshakeMessage.ht_hello_request) {
return RECORD_HELLO_REQUEST;
} else if (handshakeType == HandshakeMessage.ht_client_hello) {
return RECORD_CLIENT_HELLO;
} else if (handshakeType == HandshakeMessage.ht_server_hello) {
return RECORD_SERVER_HELLO;
} else if (handshakeType == HandshakeMessage.ht_hello_verify_request) {
return RECORD_HELLO_VERIFY_REQUEST;
} else if (handshakeType == HandshakeMessage.ht_new_session_ticket) {
return RECORD_NEW_SESSION_TICKET;
} else if (handshakeType == HandshakeMessage.ht_certificate) {
return RECORD_CERTIFICATE;
} else if (handshakeType == HandshakeMessage.ht_server_key_exchange) {
return RECORD_SERVER_KEY_EXCHANGE;
} else if (handshakeType == HandshakeMessage.ht_certificate_request) {
return RECORD_CERTIFICATE_REQUEST;
} else if (handshakeType == HandshakeMessage.ht_server_hello_done) {
return RECORD_SERVER_HELLO_DONE;
} else if (handshakeType == HandshakeMessage.ht_certificate_verify) {
return RECORD_CERTIFICATE_VERIFY;
} else if (handshakeType == HandshakeMessage.ht_client_key_exchange) {
return RECORD_CLIENT_KEY_EXCHANGE;
} else if (handshakeType == HandshakeMessage.ht_finished) {
return RECORD_FINISHED;
} else if (handshakeType == HandshakeMessage.ht_certificate_url) {
return RECORD_CERTIFICATE_URL;
} else if (handshakeType == HandshakeMessage.ht_certificate_status) {
return RECORD_CERTIFICATE_STATUS;
} else if (handshakeType == HandshakeMessage.ht_supplemental_data) {
return RECORD_SUPPLIEMENTAL_DATA;
}
// otherwise, invalid record type
throw new IllegalArgumentException(
"Invalid record type (ContentType:" + contentType +
", HandshakeType:" + handshakeType + ")");
}
}

View file

@ -62,6 +62,9 @@ public abstract class SSLContextImpl extends SSLContextSpi {
private CipherSuiteList defaultClientCipherSuiteList;
private CipherSuiteList supportedCipherSuiteList;
// DTLS cookie exchange manager
private HelloCookieManager helloCookieManager;
SSLContextImpl() {
ephemeralKeyManager = new EphemeralKeyManager();
clientCache = new SSLSessionContextImpl();
@ -175,11 +178,29 @@ public abstract class SSLContextImpl extends SSLContextSpi {
return DummyX509KeyManager.INSTANCE;
}
abstract SSLEngine createSSLEngineImpl();
abstract SSLEngine createSSLEngineImpl(String host, int port);
@Override
protected SSLEngine engineCreateSSLEngine() {
if (!isInitialized) {
throw new IllegalStateException("SSLContext is not initialized");
}
return createSSLEngineImpl();
}
@Override
protected SSLEngine engineCreateSSLEngine(String host, int port) {
if (!isInitialized) {
throw new IllegalStateException("SSLContext is not initialized");
}
return createSSLEngineImpl(host, port);
}
@Override
protected SSLSocketFactory engineGetSocketFactory() {
if (!isInitialized) {
throw new IllegalStateException(
"SSLContextImpl is not initialized");
throw new IllegalStateException("SSLContext is not initialized");
}
return new SSLSocketFactoryImpl(this);
}
@ -192,24 +213,6 @@ public abstract class SSLContextImpl extends SSLContextSpi {
return new SSLServerSocketFactoryImpl(this);
}
@Override
protected SSLEngine engineCreateSSLEngine() {
if (!isInitialized) {
throw new IllegalStateException(
"SSLContextImpl is not initialized");
}
return new SSLEngineImpl(this);
}
@Override
protected SSLEngine engineCreateSSLEngine(String host, int port) {
if (!isInitialized) {
throw new IllegalStateException(
"SSLContextImpl is not initialized");
}
return new SSLEngineImpl(this, host, port);
}
@Override
protected SSLSessionContext engineGetClientSessionContext() {
return clientCache;
@ -236,6 +239,23 @@ public abstract class SSLContextImpl extends SSLContextSpi {
return ephemeralKeyManager;
}
HelloCookieManager getHelloCookieManager() {
if (!isInitialized) {
throw new IllegalStateException("SSLContext is not initialized");
}
if (helloCookieManager == null) {
helloCookieManager = getHelloCookieManager(secureRandom);
}
return helloCookieManager;
}
HelloCookieManager getHelloCookieManager(SecureRandom secureRandom) {
throw new UnsupportedOperationException(
"Cookie exchange applies to DTLS only");
}
abstract SSLParameters getDefaultServerSSLParams();
abstract SSLParameters getDefaultClientSSLParams();
abstract SSLParameters getSupportedSSLParams();
@ -319,12 +339,20 @@ public abstract class SSLContextImpl extends SSLContextSpi {
(protocols == defaultClientProtocolList);
}
/**
* Return whether a protocol list is the original default enabled
* protocols. See: SSLSocket/SSLEngine.setEnabledProtocols()
*/
boolean isDefaultCipherSuiteList(CipherSuiteList cipherSuites) {
return (cipherSuites == defaultClientCipherSuiteList) ||
(cipherSuites == defaultServerCipherSuiteList);
}
/*
* Return the list of all available CipherSuites with a priority of
* minPriority or above.
*/
private CipherSuiteList getApplicableCipherSuiteList(
private static CipherSuiteList getApplicableCipherSuiteList(
ProtocolList protocols, boolean onlyEnabled) {
int minPriority = CipherSuite.SUPPORTED_SUITES_PRIORITY;
@ -344,8 +372,8 @@ public abstract class SSLContextImpl extends SSLContextSpi {
}
if (suite.isAvailable() &&
suite.obsoleted > protocols.min.v &&
suite.supported <= protocols.max.v) {
!protocols.min.obsoletes(suite) &&
protocols.max.supports(suite)) {
if (SSLAlgorithmConstraints.DEFAULT.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
suite.name, null)) {
@ -353,10 +381,10 @@ public abstract class SSLContextImpl extends SSLContextSpi {
}
} else if (debug != null &&
Debug.isOn("sslctx") && Debug.isOn("verbose")) {
if (suite.obsoleted <= protocols.min.v) {
if (protocols.min.obsoletes(suite)) {
System.out.println(
"Ignoring obsoleted cipher suite: " + suite);
} else if (suite.supported > protocols.max.v) {
} else if (!protocols.max.supports(suite)) {
System.out.println(
"Ignoring unsupported cipher suite: " + suite);
} else {
@ -388,8 +416,25 @@ public abstract class SSLContextImpl extends SSLContextSpi {
}
}
private static String[] getAvailableProtocols(
ProtocolVersion[] protocolCandidates) {
List<String> availableProtocols = Collections.<String>emptyList();
if (protocolCandidates != null && protocolCandidates.length != 0) {
availableProtocols = new ArrayList<>(protocolCandidates.length);
for (ProtocolVersion p : protocolCandidates) {
if (ProtocolVersion.availableProtocols.contains(p)) {
availableProtocols.add(p.name);
}
}
}
return availableProtocols.toArray(new String[0]);
}
/*
* The SSLContext implementation for TLS/SSL algorithm
* The SSLContext implementation for SSL/(D)TLS algorithm
*
* SSL/TLS protocols specify the forward compatibility and version
* roll-back attack protections, however, a number of SSL/TLS server
@ -418,14 +463,15 @@ public abstract class SSLContextImpl extends SSLContextSpi {
*/
/*
* The base abstract SSLContext implementation.
* The base abstract SSLContext implementation for the Transport Layer
* Security (TLS) protocols.
*
* This abstract class encapsulates supported and the default server
* SSL parameters.
* SSL/TLS parameters.
*
* @see SSLContext
*/
private abstract static class AbstractSSLContext extends SSLContextImpl {
private abstract static class AbstractTLSContext extends SSLContextImpl {
// parameters
private static final SSLParameters defaultServerSSLParams;
private static final SSLParameters supportedSSLParams;
@ -482,20 +528,14 @@ public abstract class SSLContextImpl extends SSLContextSpi {
return supportedSSLParams;
}
static String[] getAvailableProtocols(
ProtocolVersion[] protocolCandidates) {
@Override
SSLEngine createSSLEngineImpl() {
return new SSLEngineImpl(this, false);
}
List<String> availableProtocols = Collections.<String>emptyList();
if (protocolCandidates != null && protocolCandidates.length != 0) {
availableProtocols = new ArrayList<>(protocolCandidates.length);
for (ProtocolVersion p : protocolCandidates) {
if (ProtocolVersion.availableProtocols.contains(p)) {
availableProtocols.add(p.name);
}
}
}
return availableProtocols.toArray(new String[0]);
@Override
SSLEngine createSSLEngineImpl(String host, int port) {
return new SSLEngineImpl(this, host, port, false);
}
}
@ -504,7 +544,7 @@ public abstract class SSLContextImpl extends SSLContextSpi {
*
* @see SSLContext
*/
public static final class TLS10Context extends AbstractSSLContext {
public static final class TLS10Context extends AbstractTLSContext {
private static final SSLParameters defaultClientSSLParams;
static {
@ -537,7 +577,7 @@ public abstract class SSLContextImpl extends SSLContextSpi {
*
* @see SSLContext
*/
public static final class TLS11Context extends AbstractSSLContext {
public static final class TLS11Context extends AbstractTLSContext {
private static final SSLParameters defaultClientSSLParams;
static {
@ -572,7 +612,7 @@ public abstract class SSLContextImpl extends SSLContextSpi {
*
* @see SSLContext
*/
public static final class TLS12Context extends AbstractSSLContext {
public static final class TLS12Context extends AbstractTLSContext {
private static final SSLParameters defaultClientSSLParams;
static {
@ -604,13 +644,74 @@ public abstract class SSLContextImpl extends SSLContextSpi {
}
}
/*
* The interface for the customized SSL/(D)TLS SSLContext.
*
* @see SSLContext
*/
private static class CustomizedSSLProtocols {
private final static String PROPERTY_NAME = "jdk.tls.client.protocols";
static IllegalArgumentException reservedException = null;
static ArrayList<ProtocolVersion>
customizedProtocols = new ArrayList<>();
// Don't want a java.lang.LinkageError for illegal system property.
//
// Please don't throw exception in this static block. Otherwise,
// java.lang.LinkageError may be thrown during the instantiation of
// the provider service. Instead, please handle the initialization
// exception in the caller's constructor.
static {
String property = AccessController.doPrivileged(
new GetPropertyAction(PROPERTY_NAME));
if (property != null && property.length() != 0) {
// remove double quote marks from beginning/end of the property
if (property.length() > 1 && property.charAt(0) == '"' &&
property.charAt(property.length() - 1) == '"') {
property = property.substring(1, property.length() - 1);
}
}
if (property != null && property.length() != 0) {
String[] protocols = property.split(",");
for (int i = 0; i < protocols.length; i++) {
protocols[i] = protocols[i].trim();
// Is it a supported protocol name?
try {
ProtocolVersion pro =
ProtocolVersion.valueOf(protocols[i]);
if (SunJSSE.isFIPS() &&
((pro.v == ProtocolVersion.SSL30.v) ||
(pro.v == ProtocolVersion.SSL20Hello.v))) {
reservedException = new IllegalArgumentException(
PROPERTY_NAME + ": " + pro +
" is not FIPS compliant");
break;
}
// ignore duplicated protocols
if (!customizedProtocols.contains(pro)) {
customizedProtocols.add(pro);
}
} catch (IllegalArgumentException iae) {
reservedException = new IllegalArgumentException(
PROPERTY_NAME + ": " + protocols[i] +
" is not a standard SSL protocol name", iae);
}
}
}
}
}
/*
* The SSLContext implementation for customized TLS protocols
*
* @see SSLContext
*/
private static class CustomizedSSLContext extends AbstractSSLContext {
private static final String PROPERTY_NAME = "jdk.tls.client.protocols";
private static class CustomizedTLSContext extends AbstractTLSContext {
private static final SSLParameters defaultClientSSLParams;
private static IllegalArgumentException reservedException = null;
@ -621,78 +722,52 @@ public abstract class SSLContextImpl extends SSLContextSpi {
// the provider service. Instead, let's handle the initialization
// exception in constructor.
static {
// candidates for available protocols
ProtocolVersion[] candidates;
String property = AccessController.doPrivileged(
new GetPropertyAction(PROPERTY_NAME));
if (property == null || property.length() == 0) {
// the default enabled client TLS protocols
if (SunJSSE.isFIPS()) {
candidates = new ProtocolVersion[] {
ProtocolVersion.TLS10,
ProtocolVersion.TLS11,
ProtocolVersion.TLS12
};
} else {
candidates = new ProtocolVersion[] {
ProtocolVersion.SSL30,
ProtocolVersion.TLS10,
ProtocolVersion.TLS11,
ProtocolVersion.TLS12
};
}
} else {
// remove double quote marks from beginning/end of the property
if (property.length() > 1 && property.charAt(0) == '"' &&
property.charAt(property.length() - 1) == '"') {
property = property.substring(1, property.length() - 1);
}
String[] protocols = null;
if (property != null && property.length() != 0) {
protocols = property.split(",");
} else {
reservedException = new IllegalArgumentException(
"No protocol specified in " +
PROPERTY_NAME + " system property");
protocols = new String[0];
}
candidates = new ProtocolVersion[protocols.length];
for (int i = 0; i < protocols.length; i++) {
protocols[i] = protocols[i].trim();
// Is it a supported protocol name?
try {
candidates[i] = ProtocolVersion.valueOf(protocols[i]);
} catch (IllegalArgumentException iae) {
reservedException = new IllegalArgumentException(
PROPERTY_NAME + ": " + protocols[i] +
" is not a standard SSL/TLS protocol name", iae);
break;
}
}
if ((reservedException == null) && SunJSSE.isFIPS()) {
for (ProtocolVersion protocolVersion : candidates) {
if (ProtocolVersion.SSL20Hello.v == protocolVersion.v ||
ProtocolVersion.SSL30.v == protocolVersion.v) {
reservedException = new IllegalArgumentException(
PROPERTY_NAME + ": " + protocolVersion +
" is not FIPS compliant");
}
}
}
}
defaultClientSSLParams = new SSLParameters();
reservedException = CustomizedSSLProtocols.reservedException;
if (reservedException == null) {
ArrayList<ProtocolVersion>
customizedTLSProtocols = new ArrayList<>();
for (ProtocolVersion protocol :
CustomizedSSLProtocols.customizedProtocols) {
if (!protocol.isDTLSProtocol()) {
customizedTLSProtocols.add(protocol);
}
}
// candidates for available protocols
ProtocolVersion[] candidates;
if (customizedTLSProtocols.isEmpty()) {
// Use the default enabled client protocols if no
// customized TLS protocols.
if (SunJSSE.isFIPS()) {
candidates = new ProtocolVersion[] {
ProtocolVersion.TLS10,
ProtocolVersion.TLS11,
ProtocolVersion.TLS12
};
} else {
candidates = new ProtocolVersion[] {
ProtocolVersion.SSL30,
ProtocolVersion.TLS10,
ProtocolVersion.TLS11,
ProtocolVersion.TLS12
};
}
} else {
// Use the customized TLS protocols.
candidates =
new ProtocolVersion[customizedTLSProtocols.size()];
candidates = customizedTLSProtocols.toArray(candidates);
}
defaultClientSSLParams = new SSLParameters();
defaultClientSSLParams.setProtocols(
getAvailableProtocols(candidates));
} else {
defaultClientSSLParams = null; // unlikely to be used
}
}
protected CustomizedSSLContext() {
protected CustomizedTLSContext() {
if (reservedException != null) {
throw reservedException;
}
@ -709,7 +784,7 @@ public abstract class SSLContextImpl extends SSLContextSpi {
*
* @see SSLContext
*/
public static final class TLSContext extends CustomizedSSLContext {
public static final class TLSContext extends CustomizedTLSContext {
// use the default constructor and methods
}
@ -718,7 +793,7 @@ public abstract class SSLContextImpl extends SSLContextSpi {
*
* @see SSLContext
*/
public static final class DefaultSSLContext extends CustomizedSSLContext {
public static final class DefaultSSLContext extends CustomizedTLSContext {
private static final String NONE = "NONE";
private static final String P11KEYSTORE = "PKCS11";
@ -879,6 +954,193 @@ public abstract class SSLContextImpl extends SSLContextSpi {
}
}
/*
* The base abstract SSLContext implementation for the Datagram Transport
* Layer Security (DTLS) protocols.
*
* This abstract class encapsulates supported and the default server DTLS
* parameters.
*
* @see SSLContext
*/
private abstract static class AbstractDTLSContext extends SSLContextImpl {
// parameters
private static final SSLParameters defaultServerSSLParams;
private static final SSLParameters supportedSSLParams;
static {
// supported SSL parameters
supportedSSLParams = new SSLParameters();
// Both DTLSv1.0 and DTLSv1.2 can be used in FIPS mode.
supportedSSLParams.setProtocols(new String[] {
ProtocolVersion.DTLS10.name,
ProtocolVersion.DTLS12.name
});
// candidates for available protocols
ProtocolVersion[] candidates = new ProtocolVersion[] {
ProtocolVersion.DTLS10,
ProtocolVersion.DTLS12
};
defaultServerSSLParams = new SSLParameters();
defaultServerSSLParams.setProtocols(
getAvailableProtocols(candidates));
}
@Override
SSLParameters getDefaultServerSSLParams() {
return defaultServerSSLParams;
}
@Override
SSLParameters getSupportedSSLParams() {
return supportedSSLParams;
}
@Override
SSLEngine createSSLEngineImpl() {
return new SSLEngineImpl(this, true);
}
@Override
SSLEngine createSSLEngineImpl(String host, int port) {
return new SSLEngineImpl(this, host, port, true);
}
@Override
HelloCookieManager getHelloCookieManager(SecureRandom secureRandom) {
return new HelloCookieManager(secureRandom);
}
}
/*
* The SSLContext implementation for DTLSv1.0 algorithm.
*
* @see SSLContext
*/
public static final class DTLS10Context extends AbstractDTLSContext {
private final static SSLParameters defaultClientSSLParams;
static {
// candidates for available protocols
ProtocolVersion[] candidates = new ProtocolVersion[] {
ProtocolVersion.DTLS10
};
defaultClientSSLParams = new SSLParameters();
defaultClientSSLParams.setProtocols(
getAvailableProtocols(candidates));
}
@Override
SSLParameters getDefaultClientSSLParams() {
return defaultClientSSLParams;
}
}
/*
* The SSLContext implementation for DTLSv1.2 algorithm.
*
* @see SSLContext
*/
public static final class DTLS12Context extends AbstractDTLSContext {
private final static SSLParameters defaultClientSSLParams;
static {
// candidates for available protocols
ProtocolVersion[] candidates = new ProtocolVersion[] {
ProtocolVersion.DTLS10,
ProtocolVersion.DTLS12
};
defaultClientSSLParams = new SSLParameters();
defaultClientSSLParams.setProtocols(
getAvailableProtocols(candidates));
}
@Override
SSLParameters getDefaultClientSSLParams() {
return defaultClientSSLParams;
}
}
/*
* The SSLContext implementation for customized TLS protocols
*
* @see SSLContext
*/
private static class CustomizedDTLSContext extends AbstractDTLSContext {
private final static SSLParameters defaultClientSSLParams;
private static IllegalArgumentException reservedException = null;
// Don't want a java.lang.LinkageError for illegal system property.
//
// Please don't throw exception in this static block. Otherwise,
// java.lang.LinkageError may be thrown during the instantiation of
// the provider service. Instead, let's handle the initialization
// exception in constructor.
static {
reservedException = CustomizedSSLProtocols.reservedException;
if (reservedException == null) {
ArrayList<ProtocolVersion>
customizedDTLSProtocols = new ArrayList<>();
for (ProtocolVersion protocol :
CustomizedSSLProtocols.customizedProtocols) {
if (protocol.isDTLSProtocol()) {
customizedDTLSProtocols.add(protocol);
}
}
// candidates for available protocols
ProtocolVersion[] candidates;
if (customizedDTLSProtocols.isEmpty()) {
// Use the default enabled client protocols if no
// customized TLS protocols.
//
// Both DTLSv1.0 and DTLSv1.2 can be used in FIPS mode.
candidates = new ProtocolVersion[] {
ProtocolVersion.DTLS10,
ProtocolVersion.DTLS12
};
} else {
// Use the customized TLS protocols.
candidates =
new ProtocolVersion[customizedDTLSProtocols.size()];
candidates = customizedDTLSProtocols.toArray(candidates);
}
defaultClientSSLParams = new SSLParameters();
defaultClientSSLParams.setProtocols(
getAvailableProtocols(candidates));
} else {
defaultClientSSLParams = null; // unlikely to be used
}
}
protected CustomizedDTLSContext() {
if (reservedException != null) {
throw reservedException;
}
}
@Override
SSLParameters getDefaultClientSSLParams() {
return defaultClientSSLParams;
}
}
/*
* The SSLContext implementation for default "DTLS" algorithm
*
* @see SSLContext
*/
public static final class DTLSContext extends CustomizedDTLSContext {
// use the default constructor and methods
}
}
@ -961,7 +1223,7 @@ final class AbstractTrustManagerWrapper extends X509ExtendedTrustManager
ProtocolVersion protocolVersion =
ProtocolVersion.valueOf(session.getProtocol());
AlgorithmConstraints constraints = null;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
if (session instanceof ExtendedSSLSession) {
ExtendedSSLSession extSession =
(ExtendedSSLSession)session;
@ -1003,7 +1265,7 @@ final class AbstractTrustManagerWrapper extends X509ExtendedTrustManager
ProtocolVersion protocolVersion =
ProtocolVersion.valueOf(session.getProtocol());
AlgorithmConstraints constraints = null;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
if (session instanceof ExtendedSSLSession) {
ExtendedSSLSession extSession =
(ExtendedSSLSession)session;

View file

@ -0,0 +1,409 @@
/*
* Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ssl;
import java.io.*;
import java.nio.*;
import javax.crypto.BadPaddingException;
import javax.net.ssl.*;
import sun.misc.HexDumpEncoder;
/**
* {@code InputRecord} implementation for {@code SSLEngine}.
*/
final class SSLEngineInputRecord extends InputRecord implements SSLRecord {
// used by handshake hash computation for handshake fragment
private byte prevType = -1;
private int hsMsgOff = 0;
private int hsMsgLen = 0;
private boolean formatVerified = false; // SSLv2 ruled out?
SSLEngineInputRecord() {
this.readAuthenticator = MAC.TLS_NULL;
}
@Override
int estimateFragmentSize(int packetSize) {
int macLen = 0;
if (readAuthenticator instanceof MAC) {
macLen = ((MAC)readAuthenticator).MAClen();
}
if (packetSize > 0) {
return readCipher.estimateFragmentSize(
packetSize, macLen, headerSize);
} else {
return Record.maxDataSize;
}
}
@Override
int bytesInCompletePacket(ByteBuffer packet) throws SSLException {
/*
* SSLv2 length field is in bytes 0/1
* SSLv3/TLS length field is in bytes 3/4
*/
if (packet.remaining() < 5) {
return -1;
}
int pos = packet.position();
byte byteZero = packet.get(pos);
int len = 0;
/*
* If we have already verified previous packets, we can
* ignore the verifications steps, and jump right to the
* determination. Otherwise, try one last hueristic to
* see if it's SSL/TLS.
*/
if (formatVerified ||
(byteZero == ct_handshake) || (byteZero == ct_alert)) {
/*
* Last sanity check that it's not a wild record
*/
ProtocolVersion recordVersion = ProtocolVersion.valueOf(
packet.get(pos + 1), packet.get(pos + 2));
// check the record version
checkRecordVersion(recordVersion, false);
/*
* Reasonably sure this is a V3, disable further checks.
* We can't do the same in the v2 check below, because
* read still needs to parse/handle the v2 clientHello.
*/
formatVerified = true;
/*
* One of the SSLv3/TLS message types.
*/
len = ((packet.get(pos + 3) & 0xFF) << 8) +
(packet.get(pos + 4) & 0xFF) + headerSize;
} else {
/*
* Must be SSLv2 or something unknown.
* Check if it's short (2 bytes) or
* long (3) header.
*
* Internals can warn about unsupported SSLv2
*/
boolean isShort = ((byteZero & 0x80) != 0);
if (isShort &&
((packet.get(pos + 2) == 1) || packet.get(pos + 2) == 4)) {
ProtocolVersion recordVersion = ProtocolVersion.valueOf(
packet.get(pos + 3), packet.get(pos + 4));
// check the record version
checkRecordVersion(recordVersion, true);
/*
* Client or Server Hello
*/
int mask = (isShort ? 0x7F : 0x3F);
len = ((byteZero & mask) << 8) +
(packet.get(pos + 1) & 0xFF) + (isShort ? 2 : 3);
} else {
// Gobblygook!
throw new SSLException(
"Unrecognized SSL message, plaintext connection?");
}
}
return len;
}
@Override
void checkRecordVersion(ProtocolVersion recordVersion,
boolean allowSSL20Hello) throws SSLException {
if (recordVersion.maybeDTLSProtocol()) {
throw new SSLException(
"Unrecognized record version " + recordVersion +
" , DTLS packet?");
}
// Check if the record version is too old.
if ((recordVersion.v < ProtocolVersion.MIN.v)) {
// if it's not SSLv2, we're out of here.
if (!allowSSL20Hello ||
(recordVersion.v != ProtocolVersion.SSL20Hello.v)) {
throw new SSLException(
"Unsupported record version " + recordVersion);
}
}
}
@Override
Plaintext decode(ByteBuffer packet)
throws IOException, BadPaddingException {
if (isClosed) {
return null;
}
if (debug != null && Debug.isOn("packet")) {
Debug.printHex(
"[Raw read]: length = " + packet.remaining(), packet);
}
// The caller should have validated the record.
if (!formatVerified) {
formatVerified = true;
/*
* The first record must either be a handshake record or an
* alert message. If it's not, it is either invalid or an
* SSLv2 message.
*/
int pos = packet.position();
byte byteZero = packet.get(pos);
if (byteZero != ct_handshake && byteZero != ct_alert) {
return handleUnknownRecord(packet);
}
}
return decodeInputRecord(packet);
}
private Plaintext decodeInputRecord(ByteBuffer packet)
throws IOException, BadPaddingException {
//
// The packet should be a complete record, or more.
//
int srcPos = packet.position();
int srcLim = packet.limit();
byte contentType = packet.get(); // pos: 0
byte majorVersion = packet.get(); // pos: 1
byte minorVersion = packet.get(); // pos: 2
int contentLen = ((packet.get() & 0xFF) << 8) +
(packet.get() & 0xFF); // pos: 3, 4
if (debug != null && Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
", READ: " +
ProtocolVersion.valueOf(majorVersion, minorVersion) +
" " + Record.contentName(contentType) + ", length = " +
contentLen);
}
//
// Check for upper bound.
//
// Note: May check packetSize limit in the future.
if (contentLen < 0 || contentLen > maxLargeRecordSize - headerSize) {
throw new SSLProtocolException(
"Bad input record size, TLSCiphertext.length = " + contentLen);
}
//
// check for handshake fragment
//
if ((contentType != ct_handshake) && (hsMsgOff != hsMsgLen)) {
throw new SSLProtocolException(
"Expected to get a handshake fragment");
}
//
// Decrypt the fragment
//
int recLim = srcPos + SSLRecord.headerSize + contentLen;
packet.limit(recLim);
packet.position(srcPos + SSLRecord.headerSize);
ByteBuffer plaintext;
try {
plaintext =
decrypt(readAuthenticator, readCipher, contentType, packet);
} finally {
// comsume a complete record
packet.limit(srcLim);
packet.position(recLim);
}
//
// handshake hashing
//
if (contentType == ct_handshake) {
int pltPos = plaintext.position();
int pltLim = plaintext.limit();
int frgPos = pltPos;
for (int remains = plaintext.remaining(); remains > 0;) {
int howmuch;
byte handshakeType;
if (hsMsgOff < hsMsgLen) {
// a fragment of the handshake message
howmuch = Math.min((hsMsgLen - hsMsgOff), remains);
handshakeType = prevType;
hsMsgOff += howmuch;
if (hsMsgOff == hsMsgLen) {
// Now is a complete handshake message.
hsMsgOff = 0;
hsMsgLen = 0;
}
} else { // hsMsgOff == hsMsgLen, a new handshake message
handshakeType = plaintext.get();
int handshakeLen = ((plaintext.get() & 0xFF) << 16) |
((plaintext.get() & 0xFF) << 8) |
(plaintext.get() & 0xFF);
plaintext.position(frgPos);
if (remains < (handshakeLen + 1)) { // 1: handshake type
// This handshake message is fragmented.
prevType = handshakeType;
hsMsgOff = remains - 4; // 4: handshake header
hsMsgLen = handshakeLen;
}
howmuch = Math.min(handshakeLen + 4, remains);
}
plaintext.limit(frgPos + howmuch);
if (handshakeType == HandshakeMessage.ht_hello_request) {
// omitted from handshake hash computation
} else if ((handshakeType != HandshakeMessage.ht_finished) &&
(handshakeType != HandshakeMessage.ht_certificate_verify)) {
if (handshakeHash == null) {
// used for cache only
handshakeHash = new HandshakeHash(false);
}
handshakeHash.update(plaintext);
} else {
// Reserve until this handshake message has been processed.
if (handshakeHash == null) {
// used for cache only
handshakeHash = new HandshakeHash(false);
}
handshakeHash.reserve(plaintext);
}
plaintext.position(frgPos + howmuch);
plaintext.limit(pltLim);
frgPos += howmuch;
remains -= howmuch;
}
plaintext.position(pltPos);
}
return new Plaintext(contentType,
majorVersion, minorVersion, -1, -1L, plaintext);
// recordEpoch, recordSeq, plaintext);
}
private Plaintext handleUnknownRecord(ByteBuffer packet)
throws IOException, BadPaddingException {
//
// The packet should be a complete record.
//
int srcPos = packet.position();
int srcLim = packet.limit();
byte firstByte = packet.get(srcPos);
byte thirdByte = packet.get(srcPos + 2);
// Does it look like a Version 2 client hello (V2ClientHello)?
if (((firstByte & 0x80) != 0) && (thirdByte == 1)) {
/*
* If SSLv2Hello is not enabled, throw an exception.
*/
if (helloVersion != ProtocolVersion.SSL20Hello) {
throw new SSLHandshakeException("SSLv2Hello is not enabled");
}
byte majorVersion = packet.get(srcPos + 3);
byte minorVersion = packet.get(srcPos + 4);
if ((majorVersion == ProtocolVersion.SSL20Hello.major) &&
(minorVersion == ProtocolVersion.SSL20Hello.minor)) {
/*
* Looks like a V2 client hello, but not one saying
* "let's talk SSLv3". So we need to send an SSLv2
* error message, one that's treated as fatal by
* clients (Otherwise we'll hang.)
*/
if (debug != null && Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
"Requested to negotiate unsupported SSLv2!");
}
// hack code, the exception is caught in SSLEngineImpl
// so that SSLv2 error message can be delivered properly.
throw new UnsupportedOperationException( // SSLv2Hello
"Unsupported SSL v2.0 ClientHello");
}
/*
* If we can map this into a V3 ClientHello, read and
* hash the rest of the V2 handshake, turn it into a
* V3 ClientHello message, and pass it up.
*/
packet.position(srcPos + 2); // exclude the header
if (handshakeHash == null) {
// used for cache only
handshakeHash = new HandshakeHash(false);
}
handshakeHash.update(packet);
packet.position(srcPos);
ByteBuffer converted = convertToClientHello(packet);
if (debug != null && Debug.isOn("packet")) {
Debug.printHex(
"[Converted] ClientHello", converted);
}
return new Plaintext(ct_handshake,
majorVersion, minorVersion, -1, -1L, converted);
} else {
if (((firstByte & 0x80) != 0) && (thirdByte == 4)) {
throw new SSLException("SSL V2.0 servers are not supported.");
}
throw new SSLException("Unsupported or unrecognized SSL message");
}
}
}

View file

@ -0,0 +1,570 @@
/*
* Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ssl;
import java.io.*;
import java.nio.*;
import java.util.*;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import sun.misc.HexDumpEncoder;
import static sun.security.ssl.Ciphertext.RecordType;
/**
* {@code OutputRecord} implementation for {@code SSLEngine}.
*/
final class SSLEngineOutputRecord extends OutputRecord implements SSLRecord {
private HandshakeFragment fragmenter = null;
private LinkedList<RecordMemo> alertMemos = new LinkedList<>();
private boolean isTalkingToV2 = false; // SSLv2Hello
private ByteBuffer v2ClientHello = null; // SSLv2Hello
private boolean isCloseWaiting = false;
SSLEngineOutputRecord() {
this.writeAuthenticator = MAC.TLS_NULL;
this.packetSize = SSLRecord.maxRecordSize;
this.protocolVersion = ProtocolVersion.DEFAULT_TLS;
}
@Override
synchronized public void close() throws IOException {
if (!isClosed) {
if (alertMemos != null && !alertMemos.isEmpty()) {
isCloseWaiting = true;
} else {
super.close();
}
}
}
@Override
void encodeAlert(byte level, byte description) throws IOException {
RecordMemo memo = new RecordMemo();
memo.contentType = Record.ct_alert;
memo.majorVersion = protocolVersion.major;
memo.minorVersion = protocolVersion.minor;
memo.encodeCipher = writeCipher;
memo.encodeAuthenticator = writeAuthenticator;
memo.fragment = new byte[2];
memo.fragment[0] = level;
memo.fragment[1] = description;
alertMemos.add(memo);
}
@Override
void encodeHandshake(byte[] source,
int offset, int length) throws IOException {
if (fragmenter == null) {
fragmenter = new HandshakeFragment();
}
if (firstMessage) {
firstMessage = false;
if ((helloVersion == ProtocolVersion.SSL20Hello) &&
(source[offset] == HandshakeMessage.ht_client_hello) &&
// 5: recode header size
(source[offset + 4 + 2 + 32] == 0)) {
// V3 session ID is empty
// 4: handshake header size
// 2: client_version in ClientHello
// 32: random in ClientHello
// Double space should be big enough for the converted message.
v2ClientHello = encodeV2ClientHello(
source, (offset + 4), (length - 4));
v2ClientHello.position(2); // exclude the header
handshakeHash.update(v2ClientHello);
v2ClientHello.position(0);
return;
}
}
byte handshakeType = source[offset];
if (handshakeType != HandshakeMessage.ht_hello_request) {
handshakeHash.update(source, offset, length);
}
fragmenter.queueUpFragment(source, offset, length);
}
@Override
void encodeChangeCipherSpec() throws IOException {
if (fragmenter == null) {
fragmenter = new HandshakeFragment();
}
fragmenter.queueUpChangeCipherSpec();
}
@Override
void encodeV2NoCipher() throws IOException {
isTalkingToV2 = true;
}
@Override
Ciphertext encode(ByteBuffer[] sources, int offset, int length,
ByteBuffer destination) throws IOException {
if (writeAuthenticator.seqNumOverflow()) {
if (debug != null && Debug.isOn("ssl")) {
System.out.println(Thread.currentThread().getName() +
", sequence number extremely close to overflow " +
"(2^64-1 packets). Closing connection.");
}
throw new SSLHandshakeException("sequence number overflow");
}
int macLen = 0;
if (writeAuthenticator instanceof MAC) {
macLen = ((MAC)writeAuthenticator).MAClen();
}
int dstLim = destination.limit();
boolean isFirstRecordOfThePayload = true;
int packetLeftSize = Math.min(maxRecordSize, packetSize);
boolean needMorePayload = true;
long recordSN = 0L;
while (needMorePayload) {
int fragLen;
if (isFirstRecordOfThePayload && needToSplitPayload()) {
needMorePayload = true;
fragLen = 1;
isFirstRecordOfThePayload = false;
} else {
needMorePayload = false;
if (packetLeftSize > 0) {
fragLen = writeCipher.calculateFragmentSize(
packetLeftSize, macLen, headerSize);
fragLen = Math.min(fragLen, Record.maxDataSize);
} else {
fragLen = Record.maxDataSize;
}
if (fragmentSize > 0) {
fragLen = Math.min(fragLen, fragmentSize);
}
}
int dstPos = destination.position();
int dstContent = dstPos + headerSize +
writeCipher.getExplicitNonceSize();
destination.position(dstContent);
int remains = Math.min(fragLen, destination.remaining());
fragLen = 0;
int srcsLen = offset + length;
for (int i = offset; (i < srcsLen) && (remains > 0); i++) {
int amount = Math.min(sources[i].remaining(), remains);
int srcLimit = sources[i].limit();
sources[i].limit(sources[i].position() + amount);
destination.put(sources[i]);
sources[i].limit(srcLimit); // restore the limit
remains -= amount;
fragLen += amount;
if (remains > 0) {
offset++;
length--;
}
}
destination.limit(destination.position());
destination.position(dstContent);
if ((debug != null) && Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
", WRITE: " + protocolVersion + " " +
Record.contentName(Record.ct_application_data) +
", length = " + destination.remaining());
}
// Encrypt the fragment and wrap up a record.
recordSN = encrypt(writeAuthenticator, writeCipher,
Record.ct_application_data, destination,
dstPos, dstLim, headerSize,
protocolVersion, false);
if ((debug != null) && Debug.isOn("packet")) {
ByteBuffer temporary = destination.duplicate();
temporary.limit(temporary.position());
temporary.position(dstPos);
Debug.printHex(
"[Raw write]: length = " + temporary.remaining(),
temporary);
}
packetLeftSize -= destination.position() - dstPos;
// remain the limit unchanged
destination.limit(dstLim);
if (isFirstAppOutputRecord) {
isFirstAppOutputRecord = false;
}
}
return new Ciphertext(RecordType.RECORD_APPLICATION_DATA, recordSN);
}
@Override
Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException {
if (isTalkingToV2) { // SSLv2Hello
// We don't support SSLv2. Send an SSLv2 error message
// so that the connection can be closed gracefully.
//
// Please don't change the limit of the destination buffer.
destination.put(SSLRecord.v2NoCipher);
if (debug != null && Debug.isOn("packet")) {
Debug.printHex(
"[Raw write]: length = " + SSLRecord.v2NoCipher.length,
SSLRecord.v2NoCipher);
}
isTalkingToV2 = false;
return new Ciphertext(RecordType.RECORD_ALERT, -1L);
}
if (v2ClientHello != null) {
// deliver the SSLv2 format ClientHello message
//
// Please don't change the limit of the destination buffer.
if (debug != null) {
if (Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
", WRITE: SSLv2 ClientHello message" +
", length = " + v2ClientHello.remaining());
}
if (Debug.isOn("packet")) {
Debug.printHex(
"[Raw write]: length = " + v2ClientHello.remaining(),
v2ClientHello);
}
}
destination.put(v2ClientHello);
v2ClientHello = null;
return new Ciphertext(RecordType.RECORD_CLIENT_HELLO, -1L);
}
if (alertMemos != null && !alertMemos.isEmpty()) {
RecordMemo memo = alertMemos.pop();
int macLen = 0;
if (memo.encodeAuthenticator instanceof MAC) {
macLen = ((MAC)memo.encodeAuthenticator).MAClen();
}
int dstPos = destination.position();
int dstLim = destination.limit();
int dstContent = dstPos + headerSize +
writeCipher.getExplicitNonceSize();
destination.position(dstContent);
destination.put(memo.fragment);
destination.limit(destination.position());
destination.position(dstContent);
if ((debug != null) && Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
", WRITE: " + protocolVersion + " " +
Record.contentName(Record.ct_alert) +
", length = " + destination.remaining());
}
// Encrypt the fragment and wrap up a record.
long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher,
Record.ct_alert, destination, dstPos, dstLim, headerSize,
ProtocolVersion.valueOf(memo.majorVersion,
memo.minorVersion), false);
if ((debug != null) && Debug.isOn("packet")) {
ByteBuffer temporary = destination.duplicate();
temporary.limit(temporary.position());
temporary.position(dstPos);
Debug.printHex(
"[Raw write]: length = " + temporary.remaining(),
temporary);
}
// remain the limit unchanged
destination.limit(dstLim);
if (isCloseWaiting && (memo.contentType == Record.ct_alert)) {
isCloseWaiting = true;
close();
}
return new Ciphertext(RecordType.RECORD_ALERT, recordSN);
}
if (fragmenter != null) {
return fragmenter.acquireCiphertext(destination);
}
return null;
}
@Override
boolean isEmpty() {
return (!isTalkingToV2) && (v2ClientHello == null) &&
((fragmenter == null) || fragmenter.isEmpty()) &&
((alertMemos == null) || alertMemos.isEmpty());
}
// buffered record fragment
private static class RecordMemo {
byte contentType;
byte majorVersion;
byte minorVersion;
CipherBox encodeCipher;
Authenticator encodeAuthenticator;
byte[] fragment;
}
private static class HandshakeMemo extends RecordMemo {
byte handshakeType;
int acquireOffset;
}
final class HandshakeFragment {
private LinkedList<RecordMemo> handshakeMemos = new LinkedList<>();
void queueUpFragment(byte[] source,
int offset, int length) throws IOException {
HandshakeMemo memo = new HandshakeMemo();
memo.contentType = Record.ct_handshake;
memo.majorVersion = protocolVersion.major; // kick start version?
memo.minorVersion = protocolVersion.minor;
memo.encodeCipher = writeCipher;
memo.encodeAuthenticator = writeAuthenticator;
memo.handshakeType = source[offset];
memo.acquireOffset = 0;
memo.fragment = new byte[length - 4]; // 4: header size
// 1: HandshakeType
// 3: message length
System.arraycopy(source, offset + 4, memo.fragment, 0, length - 4);
handshakeMemos.add(memo);
}
void queueUpChangeCipherSpec() {
RecordMemo memo = new RecordMemo();
memo.contentType = Record.ct_change_cipher_spec;
memo.majorVersion = protocolVersion.major;
memo.minorVersion = protocolVersion.minor;
memo.encodeCipher = writeCipher;
memo.encodeAuthenticator = writeAuthenticator;
memo.fragment = new byte[1];
memo.fragment[0] = 1;
handshakeMemos.add(memo);
}
Ciphertext acquireCiphertext(ByteBuffer dstBuf) throws IOException {
if (isEmpty()) {
return null;
}
RecordMemo memo = handshakeMemos.getFirst();
HandshakeMemo hsMemo = null;
if (memo.contentType == Record.ct_handshake) {
hsMemo = (HandshakeMemo)memo;
}
int macLen = 0;
if (memo.encodeAuthenticator instanceof MAC) {
macLen = ((MAC)memo.encodeAuthenticator).MAClen();
}
// ChangeCipherSpec message is pretty small. Don't worry about
// the fragmentation of ChangeCipherSpec record.
int fragLen;
if (packetSize > 0) {
fragLen = Math.min(maxRecordSize, packetSize);
fragLen = memo.encodeCipher.calculateFragmentSize(
fragLen, macLen, headerSize);
} else {
fragLen = Record.maxDataSize;
}
if (fragmentSize > 0) {
fragLen = Math.min(fragLen, fragmentSize);
}
int dstPos = dstBuf.position();
int dstLim = dstBuf.limit();
int dstContent = dstPos + headerSize +
memo.encodeCipher.getExplicitNonceSize();
dstBuf.position(dstContent);
if (hsMemo != null) {
int remainingFragLen = fragLen;
while ((remainingFragLen > 0) && !handshakeMemos.isEmpty()) {
int memoFragLen = hsMemo.fragment.length;
if (hsMemo.acquireOffset == 0) {
// Don't fragment handshake message header
if (remainingFragLen <= 4) {
break;
}
dstBuf.put(hsMemo.handshakeType);
dstBuf.put((byte)((memoFragLen >> 16) & 0xFF));
dstBuf.put((byte)((memoFragLen >> 8) & 0xFF));
dstBuf.put((byte)(memoFragLen & 0xFF));
remainingFragLen -= 4;
} // Otherwise, handshake message is fragmented.
int chipLen = Math.min(remainingFragLen,
(memoFragLen - hsMemo.acquireOffset));
dstBuf.put(hsMemo.fragment, hsMemo.acquireOffset, chipLen);
hsMemo.acquireOffset += chipLen;
if (hsMemo.acquireOffset == memoFragLen) {
handshakeMemos.removeFirst();
// still have space for more records?
if ((remainingFragLen > chipLen) &&
!handshakeMemos.isEmpty()) {
// look for the next buffered record fragment
RecordMemo reMemo = handshakeMemos.getFirst();
if (reMemo.contentType == Record.ct_handshake) {
hsMemo = (HandshakeMemo)reMemo;
} else {
// not handshake message, break the loop
break;
}
}
}
remainingFragLen -= chipLen;
}
fragLen -= remainingFragLen;
} else {
fragLen = Math.min(fragLen, memo.fragment.length);
dstBuf.put(memo.fragment, 0, fragLen);
handshakeMemos.removeFirst();
}
dstBuf.limit(dstBuf.position());
dstBuf.position(dstContent);
if ((debug != null) && Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
", WRITE: " + protocolVersion + " " +
Record.contentName(memo.contentType) +
", length = " + dstBuf.remaining());
}
// Encrypt the fragment and wrap up a record.
long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher,
memo.contentType, dstBuf,
dstPos, dstLim, headerSize,
ProtocolVersion.valueOf(memo.majorVersion,
memo.minorVersion), false);
if ((debug != null) && Debug.isOn("packet")) {
ByteBuffer temporary = dstBuf.duplicate();
temporary.limit(temporary.position());
temporary.position(dstPos);
Debug.printHex(
"[Raw write]: length = " + temporary.remaining(),
temporary);
}
// remain the limit unchanged
dstBuf.limit(dstLim);
// Reset the fragmentation offset.
if (hsMemo != null) {
return new Ciphertext(RecordType.valueOf(
hsMemo.contentType, hsMemo.handshakeType), recordSN);
} else {
return new Ciphertext(
RecordType.RECORD_CHANGE_CIPHER_SPEC, recordSN);
}
}
boolean isEmpty() {
return handshakeMemos.isEmpty();
}
}
/*
* Need to split the payload except the following cases:
*
* 1. protocol version is TLS 1.1 or later;
* 2. bulk cipher does not use CBC mode, including null bulk cipher suites.
* 3. the payload is the first application record of a freshly
* negotiated TLS session.
* 4. the CBC protection is disabled;
*
* By default, we counter chosen plaintext issues on CBC mode
* ciphersuites in SSLv3/TLS1.0 by sending one byte of application
* data in the first record of every payload, and the rest in
* subsequent record(s). Note that the issues have been solved in
* TLS 1.1 or later.
*
* It is not necessary to split the very first application record of
* a freshly negotiated TLS session, as there is no previous
* application data to guess. To improve compatibility, we will not
* split such records.
*
* This avoids issues in the outbound direction. For a full fix,
* the peer must have similar protections.
*/
boolean needToSplitPayload() {
return (!protocolVersion.useTLS11PlusSpec()) &&
writeCipher.isCBCMode() && !isFirstAppOutputRecord &&
Record.enableCBCProtection;
}
}

View file

@ -0,0 +1,117 @@
/*
* Copyright (c) 1996, 2015, 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;
/**
* SSL/TLS record
*
* @author David Brownell
*/
interface SSLRecord extends Record {
static final int headerSize = 5; // SSLv3 record header
/*
* The size of the header plus the max IV length
*/
static final int headerPlusMaxIVSize =
headerSize // header
+ maxIVLength; // iv
/*
* The maximum size that may be increased when translating plaintext to
* ciphertext fragment.
*/
static final int maxPlaintextPlusSize =
headerSize // header
+ maxIVLength // iv
+ maxMacSize // MAC or AEAD tag
+ maxPadding; // block cipher padding
/*
* SSL has a maximum record size. It's header, (compressed) data,
* padding, and a trailer for the message authentication information (MAC
* for block and stream ciphers, and message authentication tag for AEAD
* ciphers).
*
* Some compression algorithms have rare cases where they expand the data.
* As we don't support compression at this time, leave that out.
*/
static final int maxRecordSize =
headerPlusMaxIVSize // header + iv
+ maxDataSize // data
+ maxPadding // padding
+ maxMacSize; // MAC or AEAD tag
/*
* For CBC protection in SSL3/TLS1, we break some plaintext into two
* packets. Max application data size for the second packet.
*/
static final int maxDataSizeMinusOneByteRecord =
maxDataSize // max data size
- ( // max one byte record size
headerPlusMaxIVSize // header + iv
+ 1 // one byte data
+ maxPadding // padding
+ maxMacSize // MAC
);
/*
* The maximum large record size.
*
* Some SSL/TLS implementations support large fragment upto 2^15 bytes,
* such as Microsoft. We support large incoming fragments.
*
* The maximum large record size is defined as maxRecordSize plus 2^14,
* this is the amount OpenSSL is using.
*/
static final int maxLargeRecordSize =
maxRecordSize // Max size with a conforming implementation
+ maxDataSize; // extra 2^14 bytes for large data packets.
/*
* Maximum record size for alert and change cipher spec records.
* They only contain 2 and 1 bytes of data, respectively.
* Allocate a smaller array.
*/
static final int maxAlertRecordSize =
headerPlusMaxIVSize // header + iv
+ 2 // alert
+ maxPadding // padding
+ maxMacSize; // MAC
/*
* We may need to send this SSL v2 "No Cipher" message back, if we
* are faced with an SSLv2 "hello" that's not saying "I talk v3".
* It's the only one documented in the V2 spec as a fatal error.
*/
static final byte[] v2NoCipher = {
(byte)0x80, (byte)0x03, // unpadded 3 byte record
(byte)0x00, // ... error message
(byte)0x00, (byte)0x01 // ... NO_CIPHER error
};
}

View file

@ -68,7 +68,7 @@ class SSLServerSocketImpl extends SSLServerSocket
private SSLContextImpl sslContext;
/* Do newly accepted connections require clients to authenticate? */
private byte doClientAuth = SSLEngineImpl.clauth_none;
private ClientAuthType clientAuthType = ClientAuthType.CLIENT_AUTH_NONE;
/* Do new connections created here use the "server" mode of SSL? */
private boolean useServerMode = true;
@ -230,13 +230,13 @@ class SSLServerSocketImpl extends SSLServerSocket
*/
@Override
public void setNeedClientAuth(boolean flag) {
doClientAuth = (flag ?
SSLEngineImpl.clauth_required : SSLEngineImpl.clauth_none);
clientAuthType = (flag ? ClientAuthType.CLIENT_AUTH_REQUIRED :
ClientAuthType.CLIENT_AUTH_NONE);
}
@Override
public boolean getNeedClientAuth() {
return (doClientAuth == SSLEngineImpl.clauth_required);
return (clientAuthType == ClientAuthType.CLIENT_AUTH_REQUIRED);
}
/**
@ -245,13 +245,13 @@ class SSLServerSocketImpl extends SSLServerSocket
*/
@Override
public void setWantClientAuth(boolean flag) {
doClientAuth = (flag ?
SSLEngineImpl.clauth_requested : SSLEngineImpl.clauth_none);
clientAuthType = (flag ? ClientAuthType.CLIENT_AUTH_REQUESTED :
ClientAuthType.CLIENT_AUTH_NONE);
}
@Override
public boolean getWantClientAuth() {
return (doClientAuth == SSLEngineImpl.clauth_requested);
return (clientAuthType == ClientAuthType.CLIENT_AUTH_REQUESTED);
}
/**
@ -341,7 +341,7 @@ class SSLServerSocketImpl extends SSLServerSocket
@Override
public Socket accept() throws IOException {
SSLSocketImpl s = new SSLSocketImpl(sslContext, useServerMode,
enabledCipherSuites, doClientAuth, enableSessionCreation,
enabledCipherSuites, clientAuthType, enableSessionCreation,
enabledProtocols, identificationProtocol, algorithmConstraints,
sniMatchers, preferLocalCipherSuites);

View file

@ -109,6 +109,8 @@ final class SSLSessionImpl extends ExtendedSSLSession {
private String[] peerSupportedSignAlgs;
private List<SNIServerName> requestedServerNames;
private int negotiatedMaxFragLen;
private int maximumPacketSize;
// Principals for non-certificate based cipher suites
private Principal peerPrincipal;
@ -177,6 +179,7 @@ final class SSLSessionImpl extends ExtendedSSLSession {
sessionCount = ++counter;
localSupportedSignAlgs =
SignatureAndHashAlgorithm.getAlgorithmNames(algorithms);
negotiatedMaxFragLen = -1;
if (debug != null && Debug.isOn("session")) {
System.out.println("%% Initialized: " + this);
@ -785,8 +788,30 @@ final class SSLSessionImpl extends ExtendedSSLSession {
*/
@Override
public synchronized int getPacketBufferSize() {
return acceptLargeFragments ?
Record.maxLargeRecordSize : Record.maxRecordSize;
// Use the bigger packet size calculated from maximumPacketSize
// and negotiatedMaxFragLen.
int packetSize = 0;
if (negotiatedMaxFragLen > 0) {
packetSize = cipherSuite.calculatePacketSize(
negotiatedMaxFragLen, protocolVersion,
protocolVersion.isDTLSProtocol());
}
if (maximumPacketSize > 0) {
return (maximumPacketSize > packetSize) ?
maximumPacketSize : packetSize;
}
if (packetSize != 0) {
return packetSize;
}
if (protocolVersion.isDTLSProtocol()) {
return DTLSRecord.maxRecordSize;
} else {
return acceptLargeFragments ?
SSLRecord.maxLargeRecordSize : SSLRecord.maxRecordSize;
}
}
/**
@ -795,7 +820,64 @@ final class SSLSessionImpl extends ExtendedSSLSession {
*/
@Override
public synchronized int getApplicationBufferSize() {
return getPacketBufferSize() - Record.headerSize;
// Use the bigger fragment size calculated from maximumPacketSize
// and negotiatedMaxFragLen.
int fragmentSize = 0;
if (maximumPacketSize > 0) {
fragmentSize = cipherSuite.calculateFragSize(
maximumPacketSize, protocolVersion,
protocolVersion.isDTLSProtocol());
}
if (negotiatedMaxFragLen > 0) {
return (negotiatedMaxFragLen > fragmentSize) ?
negotiatedMaxFragLen : fragmentSize;
}
if (fragmentSize != 0) {
return fragmentSize;
}
if (protocolVersion.isDTLSProtocol()) {
return Record.maxDataSize;
} else {
int maxPacketSize = acceptLargeFragments ?
SSLRecord.maxLargeRecordSize : SSLRecord.maxRecordSize;
return (maxPacketSize - SSLRecord.headerSize);
}
}
/**
* Sets the negotiated maximum fragment length, as specified by the
* max_fragment_length ClientHello extension in RFC 6066.
*
* @param negotiatedMaxFragLen
* the negotiated maximum fragment length, or {@code -1} if
* no such length has been negotiated.
*/
synchronized void setNegotiatedMaxFragSize(
int negotiatedMaxFragLen) {
this.negotiatedMaxFragLen = negotiatedMaxFragLen;
}
/**
* Get the negotiated maximum fragment length, as specified by the
* max_fragment_length ClientHello extension in RFC 6066.
*
* @return the negotiated maximum fragment length, or {@code -1} if
* no such length has been negotiated.
*/
synchronized int getNegotiatedMaxFragSize() {
return negotiatedMaxFragLen;
}
synchronized void setMaximumPacketSize(int maximumPacketSize) {
this.maximumPacketSize = maximumPacketSize;
}
synchronized int getMaximumPacketSize() {
return maximumPacketSize;
}
/**

View file

@ -0,0 +1,441 @@
/*
* Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ssl;
import java.io.*;
import java.nio.*;
import javax.crypto.BadPaddingException;
import javax.net.ssl.*;
import sun.misc.HexDumpEncoder;
/**
* {@code InputRecord} implementation for {@code SSLSocket}.
*
* @author David Brownell
*/
final class SSLSocketInputRecord extends InputRecord implements SSLRecord {
private OutputStream deliverStream = null;
private byte[] temporary = new byte[1024];
// used by handshake hash computation for handshake fragment
private byte prevType = -1;
private int hsMsgOff = 0;
private int hsMsgLen = 0;
private boolean formatVerified = false; // SSLv2 ruled out?
private boolean hasHeader = false; // Had read the record header
SSLSocketInputRecord() {
this.readAuthenticator = MAC.TLS_NULL;
}
@Override
int bytesInCompletePacket(InputStream is) throws IOException {
if (!hasHeader) {
// read exactly one record
int really = read(is, temporary, 0, headerSize);
if (really < 0) {
throw new EOFException("SSL peer shut down incorrectly");
}
hasHeader = true;
}
byte byteZero = temporary[0];
int len = 0;
/*
* If we have already verified previous packets, we can
* ignore the verifications steps, and jump right to the
* determination. Otherwise, try one last hueristic to
* see if it's SSL/TLS.
*/
if (formatVerified ||
(byteZero == ct_handshake) || (byteZero == ct_alert)) {
/*
* Last sanity check that it's not a wild record
*/
ProtocolVersion recordVersion =
ProtocolVersion.valueOf(temporary[1], temporary[2]);
// check the record version
checkRecordVersion(recordVersion, false);
/*
* Reasonably sure this is a V3, disable further checks.
* We can't do the same in the v2 check below, because
* read still needs to parse/handle the v2 clientHello.
*/
formatVerified = true;
/*
* One of the SSLv3/TLS message types.
*/
len = ((temporary[3] & 0xFF) << 8) +
(temporary[4] & 0xFF) + headerSize;
} else {
/*
* Must be SSLv2 or something unknown.
* Check if it's short (2 bytes) or
* long (3) header.
*
* Internals can warn about unsupported SSLv2
*/
boolean isShort = ((byteZero & 0x80) != 0);
if (isShort && ((temporary[2] == 1) || (temporary[2] == 4))) {
ProtocolVersion recordVersion =
ProtocolVersion.valueOf(temporary[3], temporary[4]);
// check the record version
checkRecordVersion(recordVersion, true);
/*
* Client or Server Hello
*/
//
// Short header is using here. We reverse the code here
// in case it it used in the future.
//
// int mask = (isShort ? 0x7F : 0x3F);
// len = ((byteZero & mask) << 8) +
// (temporary[1] & 0xFF) + (isShort ? 2 : 3);
//
len = ((byteZero & 0x7F) << 8) + (temporary[1] & 0xFF) + 2;
} else {
// Gobblygook!
throw new SSLException(
"Unrecognized SSL message, plaintext connection?");
}
}
return len;
}
// destination.position() is zero.
@Override
Plaintext decode(InputStream is, ByteBuffer destination)
throws IOException, BadPaddingException {
if (isClosed) {
return null;
}
if (!hasHeader) {
// read exactly one record
int really = read(is, temporary, 0, headerSize);
if (really < 0) {
throw new EOFException("SSL peer shut down incorrectly");
}
hasHeader = true;
}
Plaintext plaintext = null;
if (!formatVerified) {
formatVerified = true;
/*
* The first record must either be a handshake record or an
* alert message. If it's not, it is either invalid or an
* SSLv2 message.
*/
if ((temporary[0] != ct_handshake) &&
(temporary[0] != ct_alert)) {
plaintext = handleUnknownRecord(is, temporary, destination);
}
}
if (plaintext == null) {
plaintext = decodeInputRecord(is, temporary, destination);
}
// The record header should has comsumed.
hasHeader = false;
return plaintext;
}
@Override
void setDeliverStream(OutputStream outputStream) {
this.deliverStream = outputStream;
}
// Note that destination may be null
private Plaintext decodeInputRecord(InputStream is, byte[] header,
ByteBuffer destination) throws IOException, BadPaddingException {
byte contentType = header[0];
byte majorVersion = header[1];
byte minorVersion = header[2];
int contentLen = ((header[3] & 0xFF) << 8) + (header[4] & 0xFF);
//
// Check for upper bound.
//
// Note: May check packetSize limit in the future.
if (contentLen < 0 || contentLen > maxLargeRecordSize - headerSize) {
throw new SSLProtocolException(
"Bad input record size, TLSCiphertext.length = " + contentLen);
}
//
// Read a complete record.
//
if (destination == null) {
destination = ByteBuffer.allocate(headerSize + contentLen);
} // Otherwise, the destination buffer should have enough room.
int dstPos = destination.position();
destination.put(temporary, 0, headerSize);
while (contentLen > 0) {
int howmuch = Math.min(temporary.length, contentLen);
int really = read(is, temporary, 0, howmuch);
if (really < 0) {
throw new EOFException("SSL peer shut down incorrectly");
}
destination.put(temporary, 0, howmuch);
contentLen -= howmuch;
}
destination.flip();
destination.position(dstPos + headerSize);
if (debug != null && Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
", READ: " +
ProtocolVersion.valueOf(majorVersion, minorVersion) +
" " + Record.contentName(contentType) + ", length = " +
destination.remaining());
}
//
// Decrypt the fragment
//
ByteBuffer plaintext =
decrypt(readAuthenticator, readCipher, contentType, destination);
if ((contentType != ct_handshake) && (hsMsgOff != hsMsgLen)) {
throw new SSLProtocolException(
"Expected to get a handshake fragment");
}
//
// handshake hashing
//
if (contentType == ct_handshake) {
int pltPos = plaintext.position();
int pltLim = plaintext.limit();
int frgPos = pltPos;
for (int remains = plaintext.remaining(); remains > 0;) {
int howmuch;
byte handshakeType;
if (hsMsgOff < hsMsgLen) {
// a fragment of the handshake message
howmuch = Math.min((hsMsgLen - hsMsgOff), remains);
handshakeType = prevType;
hsMsgOff += howmuch;
if (hsMsgOff == hsMsgLen) {
// Now is a complete handshake message.
hsMsgOff = 0;
hsMsgLen = 0;
}
} else { // hsMsgOff == hsMsgLen, a new handshake message
handshakeType = plaintext.get();
int handshakeLen = ((plaintext.get() & 0xFF) << 16) |
((plaintext.get() & 0xFF) << 8) |
(plaintext.get() & 0xFF);
plaintext.position(frgPos);
if (remains < (handshakeLen + 1)) { // 1: handshake type
// This handshake message is fragmented.
prevType = handshakeType;
hsMsgOff = remains - 4; // 4: handshake header
hsMsgLen = handshakeLen;
}
howmuch = Math.min(handshakeLen + 4, remains);
}
plaintext.limit(frgPos + howmuch);
if (handshakeType == HandshakeMessage.ht_hello_request) {
// omitted from handshake hash computation
} else if ((handshakeType != HandshakeMessage.ht_finished) &&
(handshakeType != HandshakeMessage.ht_certificate_verify)) {
if (handshakeHash == null) {
// used for cache only
handshakeHash = new HandshakeHash(false);
}
handshakeHash.update(plaintext);
} else {
// Reserve until this handshake message has been processed.
if (handshakeHash == null) {
// used for cache only
handshakeHash = new HandshakeHash(false);
}
handshakeHash.reserve(plaintext);
}
plaintext.position(frgPos + howmuch);
plaintext.limit(pltLim);
frgPos += howmuch;
remains -= howmuch;
}
plaintext.position(pltPos);
}
return new Plaintext(contentType,
majorVersion, minorVersion, -1, -1L, plaintext);
// recordEpoch, recordSeq, plaintext);
}
private Plaintext handleUnknownRecord(InputStream is, byte[] header,
ByteBuffer destination) throws IOException, BadPaddingException {
byte firstByte = header[0];
byte thirdByte = header[2];
// Does it look like a Version 2 client hello (V2ClientHello)?
if (((firstByte & 0x80) != 0) && (thirdByte == 1)) {
/*
* If SSLv2Hello is not enabled, throw an exception.
*/
if (helloVersion != ProtocolVersion.SSL20Hello) {
throw new SSLHandshakeException("SSLv2Hello is not enabled");
}
byte majorVersion = header[3];
byte minorVersion = header[4];
if ((majorVersion == ProtocolVersion.SSL20Hello.major) &&
(minorVersion == ProtocolVersion.SSL20Hello.minor)) {
/*
* Looks like a V2 client hello, but not one saying
* "let's talk SSLv3". So we need to send an SSLv2
* error message, one that's treated as fatal by
* clients (Otherwise we'll hang.)
*/
deliverStream.write(SSLRecord.v2NoCipher); // SSLv2Hello
if (debug != null) {
if (Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
"Requested to negotiate unsupported SSLv2!");
}
if (Debug.isOn("packet")) {
Debug.printHex(
"[Raw write]: length = " +
SSLRecord.v2NoCipher.length,
SSLRecord.v2NoCipher);
}
}
throw new SSLException("Unsupported SSL v2.0 ClientHello");
}
int msgLen = ((header[0] & 0x7F) << 8) | (header[1] & 0xFF);
if (destination == null) {
destination = ByteBuffer.allocate(headerSize + msgLen);
}
destination.put(temporary, 0, headerSize);
msgLen -= 3; // had read 3 bytes of content as header
while (msgLen > 0) {
int howmuch = Math.min(temporary.length, msgLen);
int really = read(is, temporary, 0, howmuch);
if (really < 0) {
throw new EOFException("SSL peer shut down incorrectly");
}
destination.put(temporary, 0, howmuch);
msgLen -= howmuch;
}
destination.flip();
/*
* If we can map this into a V3 ClientHello, read and
* hash the rest of the V2 handshake, turn it into a
* V3 ClientHello message, and pass it up.
*/
destination.position(2); // exclude the header
if (handshakeHash == null) {
// used for cache only
handshakeHash = new HandshakeHash(false);
}
handshakeHash.update(destination);
destination.position(0);
ByteBuffer converted = convertToClientHello(destination);
if (debug != null && Debug.isOn("packet")) {
Debug.printHex(
"[Converted] ClientHello", converted);
}
return new Plaintext(ct_handshake,
majorVersion, minorVersion, -1, -1L, converted);
} else {
if (((firstByte & 0x80) != 0) && (thirdByte == 4)) {
throw new SSLException("SSL V2.0 servers are not supported.");
}
throw new SSLException("Unsupported or unrecognized SSL message");
}
}
// Read the exact bytes of data, otherwise, return -1.
private static int read(InputStream is,
byte[] buffer, int offset, int len) throws IOException {
int n = 0;
while (n < len) {
int readLen = is.read(buffer, offset + n, len - n);
if (readLen < 0) {
return -1;
}
if (debug != null && Debug.isOn("packet")) {
Debug.printHex(
"[Raw read]: length = " + readLen,
buffer, offset + n, readLen);
}
n += readLen;
}
return n;
}
}

View file

@ -0,0 +1,383 @@
/*
* Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ssl;
import java.io.*;
import java.nio.*;
import java.util.Arrays;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import sun.misc.HexDumpEncoder;
/**
* {@code OutputRecord} implementation for {@code SSLSocket}.
*/
final class SSLSocketOutputRecord extends OutputRecord implements SSLRecord {
private OutputStream deliverStream = null;
SSLSocketOutputRecord() {
this.writeAuthenticator = MAC.TLS_NULL;
this.packetSize = SSLRecord.maxRecordSize;
this.protocolVersion = ProtocolVersion.DEFAULT_TLS;
}
@Override
void encodeAlert(byte level, byte description) throws IOException {
// use the buf of ByteArrayOutputStream
int position = headerSize + writeCipher.getExplicitNonceSize();
count = position;
write(level);
write(description);
if (debug != null && Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
", WRITE: " + protocolVersion +
" " + Record.contentName(Record.ct_alert) +
", length = " + (count - headerSize));
}
// Encrypt the fragment and wrap up a record.
encrypt(writeAuthenticator, writeCipher,
Record.ct_alert, headerSize);
// deliver this message
deliverStream.write(buf, 0, count); // may throw IOException
deliverStream.flush(); // may throw IOException
if (debug != null && Debug.isOn("packet")) {
Debug.printHex(
"[Raw write]: length = " + count, buf, 0, count);
}
// reset the internal buffer
count = 0;
}
@Override
void encodeHandshake(byte[] source,
int offset, int length) throws IOException {
if (firstMessage) {
firstMessage = false;
if ((helloVersion == ProtocolVersion.SSL20Hello) &&
(source[offset] == HandshakeMessage.ht_client_hello) &&
// 5: recode header size
(source[offset + 4 + 2 + 32] == 0)) {
// V3 session ID is empty
// 4: handshake header size
// 2: client_version in ClientHello
// 32: random in ClientHello
ByteBuffer v2ClientHello = encodeV2ClientHello(
source, (offset + 4), (length - 4));
byte[] record = v2ClientHello.array(); // array offset is zero
int limit = v2ClientHello.limit();
handshakeHash.update(record, 2, (limit - 2));
if (debug != null && Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
", WRITE: SSLv2 ClientHello message" +
", length = " + limit);
}
// deliver this message
//
// Version 2 ClientHello message should be plaintext.
//
// No max fragment length negotiation.
deliverStream.write(record, 0, limit);
deliverStream.flush();
if (debug != null && Debug.isOn("packet")) {
Debug.printHex(
"[Raw write]: length = " + count, record, 0, limit);
}
return;
}
}
byte handshakeType = source[0];
if (handshakeType != HandshakeMessage.ht_hello_request) {
handshakeHash.update(source, offset, length);
}
int fragLimit = getFragLimit();
int position = headerSize + writeCipher.getExplicitNonceSize();
if (count == 0) {
count = position;
}
if ((count - position) < (fragLimit - length)) {
write(source, offset, length);
return;
}
for (int limit = (offset + length); offset < limit;) {
int remains = (limit - offset) + (count - position);
int fragLen = Math.min(fragLimit, remains);
// use the buf of ByteArrayOutputStream
write(source, offset, fragLen);
if (remains < fragLimit) {
return;
}
if (debug != null && Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
", WRITE: " + protocolVersion +
" " + Record.contentName(Record.ct_handshake) +
", length = " + (count - headerSize));
}
// Encrypt the fragment and wrap up a record.
encrypt(writeAuthenticator, writeCipher,
Record.ct_handshake, headerSize);
// deliver this message
deliverStream.write(buf, 0, count); // may throw IOException
deliverStream.flush(); // may throw IOException
if (debug != null && Debug.isOn("packet")) {
Debug.printHex(
"[Raw write]: length = " + count, buf, 0, count);
}
// reset the offset
offset += fragLen;
// reset the internal buffer
count = position;
}
}
@Override
void encodeChangeCipherSpec() throws IOException {
// use the buf of ByteArrayOutputStream
int position = headerSize + writeCipher.getExplicitNonceSize();
count = position;
write((byte)1); // byte 1: change_cipher_spec(
if (debug != null && Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
", WRITE: " + protocolVersion +
" " + Record.contentName(Record.ct_change_cipher_spec) +
", length = " + (count - headerSize));
}
// Encrypt the fragment and wrap up a record.
encrypt(writeAuthenticator, writeCipher,
Record.ct_change_cipher_spec, headerSize);
// deliver this message
deliverStream.write(buf, 0, count); // may throw IOException
// deliverStream.flush(); // flush in Finished
if (debug != null && Debug.isOn("packet")) {
Debug.printHex(
"[Raw write]: length = " + count, buf, 0, count);
}
// reset the internal buffer
count = 0;
}
@Override
public void flush() throws IOException {
int position = headerSize + writeCipher.getExplicitNonceSize();
if (count <= position) {
return;
}
if (debug != null && Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
", WRITE: " + protocolVersion +
" " + Record.contentName(Record.ct_handshake) +
", length = " + (count - headerSize));
}
// Encrypt the fragment and wrap up a record.
encrypt(writeAuthenticator, writeCipher,
Record.ct_handshake, headerSize);
// deliver this message
deliverStream.write(buf, 0, count); // may throw IOException
deliverStream.flush(); // may throw IOException
if (debug != null && Debug.isOn("packet")) {
Debug.printHex(
"[Raw write]: length = " + count, buf, 0, count);
}
// reset the internal buffer
count = 0; // DON'T use position
}
@Override
void deliver(byte[] source, int offset, int length) throws IOException {
if (writeAuthenticator.seqNumOverflow()) {
if (debug != null && Debug.isOn("ssl")) {
System.out.println(Thread.currentThread().getName() +
", sequence number extremely close to overflow " +
"(2^64-1 packets). Closing connection.");
}
throw new SSLHandshakeException("sequence number overflow");
}
boolean isFirstRecordOfThePayload = true;
for (int limit = (offset + length); offset < limit;) {
int macLen = 0;
if (writeAuthenticator instanceof MAC) {
macLen = ((MAC)writeAuthenticator).MAClen();
}
int fragLen;
if (packetSize > 0) {
fragLen = Math.min(maxRecordSize, packetSize);
fragLen = writeCipher.calculateFragmentSize(
fragLen, macLen, headerSize);
fragLen = Math.min(fragLen, Record.maxDataSize);
} else {
fragLen = Record.maxDataSize;
}
if (fragmentSize > 0) {
fragLen = Math.min(fragLen, fragmentSize);
}
if (isFirstRecordOfThePayload && needToSplitPayload()) {
fragLen = 1;
isFirstRecordOfThePayload = false;
} else {
fragLen = Math.min(fragLen, (limit - offset));
}
// use the buf of ByteArrayOutputStream
int position = headerSize + writeCipher.getExplicitNonceSize();
count = position;
write(source, offset, fragLen);
if (debug != null && Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
", WRITE: " + protocolVersion +
" " + Record.contentName(Record.ct_application_data) +
", length = " + (count - headerSize));
}
// Encrypt the fragment and wrap up a record.
encrypt(writeAuthenticator, writeCipher,
Record.ct_application_data, headerSize);
// deliver this message
deliverStream.write(buf, 0, count); // may throw IOException
deliverStream.flush(); // may throw IOException
if (debug != null && Debug.isOn("packet")) {
Debug.printHex(
"[Raw write]: length = " + count, buf, 0, count);
}
// reset the internal buffer
count = 0;
if (isFirstAppOutputRecord) {
isFirstAppOutputRecord = false;
}
offset += fragLen;
}
}
@Override
void setDeliverStream(OutputStream outputStream) {
this.deliverStream = outputStream;
}
/*
* Need to split the payload except the following cases:
*
* 1. protocol version is TLS 1.1 or later;
* 2. bulk cipher does not use CBC mode, including null bulk cipher suites.
* 3. the payload is the first application record of a freshly
* negotiated TLS session.
* 4. the CBC protection is disabled;
*
* By default, we counter chosen plaintext issues on CBC mode
* ciphersuites in SSLv3/TLS1.0 by sending one byte of application
* data in the first record of every payload, and the rest in
* subsequent record(s). Note that the issues have been solved in
* TLS 1.1 or later.
*
* It is not necessary to split the very first application record of
* a freshly negotiated TLS session, as there is no previous
* application data to guess. To improve compatibility, we will not
* split such records.
*
* This avoids issues in the outbound direction. For a full fix,
* the peer must have similar protections.
*/
boolean needToSplitPayload() {
return (!protocolVersion.useTLS11PlusSpec()) &&
writeCipher.isCBCMode() && !isFirstAppOutputRecord &&
Record.enableCBCProtection;
}
private int getFragLimit() {
int macLen = 0;
if (writeAuthenticator instanceof MAC) {
macLen = ((MAC)writeAuthenticator).MAClen();
}
int fragLimit;
if (packetSize > 0) {
fragLimit = Math.min(maxRecordSize, packetSize);
fragLimit = writeCipher.calculateFragmentSize(
fragLimit, macLen, headerSize);
fragLimit = Math.min(fragLimit, Record.maxDataSize);
} else {
fragLimit = Record.maxDataSize;
}
if (fragmentSize > 0) {
fragLimit = Math.min(fragLimit, fragmentSize);
}
return fragLimit;
}
}

View file

@ -58,7 +58,7 @@ import static sun.security.ssl.CipherSuite.KeyExchange.*;
final class ServerHandshaker extends Handshaker {
// is the server going to require the client to authenticate?
private byte doClientAuth;
private ClientAuthType doClientAuth;
// our authentication info
private X509Certificate[] certs;
@ -143,13 +143,13 @@ final class ServerHandshaker extends Handshaker {
* Constructor ... use the keys found in the auth context.
*/
ServerHandshaker(SSLSocketImpl socket, SSLContextImpl context,
ProtocolList enabledProtocols, byte clientAuth,
ProtocolList enabledProtocols, ClientAuthType clientAuth,
ProtocolVersion activeProtocolVersion, boolean isInitialHandshake,
boolean secureRenegotiation,
byte[] clientVerifyData, byte[] serverVerifyData) {
super(socket, context, enabledProtocols,
(clientAuth != SSLEngineImpl.clauth_none), false,
(clientAuth != ClientAuthType.CLIENT_AUTH_NONE), false,
activeProtocolVersion, isInitialHandshake, secureRenegotiation,
clientVerifyData, serverVerifyData);
doClientAuth = clientAuth;
@ -159,15 +159,16 @@ final class ServerHandshaker extends Handshaker {
* Constructor ... use the keys found in the auth context.
*/
ServerHandshaker(SSLEngineImpl engine, SSLContextImpl context,
ProtocolList enabledProtocols, byte clientAuth,
ProtocolList enabledProtocols, ClientAuthType clientAuth,
ProtocolVersion activeProtocolVersion,
boolean isInitialHandshake, boolean secureRenegotiation,
byte[] clientVerifyData, byte[] serverVerifyData) {
byte[] clientVerifyData, byte[] serverVerifyData,
boolean isDTLS) {
super(engine, context, enabledProtocols,
(clientAuth != SSLEngineImpl.clauth_none), false,
(clientAuth != ClientAuthType.CLIENT_AUTH_NONE), false,
activeProtocolVersion, isInitialHandshake, secureRenegotiation,
clientVerifyData, serverVerifyData);
clientVerifyData, serverVerifyData, isDTLS);
doClientAuth = clientAuth;
}
@ -176,7 +177,7 @@ final class ServerHandshaker extends Handshaker {
* whether client authentication is required. Otherwise,
* we will need to wait for the next handshake.
*/
void setClientAuth(byte clientAuth) {
void setClientAuth(ClientAuthType clientAuth) {
doClientAuth = clientAuth;
}
@ -192,21 +193,15 @@ final class ServerHandshaker extends Handshaker {
@Override
void processMessage(byte type, int message_len)
throws IOException {
//
// In SSLv3 and TLS, messages follow strictly increasing
// numerical order _except_ for one annoying special case.
//
if ((state >= type)
&& (state != HandshakeMessage.ht_client_key_exchange
&& type != HandshakeMessage.ht_certificate_verify)) {
throw new SSLProtocolException(
"Handshake message sequence violation, state = " + state
+ ", type = " + type);
}
// check the handshake state
handshakeState.check(type);
switch (type) {
case HandshakeMessage.ht_client_hello:
ClientHello ch = new ClientHello(input, message_len);
ClientHello ch = new ClientHello(input, message_len, isDTLS);
handshakeState.update(ch, resumingSession);
/*
* send it off for processing.
*/
@ -214,12 +209,14 @@ final class ServerHandshaker extends Handshaker {
break;
case HandshakeMessage.ht_certificate:
if (doClientAuth == SSLEngineImpl.clauth_none) {
if (doClientAuth == ClientAuthType.CLIENT_AUTH_NONE) {
fatalSE(Alerts.alert_unexpected_message,
"client sent unsolicited cert chain");
// NOTREACHED
}
this.clientCertificate(new CertificateMsg(input));
CertificateMsg certificateMsg = new CertificateMsg(input);
handshakeState.update(certificateMsg, resumingSession);
this.clientCertificate(certificateMsg);
break;
case HandshakeMessage.ht_client_key_exchange:
@ -237,17 +234,20 @@ final class ServerHandshaker extends Handshaker {
protocolVersion, clientRequestedVersion,
sslContext.getSecureRandom(), input,
message_len, privateKey);
handshakeState.update(pms, resumingSession);
preMasterSecret = this.clientKeyExchange(pms);
break;
case K_KRB5:
case K_KRB5_EXPORT:
preMasterSecret = this.clientKeyExchange(
KerberosClientKeyExchange kke =
new KerberosClientKeyExchange(protocolVersion,
clientRequestedVersion,
sslContext.getSecureRandom(),
input,
this.getAccSE(),
serviceCreds));
serviceCreds);
handshakeState.update(kke, resumingSession);
preMasterSecret = this.clientKeyExchange(kke);
break;
case K_DHE_RSA:
case K_DHE_DSS:
@ -258,16 +258,19 @@ final class ServerHandshaker extends Handshaker {
* protocol difference in these five flavors is in how
* the ServerKeyExchange message was constructed!
*/
preMasterSecret = this.clientKeyExchange(
new DHClientKeyExchange(input));
DHClientKeyExchange dhcke = new DHClientKeyExchange(input);
handshakeState.update(dhcke, resumingSession);
preMasterSecret = this.clientKeyExchange(dhcke);
break;
case K_ECDH_RSA:
case K_ECDH_ECDSA:
case K_ECDHE_RSA:
case K_ECDHE_ECDSA:
case K_ECDH_ANON:
preMasterSecret = this.clientKeyExchange
(new ECDHClientKeyExchange(input));
ECDHClientKeyExchange ecdhcke =
new ECDHClientKeyExchange(input);
handshakeState.update(ecdhcke, resumingSession);
preMasterSecret = this.clientKeyExchange(ecdhcke);
break;
default:
throw new SSLProtocolException
@ -282,20 +285,20 @@ final class ServerHandshaker extends Handshaker {
break;
case HandshakeMessage.ht_certificate_verify:
this.clientCertificateVerify(new CertificateVerify(input,
localSupportedSignAlgs, protocolVersion));
CertificateVerify cvm =
new CertificateVerify(input,
localSupportedSignAlgs, protocolVersion);
handshakeState.update(cvm, resumingSession);
this.clientCertificateVerify(cvm);
break;
case HandshakeMessage.ht_finished:
// A ChangeCipherSpec record must have been received prior to
// reception of the Finished message (RFC 5246, 7.4.9).
if (!receivedChangeCipherSpec()) {
fatalSE(Alerts.alert_handshake_failure,
"Received Finished message before ChangeCipherSpec");
}
Finished cfm =
new Finished(protocolVersion, input, cipherSuite);
handshakeState.update(cfm, resumingSession);
this.clientFinished(cfm);
this.clientFinished(
new Finished(protocolVersion, input, cipherSuite));
break;
default:
@ -303,17 +306,6 @@ final class ServerHandshaker extends Handshaker {
"Illegal server handshake msg, " + type);
}
//
// Move state machine forward if the message handling
// code didn't already do so
//
if (state < type) {
if(type == HandshakeMessage.ht_certificate_verify) {
state = type + 2; // an annoying special case
} else {
state = type;
}
}
}
@ -344,7 +336,7 @@ final class ServerHandshaker extends Handshaker {
//
// This will not have any impact on server initiated renegotiation.
if (rejectClientInitiatedRenego && !isInitialHandshake &&
state != HandshakeMessage.ht_hello_request) {
!serverHelloRequested) {
fatalSE(Alerts.alert_handshake_failure,
"Client initiated renegotiation is not allowed");
}
@ -438,7 +430,7 @@ final class ServerHandshaker extends Handshaker {
}
} else if (!allowUnsafeRenegotiation) {
// abort the handshake
if (activeProtocolVersion.v >= ProtocolVersion.TLS10.v) {
if (activeProtocolVersion.useTLS10PlusSpec()) {
// respond with a no_renegotiation warning
warningSE(Alerts.alert_no_renegotiation);
@ -480,11 +472,52 @@ final class ServerHandshaker extends Handshaker {
}
}
/*
* Always make sure this entire record has been digested before we
* start emitting output, to ensure correct digesting order.
*/
input.digestNow();
// check the "max_fragment_length" extension
MaxFragmentLengthExtension maxFragLenExt = (MaxFragmentLengthExtension)
mesg.extensions.get(ExtensionType.EXT_MAX_FRAGMENT_LENGTH);
if ((maxFragLenExt != null) && (maximumPacketSize != 0)) {
// Not yet consider the impact of IV/MAC/padding.
int estimatedMaxFragSize = maximumPacketSize;
if (isDTLS) {
estimatedMaxFragSize -= DTLSRecord.headerSize;
} else {
estimatedMaxFragSize -= SSLRecord.headerSize;
}
if (maxFragLenExt.getMaxFragLen() > estimatedMaxFragSize) {
// For better interoperability, abort the maximum fragment
// length negotiation, rather than terminate the connection
// with a fatal alert.
maxFragLenExt = null;
// fatalSE(Alerts.alert_illegal_parameter,
// "Not an allowed max_fragment_length value");
}
}
// cookie exchange
if (isDTLS) {
HelloCookieManager hcMgr = sslContext.getHelloCookieManager();
if ((mesg.cookie == null) || (mesg.cookie.length == 0) ||
(!hcMgr.isValid(mesg))) {
//
// Perform cookie exchange for DTLS handshaking if no cookie
// or the cookie is invalid in the ClientHello message.
//
HelloVerifyRequest m0 = new HelloVerifyRequest(hcMgr, mesg);
if (debug != null && Debug.isOn("handshake")) {
m0.print(System.out);
}
m0.write(output);
handshakeState.update(m0, resumingSession);
output.flush();
return;
}
}
/*
* FIRST, construct the ServerHello using the options and priorities
@ -580,7 +613,7 @@ final class ServerHandshaker extends Handshaker {
}
if (resumingSession &&
(doClientAuth == SSLEngineImpl.clauth_required)) {
(doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED)) {
try {
previous.getPeerPrincipal();
} catch (SSLPeerUnverifiedException e) {
@ -660,7 +693,7 @@ final class ServerHandshaker extends Handshaker {
}
}
}
} // else client did not try to resume
} // else client did not try to resume
//
// If client hasn't specified a session we can resume, start a
@ -677,7 +710,7 @@ final class ServerHandshaker extends Handshaker {
// We only need to handle the "signature_algorithm" extension
// for full handshakes and TLS 1.2 or later.
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
SignatureAlgorithmsExtension signAlgs =
(SignatureAlgorithmsExtension)mesg.extensions.get(
ExtensionType.EXT_SIGNATURE_ALGORITHMS);
@ -708,7 +741,7 @@ final class ServerHandshaker extends Handshaker {
sslContext.getSecureRandom(),
getHostAddressSE(), getPortSE());
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
if (peerSupportedSignAlgs != null) {
session.setPeerSupportedSignatureAlgorithms(
peerSupportedSignAlgs);
@ -734,12 +767,41 @@ final class ServerHandshaker extends Handshaker {
session.setLocalPrivateKey(privateKey);
// chooseCompression(mesg);
// set the negotiated maximum fragment in the session
//
// The protocol version and cipher suite have been negotiated
// in previous processes.
if (maxFragLenExt != null) {
int maxFragLen = maxFragLenExt.getMaxFragLen();
// More check of the requested "max_fragment_length" extension.
if (maximumPacketSize != 0) {
int estimatedMaxFragSize = cipherSuite.calculatePacketSize(
maxFragLen, protocolVersion, isDTLS);
if (estimatedMaxFragSize > maximumPacketSize) {
// For better interoperability, abort the maximum
// fragment length negotiation, rather than terminate
// the connection with a fatal alert.
maxFragLenExt = null;
// fatalSE(Alerts.alert_illegal_parameter,
// "Not an allowed max_fragment_length value");
}
}
if (maxFragLenExt != null) {
session.setNegotiatedMaxFragSize(maxFragLen);
}
}
session.setMaximumPacketSize(maximumPacketSize);
} else {
// set the handshake session
setHandshakeSessionSE(session);
}
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
handshakeHash.setFinishedAlg(cipherSuite.prfAlg.getPRFHashAlg());
}
@ -771,11 +833,20 @@ final class ServerHandshaker extends Handshaker {
}
}
if ((maxFragLenExt != null) && !resumingSession) {
// When resuming a session, the server MUST NOT include a
// max_fragment_length extension in the server hello.
//
// Otherwise, use the same value as the requested extension.
m1.extensions.add(maxFragLenExt);
}
if (debug != null && Debug.isOn("handshake")) {
m1.print(System.out);
System.out.println("Cipher suite: " + session.getSuite());
}
m1.write(output);
handshakeState.update(m1, resumingSession);
//
// If we are resuming a session, we finish writing handshake
@ -784,6 +855,10 @@ final class ServerHandshaker extends Handshaker {
if (resumingSession) {
calculateConnectionKeys(session.getMasterSecret());
sendChangeCipherAndFinish(false);
// expecting the final ChangeCipherSpec and Finished messages
expectingFinishFlightSE();
return;
}
@ -815,6 +890,7 @@ final class ServerHandshaker extends Handshaker {
m2.print(System.out);
}
m2.write(output);
handshakeState.update(m2, resumingSession);
// XXX has some side effects with OS TCP buffering,
// leave it out for now
@ -853,9 +929,9 @@ final class ServerHandshaker extends Handshaker {
sslContext.getSecureRandom());
privateKey = tempPrivateKey;
} catch (GeneralSecurityException e) {
throwSSLException
("Error generating RSA server key exchange", e);
m3 = null; // make compiler happy
throw new SSLException(
"Error generating RSA server key exchange", e);
}
} else {
// RSA_EXPORT with short key, don't need ServerKeyExchange
@ -873,8 +949,9 @@ final class ServerHandshaker extends Handshaker {
preferableSignatureAlgorithm,
protocolVersion);
} catch (GeneralSecurityException e) {
throwSSLException("Error generating DH server key exchange", e);
m3 = null; // make compiler happy
throw new SSLException(
"Error generating DH server key exchange", e);
}
break;
case K_DH_ANON:
@ -892,9 +969,9 @@ final class ServerHandshaker extends Handshaker {
preferableSignatureAlgorithm,
protocolVersion);
} catch (GeneralSecurityException e) {
throwSSLException(
"Error generating ECDH server key exchange", e);
m3 = null; // make compiler happy
throw new SSLException(
"Error generating ECDH server key exchange", e);
}
break;
case K_ECDH_RSA:
@ -910,6 +987,7 @@ final class ServerHandshaker extends Handshaker {
m3.print(System.out);
}
m3.write(output);
handshakeState.update(m3, resumingSession);
}
//
@ -923,7 +1001,7 @@ final class ServerHandshaker extends Handshaker {
// Illegal for anonymous flavors, so we need to check that.
//
// CertificateRequest is omitted for Kerberos ciphers
if (doClientAuth != SSLEngineImpl.clauth_none &&
if (doClientAuth != ClientAuthType.CLIENT_AUTH_NONE &&
keyExchange != K_DH_ANON && keyExchange != K_ECDH_ANON &&
keyExchange != K_KRB5 && keyExchange != K_KRB5_EXPORT) {
@ -931,7 +1009,7 @@ final class ServerHandshaker extends Handshaker {
X509Certificate caCerts[];
Collection<SignatureAndHashAlgorithm> localSignAlgs = null;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
// We currently use all local upported signature and hash
// algorithms. However, to minimize the computation cost
// of requested hash algorithms, we may use a restricted
@ -959,6 +1037,7 @@ final class ServerHandshaker extends Handshaker {
m4.print(System.out);
}
m4.write(output);
handshakeState.update(m4, resumingSession);
}
/*
@ -970,6 +1049,7 @@ final class ServerHandshaker extends Handshaker {
m5.print(System.out);
}
m5.write(output);
handshakeState.update(m5, resumingSession);
/*
* Flush any buffered messages so the client will see them.
@ -1000,7 +1080,7 @@ final class ServerHandshaker extends Handshaker {
continue;
}
if (doClientAuth == SSLEngineImpl.clauth_required) {
if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED) {
if ((suite.keyExchange == K_DH_ANON) ||
(suite.keyExchange == K_ECDH_ANON)) {
continue;
@ -1043,12 +1123,12 @@ final class ServerHandshaker extends Handshaker {
}
// must not negotiate the obsoleted weak cipher suites.
if (protocolVersion.v >= suite.obsoleted) {
if (protocolVersion.obsoletes(suite)) {
return false;
}
// must not negotiate unsupported cipher suites.
if (protocolVersion.v < suite.supported) {
if (!protocolVersion.supports(suite)) {
return false;
}
@ -1062,7 +1142,7 @@ final class ServerHandshaker extends Handshaker {
tempPublicKey = null;
Collection<SignatureAndHashAlgorithm> supportedSignAlgs = null;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
if (peerSupportedSignAlgs != null) {
supportedSignAlgs = peerSupportedSignAlgs;
} else {
@ -1151,7 +1231,7 @@ final class ServerHandshaker extends Handshaker {
}
// get preferable peer signature algorithm for server key exchange
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
preferableSignatureAlgorithm =
SignatureAndHashAlgorithm.getPreferableAlgorithm(
supportedSignAlgs, "RSA", privateKey);
@ -1169,7 +1249,7 @@ final class ServerHandshaker extends Handshaker {
}
// get preferable peer signature algorithm for server key exchange
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
preferableSignatureAlgorithm =
SignatureAndHashAlgorithm.getPreferableAlgorithm(
supportedSignAlgs, "RSA", privateKey);
@ -1184,7 +1264,7 @@ final class ServerHandshaker extends Handshaker {
break;
case K_DHE_DSS:
// get preferable peer signature algorithm for server key exchange
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
preferableSignatureAlgorithm =
SignatureAndHashAlgorithm.getPreferableAlgorithm(
supportedSignAlgs, "DSA");
@ -1202,7 +1282,7 @@ final class ServerHandshaker extends Handshaker {
break;
case K_ECDHE_ECDSA:
// get preferable peer signature algorithm for server key exchange
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
preferableSignatureAlgorithm =
SignatureAndHashAlgorithm.getPreferableAlgorithm(
supportedSignAlgs, "ECDSA");
@ -1257,7 +1337,7 @@ final class ServerHandshaker extends Handshaker {
setCipherSuite(suite);
// set the peer implicit supported signature algorithms
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
if (peerSupportedSignAlgs == null) {
setPeerSupportedSignAlgs(supportedSignAlgs);
// we had alreay update the session
@ -1571,7 +1651,7 @@ final class ServerHandshaker extends Handshaker {
mesg.print(System.out);
}
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
SignatureAndHashAlgorithm signAlg =
mesg.getPreferableSignatureAlgorithm();
if (signAlg == null) {
@ -1623,7 +1703,7 @@ final class ServerHandshaker extends Handshaker {
* Verify if client did send the certificate when client
* authentication was required, otherwise server should not proceed
*/
if (doClientAuth == SSLEngineImpl.clauth_required) {
if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED) {
// get X500Principal of the end-entity certificate for X509-based
// ciphersuites, or Kerberos principal for Kerberos ciphersuites
session.getPeerPrincipal();
@ -1664,8 +1744,9 @@ final class ServerHandshaker extends Handshaker {
* the change_cipher_spec and Finished message.
*/
if (!resumingSession) {
input.digestNow();
sendChangeCipherAndFinish(true);
} else {
handshakeFinished = true;
}
/*
@ -1695,7 +1776,8 @@ final class ServerHandshaker extends Handshaker {
private void sendChangeCipherAndFinish(boolean finishedTag)
throws IOException {
output.flush();
// Reload if this message has been reserved.
handshakeHash.reload();
Finished mesg = new Finished(protocolVersion, handshakeHash,
Finished.SERVER, session.getMasterSecret(), cipherSuite);
@ -1713,16 +1795,6 @@ final class ServerHandshaker extends Handshaker {
if (secureRenegotiation) {
serverVerifyData = mesg.getVerifyData();
}
/*
* Update state machine so client MUST send 'finished' next
* The update should only take place if it is not in the fast
* handshake mode since the server has to wait for a finished
* message from the client.
*/
if (finishedTag) {
state = HandshakeMessage.ht_finished;
}
}
@ -1757,7 +1829,7 @@ final class ServerHandshaker extends Handshaker {
* session will get an SSLPeerUnverifiedException.
*/
if ((description == Alerts.alert_no_certificate) &&
(doClientAuth == SSLEngineImpl.clauth_requested)) {
(doClientAuth == ClientAuthType.CLIENT_AUTH_REQUESTED)) {
return;
}
@ -1798,7 +1870,7 @@ final class ServerHandshaker extends Handshaker {
* If the client authentication is only *REQUESTED* (e.g.
* not *REQUIRED*, this is an acceptable condition.)
*/
if (doClientAuth == SSLEngineImpl.clauth_requested) {
if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUESTED) {
return;
} else {
fatalSE(Alerts.alert_bad_certificate,

View file

@ -61,7 +61,7 @@ public abstract class SunJSSE extends java.security.Provider {
private static String info = "Sun JSSE provider" +
"(PKCS12, SunX509/PKIX key/trust factories, " +
"SSLv3/TLSv1/TLSv1.1/TLSv1.2)";
"SSLv3/TLSv1/TLSv1.1/TLSv1.2/DTLSv1.0/DTLSv1.2)";
private static String fipsInfo =
"Sun JSSE provider (FIPS mode, crypto provider ";
@ -220,6 +220,13 @@ public abstract class SunJSSE extends java.security.Provider {
put("Alg.Alias.SSLContext.SSLv3", "TLSv1");
}
put("SSLContext.DTLSv1.0",
"sun.security.ssl.SSLContextImpl$DTLS10Context");
put("SSLContext.DTLSv1.2",
"sun.security.ssl.SSLContextImpl$DTLS12Context");
put("SSLContext.DTLS",
"sun.security.ssl.SSLContextImpl$DTLSContext");
put("SSLContext.Default",
"sun.security.ssl.SSLContextImpl$DefaultSSLContext");

View file

@ -190,7 +190,7 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager
if (session != null) {
ProtocolVersion protocolVersion =
ProtocolVersion.valueOf(session.getProtocol());
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
String[] peerSupportedSignAlgs = null;
if (session instanceof ExtendedSSLSession) {
@ -218,7 +218,7 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager
if (session != null) {
ProtocolVersion protocolVersion =
ProtocolVersion.valueOf(session.getProtocol());
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
String[] peerSupportedSignAlgs = null;
if (session instanceof ExtendedSSLSession) {

View file

@ -204,7 +204,7 @@ final class X509TrustManagerImpl extends X509ExtendedTrustManager
// create the algorithm constraints
ProtocolVersion protocolVersion =
ProtocolVersion.valueOf(session.getProtocol());
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
if (session instanceof ExtendedSSLSession) {
ExtendedSSLSession extSession =
(ExtendedSSLSession)session;
@ -256,7 +256,7 @@ final class X509TrustManagerImpl extends X509ExtendedTrustManager
// create the algorithm constraints
ProtocolVersion protocolVersion =
ProtocolVersion.valueOf(session.getProtocol());
if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
if (protocolVersion.useTLS12PlusSpec()) {
if (session instanceof ExtendedSSLSession) {
ExtendedSSLSession extSession =
(ExtendedSSLSession)session;

View file

@ -511,11 +511,11 @@ krb5.kdc.bad.policy = tryLast
jdk.certpath.disabledAlgorithms=MD2, MD5, RSA keySize < 1024
# Algorithm restrictions for Secure Socket Layer/Transport Layer Security
# (SSL/TLS) processing
# (SSL/TLS/DTLS) processing
#
# In some environments, certain algorithms or key lengths may be undesirable
# when using SSL/TLS. This section describes the mechanism for disabling
# algorithms during SSL/TLS security parameters negotiation, including
# when using SSL/TLS/DTLS. This section describes the mechanism for disabling
# algorithms during SSL/TLS/DTLS security parameters negotiation, including
# protocol version negotiation, cipher suites selection, peer authentication
# and key exchange mechanisms.
#

View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2015, 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 8043758
* @summary Datagram Transport Layer Security (DTLS)
* @compile DTLSOverDatagram.java
* @run main/othervm CipherSuite TLS_RSA_WITH_AES_128_CBC_SHA
* @run main/othervm CipherSuite TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
* @run main/othervm CipherSuite TLS_RSA_WITH_AES_128_CBC_SHA256
* @run main/othervm CipherSuite TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
* @run main/othervm CipherSuite TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
* @run main/othervm CipherSuite TLS_DHE_RSA_WITH_AES_128_CBC_SHA
* @run main/othervm CipherSuite TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
* @run main/othervm CipherSuite TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
* @run main/othervm CipherSuite TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* @run main/othervm CipherSuite TLS_RSA_WITH_AES_128_GCM_SHA256
* @run main/othervm CipherSuite TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
* @run main/othervm CipherSuite TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
* @run main/othervm CipherSuite TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
* @run main/othervm CipherSuite TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
*/
import javax.net.ssl.SSLEngine;
/**
* Test common DTLS cipher suites.
*/
public class CipherSuite extends DTLSOverDatagram {
// use the specific cipher suite
volatile static String cipherSuite;
public static void main(String[] args) throws Exception {
cipherSuite = args[0];
CipherSuite testCase = new CipherSuite();
testCase.runTest(testCase);
}
@Override
SSLEngine createSSLEngine(boolean isClient) throws Exception {
SSLEngine engine = super.createSSLEngine(isClient);
if (isClient) {
engine.setEnabledCipherSuites(new String[]{cipherSuite});
}
return engine;
}
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (c) 2015, 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 8043758
* @summary Datagram Transport Layer Security (DTLS)
* @compile DTLSOverDatagram.java
* @run main/othervm ClientAuth
*/
import javax.net.ssl.SSLEngine;
/**
* Test DTLS client authentication.
*/
public class ClientAuth extends DTLSOverDatagram {
public static void main(String[] args) throws Exception {
ClientAuth testCase = new ClientAuth();
testCase.runTest(testCase);
}
@Override
SSLEngine createSSLEngine(boolean isClient) throws Exception {
SSLEngine engine = super.createSSLEngine(isClient);
if (!isClient) {
engine.setNeedClientAuth(true);
}
return engine;
}
}

View file

@ -0,0 +1,602 @@
/*
* Copyright (c) 2015, 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 8043758
* @summary Datagram Transport Layer Security (DTLS)
* @run main/othervm DTLSOverDatagram
*/
import java.io.*;
import java.nio.*;
import java.net.*;
import java.util.*;
import java.security.*;
import java.security.cert.*;
import javax.net.ssl.*;
import java.util.concurrent.*;
import sun.misc.HexDumpEncoder;
/**
* An example to show the way to use SSLEngine in datagram connections.
*/
public class DTLSOverDatagram {
private static int MAX_HANDSHAKE_LOOPS = 60;
private static int MAX_APP_READ_LOOPS = 10;
/*
* The following is to set up the keystores.
*/
private static String pathToStores = "../etc";
private static String keyStoreFile = "keystore";
private static String trustStoreFile = "truststore";
private static String passwd = "passphrase";
private static String keyFilename =
System.getProperty("test.src", ".") + "/" + pathToStores +
"/" + keyStoreFile;
private static String trustFilename =
System.getProperty("test.src", ".") + "/" + pathToStores +
"/" + trustStoreFile;
private static Exception clientException = null;
private static Exception serverException = null;
private static ByteBuffer serverApp =
ByteBuffer.wrap("Hi Client, I'm Server".getBytes());
private static ByteBuffer clientApp =
ByteBuffer.wrap("Hi Server, I'm Client".getBytes());
/*
* =============================================================
* The test case
*/
public static void main(String[] args) throws Exception {
DTLSOverDatagram testCase = new DTLSOverDatagram();
testCase.runTest(testCase);
}
/*
* Define the server side of the test.
*/
void doServerSide() throws Exception {
DatagramSocket socket = serverDatagramSocket;
socket.setSoTimeout(10000); // 10 second
// create SSLEngine
SSLEngine engine = createSSLEngine(false);
// handshaking
handshake(engine, socket, clientSocketAddr);
// read client application data
receiveAppData(engine, socket, clientApp);
// write server application data
deliverAppData(engine, socket, serverApp, clientSocketAddr);
socket.close();
}
/*
* Define the client side of the test.
*/
void doClientSide() throws Exception {
DatagramSocket socket = clientDatagramSocket;
socket.setSoTimeout(1000); // 1 second read timeout
// create SSLEngine
SSLEngine engine = createSSLEngine(true);
// handshaking
handshake(engine, socket, serverSocketAddr);
// write client application data
deliverAppData(engine, socket, clientApp, serverSocketAddr);
// read server application data
receiveAppData(engine, socket, serverApp);
}
/*
* =============================================================
* The remainder is support stuff for DTLS operations.
*/
SSLEngine createSSLEngine(boolean isClient) throws Exception {
SSLContext context = getDTLSContext();
SSLEngine engine = context.createSSLEngine();
SSLParameters paras = engine.getSSLParameters();
paras.setMaximumPacketSize(1024);
engine.setUseClientMode(isClient);
engine.setSSLParameters(paras);
return engine;
}
// handshake
void handshake(SSLEngine engine, DatagramSocket socket,
SocketAddress peerAddr) throws Exception {
boolean endLoops = false;
int loops = MAX_HANDSHAKE_LOOPS;
engine.beginHandshake();
while (!endLoops &&
(serverException == null) && (clientException == null)) {
if (--loops < 0) {
throw new RuntimeException(
"Too much loops to produce handshake packets");
}
SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP ||
hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP_AGAIN) {
ByteBuffer iNet;
ByteBuffer iApp;
if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
// receive ClientHello request and other SSL/TLS records
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
try {
socket.receive(packet);
} catch (SocketTimeoutException ste) {
List<DatagramPacket> packets =
onReceiveTimeout(engine, peerAddr);
for (DatagramPacket p : packets) {
socket.send(p);
}
continue;
}
iNet = ByteBuffer.wrap(buf, 0, packet.getLength());
iApp = ByteBuffer.allocate(1024);
} else {
iNet = ByteBuffer.allocate(0);
iApp = ByteBuffer.allocate(1024);
}
SSLEngineResult r = engine.unwrap(iNet, iApp);
SSLEngineResult.Status rs = r.getStatus();
hs = r.getHandshakeStatus();
if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) {
// the client maximum fragment size config does not work?
throw new Exception("Buffer overflow: " +
"incorrect client maximum fragment size");
} else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
// bad packet, or the client maximum fragment size
// config does not work?
if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
throw new Exception("Buffer underflow: " +
"incorrect client maximum fragment size");
} // otherwise, ignore this packet
} else if (rs == SSLEngineResult.Status.CLOSED) {
endLoops = true;
} // otherwise, SSLEngineResult.Status.OK:
if (rs != SSLEngineResult.Status.OK) {
continue;
}
} else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
List<DatagramPacket> packets =
produceHandshakePackets(engine, peerAddr);
for (DatagramPacket p : packets) {
socket.send(p);
}
} else if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) {
runDelegatedTasks(engine);
} else if (hs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
// OK, time to do application data exchange.
endLoops = true;
} else if (hs == SSLEngineResult.HandshakeStatus.FINISHED) {
endLoops = true;
}
}
SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
throw new Exception("Not ready for application data yet");
}
}
// deliver application data
void deliverAppData(SSLEngine engine, DatagramSocket socket,
ByteBuffer appData, SocketAddress peerAddr) throws Exception {
// Note: have not consider the packet loses
List<DatagramPacket> packets =
produceApplicationPackets(engine, appData, peerAddr);
appData.flip();
for (DatagramPacket p : packets) {
socket.send(p);
}
}
// receive application data
void receiveAppData(SSLEngine engine,
DatagramSocket socket, ByteBuffer expectedApp) throws Exception {
int loops = MAX_APP_READ_LOOPS;
while ((serverException == null) && (clientException == null)) {
if (--loops < 0) {
throw new RuntimeException(
"Too much loops to receive application data");
}
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
ByteBuffer netBuffer = ByteBuffer.wrap(buf, 0, packet.getLength());
ByteBuffer recBuffer = ByteBuffer.allocate(1024);
SSLEngineResult rs = engine.unwrap(netBuffer, recBuffer);
recBuffer.flip();
if (recBuffer.remaining() != 0) {
printHex("Received application data", recBuffer);
if (!recBuffer.equals(expectedApp)) {
System.out.println("Engine status is " + rs);
throw new Exception("Not the right application data");
}
break;
}
}
}
// produce handshake packets
List<DatagramPacket> produceHandshakePackets(
SSLEngine engine, SocketAddress socketAddr) throws Exception {
List<DatagramPacket> packets = new ArrayList<>();
boolean endLoops = false;
int loops = MAX_HANDSHAKE_LOOPS;
while (!endLoops &&
(serverException == null) && (clientException == null)) {
if (--loops < 0) {
throw new RuntimeException(
"Too much loops to produce handshake packets");
}
ByteBuffer oNet = ByteBuffer.allocate(32768);
ByteBuffer oApp = ByteBuffer.allocate(0);
SSLEngineResult r = engine.wrap(oApp, oNet);
oNet.flip();
SSLEngineResult.Status rs = r.getStatus();
SSLEngineResult.HandshakeStatus hs = r.getHandshakeStatus();
if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) {
// the client maximum fragment size config does not work?
throw new Exception("Buffer overflow: " +
"incorrect server maximum fragment size");
} else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
// bad packet, or the client maximum fragment size
// config does not work?
if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
throw new Exception("Buffer underflow: " +
"incorrect server maximum fragment size");
} // otherwise, ignore this packet
} else if (rs == SSLEngineResult.Status.CLOSED) {
throw new Exception("SSLEngine has closed");
} // otherwise, SSLEngineResult.Status.OK
// SSLEngineResult.Status.OK:
if (oNet.hasRemaining()) {
byte[] ba = new byte[oNet.remaining()];
oNet.get(ba);
DatagramPacket packet = createHandshakePacket(ba, socketAddr);
packets.add(packet);
}
boolean endInnerLoop = false;
SSLEngineResult.HandshakeStatus nhs = hs;
while (!endInnerLoop) {
if (nhs == SSLEngineResult.HandshakeStatus.NEED_TASK) {
runDelegatedTasks(engine);
nhs = engine.getHandshakeStatus();
} else if ((nhs == SSLEngineResult.HandshakeStatus.FINISHED) ||
(nhs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) ||
(nhs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)) {
endInnerLoop = true;
endLoops = true;
} else if (nhs == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
endInnerLoop = true;
}
}
}
return packets;
}
DatagramPacket createHandshakePacket(byte[] ba, SocketAddress socketAddr) {
return new DatagramPacket(ba, ba.length, socketAddr);
}
// produce application packets
List<DatagramPacket> produceApplicationPackets(
SSLEngine engine, ByteBuffer source,
SocketAddress socketAddr) throws Exception {
List<DatagramPacket> packets = new ArrayList<>();
ByteBuffer appNet = ByteBuffer.allocate(32768);
SSLEngineResult r = engine.wrap(source, appNet);
appNet.flip();
SSLEngineResult.Status rs = r.getStatus();
if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) {
// the client maximum fragment size config does not work?
throw new Exception("Buffer overflow: " +
"incorrect server maximum fragment size");
} else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
// unlikely
throw new Exception("Buffer underflow during wraping");
} else if (rs == SSLEngineResult.Status.CLOSED) {
throw new Exception("SSLEngine has closed");
} // otherwise, SSLEngineResult.Status.OK
// SSLEngineResult.Status.OK:
if (appNet.hasRemaining()) {
byte[] ba = new byte[appNet.remaining()];
appNet.get(ba);
DatagramPacket packet =
new DatagramPacket(ba, ba.length, socketAddr);
packets.add(packet);
}
return packets;
}
// run delegated tasks
void runDelegatedTasks(SSLEngine engine) throws Exception {
Runnable runnable;
while ((runnable = engine.getDelegatedTask()) != null) {
runnable.run();
}
SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) {
throw new Exception("handshake shouldn't need additional tasks");
}
}
// retransmission if timeout
List<DatagramPacket> onReceiveTimeout(
SSLEngine engine, SocketAddress socketAddr) throws Exception {
SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
if (hs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
return new ArrayList<DatagramPacket>();
} else {
// retransmission of handshake messages
return produceHandshakePackets(engine, socketAddr);
}
}
// get DTSL context
SSLContext getDTLSContext() throws Exception {
KeyStore ks = KeyStore.getInstance("JKS");
KeyStore ts = KeyStore.getInstance("JKS");
char[] passphrase = "passphrase".toCharArray();
ks.load(new FileInputStream(keyFilename), passphrase);
ts.load(new FileInputStream(trustFilename), passphrase);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, passphrase);
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ts);
SSLContext sslCtx = SSLContext.getInstance("DTLS");
sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
return sslCtx;
}
/*
* =============================================================
* The remainder is support stuff to kickstart the testing.
*/
// the server side SocketAddress
volatile static SocketAddress serverSocketAddr = null;
// the client side SocketAddress
volatile static SocketAddress clientSocketAddr = null;
// the server side DatagramSocket instance
volatile static DatagramSocket serverDatagramSocket = null;
// the server side DatagramSocket instance
volatile static DatagramSocket clientDatagramSocket = null;
// get server side SocketAddress object
public SocketAddress getServerSocketAddress() {
return serverSocketAddr;
}
// get client side SocketAddress object
public SocketAddress getClientSocketAddress() {
return clientSocketAddr;
}
// get server side DatagramSocket object
public DatagramSocket getServerDatagramSocket() {
return serverDatagramSocket;
}
// get client side DatagramSocket object
public DatagramSocket getClientDatagramSocket() {
return clientDatagramSocket;
}
// Will the handshaking and application data exchange succeed?
public boolean isGoodJob() {
return true;
}
public final void runTest(DTLSOverDatagram testCase) throws Exception {
serverDatagramSocket = new DatagramSocket();
serverSocketAddr = new InetSocketAddress(
InetAddress.getLocalHost(), serverDatagramSocket.getLocalPort());
clientDatagramSocket = new DatagramSocket();
clientSocketAddr = new InetSocketAddress(
InetAddress.getLocalHost(), clientDatagramSocket.getLocalPort());
ExecutorService pool = Executors.newFixedThreadPool(2);
List<Future<String>> list = new ArrayList<Future<String>>();
try {
list.add(pool.submit(new ServerCallable(testCase))); // server task
list.add(pool.submit(new ClientCallable(testCase))); // client task
} finally {
pool.shutdown();
}
Exception reserved = null;
for (Future<String> fut : list) {
try {
System.out.println(fut.get());
} catch (CancellationException |
InterruptedException | ExecutionException cie) {
if (reserved != null) {
cie.addSuppressed(reserved);
reserved = cie;
} else {
reserved = cie;
}
}
}
if (reserved != null) {
throw reserved;
}
}
final static class ServerCallable implements Callable<String> {
DTLSOverDatagram testCase;
ServerCallable(DTLSOverDatagram testCase) {
this.testCase = testCase;
}
@Override
public String call() throws Exception {
try {
testCase.doServerSide();
} catch (Exception e) {
e.printStackTrace(System.out);
serverException = e;
if (testCase.isGoodJob()) {
throw e;
} else {
return "Well done, server!";
}
} finally {
if (serverDatagramSocket != null) {
serverDatagramSocket.close();
}
}
if (testCase.isGoodJob()) {
return "Well done, server!";
} else {
throw new Exception("No expected exception");
}
}
}
final static class ClientCallable implements Callable<String> {
DTLSOverDatagram testCase;
ClientCallable(DTLSOverDatagram testCase) {
this.testCase = testCase;
}
@Override
public String call() throws Exception {
try {
testCase.doClientSide();
} catch (Exception e) {
e.printStackTrace(System.out);
clientException = e;
if (testCase.isGoodJob()) {
throw e;
} else {
return "Well done, client!";
}
} finally {
if (clientDatagramSocket != null) {
clientDatagramSocket.close();
}
}
if (testCase.isGoodJob()) {
return "Well done, client!";
} else {
throw new Exception("No expected exception");
}
}
}
final static void printHex(String prefix, ByteBuffer bb) {
HexDumpEncoder dump = new HexDumpEncoder();
synchronized (System.out) {
System.out.println(prefix);
try {
dump.encodeBuffer(bb.slice(), System.out);
} catch (Exception e) {
// ignore
}
System.out.flush();
}
}
final static void printHex(String prefix,
byte[] bytes, int offset, int length) {
HexDumpEncoder dump = new HexDumpEncoder();
synchronized (System.out) {
System.out.println(prefix);
try {
ByteBuffer bb = ByteBuffer.wrap(bytes, offset, length);
dump.encodeBuffer(bb, System.out);
} catch (Exception e) {
// ignore
}
System.out.flush();
}
}
}

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2015, 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 8043758
* @summary Datagram Transport Layer Security (DTLS)
* @compile DTLSOverDatagram.java
* @run main/othervm InvalidCookie
*/
import java.net.DatagramPacket;
import java.net.SocketAddress;
/**
* Test that if the handshake cookie in client side is incorrect, the handshake
* process can continue as if the client does not use cookie.
*/
public class InvalidCookie extends DTLSOverDatagram {
boolean needInvalidCookie = true;
public static void main(String[] args) throws Exception {
InvalidCookie testCase = new InvalidCookie();
testCase.runTest(testCase);
}
@Override
DatagramPacket createHandshakePacket(byte[] ba, SocketAddress socketAddr) {
if (needInvalidCookie && (ba.length >= 60) &&
(ba[0] == (byte)0x16) && (ba[13] == (byte)0x03)) {
// HelloVerifyRequest
needInvalidCookie = false;
System.out.println("invalidate handshake verify cookie");
if (ba[ba.length - 1] == (byte)0xFF) {
ba[ba.length - 1] = (byte)0xFE;
} else {
ba[ba.length - 1] = (byte)0xFF;
}
}
return super.createHandshakePacket(ba, socketAddr);
}
}

View file

@ -0,0 +1,78 @@
/*
* Copyright (c) 2015, 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 8043758
* @summary Datagram Transport Layer Security (DTLS)
* @compile DTLSOverDatagram.java
* @run main/othervm InvalidRecords
*/
import java.net.DatagramPacket;
import java.net.SocketAddress;
/**
* Test that if handshake messages are crasged, the handshake would fail
* because of handshaking hash verification.
*/
public class InvalidRecords extends DTLSOverDatagram {
boolean needInvalidRecords = true;
public static void main(String[] args) throws Exception {
InvalidRecords testCase = new InvalidRecords();
testCase.runTest(testCase);
}
@Override
public boolean isGoodJob() {
return false;
}
@Override
DatagramPacket createHandshakePacket(byte[] ba, SocketAddress socketAddr) {
if (needInvalidRecords && (ba.length >= 60) &&
(ba[0x00] == (byte)0x16) && (ba[0x0D] == (byte)0x01) &&
(ba[0x3B] == (byte)0x00) && (ba[0x3C] > 0)) {
// ba[0x00]: record type
// ba[0x0D]: handshake type
// ba[0x3B]: length of session ID
// ba[0x3C]: length of cookie
// ClientHello with cookie
needInvalidRecords = false;
System.out.println("invalidate ClientHello message");
if (ba[ba.length - 1] == (byte)0xFF) {
ba[ba.length - 1] = (byte)0xFE;
} else {
ba[ba.length - 1] = (byte)0xFF;
}
}
return super.createHandshakePacket(ba, socketAddr);
}
}

View file

@ -0,0 +1,65 @@
/*
* Copyright (c) 2015, 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 8043758
* @summary Datagram Transport Layer Security (DTLS)
* @compile DTLSOverDatagram.java
* @run main/othervm NoMacInitialClientHello
*/
import java.net.DatagramPacket;
import java.net.SocketAddress;
/**
* Test that a server is able to discard invalid initial ClientHello silently.
*/
public class NoMacInitialClientHello extends DTLSOverDatagram {
boolean needInvalidRecords = true;
public static void main(String[] args) throws Exception {
NoMacInitialClientHello testCase = new NoMacInitialClientHello();
testCase.runTest(testCase);
}
@Override
DatagramPacket createHandshakePacket(byte[] ba, SocketAddress socketAddr) {
if (needInvalidRecords && (ba.length >= 60) &&
(ba[0] == (byte)0x16) && (ba[13] == (byte)0x01)) { // ClientHello
needInvalidRecords = false;
System.out.println("invalidate ClientHello message");
if (ba[ba.length - 1] == (byte)0xFF) {
ba[ba.length - 1] = (byte)0xFE;
} else {
ba[ba.length - 1] = (byte)0xFF;
}
}
return super.createHandshakePacket(ba, socketAddr);
}
}

View file

@ -0,0 +1,65 @@
/*
* Copyright (c) 2015, 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 8043758
* @summary Datagram Transport Layer Security (DTLS)
* @compile DTLSOverDatagram.java
* @run main/othervm Reordered
*/
import java.util.List;
import java.util.Collections;
import java.net.DatagramPacket;
import java.net.SocketAddress;
import javax.net.ssl.SSLEngine;
/**
* Test that DTLS implementation is able to reorder data traffic.
*/
public class Reordered extends DTLSOverDatagram {
boolean needPacketReorder = true;
public static void main(String[] args) throws Exception {
Reordered testCase = new Reordered();
testCase.runTest(testCase);
}
@Override
List<DatagramPacket> produceHandshakePackets(
SSLEngine engine, SocketAddress socketAddr) throws Exception {
List<DatagramPacket> packets =
super.produceHandshakePackets(engine, socketAddr);
if (needPacketReorder && (!engine.getUseClientMode())) {
needPacketReorder = false;
Collections.reverse(packets);
}
return packets;
}
}

View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2015, 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 8043758
* @summary Datagram Transport Layer Security (DTLS)
* @compile DTLSOverDatagram.java
* @run main/othervm Retransmission
*/
import java.util.List;
import java.util.ArrayList;
import java.net.DatagramPacket;
import java.net.SocketAddress;
import javax.net.ssl.SSLEngine;
/**
* Test that DTLS implementation is able to do retransmission internally
* automatically if packet get lost.
*/
public class Retransmission extends DTLSOverDatagram {
boolean needPacketLoss = true;
public static void main(String[] args) throws Exception {
Retransmission testCase = new Retransmission();
testCase.runTest(testCase);
}
@Override
List<DatagramPacket> produceHandshakePackets(
SSLEngine engine, SocketAddress socketAddr) throws Exception {
List<DatagramPacket> packets =
super.produceHandshakePackets(engine, socketAddr);
if (!needPacketLoss || (!engine.getUseClientMode())) {
return packets;
}
List<DatagramPacket> parts = new ArrayList<>();
int lostSeq = 2;
for (DatagramPacket packet : packets) {
lostSeq--;
if (lostSeq == 0) {
needPacketLoss = false;
// loss this packet
System.out.println("Loss a packet");
continue;
}
parts.add(packet);
}
return parts;
}
}

View file

@ -0,0 +1,69 @@
/*
* Copyright (c) 2015, 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 8043758
* @summary Datagram Transport Layer Security (DTLS)
* @compile DTLSOverDatagram.java
* @run main/othervm WeakCipherSuite TLS_DH_anon_WITH_AES_128_GCM_SHA256
* @run main/othervm WeakCipherSuite SSL_DH_anon_WITH_DES_CBC_SHA
* @run main/othervm WeakCipherSuite SSL_RSA_WITH_DES_CBC_SHA
* @run main/othervm WeakCipherSuite SSL_DHE_RSA_WITH_DES_CBC_SHA
* @run main/othervm WeakCipherSuite SSL_DHE_DSS_WITH_DES_CBC_SHA
*/
import javax.net.ssl.SSLEngine;
import java.security.Security;
/**
* Test common DTLS weak cipher suites.
*/
public class WeakCipherSuite extends DTLSOverDatagram {
// use the specific cipher suite
volatile static String cipherSuite;
public static void main(String[] args) throws Exception {
// reset security properties to make sure that the algorithms
// and keys used in this test are not disabled.
Security.setProperty("jdk.tls.disabledAlgorithms", "");
Security.setProperty("jdk.certpath.disabledAlgorithms", "");
cipherSuite = args[0];
WeakCipherSuite testCase = new WeakCipherSuite();
testCase.runTest(testCase);
}
@Override
SSLEngine createSSLEngine(boolean isClient) throws Exception {
SSLEngine engine = super.createSSLEngine(isClient);
engine.setEnabledCipherSuites(new String[]{cipherSuite});
return engine;
}
}

View file

@ -391,7 +391,7 @@ public class CheckStatus {
appOut2.rewind();
log("======================================");
log("Client Cert");
log("Client Cert and Key Exchange");
result1 = ssle1.wrap(appOut1, oneToTwo);
checkResult(appOut1, oneToTwo, result1,
Status.OK, HandshakeStatus.NEED_WRAP, 0, -1);
@ -405,22 +405,6 @@ public class CheckStatus {
oneToTwo.compact();
log("======================================");
log("Key Exchange");
result1 = ssle1.wrap(appOut1, oneToTwo);
checkResult(appOut1, oneToTwo, result1,
Status.OK, HandshakeStatus.NEED_WRAP, 0, -1);
oneToTwo.flip();
result2 = ssle2.unwrap(oneToTwo, appIn2);
checkResult(oneToTwo, appIn2, result2,
Status.OK, HandshakeStatus.NEED_TASK,
result1.bytesProduced(), 0);
runDelegatedTasks(ssle2);
oneToTwo.compact();
log("======================================");
log("CCS");
result1 = ssle1.wrap(appOut1, oneToTwo);

View file

@ -116,7 +116,7 @@ public class LargeBufs {
if ((result2.bytesConsumed() != 0) &&
(result2.bytesConsumed() != appBufferMax) &&
(result2.bytesConsumed() != 2 * OFFSET)) {
throw new Exception("result1: " + result1);
throw new Exception("result2: " + result2);
}
log("wrap1: " + result1);

View file

@ -406,6 +406,7 @@ public class CipherTestUtils {
return params;
}).forEach((params) -> {
try {
System.out.println("Testing " + params);
runTest(params);
System.out.println("Passed " + params);
} catch (Exception e) {

View file

@ -23,15 +23,16 @@
* questions.
*/
//
// SunJSSE does not support dynamic system properties, no way to re-use
// system properties in samevm/agentvm mode.
//
/*
* @test
* @bug 4873188
* @summary Support TLS 1.1
* @run main/othervm ExportableBlockCipher
*
* SunJSSE does not support dynamic system properties, no way to re-use
* system properties in samevm/agentvm mode.
*
* @author Xuelei Fan
*/
@ -109,7 +110,7 @@ public class ExportableBlockCipher {
sslIS.read();
sslOS.write('A');
sslOS.flush();
} catch (SSLException ssle) {
} catch (IOException ioe) {
// get the expected exception
interrupted = true;
} finally {

View file

@ -23,15 +23,16 @@
* questions.
*/
//
// SunJSSE does not support dynamic system properties, no way to re-use
// system properties in samevm/agentvm mode.
//
/*
* @test
* @bug 4873188
* @summary Support TLS 1.1
* @run main/othervm ExportableStreamCipher
*
* SunJSSE does not support dynamic system properties, no way to re-use
* system properties in samevm/agentvm mode.
*
* @author Xuelei Fan
*/
@ -109,7 +110,7 @@ public class ExportableStreamCipher {
sslIS.read();
sslOS.write('A');
sslOS.flush();
} catch (SSLException ssle) {
} catch (IOException ioe) {
// get the expected exception
interrupted = true;
} finally {

View file

@ -139,6 +139,11 @@ public class SSLSocketSSLEngineTemplate {
* Main entry point for this test.
*/
public static void main(String args[]) throws Exception {
// reset security properties to make sure that the algorithms
// and keys used in this test are not disabled.
Security.setProperty("jdk.tls.disabledAlgorithms", "");
Security.setProperty("jdk.certpath.disabledAlgorithms", "");
if (debug) {
System.setProperty("javax.net.debug", "all");
}
@ -153,7 +158,12 @@ public class SSLSocketSSLEngineTemplate {
*/
SSLSocketSSLEngineTemplate test =
new SSLSocketSSLEngineTemplate(protocol);
log("-------------------------------------");
log("Testing " + protocol + " for direct buffers ...");
test.runTest(true);
log("---------------------------------------");
log("Testing " + protocol + " for indirect buffers ...");
test.runTest(false);
}
@ -329,6 +339,10 @@ public class SSLSocketSSLEngineTemplate {
thread.join();
}
if (sslSocket != null) {
sslSocket.close();
}
if (serverException != null) {
if (clientException != null) {
serverException.initCause(clientException);

View file

@ -21,19 +21,22 @@
* questions.
*/
//
// SunJSSE does not support dynamic system properties, no way to re-use
// system properties in samevm/agentvm mode.
//
/*
* @test
* @bug 4514971
* @summary Verify applications do not read handshake data after failure
* @run main/othervm ReadHandshake
*
* SunJSSE does not support dynamic system properties, no way to re-use
* system properties in samevm/agentvm mode.
*/
import java.io.*;
import java.net.*;
import javax.net.ssl.*;
import java.security.Security;
public class ReadHandshake {
@ -219,6 +222,10 @@ public class ReadHandshake {
volatile Exception clientException = null;
public static void main(String[] args) throws Exception {
// reset security properties to make sure that the algorithms
// and keys used in this test are not disabled.
Security.setProperty("jdk.tls.disabledAlgorithms", "");
Security.setProperty("jdk.certpath.disabledAlgorithms", "");
if (debug)
System.setProperty("javax.net.debug", "all");

View file

@ -233,7 +233,7 @@ public class LengthCheckTest {
// sent back to the server.
if (gotException == false ||
!isTlsMessage(cTOs, TLS_RECTYPE_ALERT, TLS_ALERT_LVL_FATAL,
TLS_ALERT_INTERNAL_ERROR)) {
TLS_ALERT_UNEXPECTED_MSG)) {
throw new SSLException(
"Client failed to throw Alert:fatal:internal_error");
}
@ -285,7 +285,7 @@ public class LengthCheckTest {
// sent back to the client.
if (gotException == false ||
!isTlsMessage(sTOc, TLS_RECTYPE_ALERT, TLS_ALERT_LVL_FATAL,
TLS_ALERT_INTERNAL_ERROR)) {
TLS_ALERT_UNEXPECTED_MSG)) {
throw new SSLException(
"Server failed to throw Alert:fatal:internal_error");
}