ConnectionStateTLSpublic class ConnectionStateTLS extends org.apache.harmony.xnet.provider.jsse.ConnectionState This class incapsulates the operating environment of the TLS v1
(http://www.ietf.org/rfc/rfc2246.txt) Record Protocol and provides
relating encryption/decryption functionality.
The work functionality is based on the security
parameters negotiated during the handshake. |
Fields Summary |
---|
private static byte[] | KEY_EXPANSION_LABEL | private static byte[] | CLIENT_WRITE_KEY_LABEL | private static byte[] | SERVER_WRITE_KEY_LABEL | private static byte[] | IV_BLOCK_LABEL | private final Mac | encMac | private final Mac | decMac | private final byte[] | mac_material_header |
Constructors Summary |
---|
protected ConnectionStateTLS(org.apache.harmony.xnet.provider.jsse.SSLSessionImpl session)Creates the instance of TLS v1 Connection State. All of the
security parameters are provided by session object.
try {
CipherSuite cipherSuite = session.cipherSuite;
hash_size = cipherSuite.getMACLength();
boolean is_exportabe = cipherSuite.isExportable();
int key_size = (is_exportabe)
? cipherSuite.keyMaterial
: cipherSuite.expandedKeyMaterial;
int iv_size = cipherSuite.getBlockSize();
String algName = cipherSuite.getBulkEncryptionAlgorithm();
String macName = cipherSuite.getHmacName();
if (logger != null) {
logger.println("ConnectionStateTLS.create:");
logger.println(" cipher suite name: "
+ cipherSuite.getName());
logger.println(" encryption alg name: " + algName);
logger.println(" mac alg name: " + macName);
logger.println(" hash size: " + hash_size);
logger.println(" block size: " + iv_size);
logger.println(" IV size (== block size):" + iv_size);
logger.println(" key size: " + key_size);
}
byte[] clientRandom = session.clientRandom;
byte[] serverRandom = session.serverRandom;
// so we need PRF value of size of
// 2*hash_size + 2*key_size + 2*iv_size
byte[] key_block = new byte[2*hash_size + 2*key_size + 2*iv_size];
byte[] seed = new byte[clientRandom.length + serverRandom.length];
System.arraycopy(serverRandom, 0, seed, 0, serverRandom.length);
System.arraycopy(clientRandom, 0, seed, serverRandom.length,
clientRandom.length);
PRF.computePRF(key_block, session.master_secret,
KEY_EXPANSION_LABEL, seed);
byte[] client_mac_secret = new byte[hash_size];
byte[] server_mac_secret = new byte[hash_size];
byte[] client_key = new byte[key_size];
byte[] server_key = new byte[key_size];
boolean is_client = !session.isServer;
is_block_cipher = (iv_size > 0);
// do not count, as block_size is always 8
// block_size = iv_size;
System.arraycopy(key_block, 0, client_mac_secret, 0, hash_size);
System.arraycopy(key_block, hash_size,
server_mac_secret, 0, hash_size);
System.arraycopy(key_block, 2*hash_size, client_key, 0, key_size);
System.arraycopy(key_block, 2*hash_size+key_size,
server_key, 0, key_size);
IvParameterSpec clientIV = null;
IvParameterSpec serverIV = null;
if (is_exportabe) {
System.arraycopy(clientRandom, 0,
seed, 0, clientRandom.length);
System.arraycopy(serverRandom, 0,
seed, clientRandom.length, serverRandom.length);
byte[] final_client_key =
new byte[cipherSuite.expandedKeyMaterial];
byte[] final_server_key =
new byte[cipherSuite.expandedKeyMaterial];
PRF.computePRF(final_client_key, client_key,
CLIENT_WRITE_KEY_LABEL, seed);
PRF.computePRF(final_server_key, server_key,
SERVER_WRITE_KEY_LABEL, seed);
client_key = final_client_key;
server_key = final_server_key;
if (is_block_cipher) {
byte[] iv_block = new byte[2*iv_size];
PRF.computePRF(iv_block, null, IV_BLOCK_LABEL, seed);
clientIV = new IvParameterSpec(iv_block, 0, iv_size);
serverIV = new IvParameterSpec(iv_block, iv_size, iv_size);
}
} else if (is_block_cipher) {
clientIV = new IvParameterSpec(key_block,
2*(hash_size+key_size), iv_size);
serverIV = new IvParameterSpec(key_block,
2*(hash_size+key_size)+iv_size, iv_size);
}
if (logger != null) {
logger.println("is exportable: "+is_exportabe);
logger.println("master_secret");
logger.print(session.master_secret);
logger.println("client_random");
logger.print(clientRandom);
logger.println("server_random");
logger.print(serverRandom);
//logger.println("key_block");
//logger.print(key_block);
logger.println("client_mac_secret");
logger.print(client_mac_secret);
logger.println("server_mac_secret");
logger.print(server_mac_secret);
logger.println("client_key");
logger.print(client_key);
logger.println("server_key");
logger.print(server_key);
if (clientIV == null) {
logger.println("no IV.");
} else {
logger.println("client_iv");
logger.print(clientIV.getIV());
logger.println("server_iv");
logger.print(serverIV.getIV());
}
}
encCipher = Cipher.getInstance(algName);
decCipher = Cipher.getInstance(algName);
encMac = Mac.getInstance(macName);
decMac = Mac.getInstance(macName);
if (is_client) { // client side
encCipher.init(Cipher.ENCRYPT_MODE,
new SecretKeySpec(client_key, algName), clientIV);
decCipher.init(Cipher.DECRYPT_MODE,
new SecretKeySpec(server_key, algName), serverIV);
encMac.init(new SecretKeySpec(client_mac_secret, macName));
decMac.init(new SecretKeySpec(server_mac_secret, macName));
} else { // server side
encCipher.init(Cipher.ENCRYPT_MODE,
new SecretKeySpec(server_key, algName), serverIV);
decCipher.init(Cipher.DECRYPT_MODE,
new SecretKeySpec(client_key, algName), clientIV);
encMac.init(new SecretKeySpec(server_mac_secret, macName));
decMac.init(new SecretKeySpec(client_mac_secret, macName));
}
} catch (Exception e) {
e.printStackTrace();
throw new AlertException(AlertProtocol.INTERNAL_ERROR,
new SSLProtocolException(
"Error during computation of security parameters"));
}
|
Methods Summary |
---|
protected byte[] | decrypt(byte type, byte[] fragment, int offset, int len)Retrieves the fragment of the Plaintext structure of
the specified type from the provided data representing
the Generic[Stream|Block]Cipher structure.
// plain data of the Generic[Stream|Block]Cipher structure
byte[] data = decCipher.update(fragment, offset, len);
// the 'content' part of the structure
byte[] content;
if (is_block_cipher) {
// check padding
int padding_length = data[data.length-1];
for (int i=0; i<padding_length; i++) {
if (data[data.length-2-i] != padding_length) {
throw new AlertException(
AlertProtocol.DECRYPTION_FAILED,
new SSLProtocolException(
"Received message has bad padding"));
}
}
content = new byte[data.length - hash_size - padding_length - 1];
} else {
content = new byte[data.length - hash_size];
}
mac_material_header[0] = type;
mac_material_header[3] = (byte) ((0x00FF00 & content.length) >> 8);
mac_material_header[4] = (byte) (0x0000FF & content.length);
decMac.update(read_seq_num);
decMac.update(mac_material_header);
decMac.update(data, 0, content.length); // mac.update(fragment);
byte[] mac_value = decMac.doFinal();
if (logger != null) {
logger.println("Decrypted:");
logger.print(data);
//logger.println("MAC Material:");
//logger.print(read_seq_num);
//logger.print(mac_material_header);
//logger.print(data, 0, content.length);
logger.println("Expected mac value:");
logger.print(mac_value);
}
// checking the mac value
for (int i=0; i<hash_size; i++) {
if (mac_value[i] != data[i+content.length]) {
throw new AlertException(AlertProtocol.BAD_RECORD_MAC,
new SSLProtocolException("Bad record MAC"));
}
}
System.arraycopy(data, 0, content, 0, content.length);
incSequenceNumber(read_seq_num);
return content;
| protected byte[] | encrypt(byte type, byte[] fragment, int offset, int len)Creates the GenericStreamCipher or GenericBlockCipher
data structure for specified data of specified type.
try {
int content_mac_length = len + hash_size;
int padding_length = is_block_cipher
? ((8 - (++content_mac_length & 0x07)) & 0x07)
: 0;
byte[] res = new byte[content_mac_length + padding_length];
System.arraycopy(fragment, offset, res, 0, len);
mac_material_header[0] = type;
mac_material_header[3] = (byte) ((0x00FF00 & len) >> 8);
mac_material_header[4] = (byte) (0x0000FF & len);
encMac.update(write_seq_num);
encMac.update(mac_material_header);
encMac.update(fragment, offset, len);
encMac.doFinal(res, len);
//if (logger != null) {
// logger.println("MAC Material:");
// logger.print(write_seq_num);
// logger.print(mac_material_header);
// logger.print(fragment, offset, len);
//}
if (is_block_cipher) {
// do padding:
Arrays.fill(res, content_mac_length-1,
res.length, (byte) (padding_length));
}
if (logger != null) {
logger.println("SSLRecordProtocol.do_encryption: Generic"
+ (is_block_cipher
? "BlockCipher with padding["+padding_length+"]:"
: "StreamCipher:"));
logger.print(res);
}
byte[] rez = new byte[encCipher.getOutputSize(res.length)];
// We should not call just doFinal because it reinitialize
// the cipher, but as says rfc 2246:
// "For stream ciphers that do not use a synchronization
// vector (such as RC4), the stream cipher state from the end
// of one record is simply used on the subsequent packet."
// and for block ciphers:
// "The IV for subsequent records is the last ciphertext block from
// the previous record."
// i.e. we should keep the cipher state.
encCipher.update(res, 0, res.length, rez);
incSequenceNumber(write_seq_num);
return rez;
} catch (GeneralSecurityException e) {
e.printStackTrace();
throw new AlertException(AlertProtocol.INTERNAL_ERROR,
new SSLProtocolException("Error during the encryption"));
}
|
|