{
logger.log (Component.SSL_HANDSHAKE, "starting client handshake in {0}",
Thread.currentThread());
}
IMessageDigest md5 = HashFactory.getInstance(Registry.MD5_HASH);
IMessageDigest sha = HashFactory.getInstance(Registry.SHA160_HASH);
DigestInputStream din = new DigestInputStream(handshakeIn, md5, sha);
DigestOutputStream dout = new DigestOutputStream(handshakeOut, md5, sha);
Session continuedSession = null;
byte[] sessionId = new byte[0];
List extensions = null;
String user = null;
CertificateType certType = CertificateType.X509;
// Look through the available sessions to see if an appropriate one is
// available.
for (Enumeration e = sessionContext.getIds(); e.hasMoreElements(); )
{
byte[] id = (byte[]) e.nextElement();
continuedSession = (Session) sessionContext.getSession(id);
if (continuedSession == null)
{
continue;
}
if (!session.enabledProtocols.contains(continuedSession.protocol))
{
continue;
}
if (remoteHost != null
&& remoteHost.equals(continuedSession.getPeerHost()))
{
sessionId = id;
break;
}
}
// If a SRP suite is enabled, ask for a username so we can include it
// with our extensions list.
for (Iterator i = session.enabledSuites.iterator(); i.hasNext(); )
{
CipherSuite s = (CipherSuite) i.next();
if (s.getKeyExchange() == "SRP")
{
extensions = new LinkedList();
user = askUserName(remoteHost);
byte[] b = user.getBytes("UTF-8");
if (b.length > 255)
{
handshakeFailure();
throw new SSLException("SRP username too long");
}
extensions.add(new Extension(Extension.Type.SRP,
Util.concat(new byte[] { (byte) b.length }, b)));
break;
}
}
// If the jessie.fragment.length property is set, add the appropriate
// extension to the list. The fragment length is only actually set if
// the server responds with the same extension.
try
{
int flen = Integer.parseInt(Util.getSecurityProperty("jessie.fragment.length"));
byte[] ext = new byte[1];
if (flen == 512)
ext[0] = 1;
else if (flen == 1024)
ext[0] = 2;
else if (flen == 2048)
ext[0] = 3;
else if (flen == 4096)
ext[0] = 4;
else
throw new NumberFormatException();
if (extensions == null)
extensions = new LinkedList();
extensions.add(new Extension(Extension.Type.MAX_FRAGMENT_LENGTH, ext));
}
catch (NumberFormatException nfe) { }
// FIXME: set certificate types.
// Send the client hello.
ProtocolVersion version = session.protocol;
Random clientRandom =
new Random(Util.unixTime(), session.random.generateSeed(28));
session.protocol = (ProtocolVersion) session.enabledProtocols.last();
List comp = new ArrayList(2);
/* comp.add(CompressionMethod.ZLIB); */ // Does not work properly.
comp.add(CompressionMethod.NULL);
ClientHello clientHello =
new ClientHello(session.protocol, clientRandom, sessionId,
session.enabledSuites, comp, extensions);
Handshake msg = new Handshake(Handshake.Type.CLIENT_HELLO, clientHello);
if (DEBUG_HANDSHAKE_LAYER)
logger.log (Component.SSL_HANDSHAKE, "{0}", msg);
msg.write (dout, version);
// recordOutput.setHandshakeAvail(msg.write(dout, version));
dout.flush();
// try
// {
// Thread.sleep(150);
// }
// catch (InterruptedException ie)
// {
// }
// Receive the server hello.
msg = Handshake.read(din);
if (DEBUG_HANDSHAKE_LAYER)
logger.log (Component.SSL_HANDSHAKE, "{0}", msg);
if (msg.getType() != Handshake.Type.SERVER_HELLO)
{
throwUnexpectedMessage();
}
ServerHello serverHello = (ServerHello) msg.getBody();
Random serverRandom = serverHello.getRandom();
version = serverHello.getVersion();
// If we don't directly support the server's protocol version, choose
// the highest one we support that is less than the server's version.
if (!session.enabledProtocols.contains(version))
{
ProtocolVersion v1 = null, v2 = null;
for (Iterator it = session.enabledProtocols.iterator();
it.hasNext(); )
{
v1 = (ProtocolVersion) it.next();
if (v1.compareTo(version) > 0)
break;
v2 = v1;
}
version = v1;
}
// The server's version is either unsupported by us (unlikely) or the user
// has only enabled incompatible versions.
if (version == null)
{
Alert.Description desc = null;
if (serverHello.getVersion() == ProtocolVersion.SSL_3)
{
desc = Alert.Description.HANDSHAKE_FAILURE;
}
else
{
desc = Alert.Description.PROTOCOL_VERSION;
}
Alert alert = new Alert(Alert.Level.FATAL, desc);
sendAlert(alert);
session.currentAlert = alert;
fatal();
throw new AlertException(alert, true);
}
if (serverHello.getExtensions() != null)
{
for (Iterator it = serverHello.getExtensions().iterator();
it.hasNext(); )
{
Extension e = (Extension) it.next();
if (e.getType() == Extension.Type.MAX_FRAGMENT_LENGTH)
{
int len = Extensions.getMaxFragmentLength(e).intValue();
session.params.setFragmentLength(len);
// recordOutput.setFragmentLength(len);
// recordInput.setFragmentLength(len);
}
else if (e.getType() == Extension.Type.CERT_TYPE)
{
certType = Extensions.getServerCertType(e);
}
}
}
CipherSuite suite = serverHello.getCipherSuite().resolve(version);
boolean newSession = true;
if (sessionId.length > 0 &&
Arrays.equals(sessionId, serverHello.getSessionId()))
{
SecurityParameters params = session.params;
SecureRandom random = session.random;
session = (Session) continuedSession.clone();
session.params = params;
session.random = random;
recordInput.setSession(session);
// recordOutput.setSession(session);
suite = session.cipherSuite;
newSession = false;
}
else
{
sessionContext.removeSession(new Session.ID(sessionId));
}
if (newSession)
{
session.peerHost = remoteHost;
session.sessionId = new Session.ID(serverHello.getSessionId());
session.cipherSuite = suite;
}
session.params.reset();
// session.params.setInMac(null);
// session.params.setOutMac(null);
// session.params.setInRandom(null);
// session.params.setOutRandom(null);
// session.params.setInCipher(null);
// session.params.setOutCipher(null);
session.currentAlert = null;
session.valid = true;
session.protocol = version;
// If the server responded with the same session id that we sent, we
// assume that the session will be continued, and skip the bulk of the
// handshake.
if (newSession)
{
PublicKey serverKey = null, serverKex = null;
KeyPair clientKeys = null, clientKex = null;
CertificateRequest certReq;
boolean sendKeyExchange = false;
BigInteger srp_x = null;
IKeyAgreementParty clientKA = null;
IncomingMessage in; // used for key agreement protocol exchange
OutgoingMessage out = null;
if (suite.getKeyExchange() == "SRP")
{
String password = askPassword(user);
if (DEBUG_KEY_EXCHANGE)
{
logger.log (Component.SSL_KEY_EXCHANGE,
"SRP: password read is ''{0}''", password);
}
byte[] userSrpPassword = password.getBytes("UTF-8");
// instantiate and setup client-side key agreement party
clientKA = KeyAgreementFactory.getPartyAInstance(Registry.SRP_TLS_KA);
Map clientAttributes = new HashMap();
clientAttributes.put(SRP6KeyAgreement.HASH_FUNCTION,
Registry.SHA160_HASH);
clientAttributes.put(SRP6KeyAgreement.USER_IDENTITY, user);
clientAttributes.put(SRP6KeyAgreement.USER_PASSWORD, userSrpPassword);
try
{
clientKA.init(clientAttributes);
// initiate the exchange
out = clientKA.processMessage(null);
}
catch (KeyAgreementException x)
{
if (DEBUG_KEY_EXCHANGE)
{
logger.log (Component.SSL_KEY_EXCHANGE, "SRP exception", x);
}
throwHandshakeFailure();
}
}
if (suite.getSignature() != "anon")
{
msg = Handshake.read(din, certType);
if (DEBUG_HANDSHAKE_LAYER)
logger.log (Component.SSL_HANDSHAKE, "{0}", msg);
if (msg.getType() != Handshake.Type.CERTIFICATE)
{
throwUnexpectedMessage();
}
Certificate serverCertificate = (Certificate) msg.getBody();
X509Certificate[] peerCerts = serverCertificate.getCertificates();
try
{
session.trustManager.checkServerTrusted(peerCerts,
suite.getAuthType());
if (suite.getSignature() == "RSA" &&
!(peerCerts[0].getPublicKey() instanceof RSAPublicKey))
throw new InvalidKeyException("improper public key");
if (suite.getKeyExchange() == "DH" &&
!(peerCerts[0].getPublicKey() instanceof DHPublicKey))
throw new InvalidKeyException("improper public key");
if (suite.getKeyExchange() == "DHE")
{
if (suite.getSignature() == "RSA" &&
!(peerCerts[0].getPublicKey() instanceof RSAPublicKey))
throw new InvalidKeyException("improper public key");
if (suite.getSignature() == "DSS" &&
!(peerCerts[0].getPublicKey() instanceof DSAPublicKey))
throw new InvalidKeyException("improper public key");
}
session.peerCerts = peerCerts;
session.peerVerified = true;
}
catch (InvalidKeyException ike)
{
throwHandshakeFailure();
}
catch (Exception x)
{
if (!checkCertificates(peerCerts))
{
peerUnverified(peerCerts);
SSLPeerUnverifiedException e =
new SSLPeerUnverifiedException ("could not verify peer certificate: "+
peerCerts[0].getSubjectDN());
e.initCause (x);
throw e;
}
session.peerCerts = peerCerts;
session.peerVerified = true;
}
serverKey = peerCerts[0].getPublicKey();
serverKex = serverKey;
}
msg = Handshake.read(din, suite, serverKey);
// Receive the server's key exchange.
if (msg.getType() == Handshake.Type.SERVER_KEY_EXCHANGE)
{
if (DEBUG_HANDSHAKE_LAYER)
logger.log (Component.SSL_HANDSHAKE, "{0}", msg);
ServerKeyExchange skex = (ServerKeyExchange) msg.getBody();
serverKex = skex.getPublicKey();
if (suite.getSignature() != "anon")
{
ISignature sig = null;
if (suite.getSignature() == "RSA")
{
sig = new SSLRSASignature();
}
else if (suite.getSignature() == "DSS")
{
sig = SignatureFactory.getInstance(Registry.DSS_SIG);
}
sig.setupVerify(Collections.singletonMap(
ISignature.VERIFIER_KEY, serverKey));
byte[] buf = clientRandom.getEncoded();
sig.update(buf, 0, buf.length);
buf = serverRandom.getEncoded();
sig.update(buf, 0, buf.length);
if (suite.getKeyExchange() == "RSA")
{
updateSig(sig, ((RSAPublicKey) serverKex).getModulus());
updateSig(sig, ((RSAPublicKey) serverKex).getPublicExponent());
}
else if (suite.getKeyExchange() == "DHE")
{
updateSig(sig, ((DHPublicKey) serverKex).getParams().getP());
updateSig(sig, ((DHPublicKey) serverKex).getParams().getG());
updateSig(sig, ((DHPublicKey) serverKex).getY());
}
else if (suite.getKeyExchange() == "SRP")
{
updateSig(sig, ((SRPPublicKey) serverKex).getN());
updateSig(sig, ((SRPPublicKey) serverKex).getG());
byte[] srpSalt = skex.getSRPSalt();
sig.update((byte) srpSalt.length);
sig.update(srpSalt, 0, srpSalt.length);
updateSig(sig, ((SRPPublicKey) serverKex).getY());
}
if (!sig.verify(skex.getSignature().getSigValue()))
{
throwHandshakeFailure();
}
}
if (suite.getKeyExchange() == "SRP")
{
// use server's key exchange data to continue
// agreement protocol by faking a received incoming
// message. again the following code can be broken
// into multiple blocks for more accurate exception
// handling
try
{
out = new OutgoingMessage();
out.writeMPI(((SRPPublicKey) serverKex).getN());
out.writeMPI(((SRPPublicKey) serverKex).getG());
out.writeMPI(new BigInteger(1, skex.getSRPSalt()));
out.writeMPI(((SRPPublicKey) serverKex).getY());
in = new IncomingMessage(out.toByteArray());
out = clientKA.processMessage(in);
if (DEBUG_KEY_EXCHANGE)
{
logger.log (Component.SSL_KEY_EXCHANGE, "clientKA isComplete? {0}",
Boolean.valueOf (clientKA.isComplete()));
}
}
catch (KeyAgreementException x)
{
if (DEBUG_KEY_EXCHANGE)
{
logger.log (Component.SSL_KEY_EXCHANGE, "SRP exception", x);
}
throwHandshakeFailure();
}
}
msg = Handshake.read(din, suite, serverKey);
}
// See if the server wants us to send our certificates.
certReq = null;
if (msg.getType() == Handshake.Type.CERTIFICATE_REQUEST)
{
if (suite.getSignature() == "anon")
{
throwHandshakeFailure();
}
if (DEBUG_HANDSHAKE_LAYER)
logger.log (Component.SSL_HANDSHAKE, "{0}", msg);
certReq = (CertificateRequest) msg.getBody();
msg = Handshake.read(din);
}
// Read ServerHelloDone.
if (msg.getType() != Handshake.Type.SERVER_HELLO_DONE)
{
throwUnexpectedMessage();
}
if (DEBUG_HANDSHAKE_LAYER)
logger.log (Component.SSL_HANDSHAKE, "{0}", msg);
// Send our certificate chain if the server asked for it.
if (certReq != null)
{
String alias = session.keyManager.chooseClientAlias(
certReq.getTypeStrings(), certReq.getAuthorities(), null);
if (alias == null && version == ProtocolVersion.SSL_3)
{
Alert alert =
new Alert(Alert.Level.WARNING, Alert.Description.NO_CERTIFICATE);
sendAlert(alert);
}
else
{
X509Certificate[] chain =
session.keyManager.getCertificateChain(alias);
PrivateKey key = session.keyManager.getPrivateKey(alias);
if (chain == null)
{
chain = new X509Certificate[0];
}
Certificate cert = new Certificate(chain);
msg = new Handshake(Handshake.Type.CERTIFICATE, cert);
if (DEBUG_HANDSHAKE_LAYER)
logger.log (Component.SSL_HANDSHAKE, "{0}", msg);
msg.write(dout, version);
// recordOutput.setHandshakeAvail(msg.write(dout, version));;
dout.flush();
if (chain.length > 0)
{
session.localCerts = chain;
clientKeys = new KeyPair(chain[0].getPublicKey(), key);
}
}
}
// Send our key exchange.
byte[] preMasterSecret = null;
ClientKeyExchange ckex = null;
if (suite.getKeyExchange() == "RSA")
{
ProtocolVersion v =
(ProtocolVersion) session.enabledProtocols.last();
byte[] b = new byte[46];
session.random.nextBytes (b);
preMasterSecret = Util.concat(v.getEncoded(), b);
EME_PKCS1_V1_5 pkcs1 = EME_PKCS1_V1_5.getInstance((RSAPublicKey) serverKex);
BigInteger bi = new BigInteger(1,
pkcs1.encode(preMasterSecret, session.random));
bi = RSA.encrypt((RSAPublicKey) serverKex, bi);
ckex = new ClientKeyExchange(Util.trim(bi));
}
else if (suite.getKeyExchange().startsWith("DH"))
{
if (clientKeys == null ||
!(clientKeys.getPublic() instanceof DHPublicKey))
{
GnuDHPrivateKey tmpKey =
new GnuDHPrivateKey(null, ((DHPublicKey) serverKex).getParams().getP(),
((DHPublicKey) serverKex).getParams().getG(), null);
clientKA = KeyAgreementFactory.getPartyBInstance(Registry.DH_KA);
Map attr = new HashMap();
attr.put(DiffieHellmanKeyAgreement.KA_DIFFIE_HELLMAN_OWNER_PRIVATE_KEY,
tmpKey);
attr.put(DiffieHellmanKeyAgreement.SOURCE_OF_RANDOMNESS,
session.random);
try
{
clientKA.init(attr);
out = new OutgoingMessage();
out.writeMPI(((DHPublicKey) serverKex).getY());
in = new IncomingMessage(out.toByteArray());
out = clientKA.processMessage(in);
in = new IncomingMessage(out.toByteArray());
ckex = new ClientKeyExchange(in.readMPI());
}
catch (KeyAgreementException kae)
{
if (DEBUG_KEY_EXCHANGE)
{
logger.log (Component.SSL_KEY_EXCHANGE, "DH exception", kae);
}
internalError();
RuntimeException re = new RuntimeException (kae.getMessage());
re.initCause (kae);
throw re;
}
}
else
{
clientKA = KeyAgreementFactory.getPartyBInstance(Registry.ELGAMAL_KA);
Map attr = new HashMap();
attr.put(ElGamalKeyAgreement.KA_ELGAMAL_RECIPIENT_PRIVATE_KEY,
clientKeys.getPrivate());
try
{
// The key exchange is already complete here; our public
// value was sent with our certificate.
clientKA.init(attr);
}
catch (KeyAgreementException kae)
{
if (DEBUG_KEY_EXCHANGE)
logger.log (Component.SSL_KEY_EXCHANGE, "DH exception", kae);
internalError();
RuntimeException re = new RuntimeException (kae.getMessage());
re.initCause (kae);
throw re;
}
ckex = new ClientKeyExchange(new byte[0]);
}
}
else if (suite.getKeyExchange() == "SRP")
{
// at this point, out --the outgoing message-- already contains
// what we want. so...
BigInteger A = null;
try
{
in = new IncomingMessage(out.toByteArray());
A = in.readMPI();
if (DEBUG_KEY_EXCHANGE)
{
logger.log (Component.SSL_KEY_EXCHANGE, "client A:{0}", A);
}
}
catch (KeyAgreementException x)
{
if (DEBUG_KEY_EXCHANGE)
{
logger.log (Component.SSL_KEY_EXCHANGE, "SRP exception", x);
}
throwHandshakeFailure();
}
ckex = new ClientKeyExchange(A);
}
msg = new Handshake(Handshake.Type.CLIENT_KEY_EXCHANGE, ckex);
if (DEBUG_HANDSHAKE_LAYER)
logger.log (Component.SSL_HANDSHAKE, "{0}", msg);
msg.write (dout, version);
// recordOutput.setHandshakeAvail(msg.write(dout, version));;
// Generate the master secret.
if (suite.getKeyExchange().startsWith("DH"))
{
try
{
preMasterSecret = clientKA.getSharedSecret();
}
catch (KeyAgreementException kae)
{
if (DEBUG_KEY_EXCHANGE)
{
logger.log (Component.SSL_KEY_EXCHANGE, "DH exception", kae);
}
internalError();
RuntimeException re = new RuntimeException (kae.getMessage());
re.initCause (kae);
throw re;
}
}
else if (suite.getKeyExchange() == "SRP")
{
try
{
preMasterSecret = clientKA.getSharedSecret();
}
catch (KeyAgreementException x)
{
if (DEBUG_KEY_EXCHANGE)
{
logger.log (Component.SSL_KEY_EXCHANGE, "SRP exception", x);
}
throwHandshakeFailure();
}
finally
{
clientKA = null;
}
}
if (DEBUG_KEY_EXCHANGE)
{
logger.log (Component.SSL_KEY_EXCHANGE, "preMasterSecret:\n{0}",
Util.toHexString (preMasterSecret, ':'));
logger.log (Component.SSL_KEY_EXCHANGE, "client.random:\n{0}",
Util.toHexString(clientRandom.getEncoded(), ':'));
logger.log (Component.SSL_KEY_EXCHANGE, "server.random:\n{0}",
Util.toHexString(serverRandom.getEncoded(), ':'));
}
IRandom genSecret = null;
if (version == ProtocolVersion.SSL_3)
{
genSecret = new SSLRandom();
HashMap attr = new HashMap();
attr.put(SSLRandom.SECRET, preMasterSecret);
attr.put(SSLRandom.SEED,
Util.concat(clientRandom.getEncoded(), serverRandom.getEncoded()));
genSecret.init(attr);
}
else
{
genSecret = new TLSRandom();
HashMap attr = new HashMap();
attr.put(TLSRandom.SECRET, preMasterSecret);
attr.put(TLSRandom.SEED,
Util.concat(("master secret").getBytes("UTF-8"),
Util.concat(clientRandom.getEncoded(), serverRandom.getEncoded())));
genSecret.init(attr);
}
session.masterSecret = new byte[48];
try
{
genSecret.nextBytes(session.masterSecret, 0, 48);
for (int i = 0; i < preMasterSecret.length; i++)
{
preMasterSecret[i] = 0;
}
}
catch (LimitReachedException shouldNotHappen)
{
internalError();
RuntimeException re = new RuntimeException (shouldNotHappen.getMessage());
re.initCause (shouldNotHappen);
throw re;
}
if (DEBUG_KEY_EXCHANGE)
{
logger.log (Component.SSL_KEY_EXCHANGE, "masterSecret: {0}",
Util.toHexString(session.masterSecret, ':'));
}
// Send our certificate verify message.
if (certReq != null && clientKeys != null)
{
IMessageDigest vMD5 = (IMessageDigest) md5.clone();
IMessageDigest vSHA = (IMessageDigest) sha.clone();
PrivateKey key = clientKeys.getPrivate();
Object sig = null;
String sigAlg = null;
try
{
if (key instanceof DSAPrivateKey)
{
sig = DSSSignature.sign((DSAPrivateKey) key, vSHA.digest(),
session.random);
sigAlg = "DSS";
}
else if (key instanceof RSAPrivateKey)
{