mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			1154 lines
		
	
	
		
			42 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			1154 lines
		
	
	
		
			42 KiB
		
	
	
	
		
			Java
		
	
	
	
/* ClientHandshake.java --
 | 
						|
   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 static gnu.javax.net.ssl.provider.ClientHandshake.State.*;
 | 
						|
import static gnu.javax.net.ssl.provider.KeyExchangeAlgorithm.*;
 | 
						|
 | 
						|
import gnu.classpath.debug.Component;
 | 
						|
import gnu.java.security.action.GetSecurityPropertyAction;
 | 
						|
import gnu.javax.crypto.key.dh.GnuDHPublicKey;
 | 
						|
import gnu.javax.net.ssl.AbstractSessionContext;
 | 
						|
import gnu.javax.net.ssl.Session;
 | 
						|
import gnu.javax.net.ssl.provider.Alert.Description;
 | 
						|
import gnu.javax.net.ssl.provider.Alert.Level;
 | 
						|
import gnu.javax.net.ssl.provider.CertificateRequest.ClientCertificateType;
 | 
						|
import gnu.javax.net.ssl.provider.ServerNameList.NameType;
 | 
						|
import gnu.javax.net.ssl.provider.ServerNameList.ServerName;
 | 
						|
 | 
						|
import java.nio.ByteBuffer;
 | 
						|
import java.security.AccessController;
 | 
						|
import java.security.InvalidAlgorithmParameterException;
 | 
						|
import java.security.InvalidKeyException;
 | 
						|
import java.security.KeyPair;
 | 
						|
import java.security.KeyPairGenerator;
 | 
						|
import java.security.MessageDigest;
 | 
						|
import java.security.NoSuchAlgorithmException;
 | 
						|
import java.security.PrivateKey;
 | 
						|
import java.security.SignatureException;
 | 
						|
import java.security.cert.CertificateException;
 | 
						|
import java.security.cert.X509Certificate;
 | 
						|
import java.util.Arrays;
 | 
						|
import java.util.Collections;
 | 
						|
import java.util.LinkedList;
 | 
						|
import java.util.List;
 | 
						|
import java.util.zip.Deflater;
 | 
						|
import java.util.zip.Inflater;
 | 
						|
 | 
						|
import javax.crypto.BadPaddingException;
 | 
						|
import javax.crypto.Cipher;
 | 
						|
import javax.crypto.IllegalBlockSizeException;
 | 
						|
import javax.crypto.NoSuchPaddingException;
 | 
						|
import javax.crypto.interfaces.DHPrivateKey;
 | 
						|
import javax.crypto.interfaces.DHPublicKey;
 | 
						|
import javax.crypto.spec.DHParameterSpec;
 | 
						|
import javax.net.ssl.SSLException;
 | 
						|
import javax.net.ssl.SSLPeerUnverifiedException;
 | 
						|
import javax.net.ssl.X509ExtendedKeyManager;
 | 
						|
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
 | 
						|
import javax.security.auth.x500.X500Principal;
 | 
						|
 | 
						|
/**
 | 
						|
 * @author Casey Marshall (csm@gnu.org)
 | 
						|
 */
 | 
						|
public class ClientHandshake extends AbstractHandshake
 | 
						|
{
 | 
						|
  static enum State
 | 
						|
  {
 | 
						|
    WRITE_CLIENT_HELLO (false, true),
 | 
						|
    READ_SERVER_HELLO (true, false),
 | 
						|
    READ_CERTIFICATE (true, false),
 | 
						|
    READ_SERVER_KEY_EXCHANGE (true, false),
 | 
						|
    READ_CERTIFICATE_REQUEST (true, false),
 | 
						|
    READ_SERVER_HELLO_DONE (true, false),
 | 
						|
    WRITE_CERTIFICATE (false, true),
 | 
						|
    WRITE_CLIENT_KEY_EXCHANGE (false, true),
 | 
						|
    WRITE_CERTIFICATE_VERIFY (false, true),
 | 
						|
    WRITE_FINISHED (false, true),
 | 
						|
    READ_FINISHED (true, false),
 | 
						|
    DONE (false, false);
 | 
						|
 | 
						|
    private final boolean isWriteState;
 | 
						|
    private final boolean isReadState;
 | 
						|
 | 
						|
    private State(boolean isReadState, boolean isWriteState)
 | 
						|
    {
 | 
						|
      this.isReadState = isReadState;
 | 
						|
      this.isWriteState = isWriteState;
 | 
						|
    }
 | 
						|
 | 
						|
    boolean isReadState()
 | 
						|
    {
 | 
						|
      return isReadState;
 | 
						|
    }
 | 
						|
 | 
						|
    boolean isWriteState()
 | 
						|
    {
 | 
						|
      return isWriteState;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  private State state;
 | 
						|
  private ByteBuffer outBuffer;
 | 
						|
  private boolean continuedSession;
 | 
						|
  private SessionImpl continued;
 | 
						|
  private KeyPair dhPair;
 | 
						|
  private String keyAlias;
 | 
						|
  private PrivateKey privateKey;
 | 
						|
  private MaxFragmentLength maxFragmentLengthSent;
 | 
						|
  private boolean truncatedHMacSent;
 | 
						|
  private ProtocolVersion sentVersion;
 | 
						|
 | 
						|
  // Delegated tasks.
 | 
						|
  private CertVerifier certVerifier;
 | 
						|
  private ParamsVerifier paramsVerifier;
 | 
						|
  private DelegatedTask keyExchange;
 | 
						|
  private CertLoader certLoader;
 | 
						|
  private GenCertVerify genCertVerify;
 | 
						|
 | 
						|
  public ClientHandshake(SSLEngineImpl engine) throws NoSuchAlgorithmException
 | 
						|
  {
 | 
						|
    super(engine);
 | 
						|
    state = WRITE_CLIENT_HELLO;
 | 
						|
    continuedSession = false;
 | 
						|
  }
 | 
						|
 | 
						|
  /* (non-Javadoc)
 | 
						|
   * @see gnu.javax.net.ssl.provider.AbstractHandshake#implHandleInput()
 | 
						|
   */
 | 
						|
  @Override protected HandshakeStatus implHandleInput() throws SSLException
 | 
						|
  {
 | 
						|
    if (state == DONE)
 | 
						|
      return HandshakeStatus.FINISHED;
 | 
						|
 | 
						|
    if (state.isWriteState()
 | 
						|
        || (outBuffer != null && outBuffer.hasRemaining()))
 | 
						|
      return HandshakeStatus.NEED_WRAP;
 | 
						|
 | 
						|
    // Copy the current buffer, and prepare it for reading.
 | 
						|
    ByteBuffer buffer = handshakeBuffer.duplicate ();
 | 
						|
    buffer.flip();
 | 
						|
    buffer.position(handshakeOffset);
 | 
						|
 | 
						|
    Handshake handshake = new Handshake(buffer.slice(),
 | 
						|
                                        engine.session().suite,
 | 
						|
                                        engine.session().version);
 | 
						|
 | 
						|
    if (Debug.DEBUG)
 | 
						|
      logger.logv(Component.SSL_HANDSHAKE, "processing in state {0}:\n{1}",
 | 
						|
                  state, handshake);
 | 
						|
 | 
						|
    switch (state)
 | 
						|
      {
 | 
						|
        // Server Hello.
 | 
						|
        case READ_SERVER_HELLO:
 | 
						|
        {
 | 
						|
          if (handshake.type() != Handshake.Type.SERVER_HELLO)
 | 
						|
            throw new AlertException(new Alert(Alert.Level.FATAL,
 | 
						|
                                               Alert.Description.UNEXPECTED_MESSAGE));
 | 
						|
          ServerHello hello = (ServerHello) handshake.body();
 | 
						|
          serverRandom = hello.random().copy();
 | 
						|
          engine.session().suite = hello.cipherSuite();
 | 
						|
          engine.session().version = hello.version();
 | 
						|
          compression = hello.compressionMethod();
 | 
						|
          Session.ID serverId = new Session.ID(hello.sessionId());
 | 
						|
          if (continued != null
 | 
						|
              && continued.id().equals(serverId))
 | 
						|
            {
 | 
						|
              continuedSession = true;
 | 
						|
              engine.setSession(continued);
 | 
						|
            }
 | 
						|
          else if (engine.getEnableSessionCreation())
 | 
						|
            {
 | 
						|
              ((AbstractSessionContext) engine.contextImpl
 | 
						|
                  .engineGetClientSessionContext()).put(engine.session());
 | 
						|
            }
 | 
						|
          ExtensionList extensions = hello.extensions();
 | 
						|
          if (extensions != null)
 | 
						|
            {
 | 
						|
              for (Extension extension : extensions)
 | 
						|
                {
 | 
						|
                  Extension.Type type = extension.type();
 | 
						|
                  if (type == null)
 | 
						|
                    continue;
 | 
						|
                  switch (type)
 | 
						|
                    {
 | 
						|
                      case MAX_FRAGMENT_LENGTH:
 | 
						|
                        MaxFragmentLength mfl
 | 
						|
                          = (MaxFragmentLength) extension.value();
 | 
						|
                        if (maxFragmentLengthSent == mfl)
 | 
						|
                          engine.session().setApplicationBufferSize(mfl.maxLength());
 | 
						|
                        break;
 | 
						|
 | 
						|
                      case TRUNCATED_HMAC:
 | 
						|
                        if (truncatedHMacSent)
 | 
						|
                          engine.session().setTruncatedMac(true);
 | 
						|
                        break;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
          KeyExchangeAlgorithm kex = engine.session().suite.keyExchangeAlgorithm();
 | 
						|
          if (continuedSession)
 | 
						|
            {
 | 
						|
              byte[][] keys = generateKeys(clientRandom, serverRandom,
 | 
						|
                                           engine.session());
 | 
						|
              setupSecurityParameters(keys, true, engine, compression);
 | 
						|
              state = READ_FINISHED;
 | 
						|
            }
 | 
						|
          else if (kex == RSA || kex == DH_DSS || kex == DH_RSA
 | 
						|
                   || kex == DHE_DSS || kex == DHE_RSA || kex == RSA_PSK)
 | 
						|
            state = READ_CERTIFICATE;
 | 
						|
          else if (kex == DH_anon || kex == PSK || kex == DHE_PSK)
 | 
						|
            state = READ_SERVER_KEY_EXCHANGE;
 | 
						|
          else
 | 
						|
            state = READ_CERTIFICATE_REQUEST;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
 | 
						|
        // Server Certificate.
 | 
						|
        case READ_CERTIFICATE:
 | 
						|
        {
 | 
						|
          if (handshake.type() != Handshake.Type.CERTIFICATE)
 | 
						|
            {
 | 
						|
              // We need a certificate for non-anonymous suites.
 | 
						|
              if (engine.session().suite.signatureAlgorithm() != SignatureAlgorithm.ANONYMOUS)
 | 
						|
                throw new AlertException(new Alert(Level.FATAL,
 | 
						|
                                                   Description.UNEXPECTED_MESSAGE));
 | 
						|
              state = READ_SERVER_KEY_EXCHANGE;
 | 
						|
            }
 | 
						|
          Certificate cert = (Certificate) handshake.body();
 | 
						|
          X509Certificate[] chain = null;
 | 
						|
          try
 | 
						|
            {
 | 
						|
              chain = cert.certificates().toArray(new X509Certificate[0]);
 | 
						|
            }
 | 
						|
          catch (CertificateException ce)
 | 
						|
            {
 | 
						|
              throw new AlertException(new Alert(Level.FATAL,
 | 
						|
                                                 Description.BAD_CERTIFICATE),
 | 
						|
                                       ce);
 | 
						|
            }
 | 
						|
          catch (NoSuchAlgorithmException nsae)
 | 
						|
            {
 | 
						|
              throw new AlertException(new Alert(Level.FATAL,
 | 
						|
                                                 Description.UNSUPPORTED_CERTIFICATE),
 | 
						|
                                       nsae);
 | 
						|
            }
 | 
						|
          engine.session().setPeerCertificates(chain);
 | 
						|
          certVerifier = new CertVerifier(true, chain);
 | 
						|
          tasks.add(certVerifier);
 | 
						|
 | 
						|
          // If we are doing an RSA key exchange, generate our parameters.
 | 
						|
          KeyExchangeAlgorithm kea = engine.session().suite.keyExchangeAlgorithm();
 | 
						|
          if (kea == RSA || kea == RSA_PSK)
 | 
						|
            {
 | 
						|
              keyExchange = new RSAGen(kea == RSA);
 | 
						|
              tasks.add(keyExchange);
 | 
						|
              if (kea == RSA)
 | 
						|
                state = READ_CERTIFICATE_REQUEST;
 | 
						|
              else
 | 
						|
                state = READ_SERVER_KEY_EXCHANGE;
 | 
						|
            }
 | 
						|
          else
 | 
						|
            state = READ_SERVER_KEY_EXCHANGE;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
 | 
						|
        // Server Key Exchange.
 | 
						|
        case READ_SERVER_KEY_EXCHANGE:
 | 
						|
        {
 | 
						|
          CipherSuite s = engine.session().suite;
 | 
						|
          KeyExchangeAlgorithm kexalg = s.keyExchangeAlgorithm();
 | 
						|
          // XXX also SRP.
 | 
						|
          if (kexalg != DHE_DSS && kexalg != DHE_RSA && kexalg != DH_anon
 | 
						|
              && kexalg != DHE_PSK && kexalg != PSK && kexalg != RSA_PSK)
 | 
						|
            throw new AlertException(new Alert(Level.FATAL,
 | 
						|
                                               Description.UNEXPECTED_MESSAGE));
 | 
						|
 | 
						|
          if (handshake.type() != Handshake.Type.SERVER_KEY_EXCHANGE)
 | 
						|
            {
 | 
						|
              if (kexalg != RSA_PSK && kexalg != PSK)
 | 
						|
                throw new AlertException(new Alert(Level.FATAL,
 | 
						|
                                                   Description.UNEXPECTED_MESSAGE));
 | 
						|
              state = READ_CERTIFICATE_REQUEST;
 | 
						|
              return HandshakeStatus.NEED_UNWRAP;
 | 
						|
            }
 | 
						|
 | 
						|
          ServerKeyExchange skex = (ServerKeyExchange) handshake.body();
 | 
						|
          ByteBuffer paramsBuffer = null;
 | 
						|
          if (kexalg == DHE_DSS || kexalg == DHE_RSA || kexalg == DH_anon)
 | 
						|
            {
 | 
						|
              ServerDHParams dhParams = (ServerDHParams) skex.params();
 | 
						|
              ByteBuffer b = dhParams.buffer();
 | 
						|
              paramsBuffer = ByteBuffer.allocate(b.remaining());
 | 
						|
              paramsBuffer.put(b);
 | 
						|
            }
 | 
						|
 | 
						|
          if (s.signatureAlgorithm() != SignatureAlgorithm.ANONYMOUS)
 | 
						|
            {
 | 
						|
              byte[] signature = skex.signature().signature();
 | 
						|
              paramsVerifier = new ParamsVerifier(paramsBuffer, signature);
 | 
						|
              tasks.add(paramsVerifier);
 | 
						|
            }
 | 
						|
 | 
						|
          if (kexalg == DHE_DSS || kexalg == DHE_RSA || kexalg == DH_anon)
 | 
						|
            {
 | 
						|
              ServerDHParams dhParams = (ServerDHParams) skex.params();
 | 
						|
              DHPublicKey serverKey = new GnuDHPublicKey(null,
 | 
						|
                                                         dhParams.p(),
 | 
						|
                                                         dhParams.g(),
 | 
						|
                                                         dhParams.y());
 | 
						|
              DHParameterSpec params = new DHParameterSpec(dhParams.p(),
 | 
						|
                                                           dhParams.g());
 | 
						|
              keyExchange = new ClientDHGen(serverKey, params, true);
 | 
						|
              tasks.add(keyExchange);
 | 
						|
            }
 | 
						|
          if (kexalg == DHE_PSK)
 | 
						|
            {
 | 
						|
              ServerDHE_PSKParameters pskParams = (ServerDHE_PSKParameters)
 | 
						|
                skex.params();
 | 
						|
              ServerDHParams dhParams = pskParams.params();
 | 
						|
              DHPublicKey serverKey = new GnuDHPublicKey(null,
 | 
						|
                                                         dhParams.p(),
 | 
						|
                                                         dhParams.g(),
 | 
						|
                                                         dhParams.y());
 | 
						|
              DHParameterSpec params = new DHParameterSpec(dhParams.p(),
 | 
						|
                                                           dhParams.g());
 | 
						|
              keyExchange = new ClientDHGen(serverKey, params, false);
 | 
						|
              tasks.add(keyExchange);
 | 
						|
            }
 | 
						|
          state = READ_CERTIFICATE_REQUEST;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
 | 
						|
        // Certificate Request.
 | 
						|
        case READ_CERTIFICATE_REQUEST:
 | 
						|
        {
 | 
						|
          if (handshake.type() != Handshake.Type.CERTIFICATE_REQUEST)
 | 
						|
            {
 | 
						|
              state = READ_SERVER_HELLO_DONE;
 | 
						|
              return HandshakeStatus.NEED_UNWRAP;
 | 
						|
            }
 | 
						|
 | 
						|
          CertificateRequest req = (CertificateRequest) handshake.body();
 | 
						|
          ClientCertificateTypeList types = req.types();
 | 
						|
          LinkedList<String> typeList = new LinkedList<String>();
 | 
						|
          for (ClientCertificateType t : types)
 | 
						|
            typeList.add(t.name());
 | 
						|
 | 
						|
          X500PrincipalList issuers = req.authorities();
 | 
						|
          LinkedList<X500Principal> issuerList = new LinkedList<X500Principal>();
 | 
						|
          for (X500Principal p : issuers)
 | 
						|
            issuerList.add(p);
 | 
						|
 | 
						|
          certLoader = new CertLoader(typeList, issuerList);
 | 
						|
          tasks.add(certLoader);
 | 
						|
        }
 | 
						|
        break;
 | 
						|
 | 
						|
        // Server Hello Done.
 | 
						|
        case READ_SERVER_HELLO_DONE:
 | 
						|
        {
 | 
						|
          if (handshake.type() != Handshake.Type.SERVER_HELLO_DONE)
 | 
						|
            throw new AlertException(new Alert(Level.FATAL,
 | 
						|
                                               Description.UNEXPECTED_MESSAGE));
 | 
						|
          state = WRITE_CERTIFICATE;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
 | 
						|
        // Finished.
 | 
						|
        case READ_FINISHED:
 | 
						|
        {
 | 
						|
          if (handshake.type() != Handshake.Type.FINISHED)
 | 
						|
            throw new AlertException(new Alert(Level.FATAL,
 | 
						|
                                               Description.UNEXPECTED_MESSAGE));
 | 
						|
 | 
						|
          Finished serverFinished = (Finished) handshake.body();
 | 
						|
          MessageDigest md5copy = null;
 | 
						|
          MessageDigest shacopy = null;
 | 
						|
          try
 | 
						|
            {
 | 
						|
              md5copy = (MessageDigest) md5.clone();
 | 
						|
              shacopy = (MessageDigest) sha.clone();
 | 
						|
            }
 | 
						|
          catch (CloneNotSupportedException cnse)
 | 
						|
            {
 | 
						|
              // We're improperly configured to use a non-cloneable
 | 
						|
              // md5/sha-1, OR there's a runtime bug.
 | 
						|
              throw new SSLException(cnse);
 | 
						|
            }
 | 
						|
          Finished clientFinished =
 | 
						|
            new Finished(generateFinished(md5copy, shacopy,
 | 
						|
                                          false, engine.session()),
 | 
						|
                                          engine.session().version);
 | 
						|
 | 
						|
          if (Debug.DEBUG)
 | 
						|
            logger.logv(Component.SSL_HANDSHAKE, "clientFinished: {0}",
 | 
						|
                        clientFinished);
 | 
						|
 | 
						|
          if (engine.session().version == ProtocolVersion.SSL_3)
 | 
						|
            {
 | 
						|
              if (!Arrays.equals(clientFinished.md5Hash(),
 | 
						|
                                 serverFinished.md5Hash())
 | 
						|
                  || !Arrays.equals(clientFinished.shaHash(),
 | 
						|
                                    serverFinished.shaHash()))
 | 
						|
                {
 | 
						|
                  engine.session().invalidate();
 | 
						|
                  throw new SSLException("session verify failed");
 | 
						|
                }
 | 
						|
            }
 | 
						|
          else
 | 
						|
            {
 | 
						|
              if (!Arrays.equals(clientFinished.verifyData(),
 | 
						|
                                 serverFinished.verifyData()))
 | 
						|
                {
 | 
						|
                  engine.session().invalidate();
 | 
						|
                  throw new SSLException("session verify failed");
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
          if (continuedSession)
 | 
						|
            {
 | 
						|
              engine.changeCipherSpec();
 | 
						|
              state = WRITE_FINISHED;
 | 
						|
            }
 | 
						|
          else
 | 
						|
            state = DONE;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
 | 
						|
        default:
 | 
						|
          throw new IllegalStateException("invalid state: " + state);
 | 
						|
      }
 | 
						|
 | 
						|
    handshakeOffset += handshake.length() + 4;
 | 
						|
 | 
						|
    if (!tasks.isEmpty())
 | 
						|
      return HandshakeStatus.NEED_TASK;
 | 
						|
    if (state.isWriteState()
 | 
						|
        || (outBuffer != null && outBuffer.hasRemaining()))
 | 
						|
      return HandshakeStatus.NEED_WRAP;
 | 
						|
    if (state.isReadState())
 | 
						|
      return HandshakeStatus.NEED_UNWRAP;
 | 
						|
 | 
						|
    return HandshakeStatus.FINISHED;
 | 
						|
  }
 | 
						|
 | 
						|
  /* (non-Javadoc)
 | 
						|
   * @see gnu.javax.net.ssl.provider.AbstractHandshake#implHandleOutput(java.nio.ByteBuffer)
 | 
						|
   */
 | 
						|
  @Override protected HandshakeStatus implHandleOutput(ByteBuffer fragment)
 | 
						|
    throws SSLException
 | 
						|
  {
 | 
						|
    if (Debug.DEBUG)
 | 
						|
      logger.logv(Component.SSL_HANDSHAKE, "output to {0}; state:{1}; outBuffer:{2}",
 | 
						|
                  fragment, state, outBuffer);
 | 
						|
 | 
						|
    // Drain the output buffer, if it needs it.
 | 
						|
    if (outBuffer != null && outBuffer.hasRemaining())
 | 
						|
      {
 | 
						|
        int l = Math.min(fragment.remaining(), outBuffer.remaining());
 | 
						|
        fragment.put((ByteBuffer) outBuffer.duplicate().limit(outBuffer.position() + l));
 | 
						|
        outBuffer.position(outBuffer.position() + l);
 | 
						|
      }
 | 
						|
 | 
						|
    if (!fragment.hasRemaining())
 | 
						|
      {
 | 
						|
        if (state.isWriteState() || outBuffer.hasRemaining())
 | 
						|
          return HandshakeStatus.NEED_WRAP;
 | 
						|
        else
 | 
						|
          return HandshakeStatus.NEED_UNWRAP;
 | 
						|
      }
 | 
						|
 | 
						|
outer_loop:
 | 
						|
    while (fragment.remaining() >= 4 && state.isWriteState())
 | 
						|
      {
 | 
						|
        if (Debug.DEBUG)
 | 
						|
          logger.logv(Component.SSL_HANDSHAKE, "loop state={0}", state);
 | 
						|
 | 
						|
        switch (state)
 | 
						|
          {
 | 
						|
            case WRITE_CLIENT_HELLO:
 | 
						|
            {
 | 
						|
              ClientHelloBuilder hello = new ClientHelloBuilder();
 | 
						|
              AbstractSessionContext ctx = (AbstractSessionContext)
 | 
						|
                engine.contextImpl.engineGetClientSessionContext();
 | 
						|
              continued = (SessionImpl) ctx.getSession(engine.getPeerHost(),
 | 
						|
                                                       engine.getPeerPort());
 | 
						|
              engine.session().setId(new Session.ID(new byte[0]));
 | 
						|
              Session.ID sid = engine.session().id();
 | 
						|
              // If we have a session that we may want to continue, send
 | 
						|
              // that ID.
 | 
						|
              if (continued != null)
 | 
						|
                sid = continued.id();
 | 
						|
 | 
						|
              hello.setSessionId(sid.id());
 | 
						|
              sentVersion = chooseVersion();
 | 
						|
              hello.setVersion(sentVersion);
 | 
						|
              hello.setCipherSuites(getSuites());
 | 
						|
              hello.setCompressionMethods(getCompressionMethods());
 | 
						|
              Random r = hello.random();
 | 
						|
              r.setGmtUnixTime(Util.unixTime());
 | 
						|
              byte[] nonce = new byte[28];
 | 
						|
              engine.session().random().nextBytes(nonce);
 | 
						|
              r.setRandomBytes(nonce);
 | 
						|
              clientRandom = r.copy();
 | 
						|
              if (enableExtensions())
 | 
						|
                {
 | 
						|
                  List<Extension> extensions = new LinkedList<Extension>();
 | 
						|
                  MaxFragmentLength fraglen = maxFragmentLength();
 | 
						|
                  if (fraglen != null)
 | 
						|
                    {
 | 
						|
                      extensions.add(new Extension(Extension.Type.MAX_FRAGMENT_LENGTH,
 | 
						|
                                                   fraglen));
 | 
						|
                      maxFragmentLengthSent = fraglen;
 | 
						|
                    }
 | 
						|
 | 
						|
                  String host = engine.getPeerHost();
 | 
						|
                  if (host != null)
 | 
						|
                    {
 | 
						|
                      ServerName name
 | 
						|
                        = new ServerName(NameType.HOST_NAME, host);
 | 
						|
                      ServerNameList names
 | 
						|
                        = new ServerNameList(Collections.singletonList(name));
 | 
						|
                      extensions.add(new Extension(Extension.Type.SERVER_NAME,
 | 
						|
                                                   names));
 | 
						|
                    }
 | 
						|
 | 
						|
                  if (truncatedHMac())
 | 
						|
                    {
 | 
						|
                      extensions.add(new Extension(Extension.Type.TRUNCATED_HMAC,
 | 
						|
                                                   new TruncatedHMAC()));
 | 
						|
                      truncatedHMacSent = true;
 | 
						|
                    }
 | 
						|
 | 
						|
                  ExtensionList elist = new ExtensionList(extensions);
 | 
						|
                  hello.setExtensions(elist.buffer());
 | 
						|
                }
 | 
						|
              else
 | 
						|
                hello.setDisableExtensions(true);
 | 
						|
 | 
						|
              if (Debug.DEBUG)
 | 
						|
                logger.logv(Component.SSL_HANDSHAKE, "{0}", hello);
 | 
						|
 | 
						|
              fragment.putInt((Handshake.Type.CLIENT_HELLO.getValue() << 24)
 | 
						|
                              | (hello.length() & 0xFFFFFF));
 | 
						|
              outBuffer = hello.buffer();
 | 
						|
              int l = Math.min(fragment.remaining(), outBuffer.remaining());
 | 
						|
              fragment.put((ByteBuffer) outBuffer.duplicate()
 | 
						|
                           .limit(outBuffer.position() + l));
 | 
						|
              outBuffer.position(outBuffer.position() + l);
 | 
						|
 | 
						|
              state = READ_SERVER_HELLO;
 | 
						|
            }
 | 
						|
            break;
 | 
						|
 | 
						|
            case WRITE_CERTIFICATE:
 | 
						|
            {
 | 
						|
              java.security.cert.Certificate[] chain
 | 
						|
                = engine.session().getLocalCertificates();
 | 
						|
              if (chain != null)
 | 
						|
                {
 | 
						|
                  CertificateBuilder cert
 | 
						|
                    = new CertificateBuilder(CertificateType.X509);
 | 
						|
                  try
 | 
						|
                    {
 | 
						|
                      cert.setCertificates(Arrays.asList(chain));
 | 
						|
                    }
 | 
						|
                  catch (CertificateException ce)
 | 
						|
                    {
 | 
						|
                      throw new AlertException(new Alert(Level.FATAL,
 | 
						|
                                                         Description.INTERNAL_ERROR),
 | 
						|
                                               ce);
 | 
						|
                    }
 | 
						|
 | 
						|
                  outBuffer = cert.buffer();
 | 
						|
 | 
						|
                  fragment.putInt((Handshake.Type.CERTIFICATE.getValue() << 24)
 | 
						|
                                  | (cert.length() & 0xFFFFFF));
 | 
						|
 | 
						|
                  int l = Math.min(fragment.remaining(), outBuffer.remaining());
 | 
						|
                  fragment.put((ByteBuffer) outBuffer.duplicate()
 | 
						|
                               .limit(outBuffer.position() + l));
 | 
						|
                  outBuffer.position(outBuffer.position() + l);
 | 
						|
                }
 | 
						|
              state = WRITE_CLIENT_KEY_EXCHANGE;
 | 
						|
            }
 | 
						|
            break;
 | 
						|
 | 
						|
            case WRITE_CLIENT_KEY_EXCHANGE:
 | 
						|
            {
 | 
						|
              KeyExchangeAlgorithm kea = engine.session().suite.keyExchangeAlgorithm();
 | 
						|
              ClientKeyExchangeBuilder ckex
 | 
						|
                = new ClientKeyExchangeBuilder(engine.session().suite,
 | 
						|
                                               engine.session().version);
 | 
						|
              if (kea == DHE_DSS || kea == DHE_RSA || kea == DH_anon
 | 
						|
                  || kea == DH_DSS || kea == DH_RSA)
 | 
						|
                {
 | 
						|
                  assert(dhPair != null);
 | 
						|
                  DHPublicKey pubkey = (DHPublicKey) dhPair.getPublic();
 | 
						|
                  ClientDiffieHellmanPublic pub
 | 
						|
                    = new ClientDiffieHellmanPublic(pubkey.getY());
 | 
						|
                  ckex.setExchangeKeys(pub.buffer());
 | 
						|
                }
 | 
						|
              if (kea == RSA || kea == RSA_PSK)
 | 
						|
                {
 | 
						|
                  assert(keyExchange instanceof RSAGen);
 | 
						|
                  assert(keyExchange.hasRun());
 | 
						|
                  if (keyExchange.thrown() != null)
 | 
						|
                    throw new AlertException(new Alert(Level.FATAL,
 | 
						|
                                                       Description.HANDSHAKE_FAILURE),
 | 
						|
                                             keyExchange.thrown());
 | 
						|
                  EncryptedPreMasterSecret epms
 | 
						|
                    = new EncryptedPreMasterSecret(((RSAGen) keyExchange).encryptedSecret(),
 | 
						|
                                                   engine.session().version);
 | 
						|
                  if (kea == RSA)
 | 
						|
                    ckex.setExchangeKeys(epms.buffer());
 | 
						|
                  else
 | 
						|
                    {
 | 
						|
                      String identity = getPSKIdentity();
 | 
						|
                      if (identity == null)
 | 
						|
                        throw new SSLException("no pre-shared-key identity;"
 | 
						|
                                               + " set the security property"
 | 
						|
                                               + " \"jessie.client.psk.identity\"");
 | 
						|
                      ClientRSA_PSKParameters params =
 | 
						|
                        new ClientRSA_PSKParameters(identity, epms.buffer());
 | 
						|
                      ckex.setExchangeKeys(params.buffer());
 | 
						|
                      generatePSKSecret(identity, preMasterSecret, true);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
              if (kea == DHE_PSK)
 | 
						|
                {
 | 
						|
                  assert(keyExchange instanceof ClientDHGen);
 | 
						|
                  assert(dhPair != null);
 | 
						|
                  String identity = getPSKIdentity();
 | 
						|
                  if (identity == null)
 | 
						|
                    throw new SSLException("no pre-shared key identity; set"
 | 
						|
                                           + " the security property"
 | 
						|
                                           + " \"jessie.client.psk.identity\"");
 | 
						|
                  DHPublicKey pubkey = (DHPublicKey) dhPair.getPublic();
 | 
						|
                  ClientDHE_PSKParameters params =
 | 
						|
                    new ClientDHE_PSKParameters(identity,
 | 
						|
                                                new ClientDiffieHellmanPublic(pubkey.getY()));
 | 
						|
                  ckex.setExchangeKeys(params.buffer());
 | 
						|
                  generatePSKSecret(identity, preMasterSecret, true);
 | 
						|
                }
 | 
						|
              if (kea == PSK)
 | 
						|
                {
 | 
						|
                  String identity = getPSKIdentity();
 | 
						|
                  if (identity == null)
 | 
						|
                    throw new SSLException("no pre-shared key identity; set"
 | 
						|
                                           + " the security property"
 | 
						|
                                           + " \"jessie.client.psk.identity\"");
 | 
						|
                  generatePSKSecret(identity, null, true);
 | 
						|
                  ClientPSKParameters params = new ClientPSKParameters(identity);
 | 
						|
                  ckex.setExchangeKeys(params.buffer());
 | 
						|
                }
 | 
						|
              if (kea == NONE)
 | 
						|
                {
 | 
						|
                  Inflater inflater = null;
 | 
						|
                  Deflater deflater = null;
 | 
						|
                  if (compression == CompressionMethod.ZLIB)
 | 
						|
                    {
 | 
						|
                      inflater = new Inflater();
 | 
						|
                      deflater = new Deflater();
 | 
						|
                    }
 | 
						|
                  inParams = new InputSecurityParameters(null, null, inflater,
 | 
						|
                                                         engine.session(),
 | 
						|
                                                         engine.session().suite);
 | 
						|
                  outParams = new OutputSecurityParameters(null, null, deflater,
 | 
						|
                                                           engine.session(),
 | 
						|
                                                           engine.session().suite);
 | 
						|
                  engine.session().privateData.masterSecret = new byte[0];
 | 
						|
                }
 | 
						|
 | 
						|
              if (Debug.DEBUG)
 | 
						|
                logger.logv(Component.SSL_HANDSHAKE, "{0}", ckex);
 | 
						|
 | 
						|
              outBuffer = ckex.buffer();
 | 
						|
              if (Debug.DEBUG)
 | 
						|
                logger.logv(Component.SSL_HANDSHAKE, "client kex buffer {0}", outBuffer);
 | 
						|
              fragment.putInt((Handshake.Type.CLIENT_KEY_EXCHANGE.getValue() << 24)
 | 
						|
                              | (ckex.length() & 0xFFFFFF));
 | 
						|
              int l = Math.min(fragment.remaining(), outBuffer.remaining());
 | 
						|
              fragment.put((ByteBuffer) outBuffer.duplicate().limit(outBuffer.position() + l));
 | 
						|
              outBuffer.position(outBuffer.position() + l);
 | 
						|
 | 
						|
              if (privateKey != null)
 | 
						|
                {
 | 
						|
                  genCertVerify = new GenCertVerify(md5, sha);
 | 
						|
                  tasks.add(genCertVerify);
 | 
						|
                  state = WRITE_CERTIFICATE_VERIFY;
 | 
						|
                }
 | 
						|
              else
 | 
						|
                {
 | 
						|
                  engine.changeCipherSpec();
 | 
						|
                  state = WRITE_FINISHED;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            // Both states terminate in a NEED_TASK, or a need to change cipher
 | 
						|
            // specs; so we can't write any more messages here.
 | 
						|
            break outer_loop;
 | 
						|
 | 
						|
            case WRITE_CERTIFICATE_VERIFY:
 | 
						|
            {
 | 
						|
              assert(genCertVerify != null);
 | 
						|
              assert(genCertVerify.hasRun());
 | 
						|
              CertificateVerify verify = new CertificateVerify(genCertVerify.signed(),
 | 
						|
                                                               engine.session().suite.signatureAlgorithm());
 | 
						|
 | 
						|
              outBuffer = verify.buffer();
 | 
						|
              fragment.putInt((Handshake.Type.CERTIFICATE_VERIFY.getValue() << 24)
 | 
						|
                              | (verify.length() & 0xFFFFFF));
 | 
						|
              int l = Math.min(fragment.remaining(), outBuffer.remaining());
 | 
						|
              fragment.put((ByteBuffer) outBuffer.duplicate().limit(outBuffer.position() + l));
 | 
						|
              outBuffer.position(outBuffer.position() + l);
 | 
						|
 | 
						|
              // XXX This is a potential problem: we may not have drained
 | 
						|
              // outBuffer, but set the changeCipherSpec toggle.
 | 
						|
              engine.changeCipherSpec();
 | 
						|
              state = WRITE_FINISHED;
 | 
						|
            }
 | 
						|
            break outer_loop;
 | 
						|
 | 
						|
            case WRITE_FINISHED:
 | 
						|
            {
 | 
						|
              MessageDigest md5copy = null;
 | 
						|
              MessageDigest shacopy = null;
 | 
						|
              try
 | 
						|
                {
 | 
						|
                  md5copy = (MessageDigest) md5.clone();
 | 
						|
                  shacopy = (MessageDigest) sha.clone();
 | 
						|
                }
 | 
						|
              catch (CloneNotSupportedException cnse)
 | 
						|
                {
 | 
						|
                  // We're improperly configured to use a non-cloneable
 | 
						|
                  // md5/sha-1, OR there's a runtime bug.
 | 
						|
                  throw new SSLException(cnse);
 | 
						|
                }
 | 
						|
              outBuffer
 | 
						|
                = generateFinished(md5copy, shacopy, true,
 | 
						|
                                   engine.session());
 | 
						|
 | 
						|
              fragment.putInt((Handshake.Type.FINISHED.getValue() << 24)
 | 
						|
                              | outBuffer.remaining() & 0xFFFFFF);
 | 
						|
 | 
						|
              int l = Math.min(outBuffer.remaining(), fragment.remaining());
 | 
						|
              fragment.put((ByteBuffer) outBuffer.duplicate().limit(outBuffer.position() + l));
 | 
						|
              outBuffer.position(outBuffer.position() + l);
 | 
						|
 | 
						|
              if (continuedSession)
 | 
						|
                state = DONE;
 | 
						|
              else
 | 
						|
                state = READ_FINISHED;
 | 
						|
            }
 | 
						|
            break;
 | 
						|
 | 
						|
            default:
 | 
						|
              throw new IllegalStateException("invalid state: " + state);
 | 
						|
          }
 | 
						|
      }
 | 
						|
 | 
						|
    if (!tasks.isEmpty())
 | 
						|
      return HandshakeStatus.NEED_TASK;
 | 
						|
    if (state.isWriteState() ||
 | 
						|
        (outBuffer != null && outBuffer.hasRemaining()))
 | 
						|
      return HandshakeStatus.NEED_WRAP;
 | 
						|
    if (state.isReadState())
 | 
						|
      return HandshakeStatus.NEED_UNWRAP;
 | 
						|
 | 
						|
    return HandshakeStatus.FINISHED;
 | 
						|
  }
 | 
						|
 | 
						|
  /* (non-Javadoc)
 | 
						|
   * @see gnu.javax.net.ssl.provider.AbstractHandshake#status()
 | 
						|
   */
 | 
						|
  @Override HandshakeStatus status()
 | 
						|
  {
 | 
						|
    if (state.isReadState())
 | 
						|
      return HandshakeStatus.NEED_UNWRAP;
 | 
						|
    if (state.isWriteState())
 | 
						|
      return HandshakeStatus.NEED_WRAP;
 | 
						|
    return HandshakeStatus.FINISHED;
 | 
						|
  }
 | 
						|
 | 
						|
  @Override void checkKeyExchange() throws SSLException
 | 
						|
  {
 | 
						|
    // XXX implement.
 | 
						|
  }
 | 
						|
 | 
						|
  /* (non-Javadoc)
 | 
						|
   * @see gnu.javax.net.ssl.provider.AbstractHandshake#handleV2Hello(java.nio.ByteBuffer)
 | 
						|
   */
 | 
						|
  @Override void handleV2Hello(ByteBuffer hello) throws SSLException
 | 
						|
  {
 | 
						|
    throw new SSLException("this should be impossible");
 | 
						|
  }
 | 
						|
 | 
						|
  private ProtocolVersion chooseVersion() throws SSLException
 | 
						|
  {
 | 
						|
    // Select the highest enabled version, for our initial key exchange.
 | 
						|
    ProtocolVersion version = null;
 | 
						|
    for (String ver : engine.getEnabledProtocols())
 | 
						|
      {
 | 
						|
        try
 | 
						|
          {
 | 
						|
            ProtocolVersion v = ProtocolVersion.forName(ver);
 | 
						|
            if (version == null || version.compareTo(v) < 0)
 | 
						|
              version = v;
 | 
						|
          }
 | 
						|
        catch (Exception x)
 | 
						|
          {
 | 
						|
            continue;
 | 
						|
          }
 | 
						|
      }
 | 
						|
 | 
						|
    if (version == null)
 | 
						|
      throw new SSLException("no suitable enabled versions");
 | 
						|
 | 
						|
    return version;
 | 
						|
  }
 | 
						|
 | 
						|
  private List<CipherSuite> getSuites() throws SSLException
 | 
						|
  {
 | 
						|
    List<CipherSuite> suites = new LinkedList<CipherSuite>();
 | 
						|
    for (String s : engine.getEnabledCipherSuites())
 | 
						|
      {
 | 
						|
        CipherSuite suite = CipherSuite.forName(s);
 | 
						|
        if (suite != null)
 | 
						|
          suites.add(suite);
 | 
						|
      }
 | 
						|
    if (suites.isEmpty())
 | 
						|
      throw new SSLException("no cipher suites enabled");
 | 
						|
    return suites;
 | 
						|
  }
 | 
						|
 | 
						|
  private List<CompressionMethod> getCompressionMethods()
 | 
						|
  {
 | 
						|
    List<CompressionMethod> methods = new LinkedList<CompressionMethod>();
 | 
						|
    GetSecurityPropertyAction gspa = new GetSecurityPropertyAction("jessie.enable.compression");
 | 
						|
    if (Boolean.valueOf(AccessController.doPrivileged(gspa)))
 | 
						|
      methods.add(CompressionMethod.ZLIB);
 | 
						|
    methods.add(CompressionMethod.NULL);
 | 
						|
    return methods;
 | 
						|
  }
 | 
						|
 | 
						|
  private boolean enableExtensions()
 | 
						|
  {
 | 
						|
    GetSecurityPropertyAction action
 | 
						|
      = new GetSecurityPropertyAction("jessie.client.enable.extensions");
 | 
						|
    return Boolean.valueOf(AccessController.doPrivileged(action));
 | 
						|
  }
 | 
						|
 | 
						|
  private MaxFragmentLength maxFragmentLength()
 | 
						|
  {
 | 
						|
    GetSecurityPropertyAction action
 | 
						|
      = new GetSecurityPropertyAction("jessie.client.maxFragmentLength");
 | 
						|
    String s = AccessController.doPrivileged(action);
 | 
						|
    if (s != null)
 | 
						|
      {
 | 
						|
        try
 | 
						|
          {
 | 
						|
            int len = Integer.parseInt(s);
 | 
						|
            switch (len)
 | 
						|
              {
 | 
						|
                case 9:
 | 
						|
                case (1 <<  9): return MaxFragmentLength.LEN_2_9;
 | 
						|
                case 10:
 | 
						|
                case (1 << 10): return MaxFragmentLength.LEN_2_10;
 | 
						|
                case 11:
 | 
						|
                case (1 << 11): return MaxFragmentLength.LEN_2_11;
 | 
						|
                case 12:
 | 
						|
                case (1 << 12): return MaxFragmentLength.LEN_2_12;
 | 
						|
              }
 | 
						|
          }
 | 
						|
        catch (NumberFormatException nfe)
 | 
						|
          {
 | 
						|
          }
 | 
						|
      }
 | 
						|
    return null;
 | 
						|
  }
 | 
						|
 | 
						|
  private boolean truncatedHMac()
 | 
						|
  {
 | 
						|
    GetSecurityPropertyAction action
 | 
						|
      = new GetSecurityPropertyAction("jessie.client.truncatedHMac");
 | 
						|
    return Boolean.valueOf(AccessController.doPrivileged(action));
 | 
						|
  }
 | 
						|
 | 
						|
  private String getPSKIdentity()
 | 
						|
  {
 | 
						|
    GetSecurityPropertyAction action
 | 
						|
      = new GetSecurityPropertyAction("jessie.client.psk.identity");
 | 
						|
    return AccessController.doPrivileged(action);
 | 
						|
  }
 | 
						|
 | 
						|
  // Delegated tasks.
 | 
						|
 | 
						|
  class ParamsVerifier extends DelegatedTask
 | 
						|
  {
 | 
						|
    private final ByteBuffer paramsBuffer;
 | 
						|
    private final byte[] signature;
 | 
						|
    private boolean verified;
 | 
						|
 | 
						|
    ParamsVerifier(ByteBuffer paramsBuffer, byte[] signature)
 | 
						|
    {
 | 
						|
      this.paramsBuffer = paramsBuffer;
 | 
						|
      this.signature = signature;
 | 
						|
    }
 | 
						|
 | 
						|
    public void implRun()
 | 
						|
      throws InvalidKeyException, NoSuchAlgorithmException,
 | 
						|
             SSLPeerUnverifiedException, SignatureException
 | 
						|
    {
 | 
						|
      java.security.Signature s
 | 
						|
        = java.security.Signature.getInstance(engine.session().suite
 | 
						|
                                              .signatureAlgorithm().algorithm());
 | 
						|
      s.initVerify(engine.session().getPeerCertificates()[0]);
 | 
						|
      s.update(paramsBuffer);
 | 
						|
      verified = s.verify(signature);
 | 
						|
      synchronized (this)
 | 
						|
        {
 | 
						|
          notifyAll();
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    boolean verified()
 | 
						|
    {
 | 
						|
      return verified;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  class ClientDHGen extends DelegatedTask
 | 
						|
  {
 | 
						|
    private final DHPublicKey serverKey;
 | 
						|
    private final DHParameterSpec params;
 | 
						|
    private final boolean full;
 | 
						|
 | 
						|
    ClientDHGen(DHPublicKey serverKey, DHParameterSpec params, boolean full)
 | 
						|
    {
 | 
						|
      this.serverKey = serverKey;
 | 
						|
      this.params = params;
 | 
						|
      this.full = full;
 | 
						|
    }
 | 
						|
 | 
						|
    public void implRun()
 | 
						|
      throws InvalidAlgorithmParameterException, NoSuchAlgorithmException,
 | 
						|
             SSLException
 | 
						|
    {
 | 
						|
      if (Debug.DEBUG)
 | 
						|
        logger.log(Component.SSL_DELEGATED_TASK, "running client DH phase");
 | 
						|
      if (paramsVerifier != null)
 | 
						|
        {
 | 
						|
          synchronized (paramsVerifier)
 | 
						|
            {
 | 
						|
              try
 | 
						|
                {
 | 
						|
                  while (!paramsVerifier.hasRun())
 | 
						|
                    paramsVerifier.wait(500);
 | 
						|
                }
 | 
						|
              catch (InterruptedException ie)
 | 
						|
                {
 | 
						|
                  // Ignore.
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
      KeyPairGenerator gen = KeyPairGenerator.getInstance("DH");
 | 
						|
      gen.initialize(params, engine.session().random());
 | 
						|
      dhPair = gen.generateKeyPair();
 | 
						|
      if (Debug.DEBUG_KEY_EXCHANGE)
 | 
						|
        logger.logv(Component.SSL_KEY_EXCHANGE,
 | 
						|
                    "client keys public:{0} private:{1}", dhPair.getPublic(),
 | 
						|
                    dhPair.getPrivate());
 | 
						|
 | 
						|
      initDiffieHellman((DHPrivateKey) dhPair.getPrivate(), engine.session().random());
 | 
						|
 | 
						|
      // We have enough info to do the full key exchange; so let's do it.
 | 
						|
      DHPhase phase = new DHPhase(serverKey, full);
 | 
						|
      phase.run();
 | 
						|
      if (phase.thrown() != null)
 | 
						|
        throw new SSLException(phase.thrown());
 | 
						|
    }
 | 
						|
 | 
						|
    DHPublicKey serverKey()
 | 
						|
    {
 | 
						|
      return serverKey;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  class CertLoader extends DelegatedTask
 | 
						|
  {
 | 
						|
    private final List<String> keyTypes;
 | 
						|
    private final List<X500Principal> issuers;
 | 
						|
 | 
						|
    CertLoader(List<String> keyTypes, List<X500Principal> issuers)
 | 
						|
    {
 | 
						|
      this.keyTypes = keyTypes;
 | 
						|
      this.issuers = issuers;
 | 
						|
    }
 | 
						|
 | 
						|
    public void implRun()
 | 
						|
    {
 | 
						|
      X509ExtendedKeyManager km = engine.contextImpl.keyManager;
 | 
						|
      if (km == null)
 | 
						|
        return;
 | 
						|
      keyAlias = km.chooseEngineClientAlias(keyTypes.toArray(new String[keyTypes.size()]),
 | 
						|
                                            issuers.toArray(new X500Principal[issuers.size()]),
 | 
						|
                                            engine);
 | 
						|
      engine.session().setLocalCertificates(km.getCertificateChain(keyAlias));
 | 
						|
      privateKey = km.getPrivateKey(keyAlias);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  class RSAGen extends DelegatedTask
 | 
						|
  {
 | 
						|
    private byte[] encryptedPreMasterSecret;
 | 
						|
    private final boolean full;
 | 
						|
 | 
						|
    RSAGen()
 | 
						|
    {
 | 
						|
      this(true);
 | 
						|
    }
 | 
						|
 | 
						|
    RSAGen(boolean full)
 | 
						|
    {
 | 
						|
      this.full = full;
 | 
						|
    }
 | 
						|
 | 
						|
    public void implRun()
 | 
						|
      throws BadPaddingException, IllegalBlockSizeException, InvalidKeyException,
 | 
						|
             NoSuchAlgorithmException, NoSuchPaddingException,
 | 
						|
             SSLException
 | 
						|
    {
 | 
						|
      if (certVerifier != null)
 | 
						|
        {
 | 
						|
          synchronized (certVerifier)
 | 
						|
            {
 | 
						|
              try
 | 
						|
                {
 | 
						|
                  while (!certVerifier.hasRun())
 | 
						|
                    certVerifier.wait(500);
 | 
						|
                }
 | 
						|
              catch (InterruptedException ie)
 | 
						|
                {
 | 
						|
                  // Ignore.
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
      preMasterSecret = new byte[48];
 | 
						|
      engine.session().random().nextBytes(preMasterSecret);
 | 
						|
      preMasterSecret[0] = (byte) sentVersion.major();
 | 
						|
      preMasterSecret[1] = (byte) sentVersion.minor();
 | 
						|
      Cipher rsa = Cipher.getInstance("RSA");
 | 
						|
      java.security.cert.Certificate cert
 | 
						|
        = engine.session().getPeerCertificates()[0];
 | 
						|
      if (cert instanceof X509Certificate)
 | 
						|
        {
 | 
						|
          boolean[] keyUsage = ((X509Certificate) cert).getKeyUsage();
 | 
						|
          if (keyUsage != null && !keyUsage[2])
 | 
						|
            throw new InvalidKeyException("certificate's keyUsage does not permit keyEncipherment");
 | 
						|
        }
 | 
						|
      rsa.init(Cipher.ENCRYPT_MODE, cert.getPublicKey());
 | 
						|
      encryptedPreMasterSecret = rsa.doFinal(preMasterSecret);
 | 
						|
 | 
						|
      // Generate our session keys, because we can.
 | 
						|
      if (full)
 | 
						|
        {
 | 
						|
          generateMasterSecret(clientRandom, serverRandom, engine.session());
 | 
						|
          byte[][] keys = generateKeys(clientRandom, serverRandom, engine.session());
 | 
						|
          setupSecurityParameters(keys, true, engine, compression);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    byte[] encryptedSecret()
 | 
						|
    {
 | 
						|
      return encryptedPreMasterSecret;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  class GenCertVerify extends DelegatedTask
 | 
						|
  {
 | 
						|
    private final MessageDigest md5, sha;
 | 
						|
    private byte[] signed;
 | 
						|
 | 
						|
    GenCertVerify(MessageDigest md5, MessageDigest sha)
 | 
						|
    {
 | 
						|
      try
 | 
						|
        {
 | 
						|
          this.md5 = (MessageDigest) md5.clone();
 | 
						|
          this.sha = (MessageDigest) sha.clone();
 | 
						|
        }
 | 
						|
      catch (CloneNotSupportedException cnse)
 | 
						|
        {
 | 
						|
          // Our message digests *should* be cloneable.
 | 
						|
          throw new Error(cnse);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    public void implRun()
 | 
						|
      throws InvalidKeyException, NoSuchAlgorithmException, SignatureException
 | 
						|
    {
 | 
						|
      byte[] toSign;
 | 
						|
      if (engine.session().version == ProtocolVersion.SSL_3)
 | 
						|
        {
 | 
						|
          toSign = genV3CertificateVerify(md5, sha, engine.session());
 | 
						|
        }
 | 
						|
      else
 | 
						|
        {
 | 
						|
          if (engine.session().suite.signatureAlgorithm() == SignatureAlgorithm.RSA)
 | 
						|
            toSign = Util.concat(md5.digest(), sha.digest());
 | 
						|
          else
 | 
						|
            toSign = sha.digest();
 | 
						|
        }
 | 
						|
 | 
						|
      java.security.Signature sig =
 | 
						|
        java.security.Signature.getInstance(engine.session().suite.signatureAlgorithm().name());
 | 
						|
      sig.initSign(privateKey);
 | 
						|
      sig.update(toSign);
 | 
						|
      signed = sig.sign();
 | 
						|
    }
 | 
						|
 | 
						|
    byte[] signed()
 | 
						|
    {
 | 
						|
      return signed;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 |