mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-02 04:00:16 +00:00
8043758: Datagram Transport Layer Security (DTLS)
Reviewed-by: jnimeh, weijun, mullan, wetmore
This commit is contained in:
parent
ea68abc64e
commit
0f1698f906
@ -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).
|
||||
|
||||
@ -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>.
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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> - <code>offset</code>
|
||||
* {@code srcs.length} - {@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> - <code>offset</code>.
|
||||
* @return an <code>SSLEngineResult</code> describing the result
|
||||
* {@code dsts.length} - {@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();
|
||||
|
||||
|
||||
@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
*
|
||||
|
||||
@ -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
|
||||
|
||||
}
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
145
jdk/src/java.base/share/classes/sun/security/ssl/Ciphertext.java
Normal file
145
jdk/src/java.base/share/classes/sun/security/ssl/Ciphertext.java
Normal 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 + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 + "]");
|
||||
}
|
||||
}
|
||||
|
||||
@ -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.
|
||||
*
|
||||
|
||||
@ -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.
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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) + ")";
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 = { ");
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
122
jdk/src/java.base/share/classes/sun/security/ssl/RecordType.java
Normal file
122
jdk/src/java.base/share/classes/sun/security/ssl/RecordType.java
Normal 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 + ")");
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
117
jdk/src/java.base/share/classes/sun/security/ssl/SSLRecord.java
Normal file
117
jdk/src/java.base/share/classes/sun/security/ssl/SSLRecord.java
Normal 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
|
||||
};
|
||||
}
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
|
||||
@ -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");
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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.
|
||||
#
|
||||
|
||||
75
jdk/test/javax/net/ssl/DTLS/CipherSuite.java
Normal file
75
jdk/test/javax/net/ssl/DTLS/CipherSuite.java
Normal 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;
|
||||
}
|
||||
}
|
||||
57
jdk/test/javax/net/ssl/DTLS/ClientAuth.java
Normal file
57
jdk/test/javax/net/ssl/DTLS/ClientAuth.java
Normal 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;
|
||||
}
|
||||
}
|
||||
602
jdk/test/javax/net/ssl/DTLS/DTLSOverDatagram.java
Normal file
602
jdk/test/javax/net/ssl/DTLS/DTLSOverDatagram.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
66
jdk/test/javax/net/ssl/DTLS/InvalidCookie.java
Normal file
66
jdk/test/javax/net/ssl/DTLS/InvalidCookie.java
Normal 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);
|
||||
}
|
||||
}
|
||||
78
jdk/test/javax/net/ssl/DTLS/InvalidRecords.java
Normal file
78
jdk/test/javax/net/ssl/DTLS/InvalidRecords.java
Normal 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);
|
||||
}
|
||||
}
|
||||
65
jdk/test/javax/net/ssl/DTLS/NoMacInitialClientHello.java
Normal file
65
jdk/test/javax/net/ssl/DTLS/NoMacInitialClientHello.java
Normal 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);
|
||||
}
|
||||
}
|
||||
65
jdk/test/javax/net/ssl/DTLS/Reordered.java
Normal file
65
jdk/test/javax/net/ssl/DTLS/Reordered.java
Normal 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;
|
||||
}
|
||||
}
|
||||
79
jdk/test/javax/net/ssl/DTLS/Retransmission.java
Normal file
79
jdk/test/javax/net/ssl/DTLS/Retransmission.java
Normal 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;
|
||||
}
|
||||
}
|
||||
69
jdk/test/javax/net/ssl/DTLS/WeakCipherSuite.java
Normal file
69
jdk/test/javax/net/ssl/DTLS/WeakCipherSuite.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user