Recordpublic class Record extends Object Implements an SSL record layer that sits atop a TCP connection
and beneath the user-visible interface to an SSL socket. It
maintains all the state information necessary to encode/decode
application data. |
Fields Summary |
---|
static final byte | CCSChange Cipher Spec (20). | static final byte | ALRTAlert (21). | static final byte | HNDSHKHandshake (22). | static final byte | APPApplication data (23). | static final byte | WARNINGWarning severity level for alerts (1). | static final byte | FATALFatal severity level for alerts (2). | static final byte | CLOSE_NTFYClose notification alert type (0). | static final byte | UNEXP_MSGUnexpected message alert type (10). | static final byte | BAD_MACBad MAC alert type (20). | static final byte | HNDSHK_FAILHandshake failure alert type (40). | static final byte | NO_CERTNo certificate found alert type (41). | static final byte | BAD_CERTBad certificate alert type (42). | static final byte | UNSUP_CERTUnsupported certificate alert type (43). | static final byte | CERT_REVKDCertificate revoked alert type (44). | static final byte | CERT_EXPRDCertificate expired alaert type (45). | static final byte | CERT_UNKWNUnknown certificate feature alert type (46). | static final byte | BAD_PARAMBad parameter alert type (47). | static final byte | SERVERServer role for SSL record layout (0). | static final byte | CLIENTClient role for SSL record layout (1). | private final int | HEADER_SIZESize of record header | private InputStream | inUnderlying input stream beneath the record layer. | private OutputStream | outUnderlying output stream beneath the record layer. | private byte | rActiveFlag indicating change cipher spec received. | private byte | wActiveFlag indicating change cipher spec has been sent. | private byte | verThe SSL version in one byte (0x30=3.0). | private byte[] | inputHeaderCurrent input record header. | private int | headerBytesReadHow many bytes of the record header have been read. | private int | dataLengthHow many bytes of the data in the record. | private int | dataBytesReadHow many bytes of the data in the record have been read. | private boolean | shutdownShutdown flag, true if connection has been shutdown. | byte[] | inputDataCurrent input record data. | int | plainTextLengthLength of the plain text in the input buffer | private RecordEncoder | encoderRecords encoder | private RecordDecoder | decoderRecords decoder |
Constructors Summary |
---|
Record(InputStream ins, OutputStream outs)Creates a new SSL record layer.
in = ins;
out = outs;
ver = (byte) 0x30; // IMPL_NOTE: This is hardcoded for now
|
Methods Summary |
---|
public void | alert(byte level, byte type)Sends an alert message of the specified level and type to the SSL peer.
byte[] tmp = new byte[2];
tmp[0] = level;
tmp[1] = type;
try {
wrRec(ALRT, tmp, 0, 2);
} catch (IOException e) {
// ignore, we do not want to step on the real error
}
| void | closeInputStream()Close input stream
try {
in.close();
} catch (IOException e) {
// ignore
}
| void | closeOutputStream()Close output stream
try {
out.close();
} catch (IOException e) {
// ignore
}
| void | init(byte role, byte[] clientRand, byte[] serverRand, byte suite, byte[] masterSecret)Chops up a master secret into the client and server MAC secrets,
bulk encryption keys and IVs. Also initializes the Cipher and
MessageDigest objects used in record encoding/decoding.
CipherSuiteData data = new CipherSuiteData(suite);
data.generateKeys(clientRand, serverRand, masterSecret);
// depending on role, we choose corresponding MAC secrets
// and cipher bulk keys for encoder and decoder
byte[] encodeSecret;
byte[] decodeSecret;
SecretKey decodeCipherKey;
SecretKey encodeCipherKey;
if (role == CLIENT) {
encodeSecret = data.getClientMACSecret();
decodeSecret = data.getServerMACSecret();
encodeCipherKey = data.getClientBulkKey();
decodeCipherKey = data.getServerBulkKey();
} else {
encodeSecret = data.getServerMACSecret();
decodeSecret = data.getClientMACSecret();
encodeCipherKey = data.getServerBulkKey();
decodeCipherKey = data.getClientBulkKey();
}
Cipher encodeCipher = data.getEncodeCipher();
encodeCipher.init(Cipher.ENCRYPT_MODE, encodeCipherKey);
Cipher decodeCipher = data.getDecodeCipher();
decodeCipher.init(Cipher.DECRYPT_MODE, decodeCipherKey);
encoder = new RecordEncoder(data.getEncodeDigest(), encodeSecret,
data.getPadLength(), encodeCipher);
decoder = new RecordDecoder(data.getDecodeDigest(), decodeSecret,
data.getPadLength(), decodeCipher);
| void | rdRec(boolean block, byte type)Reads and returns a record (including the 5-byte header) of
the specified type. If the caller asks for application data
and a close_notify warning alert is found as the next available
record, this method sets plainTextLength to -1 to signal the end of the
input stream.
if (!rdRec(block)) {
return;
}
if (inputHeader[0] == type) {
// success
return;
}
// Signal end of stream.
plainTextLength = -1;
switch (inputHeader[0]) {
case CCS:
// Can change_cipher_spec can be passed to handshake clients?
// if (type == HNDSHK) return r; // fall through otherwise
case HNDSHK:
case APP:
default:
alert(FATAL, UNEXP_MSG);
throw new IOException("Unexpected SSL record, type: " +
inputHeader[0]);
case ALRT:
// An Alert record needs to be atleast 2 bytes of data
if (inputData.length < 2) {
throw new IOException("Bad alert length");
}
if ((inputData[0] == WARNING) &&
(inputData[1] == CLOSE_NTFY) &&
(type == APP)) {
/*
* We got a close_notify warning
* Shutdown the connection.
*/
shutdownConnection();
return; // signal end of InputStream
}
if ((inputData[0] < WARNING) || (inputData[0] > FATAL)) {
throw new IOException("Bad alert level");
}
throw new IOException("Alert (" + inputData[0] +
"," + inputData[1] + ")");
}
| private boolean | rdRec(boolean block)Returns the next record read from the record layer (the 5-byte
SSL record header is included). Set plainTextLength to length of the
record or -1 for end of stream.
int b;
plainTextLength = 0;
if (!block && in.available() == 0) {
return false;
}
/*
* This method could have returned last time after reading the
* part of the record, in that case headerBytesRead will not be 0.
*/
if (headerBytesRead == 0) {
b = in.read(inputHeader, 0, 1);
if (b == -1) {
/*
* Peer closed SSL connection without close_notify
*/
plainTextLength = -1;
return false;
}
headerBytesRead = 1;
dataBytesRead = 0;
dataLength = 0;
}
while (headerBytesRead < inputHeader.length) {
if (!block && in.available() == 0) {
return false;
}
b = in.read(inputHeader, headerBytesRead,
inputHeader.length - headerBytesRead);
if (b == -1) {
throw new IOException("SSL connection ended abnormally " +
"while reading record header");
}
headerBytesRead += b;
}
/*
* This method could have returned last time after reading the
* header but not all of the data, in that case dataLength
* will not be 0.
*/
if (dataLength == 0) {
// Check record type and version
if ((inputHeader[0] < CCS) ||
(inputHeader[0] > APP) ||
(inputHeader[1] != (byte) (ver >>> 4)) ||
(inputHeader[2] != (byte) (ver & 0x0f))) {
alert(FATAL, UNEXP_MSG);
throw new IOException("Bad record type (" + inputHeader[0] +
") or version (" + inputHeader[1] + "." +
inputHeader[2] + ")");
}
dataLength = ((inputHeader[3] & 0xff) << 8) +
(inputHeader[4] & 0xff);
inputData = new byte[dataLength];
}
while (dataBytesRead < dataLength) {
if (!block && in.available() == 0) {
return false;
}
b = in.read(inputData, dataBytesRead, dataLength - dataBytesRead);
if (b == -1) {
throw new IOException("SSL connection ended abnormally " +
"after reading record byte " +
(dataBytesRead + headerBytesRead));
}
dataBytesRead += b;
}
if (rActive == 1) {
try {
plainTextLength = decoder.decode(inputHeader, inputData);
} catch (IOException e) {
if (e.getMessage().compareTo("Bad MAC") == 0) {
alert(FATAL, BAD_MAC);
} else {
throw e;
}
}
} else {
plainTextLength = dataBytesRead;
}
if (inputHeader[0] == CCS) {
rActive = 1;
}
// start with a new record header next time
headerBytesRead = 0;
return true;
| public void | shutdownConnection()Send a close notify and shutdown the TCP connection if needed.
if (shutdown) {
return;
}
alert(Record.WARNING, Record.CLOSE_NTFY);
shutdown = true;
closeOutputStream();
closeInputStream();
| void | wrRec(byte type, byte[] buf, int off, int len)Writes an SSL record to the underlying socket's output stream.
byte[] rec;
if (shutdown) {
throw new IOException("Server has shutdown the connection");
}
/*
* Create a new byte array with room for the header and
* fill the record header with type, version and length
*/
rec = new byte[len + 5];
rec[0] = type;
rec[1] = (byte) (ver >>> 4);
rec[2] = (byte) (ver & 0x0f);
rec[3] = (byte) (len >>> 8);
rec[4] = (byte) (len & 0xff);
// Fill the rest of the record
System.arraycopy(buf, off, rec, 5, len);
if (wActive == 1) {
out.write(encoder.encode(rec));
} else {
out.write(rec);
}
if (type == CCS) wActive = 1;
|
|