0001: /*
0002: * @(#)Handshake.java 1.30 02/07/24 @(#)
0003: *
0004: * Copyright (c) 2000-2002 Sun Microsystems, Inc. All rights reserved.
0005: * PROPRIETARY/CONFIDENTIAL
0006: * Use is subject to license terms.
0007: */
0008:
0009: package com.sun.portal.kssl;
0010:
0011: import java.io.IOException;
0012: import java.io.InputStream;
0013:
0014: import java.lang.Exception;
0015:
0016: import com.sun.portal.microedition.pki.CertificateException;
0017:
0018: import com.sun.portal.ksecurity.CryptoException;
0019: import com.sun.portal.ksecurity.MessageDigest;
0020: import com.sun.portal.ksecurity.RandomData;
0021: import com.sun.portal.ksecurity.RSAPublicKey;
0022: import com.sun.portal.ksecurity.KeyBuilder;
0023: import com.sun.portal.ksecurity.CertStore;
0024:
0025: /**
0026: * This class implements the SSL handshake protocol which is responsible
0027: * for negotiating security parameters used by the record layer.
0028: * Currently, only client-side functionality is implemented.
0029: */
0030: // visible only within this package
0031: class Handshake {
0032:
0033: /**
0034: * This contains the cipher suite encoding length in the first
0035: * two bytes, followed by an encoding of the cipher suites followed
0036: * by the compression suite length in one byte and the compression
0037: * suite. For now, we only propose the two most commonly used
0038: * cipher suites.
0039: */
0040: private byte[] SUITES_AND_COMP = {
0041: // Use this to propose 128-bit encryption as preferred
0042: //Support for new Ciphers
0043: /*0x00, 0x06, 0x00, ARCFOUR_128_SHA, 0x00, ARCFOUR_128_MD5,
0044: 0x00, ARCFOUR_40_MD5, 0x01, 0x00*/
0045: 0x00, 0x0A, 0x00, CipherSuites.ARCFOUR_128_SHA, 0x00,
0046: CipherSuites.ARCFOUR_128_MD5, 0x00,
0047: CipherSuites.ARCFOUR_40_MD5, 0x00,
0048: CipherSuites.DES_CBC_SHA, 0x00,
0049: CipherSuites.TRIPLEDES_CBC_SHA, 0x01, 0x00
0050: // Use this to propose 40-bit encryption as preferred
0051: // 0x00, 0x06, 0x00, ARCFOUR_40_MD5, 0x00, ARCFOUR_128_RSA,
0052: // 0x00, ARCFOUR_128_SHA, 0x01, 0x00
0053: };
0054:
0055: /**
0056: * Array of suite names.
0057: */
0058: private static String[] suiteNames = { "", "", "",
0059: "SSL3_RSA_EXPORT_WITH_RC4_40_MD5",
0060: "SSL3_RSA_WITH_RC4_128_MD5", "SSL3_RSA_WITH_RC4_128_SHA",
0061: //Support for new Ciphers
0062: "", "", "", "SSL3_RSA_WITH_DES_CBC_SHA",
0063: "SSL3_RSA_WITH_3DES_EDE_CBC_SHA"
0064:
0065: };
0066:
0067: /**
0068: * Each handshake message has a four-byte header containing
0069: * the type (1 byte) and length (3 byte).
0070: */
0071: private static final byte HDR_SIZE = 4;
0072:
0073: // Handshake message types
0074: /** Hello Request (0). */
0075: private static final byte HELLO_REQ = 0;
0076: /** Client Hello (1). */
0077: private static final byte C_HELLO = 1;
0078: /** Server Hello (2). */
0079: private static final byte S_HELLO = 2;
0080: /** Certificate (11). */
0081: private static final byte CERT = 11;
0082: /** Server Key Exchange (12). */
0083: private static final byte S_KEYEXCH = 12;
0084: /** Certificate Request (13). */
0085: private static final byte CERT_REQ = 13;
0086: /** Server Hello Done (14). */
0087: private static final byte S_DONE = 14;
0088: /** Certificate Verify (15). */
0089: private static final byte CERT_VRFY = 15;
0090: /** Client Key Exchange (16). */
0091: private static final byte C_KEYEXCH = 16;
0092: /** Finished (20). */
0093: private static final byte FINISH = 20;
0094:
0095: // Number of bytes in an MD5/SHA digest
0096: /** Number of bytes in an MD5 Digest (16). */
0097: private static final byte MD5_SIZE = 16;
0098: /** Number of bytes in an SHA Digest (20). */
0099: private static final byte SHA_SIZE = 20;
0100:
0101: /**
0102: * The Finish message contains one MD5 and one SHA hash
0103: * and has a length of 4+16+20 = 40 = 0x24 bytes.
0104: */
0105: private static final byte[] FINISH_PREFIX = { FINISH, 0x00, 0x00,
0106: 0x24 };
0107:
0108: /** Current record to process. */
0109: private Record rec;
0110: /** Peer host name . */
0111: private String peerHost;
0112: /** Peer port number. */
0113: private int peerPort;
0114: /** Local random number seed. */
0115: private RandomData rnd = null;
0116: /** Previous session context to this host and port, if there was one. */
0117: private Session cSession = null;
0118: /** Session id returned by server. */
0119: private byte[] sSessionId = null;
0120: /** Client random number. */
0121: private byte[] crand = null;
0122: /** Server random number. */
0123: private byte[] srand = null;
0124:
0125: /** Proposed SSL version. */
0126: private byte ver;
0127: /** Role (always CLIENT for now). */
0128: private byte role;
0129: /** Negotiated cipher suite. */
0130: byte negSuite;
0131: /** Name of negotiated cipher suite. */
0132: String negSuiteName;
0133: /** Flag to indicate certificate request received. */
0134: private byte gotCertReq = 0;
0135: /** Pre-master secret. */
0136: private byte[] preMaster = null;
0137: /** Master secret. */
0138: private byte[] master = null;
0139: /** Server public key. */
0140: private RSAPublicKey sKey = null;
0141: /**
0142: * Public key used to encrypt the appropriate
0143: * usage of sKey certs in chain.
0144: */
0145: private RSAPublicKey eKey = null;
0146: /** Skey usage. */
0147: private int sKeyUsage = 0;
0148: /** Certificate count. */
0149: private byte certCnt = 0;
0150: // we also need a temporary place to store the server certificate
0151: // in parseChain so it can be examined later rcvSrvrKeyExch() for
0152: // keyUsage checks and the parent connection.
0153: /** Temporary storage for server certificate. */
0154: X509Certificate sCert = null;
0155:
0156: /*
0157: * These accumulate MD5 and SHA digests of all handshake
0158: * messages seen so far.
0159: */
0160: /** Accumulation of MD5 digests. */
0161: private MessageDigest ourMD5 = null;
0162: /** Accumulation of SHA digests. */
0163: private MessageDigest ourSHA = null;
0164:
0165: /*
0166: * The following fields maintain a buffer of available handshake
0167: * messages. Note that a single SSL record may include multiple
0168: * handshake messages.
0169: */
0170: /** Start of message in data buffer. */
0171: private int start = 0;
0172: /** Start of next message in data buffer. */
0173: private int nextMsgStart = 0;
0174: /** Count of bytes left in the data buffer. */
0175: private int cnt = 0;
0176:
0177: /**
0178: * Validates a chain of certificates and returns the RSA public
0179: * key from the first certificate in that chain. The format of
0180: * the chain is specific to the ServerCertificate payload in an
0181: * SSL handshake.
0182: * <P />
0183: * @param site site name to be matched against the subject
0184: * name inside the server certificate
0185: * @param msg byte array containing the SSL ServerCertificate
0186: * payload (this is a chain of DER-encoded X.509
0187: * certificates, in which each certificate is preceded
0188: * by a 3-byte length field)
0189: * @param off offset in the byte array where the cert chain begins
0190: * @param end position in the byte array where the cert chain ends + 1
0191: * @param tcs certificate store containing trusted certificates
0192: * @return the public key from the first certificate in the chain or null
0193: * (in case of problems)
0194: */
0195: private RSAPublicKey parseChain(String site, byte[] msg, int off,
0196: int end, CertStore tcs) throws IOException {
0197:
0198: RSAPublicKey sKey = null;
0199: X509Certificate c;
0200: X509Certificate pc = null; // previous cert in chain
0201: X509Certificate ts; // trusted signer certificate
0202: X509Certificate[] ic;
0203: int len;
0204: byte worstCode = CertificateException.UNRECOGNIZED_ISSUER;
0205: int pLen = -1; // 0 means 1 so -1 means no chain
0206:
0207: if (tcs == null) {
0208: throw new IllegalArgumentException(
0209: "no trusted certificate store given");
0210: }
0211:
0212: certCnt = 0; // use this to count number of certs processed
0213:
0214: // We have a 3-byte length field before each cert in list
0215: while (off < (end - 3)) {
0216: len = ((msg[off++] & 0xff) << 16)
0217: + ((msg[off++] & 0xff) << 8) + (msg[off++] & 0xff);
0218:
0219: if (len < 0 || len + off > msg.length) {
0220: throw new IOException("SSL certificate length too long");
0221: }
0222:
0223: c = X509Certificate.generateCertificate(msg, off, len);
0224:
0225: // Check if this certificate has any bad extensions
0226: c.checkExtensions();
0227:
0228: // Check if the certificate is valid at the current time
0229: c.checkValidity();
0230:
0231: if (certCnt == 0) {
0232: /*
0233: * This is the first certificate in chain.
0234: * Save the server certificate.
0235: */
0236: sCert = c;
0237:
0238: // Save the server public key and key usage bits
0239: sKey = (RSAPublicKey) c.getPublicKey();
0240: sKeyUsage = c.getKeyUsage();
0241: } else {
0242: /*
0243: * This is a chain, check chain link:
0244: * the subject of this certificate must be the issuer of
0245: * the previous certificate
0246: */
0247: if (pc.getIssuer().compareTo(c.getSubject()) != 0) {
0248: // this error cannot be overridden
0249: throw new CertificateException(pc,
0250: CertificateException.BROKEN_CHAIN);
0251: }
0252:
0253: pc.verify(c.getPublicKey());
0254:
0255: /*
0256: * Check if basicConstraints are satisfied. Note a zero
0257: * pathLength means we should have only processed one cert
0258: * so far.
0259: *
0260: * We want to check the chain length last because the
0261: * other errors are more critical.
0262: */
0263: int prevPathLen = pLen;
0264: pLen = c.getBasicConstraints();
0265: if (pLen != X509Certificate.UNLIMITED_CERT_CHAIN_LENGTH
0266: && pLen <= prevPathLen) {
0267: if (c.getSubject().equals(c.getIssuer())) {
0268: /*
0269: * This is a redundent, self signed CA cert
0270: * allowed to be at the end of the chain.
0271: * These certificates may version 1, so will not
0272: * have extensions. So this really should be the
0273: * worst code we have encountered this far for
0274: * the previous certificate in the chain.
0275: */
0276: throw new CertificateException(pc, worstCode);
0277: }
0278:
0279: if (pLen == X509Certificate.MISSING_PATH_LENGTH_CONSTRAINT) {
0280:
0281: throw new CertificateException(
0282: pc,
0283: CertificateException.UNAUTHORIZED_INTERMEDIATE_CA);
0284: }
0285:
0286: /*
0287: * An intermediate CA has over granted
0288: * its given authority to the previous CA in the
0289: * chain.
0290: */
0291: throw new CertificateException(
0292: pc,
0293: CertificateException.CERTIFICATE_CHAIN_TOO_LONG);
0294: }
0295: }
0296:
0297: certCnt++;
0298:
0299: /*
0300: * Or if this certificate was issued (signed) by a trusted
0301: * issuer, we are done. An issuer may have more than one
0302: * key so we must use the trial and error method.
0303: *
0304: * NOTE: An issuer whose certificate is expired gets
0305: * treated the same way as an unrecognized issuer
0306: */
0307: ic = tcs.getCertificates(c.getIssuer());
0308: if (ic == null) {
0309: //System.out.println("Netlet found self signed certificate.");
0310: Utils.logln(Utils.LOG_INFO,
0311: "Netlet found self signed certificate. ");
0312: return sKey;
0313: }
0314: if (ic != null) {
0315: for (int i = ic.length - 1; i >= 0; i--) {
0316: if (!ic[i].getType().equals("X.509")) {
0317: continue;
0318: }
0319:
0320: ts = ic[i];
0321:
0322: try {
0323: c.verify(ts.getPublicKey());
0324: } catch (CertificateException e) {
0325: System.out
0326: .println("Cerificate verification exception");
0327: e.printStackTrace();
0328: /*
0329: * may not be the correct key try the next key,
0330: * but this is worse than unrecognized issuer
0331: */
0332: worstCode = e.getReason();
0333: continue;
0334: }
0335:
0336: try {
0337: ts.checkValidity();
0338: } catch (CertificateException e) {
0339: /*
0340: * we need to change the exception reason, so the
0341: * application knows that the problem is with
0342: * the device and not the server.
0343: */
0344: throw new CertificateException(ts,
0345: CertificateException.ROOT_CA_EXPIRED);
0346: }
0347:
0348: return sKey;
0349: }
0350: }
0351:
0352: ts = null;
0353: off += len;
0354: pc = c;
0355: }
0356:
0357: throw new CertificateException(pc, worstCode);
0358: }
0359:
0360: /**
0361: * Creates an Handshake object that is used to negotiate a
0362: * version 3 handshake with an SSL peer.
0363: *
0364: * @param host hostname of the peer
0365: * @param port port number of the peer
0366: * @param r Record instance through which handshake
0367: * will occur
0368: */
0369: Handshake(String host, int port, Record r) {
0370: peerHost = new String(host);
0371: peerPort = port;
0372: rec = r;
0373: sKey = eKey = null;
0374: gotCertReq = 0;
0375: start = 0;
0376: cnt = 0;
0377:
0378: try {
0379: ourMD5 = MessageDigest.getInstance(MessageDigest.ALG_MD5,
0380: false);
0381: ourSHA = MessageDigest.getInstance(MessageDigest.ALG_SHA,
0382: false);
0383: rnd = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
0384: } catch (CryptoException e) {
0385: // should not happen, if code above is correct
0386: throw new RuntimeException(e.getMessage());
0387: }
0388: }
0389:
0390: /**
0391: * Frees data structures associated with the handshake layer.
0392: */
0393: public void destroy() {
0394: rec = null;
0395: peerHost = null;
0396: rnd = null;
0397: cSession = null;
0398: sSessionId = null;
0399: crand = srand = null;
0400: preMaster = null;
0401: master = null;
0402: sKey = null;
0403: eKey = null;
0404: ourMD5 = null;
0405: ourSHA = null;
0406: }
0407:
0408: /**
0409: * Obtains the next available handshake message.
0410: * <p>
0411: * The message returned has the header plus the number of
0412: * bytes indicated in the handshake message header.</p>
0413: *
0414: * @param type the desired handshake message type
0415: * @return number of bytes in the next handshake message
0416: * of the desired type or -1 if the next message is not of
0417: * the desired type
0418: * @exception IOException if there is a problem reading the
0419: * next handshake message
0420: */
0421: private int getNextMsg(byte type) throws IOException {
0422: if (cnt == 0) {
0423: rec.rdRec(Record.HNDSHK);
0424:
0425: if (rec.plainTextLength < HDR_SIZE) {
0426: throw new IOException("getNextMsg refill failed");
0427: }
0428:
0429: cnt = rec.plainTextLength;
0430: nextMsgStart = rec.plainTextOffset;
0431: }
0432:
0433: if (rec.inputData[nextMsgStart] == type) {
0434: int len = ((rec.inputData[nextMsgStart + 1] & 0xff) << 16)
0435: + ((rec.inputData[nextMsgStart + 2] & 0xff) << 8)
0436: + (rec.inputData[nextMsgStart + 3] & 0xff)
0437: + HDR_SIZE;
0438:
0439: if (cnt < len) {
0440: throw new IOException("Refill got short msg " + "c="
0441: + cnt + " l=" + len);
0442: }
0443:
0444: start = nextMsgStart;
0445: nextMsgStart += len;
0446: cnt -= len;
0447: return len;
0448: } else {
0449: return -1;
0450: }
0451: }
0452:
0453: /**
0454: * Sends an SSL version 3.0 Client hello handshake message.
0455: * <P />
0456: * @exception IOException if there is a problem writing to
0457: * the record layer
0458: */
0459: private void sndHello3() throws IOException {
0460: cSession = Session.get(peerHost, peerPort);
0461: int len = (cSession == null) ? 0 : cSession.id.length;
0462: /*
0463: * Size = 4 (HDR_SIZE) + 2 (client_version) + 32 (crand.length) +
0464: * 1 (session length) + len + 2 (cipher suite length) +
0465: * (2*CipherSuiteList.length) + 1 (compression length) + 1 (comp code)
0466: */
0467: byte[] msg = new byte[39 + len + SUITES_AND_COMP.length];
0468: int idx = 0;
0469: // Fill the header -- type (1 byte) length (3 bytes)
0470: msg[idx++] = C_HELLO;
0471: int mlen = msg.length - HDR_SIZE;
0472: msg[idx++] = (byte) (mlen >>> 16);
0473: msg[idx++] = (byte) (mlen >>> 8);
0474: msg[idx++] = (byte) (mlen & 0xff);
0475: // ... client_version
0476: msg[idx++] = (byte) (ver >>> 4);
0477: msg[idx++] = (byte) (ver & 0x0f);
0478: // ... random
0479: /*
0480: * TODO: overwrite the first four bytes of crand with
0481: * current time and date in standard 32-bit UNIX format.
0482: */
0483: crand = new byte[32];
0484: rnd.generateData(crand, (short) 0, (short) 32);
0485: System.arraycopy(crand, 0, msg, idx, crand.length);
0486: idx += crand.length;
0487: // ... session_id
0488: msg[idx++] = (byte) (len & 0xff);
0489: if (cSession != null) {
0490: System.arraycopy(cSession.id, 0, msg, idx,
0491: cSession.id.length);
0492: idx += cSession.id.length;
0493: }
0494: // ... cipher_suites and compression methods
0495: System.arraycopy(SUITES_AND_COMP, 0, msg, idx,
0496: SUITES_AND_COMP.length);
0497:
0498: ourMD5.update(msg, 0, msg.length);
0499: ourSHA.update(msg, 0, msg.length);
0500:
0501: // Finally, write this handshake record
0502: rec.wrRec(Record.HNDSHK, msg, 0, msg.length);
0503: }
0504:
0505: /**
0506: * Receives a Server hello handshake message.
0507: * <P />
0508: * @return 0 on success, -1 on failure
0509: * @exception IOException if there is a problem reading the
0510: * message
0511: */
0512: private int rcvSrvrHello() throws IOException {
0513: int msgLength = getNextMsg(S_HELLO);
0514: int idx = start + HDR_SIZE;
0515: int endOfMsg = start + msgLength;
0516:
0517: /*
0518: * Message must be long enough to contain a 4-byte header,
0519: * 2-byte version, a 32-byte random, a 1-byte session Id
0520: * length (plus variable lenght session Id), 2 byte cipher
0521: * suite, 1 byte compression method.
0522: */
0523: if (msgLength < 42) {
0524: return -1;
0525: }
0526:
0527: // Get the server version
0528: if ((rec.inputData[start + idx++] != (ver >>> 4))
0529: || (rec.inputData[start + idx++] != (ver & 0x0f))) {
0530: return -1;
0531: }
0532:
0533: // .. the 32-byte server random
0534: srand = new byte[32];
0535: System.arraycopy(rec.inputData, idx, srand, 0, 32);
0536: idx += 32;
0537:
0538: // ... the session_Id length in 1 byte (and session_Id)
0539: int slen = rec.inputData[idx++] & 0xff;
0540: if (slen != 0) {
0541: if (endOfMsg < idx + slen) {
0542: return -1;
0543: }
0544:
0545: sSessionId = new byte[slen];
0546: System.arraycopy(rec.inputData, idx, sSessionId, 0, slen);
0547: idx += slen;
0548: }
0549:
0550: // ... the cipher suite
0551: /*
0552: * NOTE: this is a hack, it works because for the cipher suites
0553: * we support, the second byte directly maps to suite code.
0554: */
0555: idx++;
0556: negSuite = rec.inputData[idx++];
0557:
0558: /*
0559: * Check the cipher suite and compression method. The compression
0560: * method better be 0x00 since that is the only one we ever propose.
0561: *
0562: */
0563:
0564: if ((negSuite != CipherSuites.ARCFOUR_128_SHA)
0565: && (negSuite != CipherSuites.ARCFOUR_128_MD5)
0566: && (negSuite != CipherSuites.ARCFOUR_40_MD5)
0567: &&
0568: //Support for new Ciphers
0569: (negSuite != CipherSuites.DES_CBC_SHA)
0570: && (negSuite != CipherSuites.TRIPLEDES_CBC_SHA)
0571: && (rec.inputData[idx++] != (byte) 0x00)) {
0572: return -1;
0573: }
0574:
0575: // Update the hash of handshake messages
0576: ourMD5.update(rec.inputData, start, msgLength);
0577: ourSHA.update(rec.inputData, start, msgLength);
0578:
0579: negSuiteName = suiteNames[negSuite];
0580:
0581: Utils.logln(Utils.LOG_INFO, "Negotiated " + negSuiteName);
0582: //System.out.println("Negotiated Cipher : "+negSuiteName);
0583:
0584: return 0;
0585: }
0586:
0587: /**
0588: * Receives a Server certificate message containing a certificate
0589: * chain starting with the server certificate.
0590: * <P />
0591: * @return 0 if a trustworthy server certificate is found, -1 otherwise
0592: * @exception IOException if there is a problem reading the message
0593: */
0594: private int rcvCert() throws IOException {
0595: int msgLength;
0596: int endOfMsg;
0597: int idx;
0598: int len;
0599:
0600: msgLength = getNextMsg(CERT);
0601: endOfMsg = start + msgLength;
0602:
0603: /*
0604: * Message should atleast have a 4-byte header and an empty cert
0605: * list with 3-byte length
0606: */
0607: if (msgLength < 7) {
0608: return -1;
0609: }
0610:
0611: idx = start + HDR_SIZE;
0612: len = 0;
0613:
0614: // Check the length ...
0615: len = ((rec.inputData[idx++] & 0xff) << 16)
0616: + ((rec.inputData[idx++] & 0xff) << 8)
0617: + (rec.inputData[idx++] & 0xff);
0618: if ((idx + len) > endOfMsg)
0619: return -1;
0620:
0621: certCnt = 0;
0622: sKeyUsage = 0;
0623:
0624: // Parse the certificate chain and get the server's public key
0625: sKey = parseChain(peerHost, rec.inputData, idx, endOfMsg,
0626: SSLStreamConnection.getTrustedCertStore());
0627:
0628: // Update the hash of handshake messages
0629: ourMD5.update(rec.inputData, start, msgLength);
0630: ourSHA.update(rec.inputData, start, msgLength);
0631:
0632: certCnt = 0;
0633: return 0;
0634: }
0635:
0636: /**
0637: * Receives a Server key exchange message. For now only RSA key
0638: * exchange is supported and this message includes temporary
0639: * RSA public key parameters signed by the server's long-term
0640: * private key. This message is optional.
0641: * <P />
0642: * @return 0 on success, -1 on failure
0643: * @exception IOException if there is a problem reading the
0644: * message
0645: */
0646: private int rcvSrvrKeyExch() throws IOException {
0647: int msgLength = getNextMsg(S_KEYEXCH);
0648: int idx = start + HDR_SIZE;
0649: int endOfMsg = start + msgLength;
0650:
0651: /*
0652: * NOTE: Based on what we propose, the only key exch is RSA
0653: * Also note that the server key exchange is optional and used
0654: * only if the public key included in the certificate chain
0655: * is unsuitable for encrypting the pre-master secret.
0656: */
0657: if (msgLength == -1) {
0658: // We can use the server key to encrypt premaster secret
0659: eKey = sKey;
0660: /*
0661: * Make sure sKey can be used for premaster secret encryption,
0662: * i.e. if key usage extensions are present, the key encipherment
0663: * bit or the serverAuth bit must be set
0664: */
0665: if ((sKeyUsage != -1) && ((sKeyUsage & 0x04) != 0x04)
0666: && ((sKeyUsage & 0x020000) != 0x020000)) {
0667: Utils.logln(Utils.LOG_ERR,
0668: "Neither keyEncipherment nor serverAuth "
0669: + "bit is set in server certificate");
0670:
0671: throw new CertificateException(sCert,
0672: CertificateException.INAPPROPRIATE_KEY_USAGE);
0673: }
0674:
0675: return 0;
0676: }
0677:
0678: /*
0679: * Make sure sKey can be used for signing RSA params,
0680: * i.e. if key usage extensions are present, the digitalSignature
0681: * bit or serverAuth bit must be set
0682: */
0683: if ((sKeyUsage != -1) && ((sKeyUsage & 0x01) != 0x01)
0684: && ((sKeyUsage & 0x020000) != 0x020000)) {
0685: Utils.logln(Utils.LOG_ERR,
0686: "Neither digitalSignature nor serverAuth "
0687: + "bit is set in server certificate");
0688: throw new CertificateException(sCert,
0689: CertificateException.INAPPROPRIATE_KEY_USAGE);
0690: }
0691:
0692: // read and verify the encryption key parameters
0693: if (endOfMsg < (idx + 4)) {
0694: return -1;
0695: }
0696:
0697: // read the modulus length
0698: int len = ((rec.inputData[idx++] & 0xff) << 16)
0699: + (rec.inputData[idx++] & 0xff);
0700: if (endOfMsg < (idx + len + 2)) {
0701: return -1;
0702: }
0703:
0704: try {
0705: // ... and the modulus
0706: /*
0707: * Some weird sites (e.g. www.verisign.com) encode a
0708: * 512-bit modulus in 65 (rather than 64 bytes) with the
0709: * first byte set to zero. We accomodate this behavior
0710: * by using a special check.
0711: */
0712: if ((len == 65) && (rec.inputData[idx] == (byte) 0x00)) {
0713: eKey = (RSAPublicKey) KeyBuilder.buildKey(
0714: KeyBuilder.TYPE_RSA_PUBLIC, (short) (64 << 3),
0715: false);
0716: eKey.setModulus(rec.inputData, (short) (idx + 1),
0717: (short) 64);
0718: } else {
0719: eKey = (RSAPublicKey) KeyBuilder.buildKey(
0720: KeyBuilder.TYPE_RSA_PUBLIC, (short) (len << 3),
0721: false);
0722: eKey
0723: .setModulus(rec.inputData, (short) idx,
0724: (short) len);
0725: }
0726:
0727: idx += len;
0728: // read the exponent length
0729: len = ((rec.inputData[idx++] & 0xff) << 16)
0730: + (rec.inputData[idx++] & 0xff);
0731: if (endOfMsg < (idx + len)) {
0732: return -1;
0733: }
0734:
0735: // ... and the exponent
0736: eKey.setExponent(rec.inputData, (short) idx, (short) len);
0737: } catch (CryptoException ce) {
0738: // Utils.logln(Utils.LOG_ERR, "rcvSrvrExch caught " + ce);
0739: return -1;
0740: }
0741:
0742: idx += len;
0743:
0744: // mark where ServerRSAparams end
0745: int end = idx;
0746:
0747: // Now read the signature length
0748: len = ((rec.inputData[idx++] & 0xff) << 16)
0749: + (rec.inputData[idx++] & 0xff);
0750: if (endOfMsg < (idx + len)) {
0751: return -1;
0752: }
0753:
0754: // ... and the signature
0755: byte[] sig = new byte[len];
0756: System.arraycopy(rec.inputData, idx, sig, 0, sig.length);
0757: idx += len;
0758: if (endOfMsg != idx) {
0759: return -1;
0760: }
0761:
0762: // Compute the expected hash
0763: byte[] dat = new byte[MD5_SIZE + SHA_SIZE];
0764: try {
0765: MessageDigest di = MessageDigest.getInstance(
0766: MessageDigest.ALG_MD5, false);
0767: di.update(crand, 0, crand.length);
0768: di.update(srand, 0, srand.length);
0769: di.doFinal(rec.inputData, HDR_SIZE, end - HDR_SIZE, dat, 0);
0770:
0771: di = MessageDigest
0772: .getInstance(MessageDigest.ALG_SHA, false);
0773: di.update(crand, 0, crand.length);
0774: di.update(srand, 0, srand.length);
0775: di.doFinal(rec.inputData, HDR_SIZE, end - HDR_SIZE, dat,
0776: MD5_SIZE);
0777: } catch (Exception e) {
0778: throw new RuntimeException("No MD5 or SHA");
0779: }
0780:
0781: try {
0782: Cipher rsa = Cipher
0783: .getInstance(Cipher.ALG_RSA_PKCS1, false);
0784: rsa.init(sKey, Cipher.MODE_DECRYPT);
0785: byte[] res = new byte[sKey.getSize() >>> 3];
0786: int val = rsa.doFinal(sig, 0, sig.length, res, 0);
0787: if (!Utils.byteMatch(res, 0, dat, 0, dat.length)) {
0788: Utils.logln(Utils.LOG_ERR,
0789: "RSA params failed verification");
0790: return -1;
0791: }
0792: // Utils.logln(Utils.LOG_DEBUG, "RSA params verified");
0793: } catch (Exception e) {
0794: throw new IOException("RSA decryption caught " + e);
0795: }
0796:
0797: // Update the hash of handshake messages
0798: ourMD5.update(rec.inputData, start, msgLength);
0799: ourSHA.update(rec.inputData, start, msgLength);
0800:
0801: return 0;
0802: }
0803:
0804: /**
0805: * Receives a Certificate request message. This message is optional.
0806: * <P />
0807: * @return 0 (this method always completes successfully)
0808: * @exception IOException if there is a problem reading the
0809: * message
0810: */
0811: private int rcvCertReq() throws IOException {
0812: int msgLength = getNextMsg(CERT_REQ);
0813: if (msgLength == -1) {
0814: return 0; // certificate request is optional
0815: }
0816:
0817: /*
0818: * We do not support client-side certificates so if we see
0819: * a request for a certificate, remember it here so we can
0820: * complain later
0821: */
0822: gotCertReq = (byte) 1;
0823:
0824: // Update the hash of handshake messages
0825: ourMD5.update(rec.inputData, start, msgLength);
0826: ourSHA.update(rec.inputData, start, msgLength);
0827:
0828: // NOTE: We return zero without attempting to parse the message body.
0829: return 0;
0830: }
0831:
0832: /**
0833: * Receives a Server hello done message.
0834: * <P />
0835: * @return 0 on success, -1 on error
0836: * @exception IOException if there is a problem reading the
0837: * message
0838: */
0839: private int rcvSrvrHelloDone() throws IOException {
0840: int msgLength = getNextMsg(S_DONE);
0841:
0842: // A server_hello_done message has no body, just the header
0843: if (msgLength != HDR_SIZE) {
0844: return -1;
0845: }
0846:
0847: // Update the hash of handshake messages
0848: ourMD5.update(rec.inputData, start, msgLength);
0849: ourSHA.update(rec.inputData, start, msgLength);
0850:
0851: return 0;
0852: }
0853:
0854: /**
0855: * Sends a Client key exchange message. For now, only RSA key
0856: * exchange is supported and this message contains a pre-master
0857: * secret encrypted with the RSA public key of the server.
0858: * <P />
0859: * @exception IOException if there is a problem writing to the
0860: * record layer
0861: */
0862: private void sndKeyExch() throws IOException {
0863: /*
0864: * If we get here, the server agreed to an RSA key exchange
0865: * and the RSA public key to be used for encrypting the
0866: * pre-master secret is available in eKey.
0867: */
0868: if (gotCertReq == 1) {
0869: // Send back an error ... we do not support client auth
0870: rec.alert(Record.FATAL, Record.NO_CERT);
0871: throw new IOException("No client cert");
0872: } else { // NOTE: The only possible key exch is RSA
0873:
0874: // Generate a 48-byte random pre-master secret
0875: preMaster = new byte[48];
0876:
0877: rnd.generateData(preMaster, (short) 0, (short) 48);
0878: // ... first two bytes must have client version
0879: preMaster[0] = (byte) (ver >>> 4);
0880: preMaster[1] = (byte) (ver & 0x0f);
0881:
0882: // Prepare a message containing the RSA encrypted pre-master
0883: int modLen = eKey.getSize() >>> 3;
0884: byte[] msg = new byte[HDR_SIZE + modLen];
0885: int idx = 0;
0886: // Fill the type
0887: msg[idx++] = C_KEYEXCH;
0888: // ... message length
0889: msg[idx++] = (byte) (modLen >>> 16);
0890: msg[idx++] = (byte) (modLen >>> 8);
0891: msg[idx++] = (byte) (modLen & 0xff);
0892:
0893: // ... the encrypted pre-master secret
0894: try {
0895: Cipher rsa = Cipher.getInstance(Cipher.ALG_RSA_PKCS1,
0896: false);
0897: rsa.init(eKey, Cipher.MODE_ENCRYPT);
0898: int val = rsa.doFinal(preMaster, 0, 48, msg, idx);
0899: if (val != modLen)
0900: throw new IOException("RSA result too short");
0901: } catch (Exception e) {
0902: //throw new IOException("premaster encryption caught " + e);
0903: e.printStackTrace();
0904: throw new IOException("premaster encryption caught "
0905: + e);
0906: }
0907:
0908: // Update the hash of handshake messages
0909: ourMD5.update(msg, 0, msg.length);
0910: ourSHA.update(msg, 0, msg.length);
0911:
0912: rec.wrRec(Record.HNDSHK, msg, 0, msg.length);
0913: }
0914: }
0915:
0916: /**
0917: * Derives the master key based on the pre-master secret and
0918: * random values exchanged in the client and server hello messages.
0919: * <P />
0920: * @exception IOException if there is a problem during the computation
0921: */
0922: private void mkMaster() throws IOException {
0923: byte[] expansion[] = { { (byte) 0x41 }, // 'A'
0924: { (byte) 0x42, (byte) 0x42 }, // 'BB'
0925: { (byte) 0x43, (byte) 0x43, (byte) 0x43 }, // 'CCC'
0926: };
0927:
0928: MessageDigest md = null;
0929: MessageDigest sd = null;
0930:
0931: /*
0932: * First, we compute the 48-byte (three MD5 outputs) master secret
0933: *
0934: * master_secret =
0935: * MD5(pre_master + SHA('A' + pre_master +
0936: * ClientHello.random + ServerHello.random)) +
0937: * MD5(pre_master + SHA('BB' + pre_master +
0938: * ClientHello.random + ServerHello.random)) +
0939: * MD5(pre_master + SHA('CCC' + pre_master +
0940: * ClientHello.random + ServerHello.random));
0941: *
0942: * To simplify things, we use
0943: * tmp = pre_master + ClientHello.random + ServerHello.random;
0944: */
0945: byte[] tmp = new byte[preMaster.length + crand.length
0946: + srand.length];
0947: System.arraycopy(preMaster, 0, tmp, 0, preMaster.length);
0948: System.arraycopy(crand, 0, tmp, preMaster.length, crand.length);
0949: System.arraycopy(srand, 0, tmp,
0950: preMaster.length + crand.length, srand.length);
0951: try {
0952: md = MessageDigest
0953: .getInstance(MessageDigest.ALG_MD5, false);
0954: sd = MessageDigest
0955: .getInstance(MessageDigest.ALG_SHA, false);
0956: } catch (Exception e) {
0957: /*
0958: * We should never catch this here (if these are missing,
0959: * we will catch this exception in the constructor)
0960: */
0961: throw new IOException("No MD5 or SHA");
0962: }
0963: master = new byte[48];
0964:
0965: for (int i = 0; i < 3; i++) {
0966: md.update(preMaster, 0, preMaster.length);
0967: sd.update(expansion[i], 0, expansion[i].length);
0968: byte[] res = new byte[SHA_SIZE];
0969: sd.doFinal(tmp, 0, tmp.length, res, 0);
0970: md.doFinal(res, 0, res.length, master, i << 4);
0971: }
0972: }
0973:
0974: /**
0975: * Sends a ChangeCipherSpec protocol message (this is not really
0976: * a handshake protocol message).
0977: * <P />
0978: * @exception IOException if there is a problem writing to the
0979: * record layer
0980: */
0981: private void sndChangeCipher() throws IOException {
0982: byte[] msg = new byte[1];
0983: // change cipher spec consists of a single byte with value 1
0984: msg[0] = (byte) 0x01;
0985: rec.wrRec(Record.CCS, msg, 0, 1); // msg.length is 1
0986: }
0987:
0988: /**
0989: * Computes the content of a Finished message.
0990: * <P />
0991: * @param who the role (either Record.CLIENT or
0992: * Record.SERVER) for which the finish message is computed
0993: * @return a byte array containing the hash of all handshake
0994: * messages seen so far
0995: * @exception IOException if handshake digests could not be computed
0996: */
0997: private byte[] computeFinished(byte who) throws IOException {
0998: byte[] sender[] = { { 0x53, 0x52, 0x56, 0x52 }, // for server
0999: { 0x43, 0x4c, 0x4e, 0x54 } // for client
1000: };
1001: byte[] msg = new byte[MD5_SIZE + SHA_SIZE];
1002: byte[] tmp = null;
1003:
1004: try {
1005: // long t1 = System.currentTimeMillis();
1006: MessageDigest d = (MessageDigest) ourMD5.clone();
1007: d.update(sender[who], 0, 4);
1008: d.update(master, 0, master.length);
1009: tmp = new byte[MD5_SIZE];
1010: // MD5 padding length is 48
1011: d.doFinal(Record.PAD1, 0, 48, tmp, 0);
1012: d.update(master, 0, master.length);
1013: d.update(Record.PAD2, 0, 48);
1014: d.doFinal(tmp, 0, MD5_SIZE, msg, 0);
1015:
1016: d = (MessageDigest) ourSHA.clone();
1017: d.update(sender[who], 0, 4);
1018: d.update(master, 0, master.length);
1019: tmp = new byte[SHA_SIZE];
1020: // SHA padding length is 40
1021: d.doFinal(rec.PAD1, 0, 40, tmp, 0);
1022: d.update(master, 0, master.length);
1023: d.update(Record.PAD2, 0, 40);
1024: d.doFinal(tmp, 0, SHA_SIZE, msg, MD5_SIZE);
1025:
1026: return msg;
1027: } catch (Exception e) {
1028: throw new IOException("MessageDigest not cloneable");
1029: }
1030: }
1031:
1032: /**
1033: * Sends a Finished message.
1034: * <P />
1035: * @exception IOException if there is a problem writing to the
1036: * record layer
1037: */
1038: private void sndFinished() throws IOException {
1039: // HDR_SIZE + MD5_SIZE + SHA_SIZE is 40
1040: byte[] msg = new byte[40];
1041:
1042: System.arraycopy(FINISH_PREFIX, 0, msg, 0, 4);
1043: // MD5_SIZE + SHA_SIZE is 36
1044: System.arraycopy(computeFinished(role), 0, msg, 4, 36);
1045:
1046: // Update the hash of handshake messages
1047: ourMD5.update(msg, 0, msg.length);
1048: ourSHA.update(msg, 0, msg.length);
1049:
1050: rec.wrRec(Record.HNDSHK, msg, 0, msg.length);
1051: }
1052:
1053: /**
1054: * Receives a ChangeCipherSpec protocol message (this is
1055: * not a handshake message).
1056: * <P />
1057: * @return 0 on success, -1 on error
1058: * @exception IOException if there is a problem reading the
1059: * message
1060: */
1061: private int rcvChangeCipher() throws IOException {
1062: /*
1063: * We make sure that there are no unread handshake messages
1064: * in the internal store when we get here.
1065: */
1066: if (cnt != 0) {
1067: Utils
1068: .logln(Utils.LOG_ERR,
1069: "Unread handshake mesg in store");
1070: return -1;
1071: }
1072:
1073: /*
1074: * Note that CCS is not a handshake message (it is its own protocol)
1075: * The record layer header is 5 bytes and the CCS body is one
1076: * byte with value 0x01.
1077: */
1078: rec.rdRec(Record.CCS);
1079: if ((rec.inputData == null) || (rec.inputData.length != 1)
1080: || (rec.inputData[0] != (byte) 0x01)) {
1081: return -1;
1082: }
1083:
1084: return 0;
1085: }
1086:
1087: /**
1088: * Receives a Finished message and verifies that it contains
1089: * the correct hash of handshake messages.
1090: * <P />
1091: * @return 0 on success, -1 on error
1092: * @exception IOException if there is a problem reading the
1093: * message
1094: */
1095: private int rcvFinished() throws IOException {
1096: int msgLength = getNextMsg(FINISH);
1097: if (msgLength != 40) {
1098: return -1;
1099: }
1100:
1101: // Compute the expected hash
1102: byte[] expected = computeFinished((byte) (1 - role));
1103:
1104: if (!Utils.byteMatch(rec.inputData, start + HDR_SIZE, expected,
1105: 0, expected.length)) {
1106: return -1;
1107: } else {
1108: // Update the hash of handshake messages
1109: ourMD5.update(rec.inputData, start, msgLength);
1110: ourSHA.update(rec.inputData, start, msgLength);
1111: // now = System.currentTimeMillis();
1112: return 0;
1113: }
1114: }
1115:
1116: /**
1117: * Initiates an SSL handshake with the peer specified previously
1118: * in the constructor.
1119: * <P />
1120: * @param aswho role played in the handshake (for now only
1121: * Record.CLIENT is supported)
1122: * @exception IOException if the handshake fails for some reason
1123: */
1124: // TODO: Allow handshake parameters such as ver, cipher suites
1125: // and compression methods to be passed as arguments.
1126: void doHandShake(byte aswho) throws IOException {
1127: long t1 = System.currentTimeMillis();
1128: int code = 0;
1129:
1130: ver = (byte) 0x30; // TODO: This is hardcoded for now
1131: role = aswho;
1132:
1133: byte val = 0;
1134: sndHello3();
1135:
1136: if (rcvSrvrHello() < 0) {
1137: complain("Bad ServerHello");
1138: }
1139: ;
1140:
1141: if ((sSessionId == null)
1142: || (cSession == null)
1143: || (sSessionId.length != cSession.id.length)
1144: || !Utils.byteMatch(sSessionId, 0, cSession.id, 0,
1145: sSessionId.length)) {
1146: // Session not resumed
1147:
1148: try {
1149: code = rcvCert();
1150: } catch (CertificateException e) {
1151: complain(e);
1152: }
1153:
1154: if (code < 0) {
1155: complain("Corrupt server certificate message");
1156: }
1157:
1158: // ... get server_key_exchange (optional)
1159: try {
1160: code = rcvSrvrKeyExch();
1161: } catch (CertificateException e) {
1162: complain(e);
1163: }
1164:
1165: if (code < 0) {
1166: complain("Bad ServerKeyExchange");
1167: }
1168:
1169: // ... get certificate_request (optional)
1170: rcvCertReq();
1171: if (rcvSrvrHelloDone() < 0) {
1172: complain("Bad ServerHelloDone");
1173: }
1174:
1175: // ... send client_key_exchange
1176: sndKeyExch();
1177: mkMaster();
1178: try {
1179: rec.init(crand, srand, negSuite, master);
1180: } catch (Exception e) {
1181: complain("Record.init() caught " + e);
1182: }
1183:
1184: // ... send change_cipher_spec
1185: sndChangeCipher();
1186: // ... send finished
1187: sndFinished();
1188:
1189: // ... get change_cipher_spec
1190: if (rcvChangeCipher() < 0) {
1191: complain("Bad ChangeCipherSpec");
1192: }
1193:
1194: // ... get finished
1195: if (rcvFinished() < 0) {
1196: complain("Bad Finished");
1197: }
1198: } else {
1199: /*
1200: * The server agreed to resume a session.
1201: * Get the needed values from the previous session
1202: * now since the references could be overwritten if a
1203: * concurrent connection is made to this host and port.
1204: */
1205: master = cSession.master;
1206: sCert = cSession.cert;
1207:
1208: try {
1209: rec.init(crand, srand, negSuite, master);
1210: } catch (Exception e) {
1211: complain("Record.init() caught " + e);
1212: }
1213:
1214: // ... get change_cipher_spec
1215: if (rcvChangeCipher() < 0) {
1216: complain("Bad ChangeCipherSpec");
1217: }
1218:
1219: // ... get finished
1220: if (rcvFinished() < 0) {
1221: complain("Bad Finished");
1222: }
1223:
1224: // ... send change_cipher_spec
1225: sndChangeCipher();
1226: // ... send finished
1227: sndFinished();
1228: }
1229:
1230: Session.add(peerHost, peerPort, sSessionId, master, sCert);
1231:
1232: // Zero out the premaster and master secrets
1233: if (preMaster != null) {
1234: // premaster can be null if we resumed an SSL session
1235: for (int i = 0; i < preMaster.length; i++) {
1236: preMaster[i] = 0;
1237: }
1238: }
1239:
1240: for (int i = 0; i < master.length; i++) {
1241: master[i] = 0;
1242: }
1243: }
1244:
1245: /**
1246: * Sends a fatal alert indicating handshake_failure and marks
1247: * the corresponding SSL session is non-resumable.
1248: * <p />
1249: * @param msg string containing the exception message to be reported
1250: * @exception IOException with the specified string
1251: */
1252: private void complain(String msg) throws IOException {
1253: complain(new IOException(msg));
1254: }
1255:
1256: /**
1257: * Sends a fatal alert indicating handshake_failure and marks
1258: * the corresponding SSL session is non-resumable.
1259: * <p />
1260: * @param e the IOException to be reported
1261: * @exception IOException
1262: */
1263: private void complain(IOException e) throws IOException {
1264: try {
1265: rec.alert(Record.FATAL, Record.HNDSHK_FAIL);
1266: if (sSessionId != null) {
1267: Session.del(peerHost, peerPort, sSessionId);
1268: }
1269: } finally {
1270: throw e;
1271: }
1272: }
1273:
1274: void setCipherSuites(byte[] cipherSuites) {
1275: this .SUITES_AND_COMP = cipherSuites;
1276: }
1277:
1278: }
1279:
1280: /**
1281: * This class implements methods to maintain resumable SSL
1282: * sessions.
1283: */
1284: // visible within the package
1285: class Session {
1286: /** Maximum number of cached resumable sessions. */
1287: private static final byte MAX_SESSIONS = 4;
1288:
1289: /**
1290: * Stores the last index where a session was overwritten, we
1291: * try to do a round-robin selection of places to overwrite
1292: */
1293: private static int delIdx = 0;
1294:
1295: /*
1296: * A session is uniquely identified by the combination of
1297: * host, port and session identifier. The master secret is
1298: * included in the cached session information.
1299: */
1300: /** Target host name. */
1301: String host;
1302: /** Target port number. */
1303: int port;
1304: /** Session identifier. */
1305: byte[] id;
1306: /** Master secret. */
1307: byte[] master;
1308: /** Target Certificate. */
1309: X509Certificate cert;
1310:
1311: /** A cache of currently resumable sessions. */
1312: private static Session[] sessions = new Session[MAX_SESSIONS];
1313:
1314: /**
1315: * Gets the master secret associated with a resumable session.
1316: * The session is uniquely identified by the combination of the
1317: * host, port.
1318: *
1319: * @param h host name of peer
1320: * @param p port number of peer
1321: *
1322: * @return matching session
1323: */
1324: static synchronized Session get(String h, int p) {
1325: for (int i = 0; i < MAX_SESSIONS; i++) {
1326: if ((sessions[i] == null) || (sessions[i].id == null))
1327: continue;
1328:
1329: if (sessions[i].host.compareTo(h) == 0
1330: && sessions[i].port == p) {
1331: return sessions[i];
1332: }
1333: }
1334:
1335: return null;
1336: }
1337:
1338: /**
1339: * Adds a new session with the specified parameters to the cache
1340: * of resumable sessions. At any given time, this class maintains
1341: * at most one resusumable session for any host/port pair.
1342: * <P />
1343: * @param h host name of peer
1344: * @param p port number of peer
1345: * @param id session identifier
1346: * @param mas master secret
1347: * @param cert certificate of peer
1348: */
1349: static synchronized void add(String h, int p, byte[] id,
1350: byte[] mas, X509Certificate cert) {
1351: // TODO: This will change if we stop using linear arrays
1352: int idx = MAX_SESSIONS;
1353: for (int i = 0; i < MAX_SESSIONS; i++) {
1354: if ((sessions[i] == null) || (sessions[i].id == null)) {
1355: idx = i; // possible candidate for overwriting
1356: continue;
1357: }
1358:
1359: if ((sessions[i].host.compareTo(h) == 0)
1360: && (sessions[i].port == p)) { // preferred candidate
1361: idx = i;
1362: break;
1363: }
1364: }
1365:
1366: /*
1367: * If all else is taken, overwrite the one specified by
1368: * delIdx and move delIdx over to the next one. Simulates FIFO.
1369: */
1370: if (idx == MAX_SESSIONS) {
1371: idx = delIdx;
1372: delIdx++;
1373: if (delIdx == MAX_SESSIONS)
1374: delIdx = 0;
1375: }
1376:
1377: if (sessions[idx] == null) {
1378: sessions[idx] = new Session();
1379: }
1380:
1381: sessions[idx].id = id;
1382:
1383: /*
1384: * Since the master will change after this method, we need to
1385: * copy it, to preserve its current value for later.
1386: */
1387: sessions[idx].master = new byte[mas.length];
1388: System.arraycopy(mas, 0, sessions[idx].master, 0, mas.length);
1389:
1390: sessions[idx].host = new String(h); // "h" will be a substring of URL
1391: sessions[idx].port = p;
1392: sessions[idx].cert = cert;
1393: }
1394:
1395: /**
1396: * Deletes the session identified by the specfied parameters
1397: * from the cache of resumable sessions.
1398: * <P />
1399: * @param h host name of peer
1400: * @param p port number of peer
1401: * @param sid session identifier
1402: */
1403: static synchronized void del(String h, int p, byte[] sid) {
1404: for (int i = 0; i < MAX_SESSIONS; i++) {
1405: if ((sessions[i] == null) || (sessions[i].id == null))
1406: continue;
1407:
1408: if (Utils.byteMatch(sessions[i].id, 0, sid, 0, sid.length)
1409: && (sessions[i].host.compareTo(h) == 0)
1410: && (sessions[i].port == p)) {
1411: sessions[i].id = null;
1412: sessions[i].master = null;
1413: sessions[i].host = null;
1414: sessions[i].cert = null;
1415: break;
1416: }
1417: }
1418: }
1419:
1420: }
|