mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			1206 lines
		
	
	
		
			42 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			1206 lines
		
	
	
		
			42 KiB
		
	
	
	
		
			Java
		
	
	
	
/* AbstractHandshake.java -- abstract handshake handler.
 | 
						|
   Copyright (C) 2006  Free Software Foundation, Inc.
 | 
						|
 | 
						|
This file is a part of GNU Classpath.
 | 
						|
 | 
						|
GNU Classpath is free software; you can redistribute it and/or modify
 | 
						|
it under the terms of the GNU General Public License as published by
 | 
						|
the Free Software Foundation; either version 2 of the License, or (at
 | 
						|
your option) any later version.
 | 
						|
 | 
						|
GNU Classpath 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 for more details.
 | 
						|
 | 
						|
You should have received a copy of the GNU General Public License
 | 
						|
along with GNU Classpath; if not, write to the Free Software
 | 
						|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
 | 
						|
USA
 | 
						|
 | 
						|
Linking this library statically or dynamically with other modules is
 | 
						|
making a combined work based on this library.  Thus, the terms and
 | 
						|
conditions of the GNU General Public License cover the whole
 | 
						|
combination.
 | 
						|
 | 
						|
As a special exception, the copyright holders of this library give you
 | 
						|
permission to link this library with independent modules to produce an
 | 
						|
executable, regardless of the license terms of these independent
 | 
						|
modules, and to copy and distribute the resulting executable under
 | 
						|
terms of your choice, provided that you also meet, for each linked
 | 
						|
independent module, the terms and conditions of the license of that
 | 
						|
module.  An independent module is a module which is not derived from
 | 
						|
or based on this library.  If you modify this library, you may extend
 | 
						|
this exception to your version of the library, but you are not
 | 
						|
obligated to do so.  If you do not wish to do so, delete this
 | 
						|
exception statement from your version.  */
 | 
						|
 | 
						|
 | 
						|
package gnu.javax.net.ssl.provider;
 | 
						|
 | 
						|
import gnu.classpath.debug.Component;
 | 
						|
import gnu.classpath.debug.SystemLogger;
 | 
						|
import gnu.java.security.action.GetSecurityPropertyAction;
 | 
						|
import gnu.java.security.prng.IRandom;
 | 
						|
import gnu.java.security.prng.LimitReachedException;
 | 
						|
import gnu.java.security.util.ByteArray;
 | 
						|
import gnu.javax.security.auth.callback.CertificateCallback;
 | 
						|
import gnu.javax.security.auth.callback.DefaultCallbackHandler;
 | 
						|
 | 
						|
import java.nio.ByteBuffer;
 | 
						|
import java.security.AccessController;
 | 
						|
import java.security.DigestException;
 | 
						|
import java.security.InvalidAlgorithmParameterException;
 | 
						|
import java.security.InvalidKeyException;
 | 
						|
import java.security.KeyManagementException;
 | 
						|
import java.security.MessageDigest;
 | 
						|
import java.security.NoSuchAlgorithmException;
 | 
						|
import java.security.PrivilegedExceptionAction;
 | 
						|
import java.security.SecureRandom;
 | 
						|
import java.security.cert.CertificateException;
 | 
						|
import java.security.cert.X509Certificate;
 | 
						|
import java.util.Arrays;
 | 
						|
import java.util.HashMap;
 | 
						|
import java.util.LinkedList;
 | 
						|
import java.util.zip.Deflater;
 | 
						|
import java.util.zip.Inflater;
 | 
						|
 | 
						|
import javax.crypto.Cipher;
 | 
						|
import javax.crypto.KeyAgreement;
 | 
						|
import javax.crypto.Mac;
 | 
						|
import javax.crypto.NoSuchPaddingException;
 | 
						|
import javax.crypto.SecretKey;
 | 
						|
import javax.crypto.interfaces.DHPrivateKey;
 | 
						|
import javax.crypto.interfaces.DHPublicKey;
 | 
						|
import javax.crypto.spec.IvParameterSpec;
 | 
						|
import javax.crypto.spec.SecretKeySpec;
 | 
						|
import javax.net.ssl.SSLEngineResult;
 | 
						|
import javax.net.ssl.SSLException;
 | 
						|
import javax.net.ssl.X509TrustManager;
 | 
						|
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
 | 
						|
import javax.security.auth.callback.Callback;
 | 
						|
import javax.security.auth.callback.CallbackHandler;
 | 
						|
import javax.security.auth.callback.ConfirmationCallback;
 | 
						|
 | 
						|
/**
 | 
						|
 * The base interface for handshake implementations. Concrete
 | 
						|
 * subclasses of this class (one for the server, one for the client)
 | 
						|
 * handle the HANDSHAKE content-type in communications.
 | 
						|
 */
 | 
						|
public abstract class AbstractHandshake
 | 
						|
{
 | 
						|
  protected static final SystemLogger logger = SystemLogger.SYSTEM;
 | 
						|
 | 
						|
  /**
 | 
						|
   * "server finished" -- TLS 1.0 and later
 | 
						|
   */
 | 
						|
  protected static final byte[] SERVER_FINISHED
 | 
						|
    = new byte[] {
 | 
						|
      115, 101, 114, 118, 101, 114,  32, 102, 105, 110, 105, 115,
 | 
						|
      104, 101, 100
 | 
						|
    };
 | 
						|
 | 
						|
  /**
 | 
						|
   * "client finished" -- TLS 1.0 and later
 | 
						|
   */
 | 
						|
  protected static final byte[] CLIENT_FINISHED
 | 
						|
    = new byte[] {
 | 
						|
       99, 108, 105, 101, 110, 116,  32, 102, 105, 110, 105, 115,
 | 
						|
      104, 101, 100
 | 
						|
    };
 | 
						|
 | 
						|
  /**
 | 
						|
   * "key expansion" -- TLS 1.0 and later
 | 
						|
   */
 | 
						|
  private static final byte[] KEY_EXPANSION =
 | 
						|
    new byte[] { 107, 101, 121,  32, 101, 120, 112,
 | 
						|
                  97, 110, 115, 105, 111, 110 };
 | 
						|
 | 
						|
  /**
 | 
						|
   * "master secret" -- TLS 1.0 and later
 | 
						|
   */
 | 
						|
  private static final byte[] MASTER_SECRET
 | 
						|
    = new byte[] {
 | 
						|
      109,  97, 115, 116, 101, 114,  32, 115, 101,  99, 114, 101, 116
 | 
						|
    };
 | 
						|
 | 
						|
  /**
 | 
						|
   * "client write key" -- TLS 1.0 exportable whitener.
 | 
						|
   */
 | 
						|
  private static final byte[] CLIENT_WRITE_KEY
 | 
						|
    = new byte[] {
 | 
						|
       99, 108, 105, 101, 110, 116,  32, 119, 114, 105, 116, 101,  32, 107,
 | 
						|
      101, 121
 | 
						|
    };
 | 
						|
 | 
						|
  /**
 | 
						|
   * "server write key" -- TLS 1.0 exportable whitener.
 | 
						|
   */
 | 
						|
  private static final byte[] SERVER_WRITE_KEY
 | 
						|
    = new byte[] {
 | 
						|
      115, 101, 114, 118, 101, 114,  32, 119, 114, 105, 116, 101,  32, 107,
 | 
						|
      101, 121
 | 
						|
    };
 | 
						|
 | 
						|
  private static final byte[] IV_BLOCK
 | 
						|
    = new byte[] {
 | 
						|
       73,  86,  32,  98, 108, 111,  99, 107
 | 
						|
    };
 | 
						|
 | 
						|
  /**
 | 
						|
   * SSL 3.0; the string "CLNT"
 | 
						|
   */
 | 
						|
  private static final byte[] SENDER_CLIENT
 | 
						|
    = new byte[] { 0x43, 0x4C, 0x4E, 0x54 };
 | 
						|
 | 
						|
  /**
 | 
						|
   * SSL 3.0; the string "SRVR"
 | 
						|
   */
 | 
						|
  private static final byte[] SENDER_SERVER
 | 
						|
    = new byte[] { 0x53, 0x52, 0x56, 0x52 };
 | 
						|
 | 
						|
  /**
 | 
						|
   * SSL 3.0; the value 0x36 40 (for SHA-1 hashes) or 48 (for MD5 hashes)
 | 
						|
   * times.
 | 
						|
   */
 | 
						|
  protected static final byte[] PAD1 = new byte[48];
 | 
						|
 | 
						|
  /**
 | 
						|
   * SSL 3.0; the value 0x5c 40 (for SHA-1 hashes) or 48 (for MD5 hashes)
 | 
						|
   * times.
 | 
						|
   */
 | 
						|
  protected static final byte[] PAD2 = new byte[48];
 | 
						|
 | 
						|
  static
 | 
						|
  {
 | 
						|
    Arrays.fill(PAD1, SSLHMac.PAD1);
 | 
						|
    Arrays.fill(PAD2, SSLHMac.PAD2);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * The currently-read handshake messages. There may be zero, or
 | 
						|
   * multiple, handshake messages in this buffer.
 | 
						|
   */
 | 
						|
  protected ByteBuffer handshakeBuffer;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The offset into `handshakeBuffer' where the first unread
 | 
						|
   * handshake message resides.
 | 
						|
   */
 | 
						|
  protected int handshakeOffset;
 | 
						|
 | 
						|
  protected MessageDigest sha;
 | 
						|
  protected MessageDigest md5;
 | 
						|
 | 
						|
  protected final SSLEngineImpl engine;
 | 
						|
  protected KeyAgreement keyAgreement;
 | 
						|
  protected byte[] preMasterSecret;
 | 
						|
  protected InputSecurityParameters inParams;
 | 
						|
  protected OutputSecurityParameters outParams;
 | 
						|
  protected LinkedList<DelegatedTask> tasks;
 | 
						|
  protected Random serverRandom;
 | 
						|
  protected Random clientRandom;
 | 
						|
  protected CompressionMethod compression;
 | 
						|
 | 
						|
  protected AbstractHandshake(SSLEngineImpl engine)
 | 
						|
    throws NoSuchAlgorithmException
 | 
						|
  {
 | 
						|
    this.engine = engine;
 | 
						|
    sha = MessageDigest.getInstance("SHA-1");
 | 
						|
    md5 = MessageDigest.getInstance("MD5");
 | 
						|
    tasks = new LinkedList<DelegatedTask>();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Handles the next input message in the handshake. This is called
 | 
						|
   * in response to a call to {@link javax.net.ssl.SSLEngine#unwrap}
 | 
						|
   * for a message with content-type HANDSHAKE.
 | 
						|
   *
 | 
						|
   * @param record The input record. The callee should not assume that
 | 
						|
   * the record's buffer is writable, and should not try to use it for
 | 
						|
   * output or temporary storage.
 | 
						|
   * @return An {@link SSLEngineResult} describing the result.
 | 
						|
   */
 | 
						|
  public final HandshakeStatus handleInput (ByteBuffer fragment)
 | 
						|
    throws SSLException
 | 
						|
  {
 | 
						|
    if (!tasks.isEmpty())
 | 
						|
      return HandshakeStatus.NEED_TASK;
 | 
						|
 | 
						|
    HandshakeStatus status = status();
 | 
						|
    if (status != HandshakeStatus.NEED_UNWRAP)
 | 
						|
      return status;
 | 
						|
 | 
						|
    // Try to read another...
 | 
						|
    if (!pollHandshake(fragment))
 | 
						|
      return HandshakeStatus.NEED_UNWRAP;
 | 
						|
 | 
						|
    while (hasMessage() && status != HandshakeStatus.NEED_WRAP)
 | 
						|
      {
 | 
						|
        int pos = handshakeOffset;
 | 
						|
        status = implHandleInput();
 | 
						|
        int len = handshakeOffset - pos;
 | 
						|
        if (len == 0)
 | 
						|
          {
 | 
						|
            // Don't bother; the impl is just telling us to go around
 | 
						|
            // again.
 | 
						|
            continue;
 | 
						|
          }
 | 
						|
        if (doHash())
 | 
						|
          {
 | 
						|
            if (Debug.DEBUG)
 | 
						|
              logger.logv(Component.SSL_HANDSHAKE, "hashing output\n{0}",
 | 
						|
                          Util.hexDump((ByteBuffer) handshakeBuffer
 | 
						|
                                       .duplicate().position(pos)
 | 
						|
                                       .limit(pos+len), " >> "));
 | 
						|
            sha.update((ByteBuffer) handshakeBuffer.duplicate()
 | 
						|
                       .position(pos).limit(pos+len));
 | 
						|
            md5.update((ByteBuffer) handshakeBuffer.duplicate()
 | 
						|
                       .position(pos).limit(pos+len));
 | 
						|
          }
 | 
						|
      }
 | 
						|
    return status;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Called to process more handshake data. This method will be called
 | 
						|
   * repeatedly while there is remaining handshake data, and while the
 | 
						|
   * status is
 | 
						|
   * @return
 | 
						|
   * @throws SSLException
 | 
						|
   */
 | 
						|
  protected abstract HandshakeStatus implHandleInput()
 | 
						|
    throws SSLException;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Produce more handshake output. This is called in response to a
 | 
						|
   * call to {@link javax.net.ssl.SSLEngine#wrap}, when the handshake
 | 
						|
   * is still in progress.
 | 
						|
   *
 | 
						|
   * @param record The output record; the callee should put its output
 | 
						|
   * handshake message (or a part of it) in the argument's
 | 
						|
   * <code>fragment</code>, and should set the record length
 | 
						|
   * appropriately.
 | 
						|
   * @return An {@link SSLEngineResult} describing the result.
 | 
						|
   */
 | 
						|
  public final HandshakeStatus handleOutput (ByteBuffer fragment)
 | 
						|
    throws SSLException
 | 
						|
  {
 | 
						|
    if (!tasks.isEmpty())
 | 
						|
      return HandshakeStatus.NEED_TASK;
 | 
						|
 | 
						|
    int orig = fragment.position();
 | 
						|
    SSLEngineResult.HandshakeStatus status = implHandleOutput(fragment);
 | 
						|
    if (doHash())
 | 
						|
      {
 | 
						|
        if (Debug.DEBUG)
 | 
						|
          logger.logv(Component.SSL_HANDSHAKE, "hashing output:\n{0}",
 | 
						|
                      Util.hexDump((ByteBuffer) fragment.duplicate().flip().position(orig), " >> "));
 | 
						|
        sha.update((ByteBuffer) fragment.duplicate().flip().position(orig));
 | 
						|
        md5.update((ByteBuffer) fragment.duplicate().flip().position(orig));
 | 
						|
      }
 | 
						|
    return status;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Called to implement the underlying output handling. The callee should
 | 
						|
   * attempt to fill the given buffer as much as it can; this can include
 | 
						|
   * multiple, and even partial, handshake messages.
 | 
						|
   *
 | 
						|
   * @param fragment The buffer the callee should write handshake messages to.
 | 
						|
   * @return The new status of the handshake.
 | 
						|
   * @throws SSLException If an error occurs processing the output message.
 | 
						|
   */
 | 
						|
  protected abstract SSLEngineResult.HandshakeStatus implHandleOutput (ByteBuffer fragment)
 | 
						|
    throws SSLException;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Return a new instance of input security parameters, initialized with
 | 
						|
   * the session key. It is, of course, only valid to invoke this method
 | 
						|
   * once the handshake is complete, and the session keys established.
 | 
						|
   *
 | 
						|
   * <p>In the presence of a well-behaving peer, this should be called once
 | 
						|
   * the <code>ChangeCipherSpec</code> message is recieved.
 | 
						|
   *
 | 
						|
   * @return The input parameters for the newly established session.
 | 
						|
   * @throws SSLException If the handshake is not complete.
 | 
						|
   */
 | 
						|
  final InputSecurityParameters getInputParams() throws SSLException
 | 
						|
  {
 | 
						|
    checkKeyExchange();
 | 
						|
    return inParams;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Return a new instance of output security parameters, initialized with
 | 
						|
   * the session key. This should be called after the
 | 
						|
   * <code>ChangeCipherSpec</code> message is sent to the peer.
 | 
						|
   *
 | 
						|
   * @return The output parameters for the newly established session.
 | 
						|
   * @throws SSLException If the handshake is not complete.
 | 
						|
   */
 | 
						|
  final OutputSecurityParameters getOutputParams() throws SSLException
 | 
						|
  {
 | 
						|
    checkKeyExchange();
 | 
						|
    return outParams;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Fetch a delegated task waiting to run, if any.
 | 
						|
   *
 | 
						|
   * @return The task.
 | 
						|
   */
 | 
						|
  final Runnable getTask()
 | 
						|
  {
 | 
						|
    if (tasks.isEmpty())
 | 
						|
      return null;
 | 
						|
    return tasks.removeFirst();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Used by the skeletal code to query the current status of the handshake.
 | 
						|
   * This <em>should</em> be the same value as returned by the previous call
 | 
						|
   * to {@link #implHandleOutput(ByteBuffer)} or {@link
 | 
						|
   *  #implHandleInput(ByteBuffer)}.
 | 
						|
   *
 | 
						|
   * @return The current handshake status.
 | 
						|
   */
 | 
						|
  abstract HandshakeStatus status();
 | 
						|
 | 
						|
  /**
 | 
						|
   * Check if the key exchange completed successfully, throwing an exception
 | 
						|
   * if not.
 | 
						|
   *
 | 
						|
   * <p>Note that we assume that the caller of our SSLEngine is correct, and
 | 
						|
   * that they did run the delegated tasks that encapsulate the key exchange.
 | 
						|
   * What we are primarily checking, therefore, is that no error occurred in the
 | 
						|
   * key exchange operation itself.
 | 
						|
   *
 | 
						|
   * @throws SSLException If the key exchange did not complete successfully.
 | 
						|
   */
 | 
						|
  abstract void checkKeyExchange() throws SSLException;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Handle an SSLv2 client hello. This is only used by SSL servers.
 | 
						|
   *
 | 
						|
   * @param hello The hello message.
 | 
						|
   */
 | 
						|
  abstract void handleV2Hello(ByteBuffer hello) throws SSLException;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Attempt to read the next handshake message from the given
 | 
						|
   * record. If only a partial handshake message is available, then
 | 
						|
   * this method saves the incoming bytes and returns false. If a
 | 
						|
   * complete handshake is read, or if there was one buffered in the
 | 
						|
   * handshake buffer, this method returns true, and `handshakeBuffer'
 | 
						|
   * can be used to read the handshake.
 | 
						|
   *
 | 
						|
   * @param record The input record.
 | 
						|
   * @return True if a complete handshake is present in the buffer;
 | 
						|
   * false if only a partial one.
 | 
						|
   */
 | 
						|
  protected boolean pollHandshake (final ByteBuffer fragment)
 | 
						|
  {
 | 
						|
    // Allocate space for the new fragment.
 | 
						|
    if (handshakeBuffer == null
 | 
						|
        || handshakeBuffer.remaining() < fragment.remaining())
 | 
						|
      {
 | 
						|
        // We need space for anything still unread in the handshake
 | 
						|
        // buffer...
 | 
						|
        int len = ((handshakeBuffer == null) ? 0
 | 
						|
                   : handshakeBuffer.position() - handshakeOffset);
 | 
						|
 | 
						|
        // Plus room for the incoming record.
 | 
						|
        len += fragment.remaining();
 | 
						|
        reallocateBuffer(len);
 | 
						|
      }
 | 
						|
 | 
						|
    if (Debug.DEBUG)
 | 
						|
      logger.logv(Component.SSL_HANDSHAKE, "inserting {0} into {1}",
 | 
						|
                  fragment, handshakeBuffer);
 | 
						|
 | 
						|
    // Put the fragment into the buffer.
 | 
						|
    handshakeBuffer.put(fragment);
 | 
						|
 | 
						|
    return hasMessage();
 | 
						|
  }
 | 
						|
 | 
						|
  protected boolean doHash()
 | 
						|
  {
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Tell if the handshake buffer currently has a full handshake
 | 
						|
   * message.
 | 
						|
   */
 | 
						|
  protected boolean hasMessage()
 | 
						|
  {
 | 
						|
    if (handshakeBuffer == null)
 | 
						|
      return false;
 | 
						|
    ByteBuffer tmp = handshakeBuffer.duplicate();
 | 
						|
    tmp.flip();
 | 
						|
    tmp.position(handshakeOffset);
 | 
						|
    if (Debug.DEBUG)
 | 
						|
      logger.logv(Component.SSL_HANDSHAKE, "current buffer: {0}; test buffer {1}",
 | 
						|
                  handshakeBuffer, tmp);
 | 
						|
    if (tmp.remaining() < 4)
 | 
						|
      return false;
 | 
						|
    Handshake handshake = new Handshake(tmp.slice());
 | 
						|
    if (Debug.DEBUG)
 | 
						|
      logger.logv(Component.SSL_HANDSHAKE, "handshake len:{0} remaining:{1}",
 | 
						|
                  handshake.length(), tmp.remaining());
 | 
						|
    return (handshake.length() <= tmp.remaining() - 4);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Reallocate the handshake buffer so it can hold `totalLen'
 | 
						|
   * bytes. The smallest buffer allocated is 1024 bytes, and the size
 | 
						|
   * doubles from there until the buffer is sufficiently large.
 | 
						|
   */
 | 
						|
  private void reallocateBuffer (final int totalLen)
 | 
						|
  {
 | 
						|
    int len = handshakeBuffer == null ? -1
 | 
						|
                                      : handshakeBuffer.capacity() - (handshakeBuffer.limit() - handshakeOffset);
 | 
						|
    if (len >= totalLen)
 | 
						|
      {
 | 
						|
        // Big enough; no need to reallocate; but maybe shift the contents
 | 
						|
        // down.
 | 
						|
        if (handshakeOffset > 0)
 | 
						|
          {
 | 
						|
            handshakeBuffer.flip().position(handshakeOffset);
 | 
						|
            handshakeBuffer.compact();
 | 
						|
            handshakeOffset = 0;
 | 
						|
          }
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
    // Start at 1K (probably the system's page size). Double the size
 | 
						|
    // from there.
 | 
						|
    len = 1024;
 | 
						|
    while (len < totalLen)
 | 
						|
      len = len << 1;
 | 
						|
    ByteBuffer newBuf = ByteBuffer.allocate (len);
 | 
						|
 | 
						|
    // Copy the unread bytes from the old buffer.
 | 
						|
    if (handshakeBuffer != null)
 | 
						|
      {
 | 
						|
        handshakeBuffer.flip ();
 | 
						|
        handshakeBuffer.position(handshakeOffset);
 | 
						|
        newBuf.put(handshakeBuffer);
 | 
						|
      }
 | 
						|
    handshakeBuffer = newBuf;
 | 
						|
 | 
						|
    // We just put only unread handshake messages in the new buffer;
 | 
						|
    // the offset of the next one is now zero.
 | 
						|
    handshakeOffset = 0;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Generate a certificate verify message for SSLv3. In SSLv3, a different
 | 
						|
   * algorithm was used to generate this value was subtly different than
 | 
						|
   * that used in TLSv1.0 and later. In TLSv1.0 and later, this value is
 | 
						|
   * just the digest over the handshake messages.
 | 
						|
   *
 | 
						|
   * <p>SSLv3 uses the algorithm:
 | 
						|
   *
 | 
						|
   * <pre>
 | 
						|
CertificateVerify.signature.md5_hash
 | 
						|
  MD5(master_secret + pad_2 +
 | 
						|
      MD5(handshake_messages + master_secret + pad_1));
 | 
						|
Certificate.signature.sha_hash
 | 
						|
  SHA(master_secret + pad_2 +
 | 
						|
      SHA(handshake_messages + master_secret + pad_1));</pre>
 | 
						|
   *
 | 
						|
   * @param md5 The running MD5 hash of the handshake.
 | 
						|
   * @param sha The running SHA-1 hash of the handshake.
 | 
						|
   * @param session The current session being negotiated.
 | 
						|
   * @return The computed to-be-signed value.
 | 
						|
   */
 | 
						|
  protected byte[] genV3CertificateVerify(MessageDigest md5,
 | 
						|
                                          MessageDigest sha,
 | 
						|
                                          SessionImpl session)
 | 
						|
  {
 | 
						|
    byte[] md5value = null;
 | 
						|
    if (session.suite.signatureAlgorithm() == SignatureAlgorithm.RSA)
 | 
						|
      {
 | 
						|
        md5.update(session.privateData.masterSecret);
 | 
						|
        md5.update(PAD1, 0, 48);
 | 
						|
        byte[] tmp = md5.digest();
 | 
						|
        md5.reset();
 | 
						|
        md5.update(session.privateData.masterSecret);
 | 
						|
        md5.update(PAD2, 0, 48);
 | 
						|
        md5.update(tmp);
 | 
						|
        md5value = md5.digest();
 | 
						|
      }
 | 
						|
 | 
						|
    sha.update(session.privateData.masterSecret);
 | 
						|
    sha.update(PAD1, 0, 40);
 | 
						|
    byte[] tmp = sha.digest();
 | 
						|
    sha.reset();
 | 
						|
    sha.update(session.privateData.masterSecret);
 | 
						|
    sha.update(PAD2, 0, 40);
 | 
						|
    sha.update(tmp);
 | 
						|
    byte[] shavalue = sha.digest();
 | 
						|
 | 
						|
    if (md5value != null)
 | 
						|
      return Util.concat(md5value, shavalue);
 | 
						|
 | 
						|
    return shavalue;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Generate the session keys from the computed master secret.
 | 
						|
   *
 | 
						|
   * @param clientRandom The client's nonce.
 | 
						|
   * @param serverRandom The server's nonce.
 | 
						|
   * @param session The session being established.
 | 
						|
   * @return The derived keys.
 | 
						|
   */
 | 
						|
  protected byte[][] generateKeys(Random clientRandom, Random serverRandom,
 | 
						|
                                  SessionImpl session)
 | 
						|
  {
 | 
						|
    int maclen = 20; // SHA-1.
 | 
						|
    if (session.suite.macAlgorithm() == MacAlgorithm.MD5)
 | 
						|
      maclen = 16;
 | 
						|
    int ivlen = 0;
 | 
						|
    if (session.suite.cipherAlgorithm() == CipherAlgorithm.DES
 | 
						|
        || session.suite.cipherAlgorithm() == CipherAlgorithm.DESede)
 | 
						|
      ivlen = 8;
 | 
						|
    if (session.suite.cipherAlgorithm() == CipherAlgorithm.AES)
 | 
						|
      ivlen = 16;
 | 
						|
    int keylen = session.suite.keyLength();
 | 
						|
 | 
						|
    byte[][] keys = new byte[6][];
 | 
						|
    keys[0] = new byte[maclen]; // client_write_MAC_secret
 | 
						|
    keys[1] = new byte[maclen]; // server_write_MAC_secret
 | 
						|
    keys[2] = new byte[keylen]; // client_write_key
 | 
						|
    keys[3] = new byte[keylen]; // server_write_key
 | 
						|
    keys[4] = new byte[ivlen];  // client_write_iv
 | 
						|
    keys[5] = new byte[ivlen];  // server_write_iv
 | 
						|
 | 
						|
    IRandom prf = null;
 | 
						|
    if (session.version == ProtocolVersion.SSL_3)
 | 
						|
      {
 | 
						|
        byte[] seed = new byte[clientRandom.length()
 | 
						|
                               + serverRandom.length()];
 | 
						|
        serverRandom.buffer().get(seed, 0, serverRandom.length());
 | 
						|
        clientRandom.buffer().get(seed, serverRandom.length(),
 | 
						|
                                  clientRandom.length());
 | 
						|
        prf = new SSLRandom();
 | 
						|
        HashMap<String,byte[]> attr = new HashMap<String,byte[]>(2);
 | 
						|
        attr.put(SSLRandom.SECRET, session.privateData.masterSecret);
 | 
						|
        attr.put(SSLRandom.SEED, seed);
 | 
						|
        prf.init(attr);
 | 
						|
      }
 | 
						|
    else
 | 
						|
      {
 | 
						|
        byte[] seed = new byte[KEY_EXPANSION.length
 | 
						|
                               + clientRandom.length()
 | 
						|
                               + serverRandom.length()];
 | 
						|
        System.arraycopy(KEY_EXPANSION, 0, seed, 0, KEY_EXPANSION.length);
 | 
						|
        serverRandom.buffer().get(seed, KEY_EXPANSION.length,
 | 
						|
                                  serverRandom.length());
 | 
						|
        clientRandom.buffer().get(seed, (KEY_EXPANSION.length
 | 
						|
                                         + serverRandom.length()),
 | 
						|
                                  clientRandom.length());
 | 
						|
 | 
						|
        prf = new TLSRandom();
 | 
						|
        HashMap<String,byte[]> attr = new HashMap<String,byte[]>(2);
 | 
						|
        attr.put(TLSRandom.SECRET, session.privateData.masterSecret);
 | 
						|
        attr.put(TLSRandom.SEED, seed);
 | 
						|
        prf.init(attr);
 | 
						|
      }
 | 
						|
 | 
						|
    try
 | 
						|
      {
 | 
						|
        prf.nextBytes(keys[0], 0, keys[0].length);
 | 
						|
        prf.nextBytes(keys[1], 0, keys[1].length);
 | 
						|
        prf.nextBytes(keys[2], 0, keys[2].length);
 | 
						|
        prf.nextBytes(keys[3], 0, keys[3].length);
 | 
						|
 | 
						|
        if (session.suite.isExportable())
 | 
						|
          {
 | 
						|
            if (session.version == ProtocolVersion.SSL_3)
 | 
						|
              {
 | 
						|
                MessageDigest md5 = MessageDigest.getInstance("MD5");
 | 
						|
                md5.update(clientRandom.buffer());
 | 
						|
                md5.update(serverRandom.buffer());
 | 
						|
                byte[] d = md5.digest();
 | 
						|
                System.arraycopy(d, 0, keys[4], 0, keys[4].length);
 | 
						|
 | 
						|
                md5.reset();
 | 
						|
                md5.update(serverRandom.buffer());
 | 
						|
                md5.update(clientRandom.buffer());
 | 
						|
                d = md5.digest();
 | 
						|
                System.arraycopy(d, 0, keys[5], 0, keys[5].length);
 | 
						|
 | 
						|
                md5.reset();
 | 
						|
                md5.update(keys[2]);
 | 
						|
                md5.update(clientRandom.buffer());
 | 
						|
                md5.update(serverRandom.buffer());
 | 
						|
                keys[2] = Util.trim(md5.digest(), 8);
 | 
						|
 | 
						|
                md5.reset();
 | 
						|
                md5.update(keys[3]);
 | 
						|
                md5.update(serverRandom.buffer());
 | 
						|
                md5.update(clientRandom.buffer());
 | 
						|
                keys[3] = Util.trim(md5.digest(), 8);
 | 
						|
              }
 | 
						|
            else
 | 
						|
              {
 | 
						|
                TLSRandom prf2 = new TLSRandom();
 | 
						|
                HashMap<String,byte[]> attr = new HashMap<String,byte[]>(2);
 | 
						|
                attr.put(TLSRandom.SECRET, keys[2]);
 | 
						|
                byte[] seed = new byte[CLIENT_WRITE_KEY.length +
 | 
						|
                                       clientRandom.length() +
 | 
						|
                                       serverRandom.length()];
 | 
						|
                System.arraycopy(CLIENT_WRITE_KEY, 0, seed, 0,
 | 
						|
                                 CLIENT_WRITE_KEY.length);
 | 
						|
                clientRandom.buffer().get(seed, CLIENT_WRITE_KEY.length,
 | 
						|
                                          clientRandom.length());
 | 
						|
                serverRandom.buffer().get(seed, CLIENT_WRITE_KEY.length
 | 
						|
                                          + clientRandom.length(),
 | 
						|
                                          serverRandom.length());
 | 
						|
                attr.put(TLSRandom.SEED, seed);
 | 
						|
                prf2.init(attr);
 | 
						|
                keys[2] = new byte[8];
 | 
						|
                prf2.nextBytes(keys[2], 0, keys[2].length);
 | 
						|
 | 
						|
                attr.put(TLSRandom.SECRET, keys[3]);
 | 
						|
                seed = new byte[SERVER_WRITE_KEY.length +
 | 
						|
                                serverRandom.length() +
 | 
						|
                                clientRandom.length()];
 | 
						|
                System.arraycopy(SERVER_WRITE_KEY, 0, seed, 0,
 | 
						|
                                 SERVER_WRITE_KEY.length);
 | 
						|
                serverRandom.buffer().get(seed, SERVER_WRITE_KEY.length,
 | 
						|
                                          serverRandom.length());
 | 
						|
                clientRandom.buffer().get(seed, SERVER_WRITE_KEY.length
 | 
						|
                                          + serverRandom.length(),
 | 
						|
                                          + clientRandom.length());
 | 
						|
                attr.put(TLSRandom.SEED, seed);
 | 
						|
                prf2.init(attr);
 | 
						|
                keys[3] = new byte[8];
 | 
						|
                prf2.nextBytes(keys[3], 0, keys[3].length);
 | 
						|
 | 
						|
                attr.put(TLSRandom.SECRET, new byte[0]);
 | 
						|
                seed = new byte[IV_BLOCK.length +
 | 
						|
                                clientRandom.length() +
 | 
						|
                                serverRandom.length()];
 | 
						|
                System.arraycopy(IV_BLOCK, 0, seed, 0, IV_BLOCK.length);
 | 
						|
                clientRandom.buffer().get(seed, IV_BLOCK.length,
 | 
						|
                                          clientRandom.length());
 | 
						|
                serverRandom.buffer().get(seed, IV_BLOCK.length
 | 
						|
                                          + clientRandom.length(),
 | 
						|
                                          serverRandom.length());
 | 
						|
                attr.put(TLSRandom.SEED, seed);
 | 
						|
                prf2.init(attr);
 | 
						|
                prf2.nextBytes(keys[4], 0, keys[4].length);
 | 
						|
                prf2.nextBytes(keys[5], 0, keys[5].length);
 | 
						|
              }
 | 
						|
          }
 | 
						|
        else
 | 
						|
          {
 | 
						|
            prf.nextBytes(keys[4], 0, keys[4].length);
 | 
						|
            prf.nextBytes(keys[5], 0, keys[5].length);
 | 
						|
          }
 | 
						|
      }
 | 
						|
    catch (LimitReachedException lre)
 | 
						|
      {
 | 
						|
        // Won't happen with our implementation.
 | 
						|
        throw new Error(lre);
 | 
						|
      }
 | 
						|
    catch (NoSuchAlgorithmException nsae)
 | 
						|
      {
 | 
						|
        throw new Error(nsae);
 | 
						|
      }
 | 
						|
 | 
						|
    if (Debug.DEBUG_KEY_EXCHANGE)
 | 
						|
      logger.logv(Component.SSL_KEY_EXCHANGE,
 | 
						|
                  "keys generated;\n  [0]: {0}\n  [1]: {1}\n  [2]: {2}\n" +
 | 
						|
                  "  [3]: {3}\n  [4]: {4}\n  [5]: {5}",
 | 
						|
                  Util.toHexString(keys[0], ':'),
 | 
						|
                  Util.toHexString(keys[1], ':'),
 | 
						|
                  Util.toHexString(keys[2], ':'),
 | 
						|
                  Util.toHexString(keys[3], ':'),
 | 
						|
                  Util.toHexString(keys[4], ':'),
 | 
						|
                  Util.toHexString(keys[5], ':'));
 | 
						|
    return keys;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Generate a "finished" message. The hashes passed in are modified
 | 
						|
   * by this function, so they should be clone copies of the digest if
 | 
						|
   * the hash function needs to be used more.
 | 
						|
   *
 | 
						|
   * @param md5 The MD5 computation.
 | 
						|
   * @param sha The SHA-1 computation.
 | 
						|
   * @param isClient Whether or not the client-side finished message is
 | 
						|
   *  being computed.
 | 
						|
   * @param session The current session.
 | 
						|
   * @return A byte buffer containing the computed finished message.
 | 
						|
   */
 | 
						|
  protected ByteBuffer generateFinished(MessageDigest md5,
 | 
						|
                                        MessageDigest sha,
 | 
						|
                                        boolean isClient,
 | 
						|
                                        SessionImpl session)
 | 
						|
  {
 | 
						|
    ByteBuffer finishedBuffer = null;
 | 
						|
    if (session.version.compareTo(ProtocolVersion.TLS_1) >= 0)
 | 
						|
      {
 | 
						|
        finishedBuffer = ByteBuffer.allocate(12);
 | 
						|
        TLSRandom prf = new TLSRandom();
 | 
						|
        byte[] md5val = md5.digest();
 | 
						|
        byte[] shaval = sha.digest();
 | 
						|
        if (Debug.DEBUG)
 | 
						|
          logger.logv(Component.SSL_HANDSHAKE, "finished md5:{0} sha:{1}",
 | 
						|
                      Util.toHexString(md5val, ':'),
 | 
						|
                      Util.toHexString(shaval, ':'));
 | 
						|
        byte[] seed = new byte[CLIENT_FINISHED.length
 | 
						|
                               + md5val.length
 | 
						|
                               + shaval.length];
 | 
						|
        if (isClient)
 | 
						|
          System.arraycopy(CLIENT_FINISHED, 0, seed, 0, CLIENT_FINISHED.length);
 | 
						|
        else
 | 
						|
          System.arraycopy(SERVER_FINISHED, 0, seed, 0, SERVER_FINISHED.length);
 | 
						|
        System.arraycopy(md5val, 0,
 | 
						|
                         seed, CLIENT_FINISHED.length,
 | 
						|
                         md5val.length);
 | 
						|
        System.arraycopy(shaval, 0,
 | 
						|
                         seed, CLIENT_FINISHED.length + md5val.length,
 | 
						|
                         shaval.length);
 | 
						|
        HashMap<String, Object> params = new HashMap<String, Object>(2);
 | 
						|
        params.put(TLSRandom.SECRET, session.privateData.masterSecret);
 | 
						|
        params.put(TLSRandom.SEED, seed);
 | 
						|
        prf.init(params);
 | 
						|
        byte[] buf = new byte[12];
 | 
						|
        prf.nextBytes(buf, 0, buf.length);
 | 
						|
        finishedBuffer.put(buf).position(0);
 | 
						|
      }
 | 
						|
    else
 | 
						|
      {
 | 
						|
        // The SSLv3 algorithm is:
 | 
						|
        //
 | 
						|
        //   enum { client(0x434C4E54), server(0x53525652) } Sender;
 | 
						|
        //
 | 
						|
        //   struct {
 | 
						|
        //     opaque md5_hash[16];
 | 
						|
        //     opaque sha_hash[20];
 | 
						|
        //   } Finished;
 | 
						|
        //
 | 
						|
        //   md5_hash       MD5(master_secret + pad2 +
 | 
						|
        //                      MD5(handshake_messages + Sender +
 | 
						|
        //                          master_secret + pad1));
 | 
						|
        //   sha_hash        SHA(master_secret + pad2 +
 | 
						|
        //                       SHA(handshake_messages + Sender +
 | 
						|
        //                           master_secret + pad1));
 | 
						|
        //
 | 
						|
 | 
						|
        finishedBuffer = ByteBuffer.allocate(36);
 | 
						|
 | 
						|
        md5.update(isClient ? SENDER_CLIENT : SENDER_SERVER);
 | 
						|
        md5.update(session.privateData.masterSecret);
 | 
						|
        md5.update(PAD1);
 | 
						|
 | 
						|
        byte[] tmp = md5.digest();
 | 
						|
        md5.reset();
 | 
						|
        md5.update(session.privateData.masterSecret);
 | 
						|
        md5.update(PAD2);
 | 
						|
        md5.update(tmp);
 | 
						|
        finishedBuffer.put(md5.digest());
 | 
						|
 | 
						|
        sha.update(isClient ? SENDER_CLIENT : SENDER_SERVER);
 | 
						|
        sha.update(session.privateData.masterSecret);
 | 
						|
        sha.update(PAD1, 0, 40);
 | 
						|
 | 
						|
        tmp = sha.digest();
 | 
						|
        sha.reset();
 | 
						|
        sha.update(session.privateData.masterSecret);
 | 
						|
        sha.update(PAD2, 0, 40);
 | 
						|
        sha.update(tmp);
 | 
						|
        finishedBuffer.put(sha.digest()).position(0);
 | 
						|
      }
 | 
						|
    return finishedBuffer;
 | 
						|
  }
 | 
						|
 | 
						|
  protected void initDiffieHellman(DHPrivateKey dhKey, SecureRandom random)
 | 
						|
    throws SSLException
 | 
						|
  {
 | 
						|
    try
 | 
						|
      {
 | 
						|
        keyAgreement = KeyAgreement.getInstance("DH");
 | 
						|
        keyAgreement.init(dhKey, random);
 | 
						|
      }
 | 
						|
    catch (InvalidKeyException ike)
 | 
						|
      {
 | 
						|
        throw new SSLException(ike);
 | 
						|
      }
 | 
						|
    catch (NoSuchAlgorithmException nsae)
 | 
						|
      {
 | 
						|
        throw new SSLException(nsae);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  protected void generateMasterSecret(Random clientRandom,
 | 
						|
                                      Random serverRandom,
 | 
						|
                                      SessionImpl session)
 | 
						|
    throws SSLException
 | 
						|
  {
 | 
						|
    assert(clientRandom != null);
 | 
						|
    assert(serverRandom != null);
 | 
						|
    assert(session != null);
 | 
						|
 | 
						|
    if (Debug.DEBUG_KEY_EXCHANGE)
 | 
						|
      logger.logv(Component.SSL_KEY_EXCHANGE, "preMasterSecret:\n{0}",
 | 
						|
                  new ByteArray(preMasterSecret));
 | 
						|
 | 
						|
    if (session.version == ProtocolVersion.SSL_3)
 | 
						|
      {
 | 
						|
        try
 | 
						|
          {
 | 
						|
            MessageDigest _md5 = MessageDigest.getInstance("MD5");
 | 
						|
            MessageDigest _sha = MessageDigest.getInstance("SHA");
 | 
						|
            session.privateData.masterSecret = new byte[48];
 | 
						|
 | 
						|
            _sha.update((byte) 'A');
 | 
						|
            _sha.update(preMasterSecret);
 | 
						|
            _sha.update(clientRandom.buffer());
 | 
						|
            _sha.update(serverRandom.buffer());
 | 
						|
            _md5.update(preMasterSecret);
 | 
						|
            _md5.update(_sha.digest());
 | 
						|
            _md5.digest(session.privateData.masterSecret, 0, 16);
 | 
						|
 | 
						|
            _sha.update((byte) 'B');
 | 
						|
            _sha.update((byte) 'B');
 | 
						|
            _sha.update(preMasterSecret);
 | 
						|
            _sha.update(clientRandom.buffer());
 | 
						|
            _sha.update(serverRandom.buffer());
 | 
						|
            _md5.update(preMasterSecret);
 | 
						|
            _md5.update(_sha.digest());
 | 
						|
            _md5.digest(session.privateData.masterSecret, 16, 16);
 | 
						|
 | 
						|
            _sha.update((byte) 'C');
 | 
						|
            _sha.update((byte) 'C');
 | 
						|
            _sha.update((byte) 'C');
 | 
						|
            _sha.update(preMasterSecret);
 | 
						|
            _sha.update(clientRandom.buffer());
 | 
						|
            _sha.update(serverRandom.buffer());
 | 
						|
            _md5.update(preMasterSecret);
 | 
						|
            _md5.update(_sha.digest());
 | 
						|
            _md5.digest(session.privateData.masterSecret, 32, 16);
 | 
						|
          }
 | 
						|
        catch (DigestException de)
 | 
						|
          {
 | 
						|
            throw new SSLException(de);
 | 
						|
          }
 | 
						|
        catch (NoSuchAlgorithmException nsae)
 | 
						|
          {
 | 
						|
            throw new SSLException(nsae);
 | 
						|
          }
 | 
						|
      }
 | 
						|
    else // TLSv1.0 and later
 | 
						|
      {
 | 
						|
        byte[] seed = new byte[clientRandom.length()
 | 
						|
                               + serverRandom.length()
 | 
						|
                               + MASTER_SECRET.length];
 | 
						|
        System.arraycopy(MASTER_SECRET, 0, seed, 0, MASTER_SECRET.length);
 | 
						|
        clientRandom.buffer().get(seed, MASTER_SECRET.length,
 | 
						|
                                  clientRandom.length());
 | 
						|
        serverRandom.buffer().get(seed,
 | 
						|
                                  MASTER_SECRET.length + clientRandom.length(),
 | 
						|
                                  serverRandom.length());
 | 
						|
        TLSRandom prf = new TLSRandom();
 | 
						|
        HashMap<String,byte[]> attr = new HashMap<String,byte[]>(2);
 | 
						|
        attr.put(TLSRandom.SECRET, preMasterSecret);
 | 
						|
        attr.put(TLSRandom.SEED, seed);
 | 
						|
        prf.init(attr);
 | 
						|
 | 
						|
        session.privateData.masterSecret = new byte[48];
 | 
						|
        prf.nextBytes(session.privateData.masterSecret, 0, 48);
 | 
						|
      }
 | 
						|
 | 
						|
    if (Debug.DEBUG_KEY_EXCHANGE)
 | 
						|
      logger.log(Component.SSL_KEY_EXCHANGE, "master_secret: {0}",
 | 
						|
                 new ByteArray(session.privateData.masterSecret));
 | 
						|
 | 
						|
    // Wipe out the preMasterSecret.
 | 
						|
    for (int i = 0; i < preMasterSecret.length; i++)
 | 
						|
      preMasterSecret[i] = 0;
 | 
						|
  }
 | 
						|
 | 
						|
  protected void setupSecurityParameters(byte[][] keys, boolean isClient,
 | 
						|
                                         SSLEngineImpl engine,
 | 
						|
                                         CompressionMethod compression)
 | 
						|
    throws SSLException
 | 
						|
  {
 | 
						|
    assert(keys.length == 6);
 | 
						|
    assert(engine != null);
 | 
						|
    assert(compression != null);
 | 
						|
 | 
						|
    try
 | 
						|
      {
 | 
						|
        CipherSuite s = engine.session().suite;
 | 
						|
        Cipher inCipher = s.cipher();
 | 
						|
        Mac inMac = s.mac(engine.session().version);
 | 
						|
        Inflater inflater = (compression == CompressionMethod.ZLIB
 | 
						|
                             ? new Inflater() : null);
 | 
						|
        inCipher.init(Cipher.DECRYPT_MODE,
 | 
						|
                      new SecretKeySpec(keys[isClient ? 3 : 2],
 | 
						|
                                        s.cipherAlgorithm().toString()),
 | 
						|
                      new IvParameterSpec(keys[isClient ? 5 : 4]));
 | 
						|
        inMac.init(new SecretKeySpec(keys[isClient ? 1 : 0],
 | 
						|
                                     inMac.getAlgorithm()));
 | 
						|
        inParams = new InputSecurityParameters(inCipher, inMac,
 | 
						|
                                               inflater,
 | 
						|
                                               engine.session(), s);
 | 
						|
 | 
						|
        Cipher outCipher = s.cipher();
 | 
						|
        Mac outMac = s.mac(engine.session().version);
 | 
						|
        Deflater deflater = (compression == CompressionMethod.ZLIB
 | 
						|
                             ? new Deflater() : null);
 | 
						|
        outCipher.init(Cipher.ENCRYPT_MODE,
 | 
						|
                       new SecretKeySpec(keys[isClient ? 2 : 3],
 | 
						|
                                         s.cipherAlgorithm().toString()),
 | 
						|
                       new IvParameterSpec(keys[isClient ? 4 : 5]));
 | 
						|
        outMac.init(new SecretKeySpec(keys[isClient ? 0 : 1],
 | 
						|
                                      outMac.getAlgorithm()));
 | 
						|
        outParams = new OutputSecurityParameters(outCipher, outMac,
 | 
						|
                                                 deflater,
 | 
						|
                                                 engine.session(), s);
 | 
						|
      }
 | 
						|
    catch (InvalidAlgorithmParameterException iape)
 | 
						|
      {
 | 
						|
        throw new SSLException(iape);
 | 
						|
      }
 | 
						|
    catch (InvalidKeyException ike)
 | 
						|
      {
 | 
						|
        throw new SSLException(ike);
 | 
						|
      }
 | 
						|
    catch (NoSuchAlgorithmException nsae)
 | 
						|
      {
 | 
						|
        throw new SSLException(nsae);
 | 
						|
      }
 | 
						|
    catch (NoSuchPaddingException nspe)
 | 
						|
      {
 | 
						|
        throw new SSLException(nspe);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  protected void generatePSKSecret(String identity, byte[] otherkey,
 | 
						|
                                   boolean isClient)
 | 
						|
    throws SSLException
 | 
						|
  {
 | 
						|
    SecretKey key = null;
 | 
						|
    try
 | 
						|
      {
 | 
						|
        key = engine.contextImpl.pskManager.getKey(identity);
 | 
						|
      }
 | 
						|
    catch (KeyManagementException kme)
 | 
						|
      {
 | 
						|
      }
 | 
						|
    if (key != null)
 | 
						|
      {
 | 
						|
        byte[] keyb = key.getEncoded();
 | 
						|
        if (otherkey == null)
 | 
						|
          {
 | 
						|
            otherkey = new byte[keyb.length];
 | 
						|
          }
 | 
						|
        preMasterSecret = new byte[otherkey.length + keyb.length + 4];
 | 
						|
        preMasterSecret[0] = (byte) (otherkey.length >>> 8);
 | 
						|
        preMasterSecret[1] = (byte)  otherkey.length;
 | 
						|
        System.arraycopy(otherkey, 0, preMasterSecret, 2, otherkey.length);
 | 
						|
        preMasterSecret[otherkey.length + 2]
 | 
						|
          = (byte) (keyb.length >>> 8);
 | 
						|
        preMasterSecret[otherkey.length + 3]
 | 
						|
          = (byte)  keyb.length;
 | 
						|
        System.arraycopy(keyb, 0, preMasterSecret,
 | 
						|
                         otherkey.length + 4, keyb.length);
 | 
						|
      }
 | 
						|
    else
 | 
						|
      {
 | 
						|
        // Generate a random, fake secret.
 | 
						|
        preMasterSecret = new byte[8];
 | 
						|
        preMasterSecret[1] = 2;
 | 
						|
        preMasterSecret[5] = 2;
 | 
						|
        preMasterSecret[6] = (byte) engine.session().random().nextInt();
 | 
						|
        preMasterSecret[7] = (byte) engine.session().random().nextInt();
 | 
						|
      }
 | 
						|
 | 
						|
    if (Debug.DEBUG_KEY_EXCHANGE)
 | 
						|
      logger.logv(Component.SSL_KEY_EXCHANGE, "PSK identity {0} key {1}",
 | 
						|
                  identity, key);
 | 
						|
 | 
						|
    generateMasterSecret(clientRandom, serverRandom,
 | 
						|
                         engine.session());
 | 
						|
    byte[][] keys = generateKeys(clientRandom, serverRandom,
 | 
						|
                                 engine.session());
 | 
						|
    setupSecurityParameters(keys, isClient, engine, compression);
 | 
						|
  }
 | 
						|
 | 
						|
  protected class DHPhase extends DelegatedTask
 | 
						|
  {
 | 
						|
    private final DHPublicKey key;
 | 
						|
    private final boolean full;
 | 
						|
 | 
						|
    protected DHPhase(DHPublicKey key)
 | 
						|
    {
 | 
						|
      this(key, true);
 | 
						|
    }
 | 
						|
 | 
						|
    protected DHPhase(DHPublicKey key, boolean full)
 | 
						|
    {
 | 
						|
      this.key = key;
 | 
						|
      this.full = full;
 | 
						|
    }
 | 
						|
 | 
						|
    protected void implRun() throws InvalidKeyException, SSLException
 | 
						|
    {
 | 
						|
      keyAgreement.doPhase(key, true);
 | 
						|
      preMasterSecret = keyAgreement.generateSecret();
 | 
						|
      if (full)
 | 
						|
        {
 | 
						|
          generateMasterSecret(clientRandom, serverRandom, engine.session());
 | 
						|
          byte[][] keys = generateKeys(clientRandom, serverRandom, engine.session());
 | 
						|
          setupSecurityParameters(keys, engine.getUseClientMode(), engine, compression);
 | 
						|
        }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  protected class CertVerifier extends DelegatedTask
 | 
						|
  {
 | 
						|
    private final boolean clientSide;
 | 
						|
    private final X509Certificate[] chain;
 | 
						|
    private boolean verified;
 | 
						|
 | 
						|
    protected CertVerifier(boolean clientSide, X509Certificate[] chain)
 | 
						|
    {
 | 
						|
      this.clientSide = clientSide;
 | 
						|
      this.chain = chain;
 | 
						|
    }
 | 
						|
 | 
						|
    boolean verified()
 | 
						|
    {
 | 
						|
      return verified;
 | 
						|
    }
 | 
						|
 | 
						|
    protected void implRun()
 | 
						|
    {
 | 
						|
      X509TrustManager tm = engine.contextImpl.trustManager;
 | 
						|
      if (clientSide)
 | 
						|
        {
 | 
						|
          try
 | 
						|
            {
 | 
						|
              tm.checkServerTrusted(chain, null);
 | 
						|
              verified = true;
 | 
						|
            }
 | 
						|
          catch (CertificateException ce)
 | 
						|
            {
 | 
						|
              if (Debug.DEBUG)
 | 
						|
                logger.log(Component.SSL_DELEGATED_TASK, "cert verify", ce);
 | 
						|
              // For client connections, ask the user if the certificate is OK.
 | 
						|
              CallbackHandler verify = new DefaultCallbackHandler();
 | 
						|
              GetSecurityPropertyAction gspa
 | 
						|
                = new GetSecurityPropertyAction("jessie.certificate.handler");
 | 
						|
              String clazz = AccessController.doPrivileged(gspa);
 | 
						|
              try
 | 
						|
                {
 | 
						|
                  ClassLoader cl =
 | 
						|
                    AccessController.doPrivileged(new PrivilegedExceptionAction<ClassLoader>()
 | 
						|
                      {
 | 
						|
                        public ClassLoader run() throws Exception
 | 
						|
                        {
 | 
						|
                          return ClassLoader.getSystemClassLoader();
 | 
						|
                        }
 | 
						|
                      });
 | 
						|
                  verify = (CallbackHandler) cl.loadClass(clazz).newInstance();
 | 
						|
                }
 | 
						|
              catch (Exception x)
 | 
						|
                {
 | 
						|
                  // Ignore.
 | 
						|
                  if (Debug.DEBUG)
 | 
						|
                    logger.log(Component.SSL_DELEGATED_TASK,
 | 
						|
                               "callback handler loading", x);
 | 
						|
                }
 | 
						|
              // XXX Internationalize
 | 
						|
              CertificateCallback confirm =
 | 
						|
                new CertificateCallback(chain[0],
 | 
						|
                "The server's certificate could not be verified. There is no proof " +
 | 
						|
                "that this server is who it claims to be, or that their certificate " +
 | 
						|
                "is valid. Do you wish to continue connecting? ");
 | 
						|
 | 
						|
              try
 | 
						|
                {
 | 
						|
                  verify.handle(new Callback[] { confirm });
 | 
						|
                  verified = confirm.getSelectedIndex() == ConfirmationCallback.YES;
 | 
						|
                }
 | 
						|
              catch (Exception x)
 | 
						|
                {
 | 
						|
                  if (Debug.DEBUG)
 | 
						|
                    logger.log(Component.SSL_DELEGATED_TASK,
 | 
						|
                               "callback handler exception", x);
 | 
						|
                  verified = false;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
      else
 | 
						|
        {
 | 
						|
          try
 | 
						|
            {
 | 
						|
              tm.checkClientTrusted(chain, null);
 | 
						|
            }
 | 
						|
          catch (CertificateException ce)
 | 
						|
            {
 | 
						|
              verified = false;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
      if (verified)
 | 
						|
        engine.session().setPeerVerified(true);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  protected class DHE_PSKGen extends DelegatedTask
 | 
						|
  {
 | 
						|
    private final DHPublicKey dhKey;
 | 
						|
    private final SecretKey psKey;
 | 
						|
    private final boolean isClient;
 | 
						|
 | 
						|
    protected DHE_PSKGen(DHPublicKey dhKey, SecretKey psKey, boolean isClient)
 | 
						|
    {
 | 
						|
      this.dhKey = dhKey;
 | 
						|
      this.psKey = psKey;
 | 
						|
      this.isClient = isClient;
 | 
						|
    }
 | 
						|
 | 
						|
    /* (non-Javadoc)
 | 
						|
     * @see gnu.javax.net.ssl.provider.DelegatedTask#implRun()
 | 
						|
     */
 | 
						|
    @Override protected void implRun() throws Throwable
 | 
						|
    {
 | 
						|
      keyAgreement.doPhase(dhKey, true);
 | 
						|
      byte[] dhSecret = keyAgreement.generateSecret();
 | 
						|
      byte[] psSecret = null;
 | 
						|
      if (psKey != null)
 | 
						|
        psSecret = psKey.getEncoded();
 | 
						|
      else
 | 
						|
        {
 | 
						|
          psSecret = new byte[8];
 | 
						|
          engine.session().random().nextBytes(psSecret);
 | 
						|
        }
 | 
						|
 | 
						|
      preMasterSecret = new byte[dhSecret.length + psSecret.length + 4];
 | 
						|
      preMasterSecret[0] = (byte) (dhSecret.length >>> 8);
 | 
						|
      preMasterSecret[1] = (byte)  dhSecret.length;
 | 
						|
      System.arraycopy(dhSecret, 0, preMasterSecret, 2, dhSecret.length);
 | 
						|
      preMasterSecret[dhSecret.length + 2] = (byte) (psSecret.length >>> 8);
 | 
						|
      preMasterSecret[dhSecret.length + 3] = (byte)  psSecret.length;
 | 
						|
      System.arraycopy(psSecret, 0, preMasterSecret, dhSecret.length + 4,
 | 
						|
                       psSecret.length);
 | 
						|
 | 
						|
      generateMasterSecret(clientRandom, serverRandom, engine.session());
 | 
						|
      byte[][] keys = generateKeys(clientRandom, serverRandom, engine.session());
 | 
						|
      setupSecurityParameters(keys, isClient, engine, compression);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 |