0001: package org.bouncycastle.crypto.tls;
0002:
0003: import org.bouncycastle.asn1.x509.RSAPublicKeyStructure;
0004: import org.bouncycastle.crypto.InvalidCipherTextException;
0005: import org.bouncycastle.crypto.encodings.PKCS1Encoding;
0006: import org.bouncycastle.crypto.engines.RSABlindedEngine;
0007: import org.bouncycastle.crypto.params.ParametersWithRandom;
0008: import org.bouncycastle.crypto.params.RSAKeyParameters;
0009: import org.bouncycastle.crypto.prng.ThreadedSeedGenerator;
0010:
0011: import java.io.ByteArrayInputStream;
0012: import java.io.ByteArrayOutputStream;
0013: import java.io.IOException;
0014: import java.io.InputStream;
0015: import java.io.OutputStream;
0016: import java.math.BigInteger;
0017: import java.security.SecureRandom;
0018:
0019: /**
0020: * An implementation of all high level protocols in TLS 1.0.
0021: */
0022: public class TlsProtocolHandler {
0023:
0024: private static final short RL_CHANGE_CIPHER_SPEC = 20;
0025:
0026: private static final short RL_ALERT = 21;
0027:
0028: private static final short RL_HANDSHAKE = 22;
0029:
0030: private static final short RL_APPLICATION_DATA = 23;
0031:
0032: /*
0033: hello_request(0), client_hello(1), server_hello(2),
0034: certificate(11), server_key_exchange (12),
0035: certificate_request(13), server_hello_done(14),
0036: certificate_verify(15), client_key_exchange(16),
0037: finished(20), (255)
0038: */
0039:
0040: private static final short HP_HELLO_REQUEST = 0;
0041:
0042: private static final short HP_CLIENT_HELLO = 1;
0043:
0044: private static final short HP_SERVER_HELLO = 2;
0045:
0046: private static final short HP_CERTIFICATE = 11;
0047:
0048: private static final short HP_SERVER_KEY_EXCHANGE = 12;
0049:
0050: private static final short HP_CERTIFICATE_REQUEST = 13;
0051:
0052: private static final short HP_SERVER_HELLO_DONE = 14;
0053:
0054: private static final short HP_CERTIFICATE_VERIFY = 15;
0055:
0056: private static final short HP_CLIENT_KEY_EXCHANGE = 16;
0057:
0058: private static final short HP_FINISHED = 20;
0059:
0060: /*
0061: * Our Connection states
0062: */
0063:
0064: private static final short CS_CLIENT_HELLO_SEND = 1;
0065:
0066: private static final short CS_SERVER_HELLO_RECEIVED = 2;
0067:
0068: private static final short CS_SERVER_CERTIFICATE_RECEIVED = 3;
0069:
0070: private static final short CS_SERVER_KEY_EXCHANGE_RECEIVED = 4;
0071:
0072: private static final short CS_SERVER_HELLO_DONE_RECEIVED = 5;
0073:
0074: private static final short CS_CLIENT_KEY_EXCHANGE_SEND = 6;
0075:
0076: private static final short CS_CLIENT_CHANGE_CIPHER_SPEC_SEND = 7;
0077:
0078: private static final short CS_CLIENT_FINISHED_SEND = 8;
0079:
0080: private static final short CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED = 9;
0081:
0082: private static final short CS_DONE = 10;
0083:
0084: protected static final short AP_close_notify = 0;
0085: protected static final short AP_unexpected_message = 10;
0086: protected static final short AP_bad_record_mac = 20;
0087: protected static final short AP_decryption_failed = 21;
0088: protected static final short AP_record_overflow = 22;
0089: protected static final short AP_decompression_failure = 30;
0090: protected static final short AP_handshake_failure = 40;
0091: protected static final short AP_bad_certificate = 42;
0092: protected static final short AP_unsupported_certificate = 43;
0093: protected static final short AP_certificate_revoked = 44;
0094: protected static final short AP_certificate_expired = 45;
0095: protected static final short AP_certificate_unknown = 46;
0096: protected static final short AP_illegal_parameter = 47;
0097: protected static final short AP_unknown_ca = 48;
0098: protected static final short AP_access_denied = 49;
0099: protected static final short AP_decode_error = 50;
0100: protected static final short AP_decrypt_error = 51;
0101: protected static final short AP_export_restriction = 60;
0102: protected static final short AP_protocol_version = 70;
0103: protected static final short AP_insufficient_security = 71;
0104: protected static final short AP_internal_error = 80;
0105: protected static final short AP_user_canceled = 90;
0106: protected static final short AP_no_renegotiation = 100;
0107:
0108: protected static final short AL_warning = 1;
0109: protected static final short AL_fatal = 2;
0110:
0111: private static final byte[] emptybuf = new byte[0];
0112:
0113: private static final String TLS_ERROR_MESSAGE = "Internal TLS error, this could be an attack";
0114:
0115: /*
0116: * Queues for data from some protocolls.
0117: */
0118:
0119: private ByteQueue applicationDataQueue = new ByteQueue();
0120:
0121: private ByteQueue changeCipherSpecQueue = new ByteQueue();
0122:
0123: private ByteQueue alertQueue = new ByteQueue();
0124:
0125: private ByteQueue handshakeQueue = new ByteQueue();
0126:
0127: /*
0128: * The Record Stream we use
0129: */
0130:
0131: private RecordStream rs;
0132:
0133: private SecureRandom random;
0134:
0135: /*
0136: * The public rsa-key of the server.
0137: */
0138:
0139: private RSAKeyParameters serverRsaKey = null;
0140:
0141: private TlsInputStream tlsInputStream = null;
0142: private TlsOuputStream tlsOutputStream = null;
0143:
0144: private boolean closed = false;
0145: private boolean failedWithError = false;
0146: private boolean appDataReady = false;
0147:
0148: private byte[] clientRandom;
0149: private byte[] serverRandom;
0150: private byte[] ms;
0151:
0152: private TlsCipherSuite choosenCipherSuite = null;
0153:
0154: private BigInteger Yc;
0155: private byte[] pms;
0156:
0157: private CertificateVerifyer verifyer = null;
0158:
0159: public TlsProtocolHandler(InputStream is, OutputStream os) {
0160: /*
0161: * We use our threaded seed generator to generate a good random
0162: * seed. If the user has a better random seed, he should use
0163: * the constructor with a SecureRandom.
0164: */
0165: ThreadedSeedGenerator tsg = new ThreadedSeedGenerator();
0166: this .random = new SecureRandom();
0167: /*
0168: * Hopefully, 20 bytes in fast mode are good enough.
0169: */
0170: this .random.setSeed(tsg.generateSeed(20, true));
0171:
0172: this .rs = new RecordStream(this , is, os);
0173: }
0174:
0175: public TlsProtocolHandler(InputStream is, OutputStream os,
0176: SecureRandom sr) {
0177: this .random = sr;
0178: this .rs = new RecordStream(this , is, os);
0179: }
0180:
0181: private short connection_state;
0182:
0183: protected void processData(short protocol, byte[] buf, int offset,
0184: int len) throws IOException {
0185: /*
0186: * Have a look at the protocol type, and add it to the correct queue.
0187: */
0188: switch (protocol) {
0189: case RL_CHANGE_CIPHER_SPEC:
0190: changeCipherSpecQueue.addData(buf, offset, len);
0191: processChangeCipherSpec();
0192: break;
0193: case RL_ALERT:
0194: alertQueue.addData(buf, offset, len);
0195: processAlert();
0196: break;
0197: case RL_HANDSHAKE:
0198: handshakeQueue.addData(buf, offset, len);
0199: processHandshake();
0200: break;
0201: case RL_APPLICATION_DATA:
0202: if (!appDataReady) {
0203: this .failWithError(AL_fatal, AP_unexpected_message);
0204: }
0205: applicationDataQueue.addData(buf, offset, len);
0206: processApplicationData();
0207: break;
0208: default:
0209: /*
0210: * Uh, we don't know this protocol.
0211: *
0212: * RFC2246 defines on page 13, that we should ignore this.
0213: */
0214:
0215: }
0216: }
0217:
0218: private void processHandshake() throws IOException {
0219: boolean read;
0220: do {
0221: read = false;
0222:
0223: /*
0224: * We need the first 4 bytes, they contain type and length of
0225: * the message.
0226: */
0227: if (handshakeQueue.size() >= 4) {
0228: byte[] beginning = new byte[4];
0229: handshakeQueue.read(beginning, 0, 4, 0);
0230: ByteArrayInputStream bis = new ByteArrayInputStream(
0231: beginning);
0232: short type = TlsUtils.readUint8(bis);
0233: int len = TlsUtils.readUint24(bis);
0234:
0235: /*
0236: * Check if we have enough bytes in the buffer to read
0237: * the full message.
0238: */
0239: if (handshakeQueue.size() >= (len + 4)) {
0240: /*
0241: * Read the message.
0242: */
0243: byte[] buf = new byte[len];
0244: handshakeQueue.read(buf, 0, len, 4);
0245: handshakeQueue.removeData(len + 4);
0246:
0247: /*
0248: * If it is not a finished message, update our hashes
0249: * we prepare for the finish message.
0250: */
0251: if (type != HP_FINISHED) {
0252: rs.hash1.update(beginning, 0, 4);
0253: rs.hash2.update(beginning, 0, 4);
0254: rs.hash1.update(buf, 0, len);
0255: rs.hash2.update(buf, 0, len);
0256: }
0257:
0258: /*
0259: * Now, parse the message.
0260: */
0261: ByteArrayInputStream is = new ByteArrayInputStream(
0262: buf);
0263:
0264: /*
0265: * Check the type.
0266: */
0267: switch (type) {
0268: case HP_CERTIFICATE:
0269: switch (connection_state) {
0270: case CS_SERVER_HELLO_RECEIVED:
0271: /*
0272: * Parse the certificates.
0273: */
0274: Certificate cert = Certificate.parse(is);
0275: assertEmpty(is);
0276:
0277: /*
0278: * Verify them.
0279: */
0280: if (!this .verifyer.isValid(cert.getCerts())) {
0281: this .failWithError(AL_fatal,
0282: AP_user_canceled);
0283: }
0284:
0285: /*
0286: * We only support RSA certificates. Lets hope
0287: * this is one.
0288: */
0289: RSAPublicKeyStructure rsaKey = null;
0290: try {
0291: rsaKey = RSAPublicKeyStructure
0292: .getInstance(cert.certs[0]
0293: .getTBSCertificate()
0294: .getSubjectPublicKeyInfo()
0295: .getPublicKey());
0296: } catch (Exception e) {
0297: /*
0298: * Sorry, we have to fail ;-(
0299: */
0300: this .failWithError(AL_fatal,
0301: AP_unsupported_certificate);
0302: }
0303:
0304: /*
0305: * Parse the servers public RSA key.
0306: */
0307: this .serverRsaKey = new RSAKeyParameters(
0308: false, rsaKey.getModulus(), rsaKey
0309: .getPublicExponent());
0310:
0311: connection_state = CS_SERVER_CERTIFICATE_RECEIVED;
0312: read = true;
0313: break;
0314: default:
0315: this .failWithError(AL_fatal,
0316: AP_unexpected_message);
0317: }
0318: break;
0319: case HP_FINISHED:
0320: switch (connection_state) {
0321: case CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED:
0322: /*
0323: * Read the checksum from the finished message,
0324: * it has always 12 bytes.
0325: */
0326: byte[] receivedChecksum = new byte[12];
0327: TlsUtils.readFully(receivedChecksum, is);
0328: assertEmpty(is);
0329:
0330: /*
0331: * Calculate our owne checksum.
0332: */
0333: byte[] checksum = new byte[12];
0334: byte[] md5andsha1 = new byte[16 + 20];
0335: rs.hash2.doFinal(md5andsha1, 0);
0336: TlsUtils.PRF(this .ms, TlsUtils
0337: .toByteArray("server finished"),
0338: md5andsha1, checksum);
0339:
0340: /*
0341: * Compare both checksums.
0342: */
0343: for (int i = 0; i < receivedChecksum.length; i++) {
0344: if (receivedChecksum[i] != checksum[i]) {
0345: /*
0346: * Wrong checksum in the finished message.
0347: */
0348: this .failWithError(AL_fatal,
0349: AP_handshake_failure);
0350: }
0351: }
0352:
0353: connection_state = CS_DONE;
0354:
0355: /*
0356: * We are now ready to receive application data.
0357: */
0358: this .appDataReady = true;
0359: read = true;
0360: break;
0361: default:
0362: this .failWithError(AL_fatal,
0363: AP_unexpected_message);
0364: }
0365: break;
0366: case HP_SERVER_HELLO:
0367: switch (connection_state) {
0368: case CS_CLIENT_HELLO_SEND:
0369: /*
0370: * Read the server hello message
0371: */
0372: TlsUtils.checkVersion(is, this );
0373:
0374: /*
0375: * Read the server random
0376: */
0377: this .serverRandom = new byte[32];
0378: TlsUtils.readFully(this .serverRandom, is);
0379:
0380: /*
0381: * Currenty, we don't support session ids
0382: */
0383: short sessionIdLength = TlsUtils
0384: .readUint8(is);
0385: byte[] sessionId = new byte[sessionIdLength];
0386: TlsUtils.readFully(sessionId, is);
0387:
0388: /*
0389: * Find out which ciphersuite the server has
0390: * choosen. If we don't support this ciphersuite,
0391: * the TlsCipherSuiteManager will throw an
0392: * exception.
0393: */
0394: this .choosenCipherSuite = TlsCipherSuiteManager
0395: .getCipherSuite(TlsUtils
0396: .readUint16(is), this );
0397:
0398: /*
0399: * We support only the null compression which
0400: * means no compression.
0401: */
0402: short compressionMethod = TlsUtils
0403: .readUint8(is);
0404: if (compressionMethod != 0) {
0405: this
0406: .failWithError(
0407: TlsProtocolHandler.AL_fatal,
0408: TlsProtocolHandler.AP_illegal_parameter);
0409: }
0410: assertEmpty(is);
0411:
0412: connection_state = CS_SERVER_HELLO_RECEIVED;
0413: read = true;
0414: break;
0415: default:
0416: this .failWithError(AL_fatal,
0417: AP_unexpected_message);
0418: }
0419: break;
0420: case HP_SERVER_HELLO_DONE:
0421: switch (connection_state) {
0422:
0423: case CS_SERVER_CERTIFICATE_RECEIVED:
0424: /*
0425: * There was no server key exchange message, check
0426: * that we are doing RSA key exchange.
0427: */
0428: if (this .choosenCipherSuite
0429: .getKeyExchangeAlgorithm() != TlsCipherSuite.KE_RSA) {
0430: this .failWithError(AL_fatal,
0431: AP_unexpected_message);
0432: }
0433:
0434: /*
0435: * NB: Fall through to next case label to continue RSA key exchange
0436: */
0437:
0438: case CS_SERVER_KEY_EXCHANGE_RECEIVED:
0439:
0440: assertEmpty(is);
0441: connection_state = CS_SERVER_HELLO_DONE_RECEIVED;
0442:
0443: /*
0444: * Send the client key exchange message, depending
0445: * on the key exchange we are using in our
0446: * ciphersuite.
0447: */
0448: short ke = this .choosenCipherSuite
0449: .getKeyExchangeAlgorithm();
0450:
0451: switch (ke) {
0452: case TlsCipherSuite.KE_RSA:
0453: /*
0454: * We are doing RSA key exchange. We will
0455: * choose a pre master secret and send it
0456: * rsa encrypted to the server.
0457: *
0458: * Prepare pre master secret.
0459: */
0460: pms = new byte[48];
0461: pms[0] = 3;
0462: pms[1] = 1;
0463: for (int i = 2; i < 48; i++) {
0464: pms[i] = (byte) random.nextInt();
0465: }
0466:
0467: /*
0468: * Encode the pms and send it to the server.
0469: *
0470: * Prepare an PKCS1Encoding with good random
0471: * padding.
0472: */
0473: RSABlindedEngine rsa = new RSABlindedEngine();
0474: PKCS1Encoding encoding = new PKCS1Encoding(
0475: rsa);
0476: encoding.init(true,
0477: new ParametersWithRandom(
0478: this .serverRsaKey,
0479: this .random));
0480: byte[] encrypted = null;
0481: try {
0482: encrypted = encoding.processBlock(
0483: pms, 0, pms.length);
0484: } catch (InvalidCipherTextException e) {
0485: /*
0486: * This should never happen, only during decryption.
0487: */
0488: this .failWithError(AL_fatal,
0489: AP_internal_error);
0490: }
0491:
0492: /*
0493: * Send the encrypted pms.
0494: */
0495: ByteArrayOutputStream bos = new ByteArrayOutputStream();
0496: TlsUtils.writeUint8(
0497: HP_CLIENT_KEY_EXCHANGE, bos);
0498: TlsUtils.writeUint24(
0499: encrypted.length + 2, bos);
0500: TlsUtils.writeUint16(encrypted.length,
0501: bos);
0502: bos.write(encrypted);
0503: byte[] message = bos.toByteArray();
0504:
0505: rs.writeMessage((short) RL_HANDSHAKE,
0506: message, 0, message.length);
0507: break;
0508: case TlsCipherSuite.KE_DHE_RSA:
0509: /*
0510: * Send the Client Key Exchange message for
0511: * DHE key exchange.
0512: */
0513: byte[] YcByte = this .Yc.toByteArray();
0514: ByteArrayOutputStream DHbos = new ByteArrayOutputStream();
0515: TlsUtils.writeUint8(
0516: HP_CLIENT_KEY_EXCHANGE, DHbos);
0517: TlsUtils.writeUint24(YcByte.length + 2,
0518: DHbos);
0519: TlsUtils.writeUint16(YcByte.length,
0520: DHbos);
0521: DHbos.write(YcByte);
0522: byte[] DHmessage = DHbos.toByteArray();
0523:
0524: rs.writeMessage((short) RL_HANDSHAKE,
0525: DHmessage, 0, DHmessage.length);
0526:
0527: break;
0528: default:
0529: /*
0530: * Proble during handshake, we don't know
0531: * how to thandle this key exchange method.
0532: */
0533: this .failWithError(AL_fatal,
0534: AP_unexpected_message);
0535:
0536: }
0537:
0538: connection_state = CS_CLIENT_KEY_EXCHANGE_SEND;
0539:
0540: /*
0541: * Now, we send change cipher state
0542: */
0543: byte[] cmessage = new byte[1];
0544: cmessage[0] = 1;
0545: rs.writeMessage(
0546: (short) RL_CHANGE_CIPHER_SPEC,
0547: cmessage, 0, cmessage.length);
0548:
0549: connection_state = CS_CLIENT_CHANGE_CIPHER_SPEC_SEND;
0550:
0551: /*
0552: * Calculate the ms
0553: */
0554: this .ms = new byte[48];
0555: byte[] random = new byte[clientRandom.length
0556: + serverRandom.length];
0557: System.arraycopy(clientRandom, 0, random,
0558: 0, clientRandom.length);
0559: System.arraycopy(serverRandom, 0, random,
0560: clientRandom.length,
0561: serverRandom.length);
0562: TlsUtils.PRF(pms, TlsUtils
0563: .toByteArray("master secret"),
0564: random, this .ms);
0565:
0566: /*
0567: * Initialize our cipher suite
0568: */
0569: rs.writeSuite = this .choosenCipherSuite;
0570: rs.writeSuite.init(this .ms, clientRandom,
0571: serverRandom);
0572:
0573: /*
0574: * Send our finished message.
0575: */
0576: byte[] checksum = new byte[12];
0577: byte[] md5andsha1 = new byte[16 + 20];
0578: rs.hash1.doFinal(md5andsha1, 0);
0579: TlsUtils.PRF(this .ms, TlsUtils
0580: .toByteArray("client finished"),
0581: md5andsha1, checksum);
0582:
0583: ByteArrayOutputStream bos = new ByteArrayOutputStream();
0584: TlsUtils.writeUint8(HP_FINISHED, bos);
0585: TlsUtils.writeUint24(12, bos);
0586: bos.write(checksum);
0587: byte[] message = bos.toByteArray();
0588:
0589: rs.writeMessage((short) RL_HANDSHAKE,
0590: message, 0, message.length);
0591:
0592: this .connection_state = CS_CLIENT_FINISHED_SEND;
0593: read = true;
0594: break;
0595: default:
0596: this .failWithError(AL_fatal,
0597: AP_handshake_failure);
0598: }
0599: break;
0600: case HP_SERVER_KEY_EXCHANGE:
0601: switch (connection_state) {
0602: case CS_SERVER_CERTIFICATE_RECEIVED:
0603: /*
0604: * Check that we are doing DHE key exchange
0605: */
0606: if (this .choosenCipherSuite
0607: .getKeyExchangeAlgorithm() != TlsCipherSuite.KE_DHE_RSA) {
0608: this .failWithError(AL_fatal,
0609: AP_unexpected_message);
0610: }
0611:
0612: /*
0613: * Parse the Structure
0614: */
0615: int pLength = TlsUtils.readUint16(is);
0616: byte[] pByte = new byte[pLength];
0617: TlsUtils.readFully(pByte, is);
0618:
0619: int gLength = TlsUtils.readUint16(is);
0620: byte[] gByte = new byte[gLength];
0621: TlsUtils.readFully(gByte, is);
0622:
0623: int YsLength = TlsUtils.readUint16(is);
0624: byte[] YsByte = new byte[YsLength];
0625: TlsUtils.readFully(YsByte, is);
0626:
0627: int sigLength = TlsUtils.readUint16(is);
0628: byte[] sigByte = new byte[sigLength];
0629: TlsUtils.readFully(sigByte, is);
0630:
0631: this .assertEmpty(is);
0632:
0633: /*
0634: * Verify the Signature.
0635: *
0636: * First, calculate the hash.
0637: */
0638: CombinedHash sigDigest = new CombinedHash();
0639: ByteArrayOutputStream signedData = new ByteArrayOutputStream();
0640: TlsUtils.writeUint16(pLength, signedData);
0641: signedData.write(pByte);
0642: TlsUtils.writeUint16(gLength, signedData);
0643: signedData.write(gByte);
0644: TlsUtils.writeUint16(YsLength, signedData);
0645: signedData.write(YsByte);
0646: byte[] signed = signedData.toByteArray();
0647:
0648: sigDigest.update(this .clientRandom, 0,
0649: this .clientRandom.length);
0650: sigDigest.update(this .serverRandom, 0,
0651: this .serverRandom.length);
0652: sigDigest.update(signed, 0, signed.length);
0653: byte[] hash = new byte[sigDigest
0654: .getDigestSize()];
0655: sigDigest.doFinal(hash, 0);
0656:
0657: /*
0658: * Now, do the RSA operation
0659: */
0660: RSABlindedEngine rsa = new RSABlindedEngine();
0661: PKCS1Encoding encoding = new PKCS1Encoding(
0662: rsa);
0663: encoding.init(false, this .serverRsaKey);
0664:
0665: /*
0666: * The data which was signed
0667: */
0668: byte[] sigHash = null;
0669:
0670: try {
0671: sigHash = encoding.processBlock(
0672: sigByte, 0, sigByte.length);
0673: } catch (InvalidCipherTextException e) {
0674: this .failWithError(AL_fatal,
0675: AP_bad_certificate);
0676: }
0677:
0678: /*
0679: * Check if the data which was signed is equal to
0680: * the hash we calculated.
0681: */
0682: if (sigHash.length != hash.length) {
0683: this .failWithError(AL_fatal,
0684: AP_bad_certificate);
0685: }
0686:
0687: for (int i = 0; i < sigHash.length; i++) {
0688: if (sigHash[i] != hash[i]) {
0689: this .failWithError(AL_fatal,
0690: AP_bad_certificate);
0691: }
0692: }
0693:
0694: /*
0695: * OK, Signature was correct.
0696: *
0697: * Do the DH calculation.
0698: */
0699: BigInteger p = new BigInteger(1, pByte);
0700: BigInteger g = new BigInteger(1, gByte);
0701: BigInteger Ys = new BigInteger(1, YsByte);
0702: BigInteger x = new BigInteger(
0703: p.bitLength() - 1, this .random);
0704: Yc = g.modPow(x, p);
0705: this .pms = (Ys.modPow(x, p)).toByteArray();
0706:
0707: /*
0708: * Remove leading zero byte, if present.
0709: */
0710: if (this .pms[0] == 0) {
0711: byte[] tmp = new byte[this .pms.length - 1];
0712: System.arraycopy(this .pms, 1, tmp, 0,
0713: tmp.length);
0714: this .pms = tmp;
0715: }
0716:
0717: this .connection_state = CS_SERVER_KEY_EXCHANGE_RECEIVED;
0718: read = true;
0719: break;
0720: default:
0721: this .failWithError(AL_fatal,
0722: AP_unexpected_message);
0723: }
0724: break;
0725: case HP_HELLO_REQUEST:
0726: case HP_CLIENT_KEY_EXCHANGE:
0727: case HP_CERTIFICATE_REQUEST:
0728: case HP_CERTIFICATE_VERIFY:
0729: case HP_CLIENT_HELLO:
0730: default:
0731: // We do not support this!
0732: this .failWithError(AL_fatal,
0733: AP_unexpected_message);
0734: break;
0735:
0736: }
0737:
0738: }
0739: }
0740: } while (read);
0741:
0742: }
0743:
0744: private void processApplicationData() {
0745: /*
0746: * There is nothing we need to do here.
0747: *
0748: * This function could be used for callbacks when application
0749: * data arrives in the future.
0750: */
0751: }
0752:
0753: private void processAlert() throws IOException {
0754: while (alertQueue.size() >= 2) {
0755: /*
0756: * An alert is always 2 bytes. Read the alert.
0757: */
0758: byte[] tmp = new byte[2];
0759: alertQueue.read(tmp, 0, 2, 0);
0760: alertQueue.removeData(2);
0761: short level = tmp[0];
0762: short description = tmp[1];
0763: if (level == AL_fatal) {
0764: /*
0765: * This is a fatal error.
0766: */
0767: this .failedWithError = true;
0768: this .closed = true;
0769: /*
0770: * Now try to close the stream, ignore errors.
0771: */
0772: try {
0773: rs.close();
0774: } catch (Exception e) {
0775:
0776: }
0777: throw new IOException(TLS_ERROR_MESSAGE);
0778: } else {
0779: /*
0780: * This is just a warning.
0781: */
0782: if (description == AP_close_notify) {
0783: /*
0784: * Close notify
0785: */
0786: this .failWithError(AL_warning, AP_close_notify);
0787: }
0788: /*
0789: * If it is just a warning, we continue.
0790: */
0791: }
0792: }
0793:
0794: }
0795:
0796: /**
0797: * This method is called, when a change cipher spec message is received.
0798: *
0799: * @throws IOException If the message has an invalid content or the
0800: * handshake is not in the correct state.
0801: */
0802: private void processChangeCipherSpec() throws IOException {
0803: while (changeCipherSpecQueue.size() > 0) {
0804: /*
0805: * A change cipher spec message is only one byte with the value 1.
0806: */
0807: byte[] b = new byte[1];
0808: changeCipherSpecQueue.read(b, 0, 1, 0);
0809: changeCipherSpecQueue.removeData(1);
0810: if (b[0] != 1) {
0811: /*
0812: * This should never happen.
0813: */
0814: this .failWithError(AL_fatal, AP_unexpected_message);
0815:
0816: } else {
0817: /*
0818: * Check if we are in the correct connection state.
0819: */
0820: if (this .connection_state == CS_CLIENT_FINISHED_SEND) {
0821: rs.readSuite = rs.writeSuite;
0822: this .connection_state = CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED;
0823: } else {
0824: /*
0825: * We are not in the correct connection state.
0826: */
0827: this .failWithError(AL_fatal, AP_handshake_failure);
0828: }
0829:
0830: }
0831: }
0832:
0833: }
0834:
0835: /**
0836: * Connects to the remote system.
0837: *
0838: * @param verifyer Will be used when a certificate is received to verify
0839: * that this certificate is accepted by the client.
0840: * @throws IOException If handshake was not successfull.
0841: */
0842: public void connect(CertificateVerifyer verifyer)
0843: throws IOException {
0844: this .verifyer = verifyer;
0845:
0846: /*
0847: * Send Client hello
0848: *
0849: * First, generate some random data.
0850: */
0851: this .clientRandom = new byte[32];
0852: int t = (int) (System.currentTimeMillis() / 1000);
0853: this .clientRandom[0] = (byte) (t >> 24);
0854: this .clientRandom[1] = (byte) (t >> 16);
0855: this .clientRandom[2] = (byte) (t >> 8);
0856: this .clientRandom[3] = (byte) t;
0857:
0858: for (int i = 4; i < clientRandom.length; i++) {
0859: this .clientRandom[i] = (byte) random.nextInt();
0860: }
0861:
0862: ByteArrayOutputStream os = new ByteArrayOutputStream();
0863: TlsUtils.writeVersion(os);
0864: os.write(this .clientRandom);
0865:
0866: /*
0867: * Length of Session id
0868: */
0869: TlsUtils.writeUint8((short) 0, os);
0870:
0871: /*
0872: * Cipher suites
0873: */
0874: TlsCipherSuiteManager.writeCipherSuites(os);
0875:
0876: /*
0877: * Compression methods, just the null method.
0878: */
0879: byte[] compressionMethods = new byte[] { 0x00 };
0880: TlsUtils.writeUint8((short) compressionMethods.length, os);
0881: os.write(compressionMethods);
0882:
0883: ByteArrayOutputStream bos = new ByteArrayOutputStream();
0884: TlsUtils.writeUint8(HP_CLIENT_HELLO, bos);
0885: TlsUtils.writeUint24(os.size(), bos);
0886: bos.write(os.toByteArray());
0887: byte[] message = bos.toByteArray();
0888: rs.writeMessage(RL_HANDSHAKE, message, 0, message.length);
0889: connection_state = CS_CLIENT_HELLO_SEND;
0890:
0891: /*
0892: * We will now read data, until we have completed the handshake.
0893: */
0894: while (connection_state != CS_DONE) {
0895: rs.readData();
0896: }
0897:
0898: this .tlsInputStream = new TlsInputStream(this );
0899: this .tlsOutputStream = new TlsOuputStream(this );
0900: }
0901:
0902: /**
0903: * Read data from the network. The method will return immed, if there is
0904: * still some data left in the buffer, or block untill some application
0905: * data has been read from the network.
0906: *
0907: * @param buf The buffer where the data will be copied to.
0908: * @param offset The position where the data will be placed in the buffer.
0909: * @param len The maximum number of bytes to read.
0910: * @return The number of bytes read.
0911: * @throws IOException If something goes wrong during reading data.
0912: */
0913: protected int readApplicationData(byte[] buf, int offset, int len)
0914: throws IOException {
0915: while (applicationDataQueue.size() == 0) {
0916: /*
0917: * We need to read some data.
0918: */
0919: if (this .failedWithError) {
0920: /*
0921: * Something went terribly wrong, we should throw an IOException
0922: */
0923: throw new IOException(TLS_ERROR_MESSAGE);
0924: }
0925: if (this .closed) {
0926: /*
0927: * Connection has been closed, there is no more data to read.
0928: */
0929: return -1;
0930: }
0931:
0932: try {
0933: rs.readData();
0934: } catch (IOException e) {
0935: if (!this .closed) {
0936: this .failWithError(AL_fatal, AP_internal_error);
0937: }
0938: throw e;
0939: } catch (RuntimeException e) {
0940: if (!this .closed) {
0941: this .failWithError(AL_fatal, AP_internal_error);
0942: }
0943: throw e;
0944: }
0945: }
0946: len = Math.min(len, applicationDataQueue.size());
0947: applicationDataQueue.read(buf, offset, len, 0);
0948: applicationDataQueue.removeData(len);
0949: return len;
0950: }
0951:
0952: /**
0953: * Send some application data to the remote system.
0954: * <p/>
0955: * The method will handle fragmentation internally.
0956: *
0957: * @param buf The buffer with the data.
0958: * @param offset The position in the buffer where the data is placed.
0959: * @param len The length of the data.
0960: * @throws IOException If something goes wrong during sending.
0961: */
0962: protected void writeData(byte[] buf, int offset, int len)
0963: throws IOException {
0964: if (this .failedWithError) {
0965: throw new IOException(TLS_ERROR_MESSAGE);
0966: }
0967: if (this .closed) {
0968: throw new IOException(
0969: "Sorry, connection has been closed, you cannot write more data");
0970: }
0971:
0972: /*
0973: * Protect against known IV attack!
0974: *
0975: * DO NOT REMOVE THIS LINE, EXCEPT YOU KNOW EXACTLY WHAT
0976: * YOU ARE DOING HERE.
0977: */
0978: rs.writeMessage(RL_APPLICATION_DATA, emptybuf, 0, 0);
0979:
0980: do {
0981: /*
0982: * We are only allowed to write fragments up to 2^14 bytes.
0983: */
0984: int toWrite = Math.min(len, 1 << 14);
0985:
0986: try {
0987: rs.writeMessage(RL_APPLICATION_DATA, buf, offset,
0988: toWrite);
0989: } catch (IOException e) {
0990: if (!closed) {
0991: this .failWithError(AL_fatal, AP_internal_error);
0992: }
0993: throw e;
0994: } catch (RuntimeException e) {
0995: if (!closed) {
0996: this .failWithError(AL_fatal, AP_internal_error);
0997: }
0998: throw e;
0999: }
1000:
1001: offset += toWrite;
1002: len -= toWrite;
1003: } while (len > 0);
1004:
1005: }
1006:
1007: /** @deprecated use 'getOutputStream' instead */
1008: public TlsOuputStream getTlsOuputStream() {
1009: return this .tlsOutputStream;
1010: }
1011:
1012: /**
1013: * @return An OutputStream which can be used to send data.
1014: */
1015: public OutputStream getOutputStream() {
1016: return this .tlsOutputStream;
1017: }
1018:
1019: /** @deprecated use 'getInputStream' instead */
1020: public TlsInputStream getTlsInputStream() {
1021: return this .tlsInputStream;
1022: }
1023:
1024: /**
1025: * @return An InputStream which can be used to read data.
1026: */
1027: public InputStream getInputStream() {
1028: return this .tlsInputStream;
1029: }
1030:
1031: /**
1032: * Terminate this connection whith an alert.
1033: * <p/>
1034: * Can be used for normal closure too.
1035: *
1036: * @param alertLevel The level of the alert, an be AL_fatal or AL_warning.
1037: * @param alertDescription The exact alert message.
1038: * @throws IOException If alert was fatal.
1039: */
1040: protected void failWithError(short alertLevel,
1041: short alertDescription) throws IOException {
1042: /*
1043: * Check if the connection is still open.
1044: */
1045: if (!closed) {
1046: /*
1047: * Prepare the message
1048: */
1049: byte[] error = new byte[2];
1050: error[0] = (byte) alertLevel;
1051: error[1] = (byte) alertDescription;
1052: this .closed = true;
1053:
1054: if (alertLevel == AL_fatal) {
1055: /*
1056: * This is a fatal message.
1057: */
1058: this .failedWithError = true;
1059: }
1060: rs.writeMessage(RL_ALERT, error, 0, 2);
1061: rs.close();
1062: if (alertLevel == AL_fatal) {
1063: throw new IOException(TLS_ERROR_MESSAGE);
1064: }
1065:
1066: } else {
1067: throw new IOException(TLS_ERROR_MESSAGE);
1068: }
1069:
1070: }
1071:
1072: /**
1073: * Closes this connection.
1074: *
1075: * @throws IOException If something goes wrong during closing.
1076: */
1077: public void close() throws IOException {
1078: if (!closed) {
1079: this .failWithError((short) 1, (short) 0);
1080: }
1081: }
1082:
1083: /**
1084: * Make sure the InputStream is now empty. Fail otherwise.
1085: *
1086: * @param is The InputStream to check.
1087: * @throws IOException If is is not empty.
1088: */
1089: protected void assertEmpty(ByteArrayInputStream is)
1090: throws IOException {
1091: if (is.available() > 0) {
1092: this .failWithError(AL_fatal, AP_decode_error);
1093: }
1094: }
1095:
1096: protected void flush() throws IOException {
1097: rs.flush();
1098: }
1099:
1100: }
|