if (remoteHost == null)
{
remoteHost = getInetAddress().getHostAddress();
}
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);
// Read the client hello.
Handshake msg = Handshake.read(din);
if (DEBUG_HANDSHAKE_LAYER)
logger.log (Component.SSL_HANDSHAKE, "{0}", msg);
if (msg.getType() != Handshake.Type.CLIENT_HELLO)
{
throwUnexpectedMessage();
}
ClientHello clientHello = (ClientHello) msg.getBody();
Random clientRandom = clientHello.getRandom();
ProtocolVersion version = clientHello.getVersion();
ProtocolVersion server =
(ProtocolVersion) session.enabledProtocols.last();
CompressionMethod comp;
if (clientHello.getCompressionMethods().contains(CompressionMethod.ZLIB))
comp = CompressionMethod.ZLIB;
else
comp = CompressionMethod.NULL;
if (!session.enabledProtocols.contains(version)
&& version.compareTo(server) < 0)
{
Alert alert = new Alert(Alert.Level.FATAL,
Alert.Description.PROTOCOL_VERSION);
sendAlert(alert);
session.currentAlert = alert;
throw new AlertException(alert, true);
}
// Look through the extensions sent by the client (if any), and react to
// them appropriately.
List extensions = null;
String remoteUser = null;
if (clientHello.getExtensions() != null)
{
for (Iterator it = clientHello.getExtensions().iterator(); it.hasNext();)
{
Extension ex = (Extension) it.next();
if (ex.getType() == Extension.Type.SERVER_NAME)
{
if (extensions == null)
{
extensions = new LinkedList();
}
extensions.add(ex);
}
else if (ex.getType() == Extension.Type.MAX_FRAGMENT_LENGTH)
{
int maxLen = Extensions.getMaxFragmentLength(ex).intValue();
// recordInput.setFragmentLength(maxLen);
// recordOutput.setFragmentLength(maxLen);
session.params.setFragmentLength(maxLen);
if (extensions == null)
{
extensions = new LinkedList();
}
extensions.add(ex);
}
else if (ex.getType() == Extension.Type.SRP)
{
if (extensions == null)
{
extensions = new LinkedList();
}
byte[] b = ex.getValue();
remoteUser = new String(ex.getValue(), 1, b[0] & 0xFF, "UTF-8");
session.putValue("srp-username", remoteUser);
}
}
}
CipherSuite suite = selectSuite(clientHello.getCipherSuites(), version);
if (suite == null)
{
return;
}
// If the selected suite turns out to be SRP, set up the key exchange
// objects.
IKeyAgreementParty serverKA = null;
IncomingMessage in;
OutgoingMessage out = null;
if (suite.getKeyExchange() == "SRP")
{
// FIXME
// Uhm, I don't think this can happen, because if remoteUser is null
// we cannot choose an SRP ciphersuite...
if (remoteUser == null)
{
Alert alert = new Alert(Alert.Level.FATAL,
Alert.Description.MISSING_SRP_USERNAME);
sendAlert(alert);
throw new AlertException(alert, true);
}
SRPAuthInfoProvider srpDB = new SRPAuthInfoProvider();
Map dbAttributes = new HashMap();
dbAttributes.put(SRPRegistry.PASSWORD_DB,
session.srpTrustManager.getPasswordFile());
srpDB.activate(dbAttributes);
// FIXME
// We can also fake that the user exists, and generate a dummy (and
// invalid) master secret, and let the handshake fail at the Finished
// message. This is better than letting the connecting side know that
// the username they sent isn't valid.
//
// But how to implement this?
if (!srpDB.contains(remoteUser))
{
Alert alert = new Alert(Alert.Level.FATAL,
Alert.Description.UNKNOWN_SRP_USERNAME);
sendAlert(alert);
throw new AlertException(alert, true);
}
serverKA = KeyAgreementFactory.getPartyBInstance(Registry.SRP_TLS_KA);
Map serverAttributes = new HashMap();
serverAttributes.put(SRP6KeyAgreement.HASH_FUNCTION,
Registry.SHA160_HASH);
serverAttributes.put(SRP6KeyAgreement.HOST_PASSWORD_DB, srpDB);
try
{
serverKA.init(serverAttributes);
out = new OutgoingMessage();
out.writeString(remoteUser);
in = new IncomingMessage(out.toByteArray());
out = serverKA.processMessage(in);
}
catch (KeyAgreementException x)
{
throwHandshakeFailure();
}
}
// Check if the session specified by the client's ID corresponds
// to a saved session, and if so, continue it.
boolean newSession = true;
if (DEBUG_HANDSHAKE_LAYER)
{
logger.log (Component.SSL_HANDSHAKE, "saved sessions: {0}", sessionContext);
}
if (sessionContext.containsSessionID(
new Session.ID(clientHello.getSessionId())))
{
Session old = session;
session = (Session) sessionContext.getSession(clientHello.getSessionId());
if (!clientHello.getCipherSuites().contains(session.cipherSuite))
{
throwHandshakeFailure();
}
if (session.getPeerHost().equals(remoteHost) &&
old.enabledProtocols.contains(session.protocol))
{
session = (Session) session.clone();
suite = session.cipherSuite;
newSession = false;
recordInput.setSession(session);
session.currentAlert = null;
session.params = old.params;
session.random = old.random;
}
else
{
if (DEBUG_HANDSHAKE_LAYER)
{
logger.log (Component.SSL_HANDSHAKE, "rejected section; hosts equal? {0}, same suites? {1}",
new Object[] { Boolean.valueOf (session.getPeerHost().equals(remoteHost)),
Boolean.valueOf (old.enabledProtocols.contains(session.protocol)) });
}
session = old;
session.peerHost = remoteHost;
newSession = true;
}
}
else if (DEBUG_HANDSHAKE_LAYER)
{
logger.log (Component.SSL_HANDSHAKE, "rejected session; have session id? {0}, saved sessions: {1}",
new Object[] { Boolean.valueOf (sessionContext.containsSessionID(new Session.ID(clientHello.getSessionId()))),
sessionContext });
}
if (newSession)
{
byte[] buf = new byte[32];
Session.ID sid = null;
do
{
session.random.nextBytes(buf);
sid = new Session.ID(buf);
}
while (sessionContext.containsSessionID(sid));
session.sessionId = sid;
}
session.valid = true;
session.peerHost = remoteHost;
session.cipherSuite = suite;
session.protocol = version;
session.params.setVersion (version);
// Send the server hello.
Random serverRandom = new Random(Util.unixTime(),
session.random.generateSeed(28));
ServerHello serverHello = new ServerHello(version, serverRandom,
session.getId(), suite,
comp, extensions);
msg = new Handshake(Handshake.Type.SERVER_HELLO, serverHello);
if (DEBUG_HANDSHAKE_LAYER)
logger.log (Component.SSL_HANDSHAKE, "{0}", msg);
msg.write(dout, version);
// recordOutput.setHandshakeAvail(msg.write(dout, version));
dout.flush();
if (newSession)
{
X509Certificate[] certs = null;
PrivateKey serverKey = null;
if (suite.getSignature() != "anon")
{
// Send our CA-issued certificate to the client.
String alias = session.keyManager.chooseServerAlias(suite.getAuthType(),
null, null);
certs = session.keyManager.getCertificateChain(alias);
serverKey = session.keyManager.getPrivateKey(alias);
if (certs == null || serverKey == null)
{
throwHandshakeFailure();
}
session.localCerts = certs;
Certificate serverCert = new Certificate(certs);
msg = new Handshake(Handshake.Type.CERTIFICATE, serverCert);
if (DEBUG_HANDSHAKE_LAYER)
logger.log (Component.SSL_HANDSHAKE, "{0}", msg);
msg.write(dout, version);
// recordOutput.setHandshakeAvail(msg.write(dout, version));;
dout.flush();
}
// If the certificate we sent does not contain enough information to
// do the key exchange (in the case of ephemeral Diffie-Hellman,
// export RSA, and SRP) we send a signed public key to be used for the
// key exchange.
KeyPair signPair = null;
if (certs != null)
{
signPair = new KeyPair(certs[0].getPublicKey(), serverKey);
}
KeyPair kexPair = signPair;
ServerKeyExchange skex = null;
// Set up our key exchange, and/or prepare our ServerKeyExchange
// message.
if ((suite.getKeyExchange() == "RSA" && suite.isExportable() &&
((RSAPrivateKey) serverKey).getModulus().bitLength() > 512))
{
kexPair = KeyPool.generateRSAKeyPair();
RSAPublicKey pubkey = (RSAPublicKey) kexPair.getPublic();
Signature s = null;
if (suite.getSignature() != "anon")
{
SSLRSASignature sig = new SSLRSASignature();
sig.setupSign(Collections.singletonMap(ISignature.SIGNER_KEY,
signPair.getPrivate()));
byte[] buf = clientRandom.getEncoded();
sig.update(buf, 0, buf.length);
buf = serverRandom.getEncoded();
sig.update(buf, 0, buf.length);
updateSig(sig, pubkey.getModulus());
updateSig(sig, pubkey.getPublicExponent());
s = new Signature(sig.sign(), "RSA");
}
skex = new ServerKeyExchange(pubkey, s);
}
else if (suite.getKeyExchange() == "DH")
{
serverKA = KeyAgreementFactory.getPartyBInstance(Registry.ELGAMAL_KA);
Map attr = new HashMap();
attr.put(ElGamalKeyAgreement.KA_ELGAMAL_RECIPIENT_PRIVATE_KEY,
serverKey);
try
{
serverKA.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;
}
// We don't send a ServerKeyExchange for this suite.
}
else if (suite.getKeyExchange() == "DHE")
{
serverKA = KeyAgreementFactory.getPartyAInstance(Registry.DH_KA);
Map attr = new HashMap();
GnuDHPrivateKey servParams = DiffieHellman.getParams();
attr.put(DiffieHellmanKeyAgreement.KA_DIFFIE_HELLMAN_OWNER_PRIVATE_KEY,
servParams);
attr.put(DiffieHellmanKeyAgreement.SOURCE_OF_RANDOMNESS,
session.random);
BigInteger serv_y = null;
try
{
serverKA.init(attr);
out = serverKA.processMessage(null);
in = new IncomingMessage(out.toByteArray());
serv_y = in.readMPI();
}
catch (KeyAgreementException kae)
{
if (DEBUG_KEY_EXCHANGE)
{
logger.log (Component.SSL_KEY_EXCHANGE, "DHE exception", kae);
}
internalError();
RuntimeException re = new RuntimeException (kae.getMessage());
re.initCause (kae);
throw re;
}
GnuDHPublicKey pubkey =
new GnuDHPublicKey(null, servParams.getParams().getP(),
servParams.getParams().getG(), serv_y);
Signature s = null;
if (suite.getSignature() != "anon")
{
ISignature sig = null;
if (suite.getSignature() == "RSA")
{
sig = new SSLRSASignature();
}
else
{
sig = SignatureFactory.getInstance(Registry.DSS_SIG);
}
sig.setupSign(Collections.singletonMap(ISignature.SIGNER_KEY,
signPair.getPrivate()));
byte[] buf = clientRandom.getEncoded();
sig.update(buf, 0, buf.length);
buf = serverRandom.getEncoded();
sig.update(buf, 0, buf.length);
updateSig(sig, pubkey.getParams().getP());
updateSig(sig, pubkey.getParams().getG());
updateSig(sig, pubkey.getY());
s = new Signature(sig.sign(), suite.getSignature());
}
skex = new ServerKeyExchange(pubkey, s);
}
else if (suite.getKeyExchange() == "SRP")
{
BigInteger N = null;
BigInteger g = null;
BigInteger salt = null;
BigInteger B = null;
try
{
in = new IncomingMessage(out.toByteArray());
N = in.readMPI();
g = in.readMPI();
salt = in.readMPI();
B = in.readMPI();
}
catch (KeyAgreementException x)
{
if (DEBUG_KEY_EXCHANGE)
{
logger.log (Component.SSL_KEY_EXCHANGE, "SRP exception", x);
}
throwHandshakeFailure();
}
Signature s = null;
final byte[] srpSalt = Util.trim(salt);
if (suite.getSignature() != "anon")
{
ISignature sig = null;
if (suite.getSignature() == "RSA")
{
sig = new SSLRSASignature();
}
else
{
sig = SignatureFactory.getInstance(Registry.DSS_SIG);
}
sig.setupSign(Collections.singletonMap(ISignature.SIGNER_KEY,
signPair.getPrivate()));
byte[] buf = clientRandom.getEncoded();
sig.update(buf, 0, buf.length);
buf = serverRandom.getEncoded();
sig.update(buf, 0, buf.length);
updateSig(sig, N);
updateSig(sig, g);
sig.update((byte) srpSalt.length);
sig.update(srpSalt, 0, srpSalt.length);
updateSig(sig, B);
s = new Signature(sig.sign(), suite.getSignature());
}
final SRPPublicKey pubkey = new SRPPublicKey(N, g, B);
skex = new ServerKeyExchange(pubkey, s, srpSalt);
}
if (skex != null)
{
msg = new Handshake(Handshake.Type.SERVER_KEY_EXCHANGE, skex);
if (DEBUG_HANDSHAKE_LAYER)
logger.log (Component.SSL_HANDSHAKE, "{0}", msg);
msg.write(dout, version);
// recordOutput.setHandshakeAvail(msg.write(dout, version));;
dout.flush();
}
// If we are configured to want or need client authentication, then
// ask for it.
if (wantClientAuth || needClientAuth)
{
Principal[] auths = null;
CertificateRequest.ClientType[] types =
new CertificateRequest.ClientType[] {
CertificateRequest.ClientType.RSA_SIGN,
CertificateRequest.ClientType.DSS_SIGN,
CertificateRequest.ClientType.RSA_FIXED_DH,
CertificateRequest.ClientType.DSS_FIXED_DH
};
try
{
auths = (Principal[])
Util.transform(session.trustManager.getAcceptedIssuers(),
Principal.class, "getSubjectDN", null);
}
catch (Exception x)
{
internalError();
RuntimeException re = new RuntimeException (x.getMessage());
re.initCause (x);
throw re;
}
CertificateRequest req = new CertificateRequest(types, auths);
msg = new Handshake(Handshake.Type.CERTIFICATE_REQUEST, req);
msg.write(dout, version);
dout.flush();
}
// Send our server hello done.
msg = new Handshake(Handshake.Type.SERVER_HELLO_DONE, null);
if (DEBUG_HANDSHAKE_LAYER)
logger.log (Component.SSL_HANDSHAKE, "{0}", msg);
msg.write(dout, version);
dout.flush();
if (suite.getKeyExchange() == "RSA")
{
msg = Handshake.read(din, suite, kexPair.getPublic());
}
else
{
msg = Handshake.read(din, suite, null);
}
boolean clientCertOk = false;
boolean clientCanSign = false;
X509Certificate[] clientChain = null;
PublicKey clientKey = null;
// Read the client's certificate, if sent.
if (msg.getType() == Handshake.Type.CERTIFICATE)
{
if (DEBUG_HANDSHAKE_LAYER)
logger.log (Component.SSL_HANDSHAKE, "{0}", msg);
Certificate cliCert = (Certificate) msg.getBody();
clientChain = cliCert.getCertificates();
try
{
session.trustManager.checkClientTrusted(clientChain,
suite.getAuthType());
session.peerCerts = clientChain;
session.peerVerified = true;
clientKey = clientChain[0].getPublicKey();
}
catch (Exception x)
{
}
clientCanSign = ((clientKey instanceof DSAPublicKey) ||
(clientKey instanceof RSAPublicKey));
if (suite.getKeyExchange().startsWith("DH"))
{
msg = Handshake.read(din, suite, clientKey);
}
else
{
msg = Handshake.read(din, suite, kexPair.getPublic());
}
}
// If we require client authentication, and the client sent an
// unverifiable certificate or no certificate at all, drop the
// connection.
if (!session.peerVerified && needClientAuth)
{
throwHandshakeFailure();
}
// Read the client key exchange.
if (msg.getType() != Handshake.Type.CLIENT_KEY_EXCHANGE)
{
throwUnexpectedMessage();
}
if (DEBUG_HANDSHAKE_LAYER)
logger.log (Component.SSL_HANDSHAKE, "{0}", msg);
ClientKeyExchange ckex = (ClientKeyExchange) msg.getBody();
byte[] preMasterSecret = null;
if (suite.getKeyExchange() == "RSA")
{
byte[] enc = (byte[]) ckex.getExchangeObject();
BigInteger bi = new BigInteger(1, enc);
try
{
bi = RSA.decrypt(kexPair.getPrivate(), bi);
EME_PKCS1_V1_5 pkcs1 = EME_PKCS1_V1_5.getInstance(
(RSAPrivateKey) kexPair.getPrivate());
preMasterSecret = pkcs1.decode(Util.concat(new byte[1], bi.toByteArray()));
//rsa.init(kexPair);
//preMasterSecret = rsa.decrypt(enc);
}
catch (Exception x)
{
if (DEBUG_KEY_EXCHANGE)
{
logger.log (Component.SSL_KEY_EXCHANGE, "RSA exception", x);
}
// Generate a fake pre-master secret if the RSA decryption
// fails.
byte[] b = new byte[46];
session.random.nextBytes (b);
preMasterSecret = Util.concat(version.getEncoded(), b);
}
}
else if (suite.getKeyExchange().startsWith("DH"))
{
try
{
out = new OutgoingMessage();
if (clientKey == null)
out.writeMPI((BigInteger) ckex.getExchangeObject());
else
out.writeMPI(((DHPublicKey) clientKey).getY());
in = new IncomingMessage(out.toByteArray());
serverKA.processMessage(in);
preMasterSecret = serverKA.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")
{
BigInteger A = (BigInteger) ckex.getExchangeObject();
if (DEBUG_KEY_EXCHANGE)
{
logger.log (Component.SSL_KEY_EXCHANGE, "SRP: client A: {0}", A);
}
try
{
out = new OutgoingMessage();
out.writeMPI(A);
in = new IncomingMessage(out.toByteArray());
out = serverKA.processMessage(in);
preMasterSecret = serverKA.getSharedSecret();
}
catch (KeyAgreementException x)
{
if (DEBUG_KEY_EXCHANGE)
{
logger.log (Component.SSL_KEY_EXCHANGE, "SRP exception", x);
}
throwHandshakeFailure();
}
finally
{
serverKA = 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(), ':'));
}
// Generate the master secret.
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();
re.initCause (shouldNotHappen);
throw re;
}
if (DEBUG_KEY_EXCHANGE)
{
logger.log (Component.SSL_KEY_EXCHANGE, "masterSecret: {0}",
Util.toHexString(session.masterSecret, ':'));
}
// Read the client's certificate verify message, if needed.
if (clientCanSign && (wantClientAuth || needClientAuth))
{
msg = Handshake.read(din);
if (msg.getType() != Handshake.Type.CERTIFICATE_VERIFY)
{
throwUnexpectedMessage();
}
CertificateVerify verify = (CertificateVerify) msg.getBody();
if (clientChain != null && clientChain.length > 0)
{
IMessageDigest cvMD5 = (IMessageDigest) md5.clone();
IMessageDigest cvSHA = (IMessageDigest) sha.clone();
clientKey = clientChain[0].getPublicKey();
if (clientKey instanceof RSAPublicKey)
{
SSLRSASignature sig = new SSLRSASignature(cvMD5, cvSHA);
sig.setupVerify(Collections.singletonMap(ISignature.VERIFIER_KEY, clientKey));
if (!sig.verify(verify.getSigValue()))
{
handshakeFailure();
throw new SSLHandshakeException("client certificate verify failed");
}
}
else if (clientKey instanceof DSAPublicKey)
{
try
{
if (!DSSSignature.verify((DSAPublicKey) clientKey, cvSHA.digest(),
(BigInteger[]) verify.getSigValue()))
{
throw new Exception("client's certificate could not be verified");
}
}