Handshakepublic class Handshake extends Object This class implements the SSL handshake protocol which is responsible
for negotiating security parameters used by the record layer.
Currently, only client-side functionality is implemented. |
Fields Summary |
---|
static final byte | ARCFOUR_128_SHAARCFOUR_128_SHA (0x05). | static final byte | ARCFOUR_128_MD5ARCFOUR_128_MD5 (0x04). | static final byte | ARCFOUR_40_MD5ARCFOUR_40_MD5 (0x03). | private static final byte[] | SUITES_AND_COMPThis contains the cipher suite encoding length in the first
two bytes, followed by an encoding of the cipher suites followed
by the compression suite length in one byte and the compression
suite. For now, we only propose the two most commonly used
cipher suites. | private static String[] | suiteNamesArray of suite names. | private static final byte | HDR_SIZEEach handshake message has a four-byte header containing
the type (1 byte) and length (3 byte). | private static final byte | HELLO_REQHello Request (0). | private static final byte | C_HELLOClient Hello (1). | private static final byte | S_HELLOServer Hello (2). | private static final byte | CERTCertificate (11). | private static final byte | S_KEYEXCHServer Key Exchange (12). | private static final byte | CERT_REQCertificate Request (13). | private static final byte | S_DONEServer Hello Done (14). | private static final byte | CERT_VRFYCertificate Verify (15). | private static final byte | C_KEYEXCHClient Key Exchange (16). | private static final byte | FINISHFinished (20). | private static final byte | MD5_SIZENumber of bytes in an MD5 Digest (16). | private static final byte | SHA_SIZENumber of bytes in an SHA Digest (20). | private static final byte[] | FINISH_PREFIXThe Finish message contains one MD5 and one SHA hash
and has a length of 4+16+20 = 40 = 0x24 bytes. | private CertStore | certStoreHandle to trusted certificate store. | private Record | recCurrent record to process. | private String | peerHostPeer host name . | private int | peerPortPeer port number. | private SecureRandom | rndLocal random number seed. | private Session | cSessionPrevious session context to this host and port, if there was one. | private byte[] | sSessionIdSession id returned by server. | private byte[] | crandClient random number. | private byte[] | srandServer random number. | private byte | verProposed SSL version. | private byte | roleRole (always CLIENT for now). | byte | negSuiteNegotiated cipher suite. | String | negSuiteNameName of negotiated cipher suite. | private byte | gotCertReqFlag to indicate certificate request received. | private byte[] | preMasterPre-master secret. | private byte[] | masterMaster secret. | private RSAPublicKey | eKeyPublic key used to encrypt the appropriate
usage of sKey certs in chain. | X509Certificate | sCertTemporary storage for server certificate. | private MessageDigest | ourMD5Accumulation of MD5 digests. | private MessageDigest | ourSHAAccumulation of SHA digests. | private int | startStart of message in data buffer. | private int | nextMsgStartStart of next message in data buffer. | private int | cntCount of bytes left in the data buffer. |
Constructors Summary |
---|
Handshake(String host, int port, Record r, CertStore tcs)Creates an Handshake object that is used to negotiate a
version 3 handshake with an SSL peer.
peerHost = new String(host);
peerPort = port;
rec = r;
certStore = tcs;
eKey = null;
gotCertReq = 0;
start = 0;
cnt = 0;
try {
ourMD5 = MessageDigest.getInstance("MD5");
ourSHA = MessageDigest.getInstance("SHA-1");
rnd = SecureRandom.getInstance(SecureRandom.ALG_SECURE_RANDOM);
} catch (NoSuchAlgorithmException e) {
// should only happen, if digests are not included in the build
throw new RuntimeException(e.getMessage());
}
|
Methods Summary |
---|
private void | complain(java.lang.String msg)Sends a fatal alert indicating handshake_failure and marks
the corresponding SSL session is non-resumable.
complain(new IOException(msg));
| private void | complain(java.io.IOException e)Sends a fatal alert indicating handshake_failure and marks
the corresponding SSL session is non-resumable.
try {
rec.alert(Record.FATAL, Record.HNDSHK_FAIL);
if (sSessionId != null) {
Session.del(peerHost, peerPort, sSessionId);
}
} catch (Throwable t) {
// Ignore, we are processing an exception currently
}
throw e;
| private byte[] | computeFinished(byte who)Computes the content of a Finished message.
byte[] sender[] = {
{ 0x53, 0x52, 0x56, 0x52}, // for server
{ 0x43, 0x4c, 0x4e, 0x54} // for client
};
byte[] msg = new byte[MD5_SIZE + SHA_SIZE];
byte[] tmp = null;
try {
// long t1 = System.currentTimeMillis();
MessageDigest d = (MessageDigest) ourMD5.clone();
d.update(sender[who], 0, 4);
d.update(master, 0, master.length);
tmp = new byte[MD5_SIZE];
// MD5 padding length is 48
d.update(MAC.PAD1, 0, 48);
d.digest(tmp, 0, tmp.length);
d.update(master, 0, master.length);
d.update(MAC.PAD2, 0, 48);
d.update(tmp, 0, tmp.length);
d.digest(msg, 0, MD5_SIZE);
d = (MessageDigest) ourSHA.clone();
d.update(sender[who], 0, 4);
d.update(master, 0, master.length);
tmp = new byte[SHA_SIZE];
// SHA padding length is 40
d.update(MAC.PAD1, 0, 40);
d.digest(tmp, 0, tmp.length);
d.update(master, 0, master.length);
d.update(MAC.PAD2, 0, 40);
d.update(tmp, 0, tmp.length);
d.digest(msg, MD5_SIZE, SHA_SIZE);
return msg;
} catch (Exception e) {
throw new IOException("MessageDigest not cloneable");
}
| void | doHandShake(byte aswho)Initiates an SSL handshake with the peer specified previously
in the constructor.
long t1 = System.currentTimeMillis();
int code = 0;
ver = (byte) 0x30; // IMPL_NOTE: This is hardcoded for now
role = aswho;
byte val = 0;
sndHello3();
if (rcvSrvrHello() < 0) {
complain("Bad ServerHello");
};
if ((sSessionId == null) || (cSession == null) ||
(sSessionId.length != cSession.id.length) ||
!Utils.byteMatch(sSessionId, 0, cSession.id, 0,
sSessionId.length)) {
// Session not resumed
try {
code = rcvCert();
} catch (CertificateException e) {
complain(e);
}
if (code < 0) {
complain("Corrupt server certificate message");
}
// ... get server_key_exchange (optional)
try {
code = rcvSrvrKeyExch();
} catch (CertificateException e) {
complain(e);
}
if (code < 0) {
complain("Bad ServerKeyExchange");
}
// ... get certificate_request (optional)
rcvCertReq();
if (rcvSrvrHelloDone() < 0) {
complain("Bad ServerHelloDone");
}
// ... send client_key_exchange
sndKeyExch();
mkMaster();
try {
rec.init(role, crand, srand, negSuite, master);
} catch (Exception e) {
complain("Record.init() caught " + e);
}
// ... send change_cipher_spec
sndChangeCipher();
// ... send finished
sndFinished();
// ... get change_cipher_spec
if (rcvChangeCipher() < 0) {
complain("Bad ChangeCipherSpec");
}
// ... get finished
if (rcvFinished() < 0) {
complain("Bad Finished");
}
} else {
/*
* The server agreed to resume a session.
* Get the needed values from the previous session
* now since the references could be overwritten if a
* concurrent connection is made to this host and port.
*/
master = cSession.master;
sCert = cSession.cert;
try {
rec.init(role, crand, srand, negSuite, master);
} catch (Exception e) {
complain("Record.init() caught " + e);
}
// ... get change_cipher_spec
if (rcvChangeCipher() < 0) {
complain("Bad ChangeCipherSpec");
}
// ... get finished
if (rcvFinished() < 0) {
complain("Bad Finished");
}
// ... send change_cipher_spec
sndChangeCipher();
// ... send finished
sndFinished();
}
Session.add(peerHost, peerPort, sSessionId, master, sCert);
// Zero out the premaster and master secrets
if (preMaster != null) {
// premaster can be null if we resumed an SSL session
for (int i = 0; i < preMaster.length; i++) {
preMaster[i] = 0;
}
}
for (int i = 0; i < master.length; i++) {
master[i] = 0;
}
| private int | getNextMsg(byte type)Obtains the next available handshake message.
The message returned has the header plus the number of
bytes indicated in the handshake message header.
if (cnt == 0) {
rec.rdRec(true, Record.HNDSHK);
if (rec.plainTextLength < HDR_SIZE) {
throw new IOException("getNextMsg refill failed");
}
cnt = rec.plainTextLength;
nextMsgStart = 0;
}
if (rec.inputData[nextMsgStart] == type) {
int len = ((rec.inputData[nextMsgStart + 1] & 0xff) << 16) +
((rec.inputData[nextMsgStart + 2] & 0xff) << 8) +
(rec.inputData[nextMsgStart + 3] & 0xff) + HDR_SIZE;
if (cnt < len) {
throw new IOException("Refill got short msg " +
"c=" + cnt + " l=" + len);
}
start = nextMsgStart;
nextMsgStart += len;
cnt -= len;
return len;
} else {
return -1;
}
| private void | mkMaster()Derives the master key based on the pre-master secret and
random values exchanged in the client and server hello messages.
byte[] expansion[] = {
{ (byte) 0x41 }, // 'A'
{ (byte) 0x42, (byte) 0x42 }, // 'BB'
{ (byte) 0x43, (byte) 0x43, (byte) 0x43 }, // 'CCC'
};
MessageDigest md = null;
MessageDigest sd = null;
/*
* First, we compute the 48-byte (three MD5 outputs) master secret
*
* master_secret =
* MD5(pre_master + SHA('A' + pre_master +
* ClientHello.random + ServerHello.random)) +
* MD5(pre_master + SHA('BB' + pre_master +
* ClientHello.random + ServerHello.random)) +
* MD5(pre_master + SHA('CCC' + pre_master +
* ClientHello.random + ServerHello.random));
*
* To simplify things, we use
* tmp = pre_master + ClientHello.random + ServerHello.random;
*/
byte[] tmp = new byte[preMaster.length + crand.length + srand.length];
System.arraycopy(preMaster, 0, tmp, 0, preMaster.length);
System.arraycopy(crand, 0, tmp, preMaster.length, crand.length);
System.arraycopy(srand, 0, tmp, preMaster.length + crand.length,
srand.length);
try {
md = MessageDigest.getInstance("MD5");
sd = MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException e) {
/*
* We should never catch this here (if these are missing,
* we will catch this exception in the constructor)
*/
throw new RuntimeException("No MD5 or SHA");
}
master = new byte[48];
try {
for (int i = 0; i < 3; i++) {
md.update(preMaster, 0, preMaster.length);
sd.update(expansion[i], 0, expansion[i].length);
byte[] res = new byte[SHA_SIZE];
sd.update(tmp, 0, tmp.length);
sd.digest(res, 0, res.length);
md.update(res, 0, res.length);
md.digest(master, i << 4, MD5_SIZE);
}
} catch (DigestException e) {
/*
* We should never catch this here.
*/
throw new RuntimeException("digest exception");
}
| private X509Certificate | parseChain(byte[] msg, int off, int end)Validates a chain of certificates and returns the RSA public
key from the first certificate in that chain. The format of
the chain is specific to the ServerCertificate payload in an
SSL handshake.
Vector certs = new Vector();
int len;
// We have a 3-byte length field before each cert in list
while (off < (end - 3)) {
len = ((msg[off++] & 0xff) << 16) +
((msg[off++] & 0xff) << 8) + (msg[off++] & 0xff);
if (len < 0 || len + off > msg.length) {
throw new IOException("SSL certificate length too long");
}
certs.addElement(
X509Certificate.generateCertificate(msg, off, len));
off += len;
}
/*
* The key usage extension of the server certificate is checked later
* a based on the key exchange. Only the extended key usage is checked
* now.
*/
X509Certificate.verifyChain(certs, -1,
X509Certificate.SERVER_AUTH_EXT_KEY_USAGE, certStore);
// The first cert if specified to be the server cert.
return (X509Certificate)certs.elementAt(0);
| private int | rcvCert()Receives a Server certificate message containing a certificate
chain starting with the server certificate.
int msgLength;
int endOfMsg;
int idx;
int len;
msgLength = getNextMsg(CERT);
endOfMsg = start + msgLength;
/*
* Message should atleast have a 4-byte header and an empty cert
* list with 3-byte length
*/
if (msgLength < 7) {
return -1;
}
idx = start + HDR_SIZE;
len = 0;
// Check the length ...
len = ((rec.inputData[idx++] & 0xff) << 16) +
((rec.inputData[idx++] & 0xff) << 8) + (rec.inputData[idx++] &
0xff);
if ((idx + len) > endOfMsg)
return -1;
// Parse the certificate chain and get the server's public key
sCert = parseChain(rec.inputData, idx, endOfMsg);
// Update the hash of handshake messages
ourMD5.update(rec.inputData, start, msgLength);
ourSHA.update(rec.inputData, start, msgLength);
return 0;
| private int | rcvCertReq()Receives a Certificate request message. This message is optional.
int msgLength = getNextMsg(CERT_REQ);
if (msgLength == -1) {
return 0; // certificate request is optional
}
/*
* We do not support client-side certificates so if we see
* a request for a certificate, remember it here so we can
* complain later
*/
gotCertReq = (byte) 1;
// Update the hash of handshake messages
ourMD5.update(rec.inputData, start, msgLength);
ourSHA.update(rec.inputData, start, msgLength);
// NOTE: We return zero without attempting to parse the message body.
return 0;
| private int | rcvChangeCipher()Receives a ChangeCipherSpec protocol message (this is
not a handshake message).
/*
* We make sure that there are no unread handshake messages
* in the internal store when we get here.
*/
if (cnt != 0) {
if (Logging.REPORT_LEVEL <= Logging.ERROR) {
Logging.report(Logging.ERROR, LogChannels.LC_SECURITY,
"Unread handshake mesg in store");
}
return -1;
}
/*
* Note that CCS is not a handshake message (it is its own protocol)
* The record layer header is 5 bytes and the CCS body is one
* byte with value 0x01.
*/
rec.rdRec(true, Record.CCS);
if ((rec.inputData == null) || (rec.inputData.length != 1) ||
(rec.inputData[0] != (byte) 0x01)) {
return -1;
}
return 0;
| private int | rcvFinished()Receives a Finished message and verifies that it contains
the correct hash of handshake messages.
int msgLength = getNextMsg(FINISH);
if (msgLength != 40) {
return -1;
}
// Compute the expected hash
byte[] expected = computeFinished((byte) (1 - role));
if (!Utils.byteMatch(rec.inputData, start + HDR_SIZE, expected, 0,
expected.length)) {
return -1;
} else {
// Update the hash of handshake messages
ourMD5.update(rec.inputData, start, msgLength);
ourSHA.update(rec.inputData, start, msgLength);
// now = System.currentTimeMillis();
return 0;
}
| private int | rcvSrvrHello()Receives a Server hello handshake message.
int msgLength = getNextMsg(S_HELLO);
int idx = start + HDR_SIZE;
int endOfMsg = start + msgLength;
/*
* Message must be long enough to contain a 4-byte header,
* 2-byte version, a 32-byte random, a 1-byte session Id
* length (plus variable lenght session Id), 2 byte cipher
* suite, 1 byte compression method.
*/
if (msgLength < 42) {
return -1;
}
// Get the server version
if ((rec.inputData[start + idx++] != (ver >>> 4)) ||
(rec.inputData[start + idx++] != (ver & 0x0f))) {
return -1;
}
// .. the 32-byte server random
srand = new byte[32];
System.arraycopy(rec.inputData, idx, srand, 0, 32);
idx += 32;
// ... the session_Id length in 1 byte (and session_Id)
int slen = rec.inputData[idx++] & 0xff;
if (slen != 0) {
if (endOfMsg < idx + slen) {
return -1;
}
sSessionId = new byte[slen];
System.arraycopy(rec.inputData, idx, sSessionId, 0, slen);
idx += slen;
}
// ... the cipher suite
/*
* NOTE: this impl works because the cipher suites
* we support, the second byte directly maps to suite code.
*/
idx++;
negSuite = rec.inputData[idx++];
/*
* Check the cipher suite and compression method. The compression
* method better be 0x00 since that is the only one we ever propose.
*/
if ((negSuite != ARCFOUR_128_SHA) &&
(negSuite != ARCFOUR_128_MD5) &&
(negSuite != ARCFOUR_40_MD5) &&
(rec.inputData[idx++] != (byte) 0x00)) {
return -1;
}
// Update the hash of handshake messages
ourMD5.update(rec.inputData, start, msgLength);
ourSHA.update(rec.inputData, start, msgLength);
negSuiteName = suiteNames[negSuite];
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_SECURITY,
"Negotiated " + negSuiteName);
}
return 0;
| private int | rcvSrvrHelloDone()Receives a Server hello done message.
int msgLength = getNextMsg(S_DONE);
// A server_hello_done message has no body, just the header
if (msgLength != HDR_SIZE) {
return -1;
}
// Update the hash of handshake messages
ourMD5.update(rec.inputData, start, msgLength);
ourSHA.update(rec.inputData, start, msgLength);
return 0;
| private int | rcvSrvrKeyExch()Receives a Server key exchange message. For now only RSA key
exchange is supported and this message includes temporary
RSA public key parameters signed by the server's long-term
private key. This message is optional.
int msgLength = getNextMsg(S_KEYEXCH);
int idx = start + HDR_SIZE;
int endOfMsg = start + msgLength;
RSAPublicKey sKey = (RSAPublicKey)sCert.getPublicKey();
int keyUsage = sCert.getKeyUsage();
/*
* NOTE: Based on what we propose, the only key exch is RSA
* Also note that the server key exchange is optional and used
* only if the public key included in the certificate chain
* is unsuitable for encrypting the pre-master secret.
*/
if (msgLength == -1) {
// We can use the server key to encrypt premaster secret
eKey = sKey;
/*
* Make sure sKey can be used for premaster secret encryption,
* i.e. if key usage extension is present, the key encipherment
* bit must be set
*/
if (keyUsage != -1 &&
(keyUsage & X509Certificate.KEY_ENCIPHER_KEY_USAGE) !=
X509Certificate.KEY_ENCIPHER_KEY_USAGE) {
if (Logging.REPORT_LEVEL <= Logging.ERROR) {
Logging.report(Logging.ERROR, LogChannels.LC_SECURITY,
"The keyEncipherment was bit is " +
"set in server certificate key " +
"usage extension.");
}
throw new CertificateException(sCert,
CertificateException.INAPPROPRIATE_KEY_USAGE);
}
return 0;
}
// read and verify the encryption key parameters
if (endOfMsg < (idx + 4)) {
return -1;
}
// read the modulus length
int len = ((rec.inputData[idx++] & 0xff) << 16) +
(rec.inputData[idx++] & 0xff);
if (endOfMsg < (idx + len + 2)) {
return -1;
}
int modulusPos;
int modulusLen;
int exponentPos;
int exponentLen;
// ... and the modulus
/*
* Some weird sites (e.g. www.verisign.com) encode a
* 512-bit modulus in 65 (rather than 64 bytes) with the
* first byte set to zero. We accomodate this behavior
* by using a special check.
*/
if ((len == 65) && (rec.inputData[idx] == (byte)0x00)) {
modulusPos = idx + 1;
modulusLen = 64;
} else {
modulusPos = idx;
modulusLen = len;
}
idx += len;
// read the exponent length
len = ((rec.inputData[idx++] & 0xff) << 16) +
(rec.inputData[idx++] & 0xff);
if (endOfMsg < (idx + len)) {
return -1;
}
// ... and the exponent
exponentPos = idx;
exponentLen = len;
eKey = new RSAPublicKey(rec.inputData, modulusPos, modulusLen,
rec.inputData, exponentPos, exponentLen);
idx += len;
// mark where ServerRSAparams end
int end = idx;
// Now read the signature length
len = ((rec.inputData[idx++] & 0xff) << 16) +
(rec.inputData[idx++] & 0xff);
if (endOfMsg < (idx + len)) {
return -1;
}
// ... and the signature
byte[] sig = new byte[len];
System.arraycopy(rec.inputData, idx, sig, 0, sig.length);
idx += len;
if (endOfMsg != idx) {
return -1;
}
// Compute the expected hash
byte[] dat = new byte[MD5_SIZE + SHA_SIZE];
try {
MessageDigest di = MessageDigest.getInstance("MD5");
di.update(crand, 0, crand.length);
di.update(srand, 0, srand.length);
di.update(rec.inputData, HDR_SIZE, end - HDR_SIZE);
di.digest(dat, 0, MD5_SIZE);
di = MessageDigest.getInstance("SHA-1");
di.update(crand, 0, crand.length);
di.update(srand, 0, srand.length);
di.update(rec.inputData, HDR_SIZE, end - HDR_SIZE);
di.digest(dat, MD5_SIZE, SHA_SIZE);
} catch (Exception e) {
throw new RuntimeException("No MD5 or SHA");
}
try {
Cipher rsa = Cipher.getInstance("RSA");
rsa.init(Cipher.DECRYPT_MODE, sKey);
byte[] res = new byte[sKey.getModulusLen()];
int val = rsa.doFinal(sig, 0, sig.length, res, 0);
if (!Utils.byteMatch(res, 0, dat, 0, dat.length)) {
if (Logging.REPORT_LEVEL <= Logging.ERROR) {
Logging.report(Logging.ERROR, LogChannels.LC_SECURITY,
"RSA params failed verification");
}
return -1;
}
} catch (Exception e) {
throw new IOException("RSA decryption caught " + e);
}
// Update the hash of handshake messages
ourMD5.update(rec.inputData, start, msgLength);
ourSHA.update(rec.inputData, start, msgLength);
return 0;
| private void | sndChangeCipher()Sends a ChangeCipherSpec protocol message (this is not really
a handshake protocol message).
byte[] msg = new byte[1];
// change cipher spec consists of a single byte with value 1
msg[0] = (byte) 0x01;
rec.wrRec(Record.CCS, msg, 0, 1); // msg.length is 1
| private void | sndFinished()Sends a Finished message.
// HDR_SIZE + MD5_SIZE + SHA_SIZE is 40
byte[] msg = new byte[40];
System.arraycopy(FINISH_PREFIX, 0, msg, 0, 4);
// MD5_SIZE + SHA_SIZE is 36
System.arraycopy(computeFinished(role), 0, msg, 4, 36);
// Update the hash of handshake messages
ourMD5.update(msg, 0, msg.length);
ourSHA.update(msg, 0, msg.length);
rec.wrRec(Record.HNDSHK, msg, 0, msg.length);
| private void | sndHello3()Sends an SSL version 3.0 Client hello handshake message.
cSession = Session.get(peerHost, peerPort);
int len = (cSession == null) ? 0 : cSession.id.length;
/*
* Size = 4 (HDR_SIZE) + 2 (client_version) + 32 (crand.length) +
* 1 (session length) + len + 2 (cipher suite length) +
* (2*CipherSuiteList.length) + 1 (compression length) + 1 (comp code)
*/
byte[] msg = new byte[39 + len + SUITES_AND_COMP.length];
int idx = 0;
// Fill the header -- type (1 byte) length (3 bytes)
msg[idx++] = C_HELLO;
int mlen = msg.length - HDR_SIZE;
msg[idx++] = (byte) (mlen >>> 16);
msg[idx++] = (byte) (mlen >>> 8);
msg[idx++] = (byte) (mlen & 0xff);
// ... client_version
msg[idx++] = (byte) (ver >>> 4);
msg[idx++] = (byte) (ver & 0x0f);
// ... random
/*
* IMPL_NOTE: overwrite the first four bytes of crand with
* current time and date in standard 32-bit UNIX format.
*/
crand = new byte[32];
rnd.nextBytes(crand, 0, 32);
System.arraycopy(crand, 0, msg, idx, crand.length);
idx += crand.length;
// ... session_id
msg[idx++] = (byte) (len & 0xff);
if (cSession != null) {
System.arraycopy(cSession.id, 0, msg, idx, cSession.id.length);
idx += cSession.id.length;
}
// ... cipher_suites and compression methods
System.arraycopy(SUITES_AND_COMP, 0, msg, idx, SUITES_AND_COMP.length);
ourMD5.update(msg, 0, msg.length);
ourSHA.update(msg, 0, msg.length);
// Finally, write this handshake record
rec.wrRec(Record.HNDSHK, msg, 0, msg.length);
| private void | sndKeyExch()Sends a Client key exchange message. For now, only RSA key
exchange is supported and this message contains a pre-master
secret encrypted with the RSA public key of the server.
/*
* If we get here, the server agreed to an RSA key exchange
* and the RSA public key to be used for encrypting the
* pre-master secret is available in eKey.
*/
if (gotCertReq == 1) {
// Send back an error ... we do not support client auth
rec.alert(Record.FATAL, Record.NO_CERT);
throw new IOException("No client cert");
} else { // NOTE: The only possible key exch is RSA
// Generate a 48-byte random pre-master secret
preMaster = new byte[48];
rnd.nextBytes(preMaster, 0, 48);
// ... first two bytes must have client version
preMaster[0] = (byte) (ver >>> 4);
preMaster[1] = (byte) (ver & 0x0f);
// Prepare a message containing the RSA encrypted pre-master
int modLen = eKey.getModulusLen();
byte[] msg = new byte[HDR_SIZE + modLen];
int idx = 0;
// Fill the type
msg[idx++] = C_KEYEXCH;
// ... message length
msg[idx++] = (byte) (modLen >>> 16);
msg[idx++] = (byte) (modLen >>> 8);
msg[idx++] = (byte) (modLen & 0xff);
// ... the encrypted pre-master secret
try {
Cipher rsa = Cipher.getInstance("RSA");
rsa.init(Cipher.ENCRYPT_MODE, eKey);
int val = rsa.doFinal(preMaster, 0, 48, msg, idx);
if (val != modLen)
throw new IOException("RSA result too short");
} catch (Exception e) {
throw new IOException("premaster encryption caught " + e);
}
// Update the hash of handshake messages
ourMD5.update(msg, 0, msg.length);
ourSHA.update(msg, 0, msg.length);
rec.wrRec(Record.HNDSHK, msg, 0, msg.length);
}
|
|