0N/A/*
5799N/A * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.security.ssl;
0N/A
0N/Aimport java.io.*;
0N/Aimport java.nio.*;
0N/Aimport java.nio.ReadOnlyBufferException;
0N/Aimport java.util.LinkedList;
0N/Aimport java.security.*;
0N/A
0N/Aimport javax.crypto.BadPaddingException;
0N/A
0N/Aimport javax.net.ssl.*;
0N/Aimport javax.net.ssl.SSLEngineResult.*;
0N/A
0N/Aimport com.sun.net.ssl.internal.ssl.X509ExtendedTrustManager;
0N/A
0N/A/**
0N/A * Implementation of an non-blocking SSLEngine.
0N/A *
0N/A * *Currently*, the SSLEngine code exists in parallel with the current
0N/A * SSLSocket. As such, the current implementation is using legacy code
0N/A * with many of the same abstractions. However, it varies in many
0N/A * areas, most dramatically in the IO handling.
0N/A *
0N/A * There are three main I/O threads that can be existing in parallel:
0N/A * wrap(), unwrap(), and beginHandshake(). We are encouraging users to
0N/A * not call multiple instances of wrap or unwrap, because the data could
0N/A * appear to flow out of the SSLEngine in a non-sequential order. We
0N/A * take all steps we can to at least make sure the ordering remains
0N/A * consistent, but once the calls returns, anything can happen. For
0N/A * example, thread1 and thread2 both call wrap, thread1 gets the first
0N/A * packet, thread2 gets the second packet, but thread2 gets control back
0N/A * before thread1, and sends the data. The receiving side would see an
0N/A * out-of-order error.
0N/A *
0N/A * Handshaking is still done the same way as SSLSocket using the normal
0N/A * InputStream/OutputStream abstactions. We create
0N/A * ClientHandshakers/ServerHandshakers, which produce/consume the
0N/A * handshaking data. The transfer of the data is largely handled by the
0N/A * HandshakeInStream/HandshakeOutStreams. Lastly, the
0N/A * InputRecord/OutputRecords still have the same functionality, except
0N/A * that they are overridden with EngineInputRecord/EngineOutputRecord,
0N/A * which provide SSLEngine-specific functionality.
0N/A *
0N/A * Some of the major differences are:
0N/A *
0N/A * EngineInputRecord/EngineOutputRecord/EngineWriter:
0N/A *
0N/A * In order to avoid writing whole new control flows for
0N/A * handshaking, and to reuse most of the same code, we kept most
0N/A * of the actual handshake code the same. As usual, reading
0N/A * handshake data may trigger output of more handshake data, so
0N/A * what we do is write this data to internal buffers, and wait for
0N/A * wrap() to be called to give that data a ride.
0N/A *
0N/A * All data is routed through
0N/A * EngineInputRecord/EngineOutputRecord. However, all handshake
0N/A * data (ct_alert/ct_change_cipher_spec/ct_handshake) are passed
0N/A * through to the the underlying InputRecord/OutputRecord, and
0N/A * the data uses the internal buffers.
0N/A *
0N/A * Application data is handled slightly different, we copy the data
0N/A * directly from the src to the dst buffers, and do all operations
0N/A * on those buffers, saving the overhead of multiple copies.
0N/A *
0N/A * In the case of an inbound record, unwrap passes the inbound
0N/A * ByteBuffer to the InputRecord. If the data is handshake data,
0N/A * the data is read into the InputRecord's internal buffer. If
0N/A * the data is application data, the data is decoded directly into
0N/A * the dst buffer.
0N/A *
0N/A * In the case of an outbound record, when the write to the
0N/A * "real" OutputStream's would normally take place, instead we
0N/A * call back up to the EngineOutputRecord's version of
0N/A * writeBuffer, at which time we capture the resulting output in a
0N/A * ByteBuffer, and send that back to the EngineWriter for internal
0N/A * storage.
0N/A *
0N/A * EngineWriter is responsible for "handling" all outbound
0N/A * data, be it handshake or app data, and for returning the data
0N/A * to wrap() in the proper order.
0N/A *
0N/A * ClientHandshaker/ServerHandshaker/Handshaker:
0N/A * Methods which relied on SSLSocket now have work on either
0N/A * SSLSockets or SSLEngines.
0N/A *
0N/A * @author Brad Wetmore
0N/A */
0N/Afinal public class SSLEngineImpl extends SSLEngine {
0N/A
0N/A //
0N/A // Fields and global comments
0N/A //
0N/A
0N/A /*
0N/A * There's a state machine associated with each connection, which
0N/A * among other roles serves to negotiate session changes.
0N/A *
0N/A * - START with constructor, until the TCP connection's around.
0N/A * - HANDSHAKE picks session parameters before allowing traffic.
0N/A * There are many substates due to sequencing requirements
0N/A * for handshake messages.
0N/A * - DATA may be transmitted.
0N/A * - RENEGOTIATE state allows concurrent data and handshaking
0N/A * traffic ("same" substates as HANDSHAKE), and terminates
0N/A * in selection of new session (and connection) parameters
0N/A * - ERROR state immediately precedes abortive disconnect.
0N/A * - CLOSED when one side closes down, used to start the shutdown
0N/A * process. SSL connection objects are not reused.
0N/A *
0N/A * State affects what SSL record types may legally be sent:
0N/A *
0N/A * - Handshake ... only in HANDSHAKE and RENEGOTIATE states
0N/A * - App Data ... only in DATA and RENEGOTIATE states
0N/A * - Alert ... in HANDSHAKE, DATA, RENEGOTIATE
0N/A *
0N/A * Re what may be received: same as what may be sent, except that
0N/A * HandshakeRequest handshaking messages can come from servers even
0N/A * in the application data state, to request entry to RENEGOTIATE.
0N/A *
0N/A * The state machine within HANDSHAKE and RENEGOTIATE states controls
0N/A * the pending session, not the connection state, until the change
0N/A * cipher spec and "Finished" handshake messages are processed and
0N/A * make the "new" session become the current one.
0N/A *
0N/A * NOTE: details of the SMs always need to be nailed down better.
0N/A * The text above illustrates the core ideas.
0N/A *
0N/A * +---->-------+------>--------->-------+
0N/A * | | |
0N/A * <-----< ^ ^ <-----< |
0N/A *START>----->HANDSHAKE>----->DATA>----->RENEGOTIATE |
0N/A * v v v |
0N/A * | | | |
0N/A * +------------+---------------+ |
0N/A * | |
0N/A * v |
0N/A * ERROR>------>----->CLOSED<--------<----+
0N/A *
0N/A * ALSO, note that the the purpose of handshaking (renegotiation is
0N/A * included) is to assign a different, and perhaps new, session to
0N/A * the connection. The SSLv3 spec is a bit confusing on that new
0N/A * protocol feature.
0N/A */
0N/A private int connectionState;
0N/A
0N/A private static final int cs_START = 0;
0N/A private static final int cs_HANDSHAKE = 1;
0N/A private static final int cs_DATA = 2;
0N/A private static final int cs_RENEGOTIATE = 3;
0N/A private static final int cs_ERROR = 4;
0N/A private static final int cs_CLOSED = 6;
0N/A
0N/A /*
0N/A * Once we're in state cs_CLOSED, we can continue to
0N/A * wrap/unwrap until we finish sending/receiving the messages
0N/A * for close_notify. EngineWriter handles outboundDone.
0N/A */
0N/A private boolean inboundDone = false;
0N/A
0N/A EngineWriter writer;
0N/A
0N/A /*
0N/A * The authentication context holds all information used to establish
0N/A * who this end of the connection is (certificate chains, private keys,
0N/A * etc) and who is trusted (e.g. as CAs or websites).
0N/A */
0N/A private SSLContextImpl sslContext;
0N/A
0N/A /*
0N/A * This connection is one of (potentially) many associated with
0N/A * any given session. The output of the handshake protocol is a
0N/A * new session ... although all the protocol description talks
0N/A * about changing the cipher spec (and it does change), in fact
0N/A * that's incidental since it's done by changing everything that
0N/A * is associated with a session at the same time. (TLS/IETF may
0N/A * change that to add client authentication w/o new key exchg.)
0N/A */
3002N/A private Handshaker handshaker;
3002N/A private SSLSessionImpl sess;
3002N/A private volatile SSLSessionImpl handshakeSession;
3002N/A
0N/A
0N/A /*
0N/A * Client authentication be off, requested, or required.
0N/A *
0N/A * This will be used by both this class and SSLSocket's variants.
0N/A */
0N/A static final byte clauth_none = 0;
0N/A static final byte clauth_requested = 1;
0N/A static final byte clauth_required = 2;
0N/A
0N/A /*
0N/A * Flag indicating if the next record we receive MUST be a Finished
0N/A * message. Temporarily set during the handshake to ensure that
0N/A * a change cipher spec message is followed by a finished message.
0N/A */
0N/A private boolean expectingFinished;
0N/A
0N/A
0N/A /*
0N/A * If someone tries to closeInbound() (say at End-Of-Stream)
0N/A * our engine having received a close_notify, we need to
0N/A * notify the app that we may have a truncation attack underway.
0N/A */
0N/A private boolean recvCN;
0N/A
0N/A /*
0N/A * For improved diagnostics, we detail connection closure
0N/A * If the engine is closed (connectionState >= cs_ERROR),
0N/A * closeReason != null indicates if the engine was closed
0N/A * because of an error or because or normal shutdown.
0N/A */
0N/A private SSLException closeReason;
0N/A
0N/A /*
0N/A * Per-connection private state that doesn't change when the
0N/A * session is changed.
0N/A */
0N/A private byte doClientAuth;
0N/A private boolean enableSessionCreation = true;
0N/A EngineInputRecord inputRecord;
0N/A EngineOutputRecord outputRecord;
0N/A private AccessControlContext acc;
0N/A
2998N/A // The cipher suites enabled for use on this connection.
2998N/A private CipherSuiteList enabledCipherSuites;
2998N/A
3002N/A // the endpoint identification protocol
3002N/A private String identificationProtocol = null;
3002N/A
3002N/A // The cryptographic algorithm constraints
3002N/A private AlgorithmConstraints algorithmConstraints = null;
0N/A
0N/A // Have we been told whether we're client or server?
0N/A private boolean serverModeSet = false;
0N/A private boolean roleIsServer;
0N/A
0N/A /*
2998N/A * The protocol versions enabled for use on this connection.
2998N/A *
2998N/A * Note: we support a pseudo protocol called SSLv2Hello which when
2998N/A * set will result in an SSL v2 Hello being sent with SSL (version 3.0)
2998N/A * or TLS (version 3.1, 3.2, etc.) version info.
0N/A */
0N/A private ProtocolList enabledProtocols;
0N/A
0N/A /*
0N/A * The SSL version associated with this connection.
0N/A */
0N/A private ProtocolVersion protocolVersion = ProtocolVersion.DEFAULT;
0N/A
0N/A /*
0N/A * Crypto state that's reinitialized when the session changes.
0N/A */
0N/A private MAC readMAC, writeMAC;
0N/A private CipherBox readCipher, writeCipher;
0N/A // NOTE: compression state would be saved here
0N/A
2890N/A /*
2890N/A * security parameters for secure renegotiation.
2890N/A */
2890N/A private boolean secureRenegotiation;
2890N/A private byte[] clientVerifyData;
2890N/A private byte[] serverVerifyData;
0N/A
0N/A /*
0N/A * READ ME * READ ME * READ ME * READ ME * READ ME * READ ME *
0N/A * IMPORTANT STUFF TO UNDERSTANDING THE SYNCHRONIZATION ISSUES.
0N/A * READ ME * READ ME * READ ME * READ ME * READ ME * READ ME *
0N/A *
0N/A * There are several locks here.
0N/A *
0N/A * The primary lock is the per-instance lock used by
0N/A * synchronized(this) and the synchronized methods. It controls all
0N/A * access to things such as the connection state and variables which
0N/A * affect handshaking. If we are inside a synchronized method, we
0N/A * can access the state directly, otherwise, we must use the
0N/A * synchronized equivalents.
0N/A *
0N/A * Note that we must never acquire the <code>this</code> lock after
0N/A * <code>writeLock</code> or run the risk of deadlock.
0N/A *
0N/A * Grab some coffee, and be careful with any code changes.
0N/A */
0N/A private Object wrapLock;
0N/A private Object unwrapLock;
0N/A Object writeLock;
0N/A
0N/A /*
4466N/A * Is it the first application record to write?
4466N/A */
4466N/A private boolean isFirstAppOutputRecord = true;
4466N/A
4466N/A /*
0N/A * Class and subclass dynamic debugging support
0N/A */
0N/A private static final Debug debug = Debug.getInstance("ssl");
0N/A
0N/A //
0N/A // Initialization/Constructors
0N/A //
0N/A
0N/A /**
0N/A * Constructor for an SSLEngine from SSLContext, without
0N/A * host/port hints. This Engine will not be able to cache
0N/A * sessions, but must renegotiate everything by hand.
0N/A */
0N/A SSLEngineImpl(SSLContextImpl ctx) {
0N/A super();
0N/A init(ctx);
0N/A }
0N/A
0N/A /**
0N/A * Constructor for an SSLEngine from SSLContext.
0N/A */
0N/A SSLEngineImpl(SSLContextImpl ctx, String host, int port) {
0N/A super(host, port);
0N/A init(ctx);
0N/A }
0N/A
0N/A /**
0N/A * Initializes the Engine
0N/A */
0N/A private void init(SSLContextImpl ctx) {
0N/A if (debug != null && Debug.isOn("ssl")) {
0N/A System.out.println("Using SSLEngineImpl.");
0N/A }
0N/A
0N/A sslContext = ctx;
0N/A sess = SSLSessionImpl.nullSession;
3002N/A handshakeSession = null;
0N/A
0N/A /*
0N/A * State is cs_START until we initialize the handshaker.
0N/A *
0N/A * Apps using SSLEngine are probably going to be server.
0N/A * Somewhat arbitrary choice.
0N/A */
0N/A roleIsServer = true;
0N/A connectionState = cs_START;
0N/A
0N/A /*
0N/A * default read and write side cipher and MAC support
0N/A *
0N/A * Note: compression support would go here too
0N/A */
0N/A readCipher = CipherBox.NULL;
0N/A readMAC = MAC.NULL;
0N/A writeCipher = CipherBox.NULL;
0N/A writeMAC = MAC.NULL;
0N/A
2890N/A // default security parameters for secure renegotiation
2890N/A secureRenegotiation = false;
2890N/A clientVerifyData = new byte[0];
2890N/A serverVerifyData = new byte[0];
2890N/A
3988N/A enabledCipherSuites =
3988N/A sslContext.getDefaultCipherSuiteList(roleIsServer);
3988N/A enabledProtocols =
3988N/A sslContext.getDefaultProtocolList(roleIsServer);
0N/A
0N/A wrapLock = new Object();
0N/A unwrapLock = new Object();
0N/A writeLock = new Object();
0N/A
0N/A /*
0N/A * Save the Access Control Context. This will be used later
0N/A * for a couple of things, including providing a context to
0N/A * run tasks in, and for determining which credentials
0N/A * to use for Subject based (JAAS) decisions
0N/A */
0N/A acc = AccessController.getContext();
0N/A
0N/A /*
0N/A * All outbound application data goes through this OutputRecord,
0N/A * other data goes through their respective records created
0N/A * elsewhere. All inbound data goes through this one
0N/A * input record.
0N/A */
0N/A outputRecord =
0N/A new EngineOutputRecord(Record.ct_application_data, this);
0N/A inputRecord = new EngineInputRecord(this);
0N/A inputRecord.enableFormatChecks();
0N/A
0N/A writer = new EngineWriter();
0N/A }
0N/A
0N/A /**
0N/A * Initialize the handshaker object. This means:
0N/A *
0N/A * . if a handshake is already in progress (state is cs_HANDSHAKE
0N/A * or cs_RENEGOTIATE), do nothing and return
0N/A *
0N/A * . if the engine is already closed, throw an Exception (internal error)
0N/A *
0N/A * . otherwise (cs_START or cs_DATA), create the appropriate handshaker
2998N/A * object and advance the connection state (to cs_HANDSHAKE or
2998N/A * cs_RENEGOTIATE, respectively).
0N/A *
0N/A * This method is called right after a new engine is created, when
0N/A * starting renegotiation, or when changing client/server mode of the
0N/A * engine.
0N/A */
0N/A private void initHandshaker() {
0N/A switch (connectionState) {
0N/A
0N/A //
0N/A // Starting a new handshake.
0N/A //
0N/A case cs_START:
0N/A case cs_DATA:
0N/A break;
0N/A
0N/A //
0N/A // We're already in the middle of a handshake.
0N/A //
0N/A case cs_HANDSHAKE:
0N/A case cs_RENEGOTIATE:
0N/A return;
0N/A
0N/A //
0N/A // Anyone allowed to call this routine is required to
0N/A // do so ONLY if the connection state is reasonable...
0N/A //
0N/A default:
0N/A throw new IllegalStateException("Internal error");
0N/A }
0N/A
0N/A // state is either cs_START or cs_DATA
0N/A if (connectionState == cs_START) {
0N/A connectionState = cs_HANDSHAKE;
0N/A } else { // cs_DATA
0N/A connectionState = cs_RENEGOTIATE;
0N/A }
0N/A if (roleIsServer) {
2261N/A handshaker = new ServerHandshaker(this, sslContext,
2890N/A enabledProtocols, doClientAuth,
2890N/A protocolVersion, connectionState == cs_HANDSHAKE,
2890N/A secureRenegotiation, clientVerifyData, serverVerifyData);
0N/A } else {
2261N/A handshaker = new ClientHandshaker(this, sslContext,
2890N/A enabledProtocols,
2890N/A protocolVersion, connectionState == cs_HANDSHAKE,
2890N/A secureRenegotiation, clientVerifyData, serverVerifyData);
0N/A }
2998N/A handshaker.setEnabledCipherSuites(enabledCipherSuites);
0N/A handshaker.setEnableSessionCreation(enableSessionCreation);
0N/A }
0N/A
0N/A /*
0N/A * Report the current status of the Handshaker
0N/A */
0N/A private HandshakeStatus getHSStatus(HandshakeStatus hss) {
0N/A
0N/A if (hss != null) {
0N/A return hss;
0N/A }
0N/A
0N/A synchronized (this) {
0N/A if (writer.hasOutboundData()) {
0N/A return HandshakeStatus.NEED_WRAP;
0N/A } else if (handshaker != null) {
0N/A if (handshaker.taskOutstanding()) {
0N/A return HandshakeStatus.NEED_TASK;
0N/A } else {
0N/A return HandshakeStatus.NEED_UNWRAP;
0N/A }
0N/A } else if (connectionState == cs_CLOSED) {
0N/A /*
0N/A * Special case where we're closing, but
0N/A * still need the close_notify before we
0N/A * can officially be closed.
0N/A *
0N/A * Note isOutboundDone is taken care of by
0N/A * hasOutboundData() above.
0N/A */
0N/A if (!isInboundDone()) {
0N/A return HandshakeStatus.NEED_UNWRAP;
0N/A } // else not handshaking
0N/A }
0N/A
0N/A return HandshakeStatus.NOT_HANDSHAKING;
0N/A }
0N/A }
0N/A
0N/A synchronized private void checkTaskThrown() throws SSLException {
0N/A if (handshaker != null) {
0N/A handshaker.checkThrown();
0N/A }
0N/A }
0N/A
0N/A //
0N/A // Handshaking and connection state code
0N/A //
0N/A
0N/A /*
0N/A * Provides "this" synchronization for connection state.
0N/A * Otherwise, you can access it directly.
0N/A */
0N/A synchronized private int getConnectionState() {
0N/A return connectionState;
0N/A }
0N/A
0N/A synchronized private void setConnectionState(int state) {
0N/A connectionState = state;
0N/A }
0N/A
0N/A /*
0N/A * Get the Access Control Context.
0N/A *
0N/A * Used for a known context to
0N/A * run tasks in, and for determining which credentials
0N/A * to use for Subject-based (JAAS) decisions.
0N/A */
0N/A AccessControlContext getAcc() {
0N/A return acc;
0N/A }
0N/A
0N/A /*
0N/A * Is a handshake currently underway?
0N/A */
0N/A public SSLEngineResult.HandshakeStatus getHandshakeStatus() {
0N/A return getHSStatus(null);
0N/A }
0N/A
0N/A /*
0N/A * When a connection finishes handshaking by enabling use of a newly
0N/A * negotiated session, each end learns about it in two halves (read,
0N/A * and write). When both read and write ciphers have changed, and the
0N/A * last handshake message has been read, the connection has joined
0N/A * (rejoined) the new session.
0N/A *
0N/A * NOTE: The SSLv3 spec is rather unclear on the concepts here.
0N/A * Sessions don't change once they're established (including cipher
0N/A * suite and master secret) but connections can join them (and leave
0N/A * them). They're created by handshaking, though sometime handshaking
0N/A * causes connections to join up with pre-established sessions.
0N/A *
0N/A * Synchronized on "this" from readRecord.
0N/A */
0N/A private void changeReadCiphers() throws SSLException {
0N/A if (connectionState != cs_HANDSHAKE
0N/A && connectionState != cs_RENEGOTIATE) {
0N/A throw new SSLProtocolException(
0N/A "State error, change cipher specs");
0N/A }
0N/A
0N/A // ... create decompressor
0N/A
782N/A CipherBox oldCipher = readCipher;
782N/A
0N/A try {
0N/A readCipher = handshaker.newReadCipher();
0N/A readMAC = handshaker.newReadMAC();
0N/A } catch (GeneralSecurityException e) {
0N/A // "can't happen"
0N/A throw (SSLException)new SSLException
0N/A ("Algorithm missing: ").initCause(e);
0N/A }
782N/A
782N/A /*
782N/A * Dispose of any intermediate state in the underlying cipher.
782N/A * For PKCS11 ciphers, this will release any attached sessions,
782N/A * and thus make finalization faster.
782N/A *
782N/A * Since MAC's doFinal() is called for every SSL/TLS packet, it's
782N/A * not necessary to do the same with MAC's.
782N/A */
782N/A oldCipher.dispose();
0N/A }
0N/A
0N/A /*
0N/A * used by Handshaker to change the active write cipher, follows
0N/A * the output of the CCS message.
0N/A *
0N/A * Also synchronized on "this" from readRecord/delegatedTask.
0N/A */
0N/A void changeWriteCiphers() throws SSLException {
0N/A if (connectionState != cs_HANDSHAKE
0N/A && connectionState != cs_RENEGOTIATE) {
0N/A throw new SSLProtocolException(
0N/A "State error, change cipher specs");
0N/A }
0N/A
0N/A // ... create compressor
0N/A
782N/A CipherBox oldCipher = writeCipher;
782N/A
0N/A try {
0N/A writeCipher = handshaker.newWriteCipher();
0N/A writeMAC = handshaker.newWriteMAC();
0N/A } catch (GeneralSecurityException e) {
0N/A // "can't happen"
0N/A throw (SSLException)new SSLException
0N/A ("Algorithm missing: ").initCause(e);
0N/A }
782N/A
782N/A // See comment above.
782N/A oldCipher.dispose();
4466N/A
4466N/A // reset the flag of the first application record
4466N/A isFirstAppOutputRecord = true;
0N/A }
0N/A
0N/A /*
0N/A * Updates the SSL version associated with this connection.
0N/A * Called from Handshaker once it has determined the negotiated version.
0N/A */
0N/A synchronized void setVersion(ProtocolVersion protocolVersion) {
0N/A this.protocolVersion = protocolVersion;
0N/A outputRecord.setVersion(protocolVersion);
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Kickstart the handshake if it is not already in progress.
0N/A * This means:
0N/A *
0N/A * . if handshaking is already underway, do nothing and return
0N/A *
0N/A * . if the engine is not connected or already closed, throw an
0N/A * Exception.
0N/A *
0N/A * . otherwise, call initHandshake() to initialize the handshaker
0N/A * object and progress the state. Then, send the initial
0N/A * handshaking message if appropriate (always on clients and
0N/A * on servers when renegotiating).
0N/A */
0N/A private synchronized void kickstartHandshake() throws IOException {
0N/A switch (connectionState) {
0N/A
0N/A case cs_START:
0N/A if (!serverModeSet) {
0N/A throw new IllegalStateException(
0N/A "Client/Server mode not yet set.");
0N/A }
0N/A initHandshaker();
0N/A break;
0N/A
0N/A case cs_HANDSHAKE:
0N/A // handshaker already setup, proceed
0N/A break;
0N/A
0N/A case cs_DATA:
2890N/A if (!secureRenegotiation && !Handshaker.allowUnsafeRenegotiation) {
2890N/A throw new SSLHandshakeException(
2890N/A "Insecure renegotiation is not allowed");
2890N/A }
2890N/A
2890N/A if (!secureRenegotiation) {
2890N/A if (debug != null && Debug.isOn("handshake")) {
2890N/A System.out.println(
2890N/A "Warning: Using insecure renegotiation");
2890N/A }
2261N/A }
2261N/A
0N/A // initialize the handshaker, move to cs_RENEGOTIATE
0N/A initHandshaker();
0N/A break;
0N/A
0N/A case cs_RENEGOTIATE:
0N/A // handshaking already in progress, return
0N/A return;
0N/A
0N/A default:
0N/A // cs_ERROR/cs_CLOSED
0N/A throw new SSLException("SSLEngine is closing/closed");
0N/A }
0N/A
0N/A //
0N/A // Kickstart handshake state machine if we need to ...
0N/A //
0N/A // Note that handshaker.kickstart() writes the message
0N/A // to its HandshakeOutStream, which calls back into
0N/A // SSLSocketImpl.writeRecord() to send it.
0N/A //
2998N/A if (!handshaker.activated()) {
2998N/A // prior to handshaking, activate the handshake
2998N/A if (connectionState == cs_RENEGOTIATE) {
2998N/A // don't use SSLv2Hello when renegotiating
2998N/A handshaker.activate(protocolVersion);
2998N/A } else {
2998N/A handshaker.activate(null);
2998N/A }
2998N/A
0N/A if (handshaker instanceof ClientHandshaker) {
0N/A // send client hello
0N/A handshaker.kickstart();
0N/A } else { // instanceof ServerHandshaker
0N/A if (connectionState == cs_HANDSHAKE) {
0N/A // initial handshake, no kickstart message to send
0N/A } else {
0N/A // we want to renegotiate, send hello request
0N/A handshaker.kickstart();
2998N/A
0N/A // hello request is not included in the handshake
0N/A // hashes, reset them
0N/A handshaker.handshakeHash.reset();
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Start a SSLEngine handshake
0N/A */
0N/A public void beginHandshake() throws SSLException {
0N/A try {
0N/A kickstartHandshake();
0N/A } catch (Exception e) {
0N/A fatal(Alerts.alert_handshake_failure,
0N/A "Couldn't kickstart handshaking", e);
0N/A }
0N/A }
0N/A
0N/A
0N/A //
0N/A // Read/unwrap side
0N/A //
0N/A
0N/A
0N/A /**
0N/A * Unwraps a buffer. Does a variety of checks before grabbing
0N/A * the unwrapLock, which blocks multiple unwraps from occuring.
0N/A */
0N/A public SSLEngineResult unwrap(ByteBuffer netData, ByteBuffer [] appData,
0N/A int offset, int length) throws SSLException {
0N/A
0N/A EngineArgs ea = new EngineArgs(netData, appData, offset, length);
0N/A
0N/A try {
0N/A synchronized (unwrapLock) {
0N/A return readNetRecord(ea);
0N/A }
0N/A } catch (Exception e) {
0N/A /*
0N/A * Don't reset position so it looks like we didn't
0N/A * consume anything. We did consume something, and it
0N/A * got us into this situation, so report that much back.
0N/A * Our days of consuming are now over anyway.
0N/A */
0N/A fatal(Alerts.alert_internal_error,
0N/A "problem unwrapping net record", e);
0N/A return null; // make compiler happy
0N/A } finally {
0N/A /*
0N/A * Just in case something failed to reset limits properly.
0N/A */
0N/A ea.resetLim();
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Makes additional checks for unwrap, but this time more
0N/A * specific to this packet and the current state of the machine.
0N/A */
0N/A private SSLEngineResult readNetRecord(EngineArgs ea) throws IOException {
0N/A
0N/A Status status = null;
0N/A HandshakeStatus hsStatus = null;
0N/A
0N/A /*
0N/A * See if the handshaker needs to report back some SSLException.
0N/A */
0N/A checkTaskThrown();
0N/A
0N/A /*
0N/A * Check if we are closing/closed.
0N/A */
0N/A if (isInboundDone()) {
0N/A return new SSLEngineResult(Status.CLOSED, getHSStatus(null), 0, 0);
0N/A }
0N/A
0N/A /*
0N/A * If we're still in cs_HANDSHAKE, make sure it's been
0N/A * started.
0N/A */
0N/A synchronized (this) {
0N/A if ((connectionState == cs_HANDSHAKE) ||
0N/A (connectionState == cs_START)) {
0N/A kickstartHandshake();
0N/A
0N/A /*
0N/A * If there's still outbound data to flush, we
0N/A * can return without trying to unwrap anything.
0N/A */
0N/A hsStatus = getHSStatus(null);
0N/A
0N/A if (hsStatus == HandshakeStatus.NEED_WRAP) {
0N/A return new SSLEngineResult(Status.OK, hsStatus, 0, 0);
0N/A }
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Grab a copy of this if it doesn't already exist,
0N/A * and we can use it several places before anything major
0N/A * happens on this side. Races aren't critical
0N/A * here.
0N/A */
0N/A if (hsStatus == null) {
0N/A hsStatus = getHSStatus(null);
0N/A }
0N/A
0N/A /*
0N/A * If we have a task outstanding, this *MUST* be done before
0N/A * doing any more unwrapping, because we could be in the middle
0N/A * of receiving a handshake message, for example, a finished
0N/A * message which would change the ciphers.
0N/A */
0N/A if (hsStatus == HandshakeStatus.NEED_TASK) {
0N/A return new SSLEngineResult(
0N/A Status.OK, hsStatus, 0, 0);
0N/A }
0N/A
0N/A /*
0N/A * Check the packet to make sure enough is here.
0N/A * This will also indirectly check for 0 len packets.
0N/A */
0N/A int packetLen = inputRecord.bytesInCompletePacket(ea.netData);
0N/A
0N/A // Is this packet bigger than SSL/TLS normally allows?
0N/A if (packetLen > sess.getPacketBufferSize()) {
0N/A if (packetLen > Record.maxLargeRecordSize) {
0N/A throw new SSLProtocolException(
0N/A "Input SSL/TLS record too big: max = " +
0N/A Record.maxLargeRecordSize +
0N/A " len = " + packetLen);
0N/A } else {
0N/A // Expand the expected maximum packet/application buffer
0N/A // sizes.
0N/A sess.expandBufferSizes();
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Check for OVERFLOW.
0N/A *
0N/A * To be considered: We could delay enforcing the application buffer
0N/A * free space requirement until after the initial handshaking.
0N/A */
0N/A if ((packetLen - Record.headerSize) > ea.getAppRemaining()) {
0N/A return new SSLEngineResult(Status.BUFFER_OVERFLOW, hsStatus, 0, 0);
0N/A }
0N/A
0N/A // check for UNDERFLOW.
0N/A if ((packetLen == -1) || (ea.netData.remaining() < packetLen)) {
0N/A return new SSLEngineResult(
0N/A Status.BUFFER_UNDERFLOW, hsStatus, 0, 0);
0N/A }
0N/A
0N/A /*
0N/A * We're now ready to actually do the read.
0N/A * The only result code we really need to be exactly
0N/A * right is the HS finished, for signaling to
0N/A * HandshakeCompletedListeners.
0N/A */
0N/A try {
0N/A hsStatus = readRecord(ea);
0N/A } catch (SSLException e) {
0N/A throw e;
0N/A } catch (IOException e) {
0N/A SSLException ex = new SSLException("readRecord");
0N/A ex.initCause(e);
0N/A throw ex;
0N/A }
0N/A
0N/A /*
0N/A * Check the various condition that we could be reporting.
0N/A *
0N/A * It's *possible* something might have happened between the
0N/A * above and now, but it was better to minimally lock "this"
0N/A * during the read process. We'll return the current
0N/A * status, which is more representative of the current state.
0N/A *
0N/A * status above should cover: FINISHED, NEED_TASK
0N/A */
0N/A status = (isInboundDone() ? Status.CLOSED : Status.OK);
0N/A hsStatus = getHSStatus(hsStatus);
0N/A
0N/A return new SSLEngineResult(status, hsStatus,
0N/A ea.deltaNet(), ea.deltaApp());
0N/A }
0N/A
0N/A /*
0N/A * Actually do the read record processing.
0N/A *
0N/A * Returns a Status if it can make specific determinations
0N/A * of the engine state. In particular, we need to signal
0N/A * that a handshake just completed.
0N/A *
0N/A * It would be nice to be symmetrical with the write side and move
0N/A * the majority of this to EngineInputRecord, but there's too much
0N/A * SSLEngine state to do that cleanly. It must still live here.
0N/A */
0N/A private HandshakeStatus readRecord(EngineArgs ea) throws IOException {
0N/A
0N/A HandshakeStatus hsStatus = null;
0N/A
0N/A /*
0N/A * The various operations will return new sliced BB's,
0N/A * this will avoid having to worry about positions and
0N/A * limits in the netBB.
0N/A */
0N/A ByteBuffer readBB = null;
0N/A ByteBuffer decryptedBB = null;
0N/A
0N/A if (getConnectionState() != cs_ERROR) {
0N/A
0N/A /*
0N/A * Read a record ... maybe emitting an alert if we get a
0N/A * comprehensible but unsupported "hello" message during
0N/A * format checking (e.g. V2).
0N/A */
0N/A try {
0N/A readBB = inputRecord.read(ea.netData);
0N/A } catch (IOException e) {
0N/A fatal(Alerts.alert_unexpected_message, e);
0N/A }
0N/A
0N/A /*
0N/A * The basic SSLv3 record protection involves (optional)
0N/A * encryption for privacy, and an integrity check ensuring
0N/A * data origin authentication. We do them both here, and
0N/A * throw a fatal alert if the integrity check fails.
0N/A */
0N/A try {
5799N/A decryptedBB = inputRecord.decrypt(readMAC, readCipher, readBB);
0N/A } catch (BadPaddingException e) {
0N/A byte alertType = (inputRecord.contentType() ==
0N/A Record.ct_handshake) ?
0N/A Alerts.alert_handshake_failure :
0N/A Alerts.alert_bad_record_mac;
5799N/A fatal(alertType, e.getMessage(), e);
0N/A }
0N/A
0N/A
0N/A // if (!inputRecord.decompress(c))
0N/A // fatal(Alerts.alert_decompression_failure,
0N/A // "decompression failure");
0N/A
0N/A
0N/A /*
0N/A * Process the record.
0N/A */
0N/A
0N/A synchronized (this) {
0N/A switch (inputRecord.contentType()) {
0N/A case Record.ct_handshake:
0N/A /*
0N/A * Handshake messages always go to a pending session
0N/A * handshaker ... if there isn't one, create one. This
0N/A * must work asynchronously, for renegotiation.
0N/A *
0N/A * NOTE that handshaking will either resume a session
0N/A * which was in the cache (and which might have other
0N/A * connections in it already), or else will start a new
0N/A * session (new keys exchanged) with just this connection
0N/A * in it.
0N/A */
0N/A initHandshaker();
2998N/A if (!handshaker.activated()) {
2998N/A // prior to handshaking, activate the handshake
2998N/A if (connectionState == cs_RENEGOTIATE) {
2998N/A // don't use SSLv2Hello when renegotiating
2998N/A handshaker.activate(protocolVersion);
2998N/A } else {
2998N/A handshaker.activate(null);
2998N/A }
2998N/A }
0N/A
0N/A /*
0N/A * process the handshake record ... may contain just
0N/A * a partial handshake message or multiple messages.
0N/A *
0N/A * The handshaker state machine will ensure that it's
0N/A * a finished message.
0N/A */
0N/A handshaker.process_record(inputRecord, expectingFinished);
0N/A expectingFinished = false;
0N/A
2261N/A if (handshaker.invalidated) {
2261N/A handshaker = null;
2261N/A // if state is cs_RENEGOTIATE, revert it to cs_DATA
2261N/A if (connectionState == cs_RENEGOTIATE) {
2261N/A connectionState = cs_DATA;
2261N/A }
2261N/A } else if (handshaker.isDone()) {
2890N/A // reset the parameters for secure renegotiation.
2890N/A secureRenegotiation =
2890N/A handshaker.isSecureRenegotiation();
2890N/A clientVerifyData = handshaker.getClientVerifyData();
2890N/A serverVerifyData = handshaker.getServerVerifyData();
2890N/A
0N/A sess = handshaker.getSession();
3002N/A handshakeSession = null;
0N/A if (!writer.hasOutboundData()) {
0N/A hsStatus = HandshakeStatus.FINISHED;
0N/A }
0N/A handshaker = null;
0N/A connectionState = cs_DATA;
0N/A
0N/A // No handshakeListeners here. That's a
0N/A // SSLSocket thing.
0N/A } else if (handshaker.taskOutstanding()) {
0N/A hsStatus = HandshakeStatus.NEED_TASK;
0N/A }
0N/A break;
0N/A
0N/A case Record.ct_application_data:
0N/A // Pass this right back up to the application.
0N/A if ((connectionState != cs_DATA)
0N/A && (connectionState != cs_RENEGOTIATE)
0N/A && (connectionState != cs_CLOSED)) {
0N/A throw new SSLProtocolException(
0N/A "Data received in non-data state: " +
0N/A connectionState);
0N/A }
0N/A
0N/A if (expectingFinished) {
0N/A throw new SSLProtocolException
0N/A ("Expecting finished message, received data");
0N/A }
0N/A
0N/A /*
0N/A * Don't return data once the inbound side is
0N/A * closed.
0N/A */
0N/A if (!inboundDone) {
0N/A ea.scatter(decryptedBB.slice());
0N/A }
0N/A break;
0N/A
0N/A case Record.ct_alert:
0N/A recvAlert();
0N/A break;
0N/A
0N/A case Record.ct_change_cipher_spec:
0N/A if ((connectionState != cs_HANDSHAKE
0N/A && connectionState != cs_RENEGOTIATE)
0N/A || inputRecord.available() != 1
0N/A || inputRecord.read() != 1) {
0N/A fatal(Alerts.alert_unexpected_message,
0N/A "illegal change cipher spec msg, state = "
0N/A + connectionState);
0N/A }
0N/A
0N/A //
0N/A // The first message after a change_cipher_spec
0N/A // record MUST be a "Finished" handshake record,
0N/A // else it's a protocol violation. We force this
0N/A // to be checked by a minor tweak to the state
0N/A // machine.
0N/A //
0N/A changeReadCiphers();
0N/A // next message MUST be a finished message
0N/A expectingFinished = true;
0N/A break;
0N/A
0N/A default:
0N/A //
0N/A // TLS requires that unrecognized records be ignored.
0N/A //
0N/A if (debug != null && Debug.isOn("ssl")) {
0N/A System.out.println(threadName() +
0N/A ", Received record type: "
0N/A + inputRecord.contentType());
0N/A }
0N/A break;
0N/A } // switch
2998N/A
2998N/A /*
2998N/A * We only need to check the sequence number state for
2998N/A * non-handshaking record.
2998N/A *
2998N/A * Note that in order to maintain the handshake status
2998N/A * properly, we check the sequence number after the last
2998N/A * record reading process. As we request renegotiation
2998N/A * or close the connection for wrapped sequence number
2998N/A * when there is enough sequence number space left to
2998N/A * handle a few more records, so the sequence number
2998N/A * of the last record cannot be wrapped.
2998N/A */
2998N/A if (connectionState < cs_ERROR && !isInboundDone() &&
2998N/A (hsStatus == HandshakeStatus.NOT_HANDSHAKING)) {
2998N/A if (checkSequenceNumber(readMAC,
2998N/A inputRecord.contentType())) {
2998N/A hsStatus = getHSStatus(null);
2998N/A }
2998N/A }
0N/A } // synchronized (this)
0N/A }
0N/A
0N/A return hsStatus;
0N/A }
0N/A
0N/A
0N/A //
0N/A // write/wrap side
0N/A //
0N/A
0N/A
0N/A /**
0N/A * Wraps a buffer. Does a variety of checks before grabbing
0N/A * the wrapLock, which blocks multiple wraps from occuring.
0N/A */
0N/A public SSLEngineResult wrap(ByteBuffer [] appData,
0N/A int offset, int length, ByteBuffer netData) throws SSLException {
0N/A
0N/A EngineArgs ea = new EngineArgs(appData, offset, length, netData);
0N/A
0N/A /*
0N/A * We can be smarter about using smaller buffer sizes later.
0N/A * For now, force it to be large enough to handle any
0N/A * valid SSL/TLS record.
0N/A */
0N/A if (netData.remaining() < outputRecord.maxRecordSize) {
0N/A return new SSLEngineResult(
0N/A Status.BUFFER_OVERFLOW, getHSStatus(null), 0, 0);
0N/A }
0N/A
0N/A try {
0N/A synchronized (wrapLock) {
0N/A return writeAppRecord(ea);
0N/A }
0N/A } catch (Exception e) {
0N/A ea.resetPos();
0N/A
0N/A fatal(Alerts.alert_internal_error,
4613N/A "problem wrapping app data", e);
0N/A return null; // make compiler happy
0N/A } finally {
0N/A /*
0N/A * Just in case something didn't reset limits properly.
0N/A */
0N/A ea.resetLim();
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Makes additional checks for unwrap, but this time more
0N/A * specific to this packet and the current state of the machine.
0N/A */
0N/A private SSLEngineResult writeAppRecord(EngineArgs ea) throws IOException {
0N/A
0N/A Status status = null;
0N/A HandshakeStatus hsStatus = null;
0N/A
0N/A /*
0N/A * See if the handshaker needs to report back some SSLException.
0N/A */
0N/A checkTaskThrown();
0N/A
0N/A /*
0N/A * short circuit if we're closed/closing.
0N/A */
0N/A if (writer.isOutboundDone()) {
0N/A return new SSLEngineResult(Status.CLOSED, getHSStatus(null), 0, 0);
0N/A }
0N/A
0N/A /*
0N/A * If we're still in cs_HANDSHAKE, make sure it's been
0N/A * started.
0N/A */
0N/A synchronized (this) {
0N/A if ((connectionState == cs_HANDSHAKE) ||
0N/A (connectionState == cs_START)) {
0N/A kickstartHandshake();
0N/A
0N/A /*
0N/A * If there's no HS data available to write, we can return
0N/A * without trying to wrap anything.
0N/A */
0N/A hsStatus = getHSStatus(null);
0N/A
0N/A if (hsStatus == HandshakeStatus.NEED_UNWRAP) {
0N/A return new SSLEngineResult(Status.OK, hsStatus, 0, 0);
0N/A }
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Grab a copy of this if it doesn't already exist,
0N/A * and we can use it several places before anything major
0N/A * happens on this side. Races aren't critical
0N/A * here.
0N/A */
0N/A if (hsStatus == null) {
0N/A hsStatus = getHSStatus(null);
0N/A }
0N/A
0N/A /*
0N/A * If we have a task outstanding, this *MUST* be done before
0N/A * doing any more wrapping, because we could be in the middle
0N/A * of receiving a handshake message, for example, a finished
0N/A * message which would change the ciphers.
0N/A */
0N/A if (hsStatus == HandshakeStatus.NEED_TASK) {
0N/A return new SSLEngineResult(
0N/A Status.OK, hsStatus, 0, 0);
0N/A }
0N/A
0N/A /*
0N/A * This will obtain any waiting outbound data, or will
0N/A * process the outbound appData.
0N/A */
0N/A try {
0N/A synchronized (writeLock) {
0N/A hsStatus = writeRecord(outputRecord, ea);
0N/A }
0N/A } catch (SSLException e) {
0N/A throw e;
0N/A } catch (IOException e) {
0N/A SSLException ex = new SSLException("Write problems");
0N/A ex.initCause(e);
0N/A throw ex;
0N/A }
0N/A
0N/A /*
0N/A * writeRecord might have reported some status.
0N/A * Now check for the remaining cases.
0N/A *
0N/A * status above should cover: NEED_WRAP/FINISHED
0N/A */
0N/A status = (isOutboundDone() ? Status.CLOSED : Status.OK);
0N/A hsStatus = getHSStatus(hsStatus);
0N/A
0N/A return new SSLEngineResult(status, hsStatus,
0N/A ea.deltaApp(), ea.deltaNet());
0N/A }
0N/A
0N/A /*
0N/A * Central point to write/get all of the outgoing data.
0N/A */
0N/A private HandshakeStatus writeRecord(EngineOutputRecord eor,
0N/A EngineArgs ea) throws IOException {
0N/A
0N/A // eventually compress as well.
2998N/A HandshakeStatus hsStatus =
2998N/A writer.writeRecord(eor, ea, writeMAC, writeCipher);
2998N/A
2998N/A /*
2998N/A * We only need to check the sequence number state for
2998N/A * non-handshaking record.
2998N/A *
2998N/A * Note that in order to maintain the handshake status
2998N/A * properly, we check the sequence number after the last
2998N/A * record writing process. As we request renegotiation
2998N/A * or close the connection for wrapped sequence number
2998N/A * when there is enough sequence number space left to
2998N/A * handle a few more records, so the sequence number
2998N/A * of the last record cannot be wrapped.
2998N/A */
2998N/A if (connectionState < cs_ERROR && !isOutboundDone() &&
2998N/A (hsStatus == HandshakeStatus.NOT_HANDSHAKING)) {
2998N/A if (checkSequenceNumber(writeMAC, eor.contentType())) {
2998N/A hsStatus = getHSStatus(null);
2998N/A }
2998N/A }
2998N/A
4466N/A /*
4466N/A * turn off the flag of the first application record if we really
4466N/A * consumed at least byte.
4466N/A */
4466N/A if (isFirstAppOutputRecord && ea.deltaApp() > 0) {
4466N/A isFirstAppOutputRecord = false;
4466N/A }
4466N/A
2998N/A return hsStatus;
0N/A }
0N/A
0N/A /*
4466N/A * Need to split the payload except the following cases:
4466N/A *
4466N/A * 1. protocol version is TLS 1.1 or later;
4466N/A * 2. bulk cipher does not use CBC mode, including null bulk cipher suites.
4466N/A * 3. the payload is the first application record of a freshly
4466N/A * negotiated TLS session.
4466N/A * 4. the CBC protection is disabled;
4466N/A *
4466N/A * More details, please refer to
4466N/A * EngineOutputRecord.write(EngineArgs, MAC, CipherBox).
4466N/A */
4466N/A boolean needToSplitPayload(CipherBox cipher, ProtocolVersion protocol) {
4466N/A return (protocol.v <= ProtocolVersion.TLS10.v) &&
4466N/A cipher.isCBCMode() && !isFirstAppOutputRecord &&
4466N/A Record.enableCBCProtection;
4466N/A }
4466N/A
4466N/A /*
0N/A * Non-application OutputRecords go through here.
0N/A */
0N/A void writeRecord(EngineOutputRecord eor) throws IOException {
0N/A // eventually compress as well.
0N/A writer.writeRecord(eor, writeMAC, writeCipher);
2998N/A
2998N/A /*
2998N/A * Check the sequence number state
2998N/A *
2998N/A * Note that in order to maintain the connection I/O
2998N/A * properly, we check the sequence number after the last
2998N/A * record writing process. As we request renegotiation
2998N/A * or close the connection for wrapped sequence number
2998N/A * when there is enough sequence number space left to
2998N/A * handle a few more records, so the sequence number
2998N/A * of the last record cannot be wrapped.
2998N/A */
2998N/A if ((connectionState < cs_ERROR) && !isOutboundDone()) {
2998N/A checkSequenceNumber(writeMAC, eor.contentType());
2998N/A }
0N/A }
0N/A
0N/A //
0N/A // Close code
0N/A //
0N/A
0N/A /**
2998N/A * Check the sequence number state
2998N/A *
2998N/A * RFC 4346 states that, "Sequence numbers are of type uint64 and
2998N/A * may not exceed 2^64-1. Sequence numbers do not wrap. If a TLS
2998N/A * implementation would need to wrap a sequence number, it must
2998N/A * renegotiate instead."
2998N/A *
2998N/A * Return true if the handshake status may be changed.
2998N/A */
2998N/A private boolean checkSequenceNumber(MAC mac, byte type)
2998N/A throws IOException {
2998N/A
2998N/A /*
2998N/A * Don't bother to check the sequence number for error or
2998N/A * closed connections, or NULL MAC
2998N/A */
2998N/A if (connectionState >= cs_ERROR || mac == MAC.NULL) {
2998N/A return false;
2998N/A }
2998N/A
2998N/A /*
2998N/A * Conservatively, close the connection immediately when the
2998N/A * sequence number is close to overflow
2998N/A */
2998N/A if (mac.seqNumOverflow()) {
2998N/A /*
2998N/A * TLS protocols do not define a error alert for sequence
2998N/A * number overflow. We use handshake_failure error alert
2998N/A * for handshaking and bad_record_mac for other records.
2998N/A */
2998N/A if (debug != null && Debug.isOn("ssl")) {
2998N/A System.out.println(threadName() +
2998N/A ", sequence number extremely close to overflow " +
2998N/A "(2^64-1 packets). Closing connection.");
2998N/A }
2998N/A
2998N/A fatal(Alerts.alert_handshake_failure, "sequence number overflow");
2998N/A
2998N/A return true; // make the compiler happy
2998N/A }
2998N/A
2998N/A /*
2998N/A * Ask for renegotiation when need to renew sequence number.
2998N/A *
2998N/A * Don't bother to kickstart the renegotiation when the local is
2998N/A * asking for it.
2998N/A */
2998N/A if ((type != Record.ct_handshake) && mac.seqNumIsHuge()) {
2998N/A if (debug != null && Debug.isOn("ssl")) {
2998N/A System.out.println(threadName() + ", request renegotiation " +
2998N/A "to avoid sequence number overflow");
2998N/A }
2998N/A
2998N/A beginHandshake();
2998N/A return true;
2998N/A }
2998N/A
2998N/A return false;
2998N/A }
2998N/A
2998N/A /**
0N/A * Signals that no more outbound application data will be sent
0N/A * on this <code>SSLEngine</code>.
0N/A */
0N/A private void closeOutboundInternal() {
0N/A
0N/A if ((debug != null) && Debug.isOn("ssl")) {
0N/A System.out.println(threadName() + ", closeOutboundInternal()");
0N/A }
0N/A
0N/A /*
0N/A * Already closed, ignore
0N/A */
0N/A if (writer.isOutboundDone()) {
0N/A return;
0N/A }
0N/A
0N/A switch (connectionState) {
0N/A
0N/A /*
0N/A * If we haven't even started yet, don't bother reading inbound.
0N/A */
0N/A case cs_START:
0N/A writer.closeOutbound();
0N/A inboundDone = true;
0N/A break;
0N/A
0N/A case cs_ERROR:
0N/A case cs_CLOSED:
0N/A break;
0N/A
0N/A /*
0N/A * Otherwise we indicate clean termination.
0N/A */
0N/A // case cs_HANDSHAKE:
0N/A // case cs_DATA:
0N/A // case cs_RENEGOTIATE:
0N/A default:
0N/A warning(Alerts.alert_close_notify);
0N/A writer.closeOutbound();
0N/A break;
0N/A }
0N/A
782N/A // See comment in changeReadCiphers()
782N/A writeCipher.dispose();
782N/A
0N/A connectionState = cs_CLOSED;
0N/A }
0N/A
0N/A synchronized public void closeOutbound() {
0N/A /*
0N/A * Dump out a close_notify to the remote side
0N/A */
0N/A if ((debug != null) && Debug.isOn("ssl")) {
0N/A System.out.println(threadName() + ", called closeOutbound()");
0N/A }
0N/A
0N/A closeOutboundInternal();
0N/A }
0N/A
0N/A /**
0N/A * Returns the outbound application data closure state
0N/A */
0N/A public boolean isOutboundDone() {
0N/A return writer.isOutboundDone();
0N/A }
0N/A
0N/A /**
0N/A * Signals that no more inbound network data will be sent
0N/A * to this <code>SSLEngine</code>.
0N/A */
0N/A private void closeInboundInternal() {
0N/A
0N/A if ((debug != null) && Debug.isOn("ssl")) {
0N/A System.out.println(threadName() + ", closeInboundInternal()");
0N/A }
0N/A
0N/A /*
0N/A * Already closed, ignore
0N/A */
0N/A if (inboundDone) {
0N/A return;
0N/A }
0N/A
0N/A closeOutboundInternal();
0N/A inboundDone = true;
782N/A
782N/A // See comment in changeReadCiphers()
782N/A readCipher.dispose();
782N/A
0N/A connectionState = cs_CLOSED;
0N/A }
0N/A
0N/A /*
0N/A * Close the inbound side of the connection. We grab the
0N/A * lock here, and do the real work in the internal verison.
0N/A * We do check for truncation attacks.
0N/A */
0N/A synchronized public void closeInbound() throws SSLException {
0N/A /*
0N/A * Currently closes the outbound side as well. The IETF TLS
0N/A * working group has expressed the opinion that 1/2 open
0N/A * connections are not allowed by the spec. May change
0N/A * someday in the future.
0N/A */
0N/A if ((debug != null) && Debug.isOn("ssl")) {
0N/A System.out.println(threadName() + ", called closeInbound()");
0N/A }
0N/A
0N/A /*
0N/A * No need to throw an Exception if we haven't even started yet.
0N/A */
0N/A if ((connectionState != cs_START) && !recvCN) {
0N/A recvCN = true; // Only receive the Exception once
0N/A fatal(Alerts.alert_internal_error,
0N/A "Inbound closed before receiving peer's close_notify: " +
0N/A "possible truncation attack?");
0N/A } else {
0N/A /*
0N/A * Currently, this is a no-op, but in case we change
0N/A * the close inbound code later.
0N/A */
0N/A closeInboundInternal();
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns the network inbound data closure state
0N/A */
0N/A synchronized public boolean isInboundDone() {
0N/A return inboundDone;
0N/A }
0N/A
0N/A
0N/A //
0N/A // Misc stuff
0N/A //
0N/A
0N/A
0N/A /**
0N/A * Returns the current <code>SSLSession</code> for this
0N/A * <code>SSLEngine</code>
0N/A * <P>
0N/A * These can be long lived, and frequently correspond to an
0N/A * entire login session for some user.
0N/A */
0N/A synchronized public SSLSession getSession() {
0N/A return sess;
0N/A }
0N/A
3002N/A @Override
3002N/A synchronized public SSLSession getHandshakeSession() {
3002N/A return handshakeSession;
3002N/A }
3002N/A
3002N/A synchronized void setHandshakeSession(SSLSessionImpl session) {
3002N/A handshakeSession = session;
3002N/A }
3002N/A
0N/A /**
0N/A * Returns a delegated <code>Runnable</code> task for
0N/A * this <code>SSLEngine</code>.
0N/A */
0N/A synchronized public Runnable getDelegatedTask() {
0N/A if (handshaker != null) {
0N/A return handshaker.getTask();
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A
0N/A //
0N/A // EXCEPTION AND ALERT HANDLING
0N/A //
0N/A
0N/A /*
0N/A * Send a warning alert.
0N/A */
0N/A void warning(byte description) {
0N/A sendAlert(Alerts.alert_warning, description);
0N/A }
0N/A
0N/A synchronized void fatal(byte description, String diagnostic)
0N/A throws SSLException {
0N/A fatal(description, diagnostic, null);
0N/A }
0N/A
0N/A synchronized void fatal(byte description, Throwable cause)
0N/A throws SSLException {
0N/A fatal(description, null, cause);
0N/A }
0N/A
0N/A /*
0N/A * We've got a fatal error here, so start the shutdown process.
0N/A *
0N/A * Because of the way the code was written, we have some code
0N/A * calling fatal directly when the "description" is known
0N/A * and some throwing Exceptions which are then caught by higher
0N/A * levels which then call here. This code needs to determine
0N/A * if one of the lower levels has already started the process.
0N/A *
0N/A * We won't worry about Error's, if we have one of those,
0N/A * we're in worse trouble. Note: the networking code doesn't
0N/A * deal with Errors either.
0N/A */
0N/A synchronized void fatal(byte description, String diagnostic,
0N/A Throwable cause) throws SSLException {
0N/A
0N/A /*
0N/A * If we have no further information, make a general-purpose
0N/A * message for folks to see. We generally have one or the other.
0N/A */
0N/A if (diagnostic == null) {
0N/A diagnostic = "General SSLEngine problem";
0N/A }
0N/A if (cause == null) {
0N/A cause = Alerts.getSSLException(description, cause, diagnostic);
0N/A }
0N/A
0N/A /*
0N/A * If we've already shutdown because of an error,
0N/A * there is nothing we can do except rethrow the exception.
0N/A *
0N/A * Most exceptions seen here will be SSLExceptions.
0N/A * We may find the occasional Exception which hasn't been
0N/A * converted to a SSLException, so we'll do it here.
0N/A */
0N/A if (closeReason != null) {
0N/A if ((debug != null) && Debug.isOn("ssl")) {
0N/A System.out.println(threadName() +
0N/A ", fatal: engine already closed. Rethrowing " +
0N/A cause.toString());
0N/A }
0N/A if (cause instanceof RuntimeException) {
0N/A throw (RuntimeException)cause;
0N/A } else if (cause instanceof SSLException) {
0N/A throw (SSLException)cause;
0N/A } else if (cause instanceof Exception) {
0N/A SSLException ssle = new SSLException(
0N/A "fatal SSLEngine condition");
0N/A ssle.initCause(cause);
0N/A throw ssle;
0N/A }
0N/A }
0N/A
0N/A if ((debug != null) && Debug.isOn("ssl")) {
0N/A System.out.println(threadName()
0N/A + ", fatal error: " + description +
0N/A ": " + diagnostic + "\n" + cause.toString());
0N/A }
0N/A
0N/A /*
0N/A * Ok, this engine's going down.
0N/A */
0N/A int oldState = connectionState;
0N/A connectionState = cs_ERROR;
0N/A
0N/A inboundDone = true;
0N/A
0N/A sess.invalidate();
3002N/A if (handshakeSession != null) {
3002N/A handshakeSession.invalidate();
3002N/A }
0N/A
0N/A /*
0N/A * If we haven't even started handshaking yet, no need
0N/A * to generate the fatal close alert.
0N/A */
0N/A if (oldState != cs_START) {
0N/A sendAlert(Alerts.alert_fatal, description);
0N/A }
0N/A
0N/A if (cause instanceof SSLException) { // only true if != null
0N/A closeReason = (SSLException)cause;
0N/A } else {
0N/A /*
0N/A * Including RuntimeExceptions, but we'll throw those
0N/A * down below. The closeReason isn't used again,
0N/A * except for null checks.
0N/A */
0N/A closeReason =
0N/A Alerts.getSSLException(description, cause, diagnostic);
0N/A }
0N/A
0N/A writer.closeOutbound();
0N/A
0N/A connectionState = cs_CLOSED;
0N/A
782N/A // See comment in changeReadCiphers()
782N/A readCipher.dispose();
782N/A writeCipher.dispose();
782N/A
0N/A if (cause instanceof RuntimeException) {
0N/A throw (RuntimeException)cause;
0N/A } else {
0N/A throw closeReason;
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Process an incoming alert ... caller must already have synchronized
0N/A * access to "this".
0N/A */
0N/A private void recvAlert() throws IOException {
0N/A byte level = (byte)inputRecord.read();
0N/A byte description = (byte)inputRecord.read();
0N/A if (description == -1) { // check for short message
0N/A fatal(Alerts.alert_illegal_parameter, "Short alert message");
0N/A }
0N/A
0N/A if (debug != null && (Debug.isOn("record") ||
0N/A Debug.isOn("handshake"))) {
0N/A synchronized (System.out) {
0N/A System.out.print(threadName());
0N/A System.out.print(", RECV " + protocolVersion + " ALERT: ");
0N/A if (level == Alerts.alert_fatal) {
0N/A System.out.print("fatal, ");
0N/A } else if (level == Alerts.alert_warning) {
0N/A System.out.print("warning, ");
0N/A } else {
0N/A System.out.print("<level " + (0x0ff & level) + ">, ");
0N/A }
0N/A System.out.println(Alerts.alertDescription(description));
0N/A }
0N/A }
0N/A
0N/A if (level == Alerts.alert_warning) {
0N/A if (description == Alerts.alert_close_notify) {
0N/A if (connectionState == cs_HANDSHAKE) {
0N/A fatal(Alerts.alert_unexpected_message,
0N/A "Received close_notify during handshake");
0N/A } else {
0N/A recvCN = true;
0N/A closeInboundInternal(); // reply to close
0N/A }
0N/A } else {
0N/A
0N/A //
0N/A // The other legal warnings relate to certificates,
0N/A // e.g. no_certificate, bad_certificate, etc; these
0N/A // are important to the handshaking code, which can
0N/A // also handle illegal protocol alerts if needed.
0N/A //
0N/A if (handshaker != null) {
0N/A handshaker.handshakeAlert(description);
0N/A }
0N/A }
0N/A } else { // fatal or unknown level
0N/A String reason = "Received fatal alert: "
0N/A + Alerts.alertDescription(description);
0N/A if (closeReason == null) {
0N/A closeReason = Alerts.getSSLException(description, reason);
0N/A }
0N/A fatal(Alerts.alert_unexpected_message, reason);
0N/A }
0N/A }
0N/A
0N/A
0N/A /*
0N/A * Emit alerts. Caller must have synchronized with "this".
0N/A */
0N/A private void sendAlert(byte level, byte description) {
2998N/A // the connectionState cannot be cs_START
0N/A if (connectionState >= cs_CLOSED) {
0N/A return;
0N/A }
0N/A
2998N/A // For initial handshaking, don't send alert message to peer if
2998N/A // handshaker has not started.
2998N/A if (connectionState == cs_HANDSHAKE &&
2998N/A (handshaker == null || !handshaker.started())) {
2998N/A return;
2998N/A }
2998N/A
0N/A EngineOutputRecord r = new EngineOutputRecord(Record.ct_alert, this);
0N/A r.setVersion(protocolVersion);
0N/A
0N/A boolean useDebug = debug != null && Debug.isOn("ssl");
0N/A if (useDebug) {
0N/A synchronized (System.out) {
0N/A System.out.print(threadName());
0N/A System.out.print(", SEND " + protocolVersion + " ALERT: ");
0N/A if (level == Alerts.alert_fatal) {
0N/A System.out.print("fatal, ");
0N/A } else if (level == Alerts.alert_warning) {
0N/A System.out.print("warning, ");
0N/A } else {
0N/A System.out.print("<level = " + (0x0ff & level) + ">, ");
0N/A }
0N/A System.out.println("description = "
0N/A + Alerts.alertDescription(description));
0N/A }
0N/A }
0N/A
0N/A r.write(level);
0N/A r.write(description);
0N/A try {
0N/A writeRecord(r);
0N/A } catch (IOException e) {
0N/A if (useDebug) {
0N/A System.out.println(threadName() +
0N/A ", Exception sending alert: " + e);
0N/A }
0N/A }
0N/A }
0N/A
0N/A
0N/A //
0N/A // VARIOUS OTHER METHODS (COMMON TO SSLSocket)
0N/A //
0N/A
0N/A
0N/A /**
0N/A * Controls whether new connections may cause creation of new SSL
0N/A * sessions.
0N/A *
0N/A * As long as handshaking has not started, we can change
0N/A * whether we enable session creations. Otherwise,
0N/A * we will need to wait for the next handshake.
0N/A */
0N/A synchronized public void setEnableSessionCreation(boolean flag) {
0N/A enableSessionCreation = flag;
0N/A
2998N/A if ((handshaker != null) && !handshaker.activated()) {
0N/A handshaker.setEnableSessionCreation(enableSessionCreation);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns true if new connections may cause creation of new SSL
0N/A * sessions.
0N/A */
0N/A synchronized public boolean getEnableSessionCreation() {
0N/A return enableSessionCreation;
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Sets the flag controlling whether a server mode engine
0N/A * *REQUIRES* SSL client authentication.
0N/A *
0N/A * As long as handshaking has not started, we can change
0N/A * whether client authentication is needed. Otherwise,
0N/A * we will need to wait for the next handshake.
0N/A */
0N/A synchronized public void setNeedClientAuth(boolean flag) {
0N/A doClientAuth = (flag ?
0N/A SSLEngineImpl.clauth_required : SSLEngineImpl.clauth_none);
0N/A
0N/A if ((handshaker != null) &&
0N/A (handshaker instanceof ServerHandshaker) &&
2998N/A !handshaker.activated()) {
0N/A ((ServerHandshaker) handshaker).setClientAuth(doClientAuth);
0N/A }
0N/A }
0N/A
0N/A synchronized public boolean getNeedClientAuth() {
0N/A return (doClientAuth == SSLEngineImpl.clauth_required);
0N/A }
0N/A
0N/A /**
0N/A * Sets the flag controlling whether a server mode engine
0N/A * *REQUESTS* SSL client authentication.
0N/A *
0N/A * As long as handshaking has not started, we can change
0N/A * whether client authentication is requested. Otherwise,
0N/A * we will need to wait for the next handshake.
0N/A */
0N/A synchronized public void setWantClientAuth(boolean flag) {
0N/A doClientAuth = (flag ?
0N/A SSLEngineImpl.clauth_requested : SSLEngineImpl.clauth_none);
0N/A
0N/A if ((handshaker != null) &&
0N/A (handshaker instanceof ServerHandshaker) &&
2998N/A !handshaker.activated()) {
0N/A ((ServerHandshaker) handshaker).setClientAuth(doClientAuth);
0N/A }
0N/A }
0N/A
0N/A synchronized public boolean getWantClientAuth() {
0N/A return (doClientAuth == SSLEngineImpl.clauth_requested);
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Sets the flag controlling whether the engine is in SSL
0N/A * client or server mode. Must be called before any SSL
0N/A * traffic has started.
0N/A */
0N/A synchronized public void setUseClientMode(boolean flag) {
0N/A switch (connectionState) {
0N/A
0N/A case cs_START:
2998N/A /*
2998N/A * If we need to change the engine mode and the enabled
2998N/A * protocols haven't specifically been set by the user,
2998N/A * change them to the corresponding default ones.
2998N/A */
2998N/A if (roleIsServer != (!flag) &&
3988N/A sslContext.isDefaultProtocolList(enabledProtocols)) {
3988N/A enabledProtocols = sslContext.getDefaultProtocolList(!flag);
2998N/A }
2998N/A
0N/A roleIsServer = !flag;
0N/A serverModeSet = true;
0N/A break;
0N/A
0N/A case cs_HANDSHAKE:
0N/A /*
0N/A * If we have a handshaker, but haven't started
0N/A * SSL traffic, we can throw away our current
0N/A * handshaker, and start from scratch. Don't
0N/A * need to call doneConnect() again, we already
0N/A * have the streams.
0N/A */
0N/A assert(handshaker != null);
2998N/A if (!handshaker.activated()) {
2998N/A /*
2998N/A * If we need to change the engine mode and the enabled
2998N/A * protocols haven't specifically been set by the user,
2998N/A * change them to the corresponding default ones.
2998N/A */
2998N/A if (roleIsServer != (!flag) &&
3988N/A sslContext.isDefaultProtocolList(enabledProtocols)) {
3988N/A enabledProtocols = sslContext.getDefaultProtocolList(!flag);
2998N/A }
2998N/A
0N/A roleIsServer = !flag;
0N/A connectionState = cs_START;
0N/A initHandshaker();
0N/A break;
0N/A }
0N/A
0N/A // If handshake has started, that's an error. Fall through...
0N/A
0N/A default:
0N/A if (debug != null && Debug.isOn("ssl")) {
0N/A System.out.println(threadName() +
0N/A ", setUseClientMode() invoked in state = " +
0N/A connectionState);
0N/A }
0N/A
0N/A /*
0N/A * We can let them continue if they catch this correctly,
0N/A * we don't need to shut this down.
0N/A */
0N/A throw new IllegalArgumentException(
0N/A "Cannot change mode after SSL traffic has started");
0N/A }
0N/A }
0N/A
0N/A synchronized public boolean getUseClientMode() {
0N/A return !roleIsServer;
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Returns the names of the cipher suites which could be enabled for use
0N/A * on an SSL connection. Normally, only a subset of these will actually
0N/A * be enabled by default, since this list may include cipher suites which
0N/A * do not support the mutual authentication of servers and clients, or
0N/A * which do not protect data confidentiality. Servers may also need
0N/A * certain kinds of certificates to use certain cipher suites.
0N/A *
0N/A * @return an array of cipher suite names
0N/A */
0N/A public String[] getSupportedCipherSuites() {
5356N/A return sslContext.getSupportedCipherSuiteList().toStringArray();
0N/A }
0N/A
0N/A /**
0N/A * Controls which particular cipher suites are enabled for use on
0N/A * this connection. The cipher suites must have been listed by
0N/A * getCipherSuites() as being supported. Even if a suite has been
0N/A * enabled, it might never be used if no peer supports it or the
0N/A * requisite certificates (and private keys) are not available.
0N/A *
0N/A * @param suites Names of all the cipher suites to enable.
0N/A */
0N/A synchronized public void setEnabledCipherSuites(String[] suites) {
0N/A enabledCipherSuites = new CipherSuiteList(suites);
2998N/A if ((handshaker != null) && !handshaker.activated()) {
2998N/A handshaker.setEnabledCipherSuites(enabledCipherSuites);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns the names of the SSL cipher suites which are currently enabled
0N/A * for use on this connection. When an SSL engine is first created,
0N/A * all enabled cipher suites <em>(a)</em> protect data confidentiality,
0N/A * by traffic encryption, and <em>(b)</em> can mutually authenticate
0N/A * both clients and servers. Thus, in some environments, this value
0N/A * might be empty.
0N/A *
0N/A * @return an array of cipher suite names
0N/A */
0N/A synchronized public String[] getEnabledCipherSuites() {
0N/A return enabledCipherSuites.toStringArray();
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Returns the protocols that are supported by this implementation.
0N/A * A subset of the supported protocols may be enabled for this connection
3002N/A * @return an array of protocol names.
0N/A */
0N/A public String[] getSupportedProtocols() {
3988N/A return sslContext.getSuportedProtocolList().toStringArray();
0N/A }
0N/A
0N/A /**
0N/A * Controls which protocols are enabled for use on
0N/A * this connection. The protocols must have been listed by
0N/A * getSupportedProtocols() as being supported.
0N/A *
0N/A * @param protocols protocols to enable.
0N/A * @exception IllegalArgumentException when one of the protocols
0N/A * named by the parameter is not supported.
0N/A */
0N/A synchronized public void setEnabledProtocols(String[] protocols) {
0N/A enabledProtocols = new ProtocolList(protocols);
2998N/A if ((handshaker != null) && !handshaker.activated()) {
0N/A handshaker.setEnabledProtocols(enabledProtocols);
0N/A }
0N/A }
0N/A
0N/A synchronized public String[] getEnabledProtocols() {
0N/A return enabledProtocols.toStringArray();
0N/A }
0N/A
0N/A /**
3002N/A * Returns the SSLParameters in effect for this SSLEngine.
0N/A */
3002N/A synchronized public SSLParameters getSSLParameters() {
3002N/A SSLParameters params = super.getSSLParameters();
3002N/A
3002N/A // the super implementation does not handle the following parameters
3002N/A params.setEndpointIdentificationAlgorithm(identificationProtocol);
3002N/A params.setAlgorithmConstraints(algorithmConstraints);
3002N/A
3002N/A return params;
0N/A }
0N/A
0N/A /**
3002N/A * Applies SSLParameters to this engine.
0N/A */
3002N/A synchronized public void setSSLParameters(SSLParameters params) {
3002N/A super.setSSLParameters(params);
3002N/A
3002N/A // the super implementation does not handle the following parameters
3002N/A identificationProtocol = params.getEndpointIdentificationAlgorithm();
3002N/A algorithmConstraints = params.getAlgorithmConstraints();
3002N/A if ((handshaker != null) && !handshaker.started()) {
3002N/A handshaker.setIdentificationProtocol(identificationProtocol);
3002N/A handshaker.setAlgorithmConstraints(algorithmConstraints);
3002N/A }
0N/A }
0N/A
0N/A /**
0N/A * Return the name of the current thread. Utility method.
0N/A */
0N/A private static String threadName() {
0N/A return Thread.currentThread().getName();
0N/A }
0N/A
0N/A /**
0N/A * Returns a printable representation of this end of the connection.
0N/A */
0N/A public String toString() {
0N/A StringBuilder retval = new StringBuilder(80);
0N/A
0N/A retval.append(Integer.toHexString(hashCode()));
0N/A retval.append("[");
0N/A retval.append("SSLEngine[hostname=");
0N/A String host = getPeerHost();
0N/A retval.append((host == null) ? "null" : host);
0N/A retval.append(" port=");
0N/A retval.append(Integer.toString(getPeerPort()));
0N/A retval.append("] ");
0N/A retval.append(getSession().getCipherSuite());
0N/A retval.append("]");
0N/A
0N/A return retval.toString();
0N/A }
0N/A}