.net - C# TLS 1.1 Implementation -
for time i've been bit desperately trying implement tls 1.1 in application. reason behind usage of sockettype.raw sockets, no sslstream or other higher-level classes available me.
so far i'm stuck @ finished message in tls handshake protocol - keep on receiving bad_record_mac(20) in server response. cipher suite 0x0005 - tls_rsa_with_rc4_128_sha.
here's sample code on what's happening:
byte[] client_random, server_random = new byte[28]; byte[] pre_master_secret, master_secret; byte[] client_write_mac_secret, server_write_mac_secret, client_write_key, server_write_key; byte[] handshake_messages, verify_data; rsacryptoserviceprovider rsa; list<x509certificate2> certificates = new list<x509certificate2>(); //certificates added rsa = (rsacryptoserviceprovider)certificates.first().publickey.key; private byte[] clientkeyexchange() { pre_master_secret = new byte[48]; (new random()).nextbytes(pre_master_secret); //version 0302 tls 1.1 pre_master_secret[0] = 3; pre_master_secret[1] = 2; byte[] crypteddata = rsa.encrypt(pre_master_secret, false); //"1603020086" string tmp_string = "100000820080" + utils.bitconverter.tostring(crypteddata).replace("-", ""); addhandshakedata(utils.bitconverter.stringtobytearray(tmp_string)); tmp_string = "1603020086" + tmp_string + "140302000101" //cipher change spec + ""; return utils.bitconverter.stringtobytearray(tmp_string); } private void computemastersecret() { byte[] label = utils.bitconverter.stringtobytearray(utils.bitconverter.convertstringtohex("master secret", encoding.ascii)); byte[] seed = new byte[client_random.length + server_random.length]; buffer.blockcopy(client_random, 0, seed, 0, client_random.length); buffer.blockcopy(server_random, 0, seed, client_random.length, server_random.length); master_secret = prf(pre_master_secret, label, seed, 48); } private void computekeys() { byte[] label = utils.bitconverter.stringtobytearray(utils.bitconverter.convertstringtohex("key expansion", encoding.ascii)); byte[] seed = new byte[client_random.length + server_random.length]; buffer.blockcopy(client_random, 0, seed, 0, client_random.length); buffer.blockcopy(server_random, 0, seed, client_random.length, server_random.length); byte[] key_material = prf(master_secret, label, seed, 72); client_write_mac_secret = new byte[20]; buffer.blockcopy(key_material, 0, client_write_mac_secret, 0, 20); server_write_mac_secret = new byte[20]; buffer.blockcopy(key_material, 20, server_write_mac_secret, 0, 20); client_write_key = new byte[16]; buffer.blockcopy(key_material, 40, client_write_key, 0, 16); server_write_key = new byte[16]; buffer.blockcopy(key_material, 56, server_write_key, 0, 16); } private void computeverifydata() { byte[] label = utils.bitconverter.stringtobytearray(utils.bitconverter.convertstringtohex("client finished", encoding.ascii)); sha1 sha1 = sha1.create(); md5 md5 = md5.create(); md5.computehash(handshake_messages); sha1.computehash(handshake_messages); byte[] seed = new byte[md5.hashsize / 8 + sha1.hashsize / 8]; buffer.blockcopy(md5.hash, 0, seed, 0, md5.hashsize / 8); buffer.blockcopy(sha1.hash, 0, seed, md5.hashsize / 8, sha1.hashsize / 8); verify_data = prf(master_secret, label, seed, 12); } private byte[] prf(byte[] secret, byte[] label, byte[] seed, int output_size) { int md5_iterations = (int)math.ceiling((double)output_size / 16), sha1_iterations = (int)math.ceiling((double)output_size / 20); byte[] md5_data = new byte[output_size], sha1_data = new byte[output_size]; //особое колдунство для нечетного числа byte[] secret_1 = new byte[(int)math.ceiling((double)secret.length / 2)], secret_2 = new byte[(int)math.ceiling((double)secret.length / 2)]; buffer.blockcopy(secret, 0, secret_1, 0, secret_1.length); buffer.blockcopy(secret, secret.length / 2, secret_2, 0, secret_2.length); byte[] = new byte[label.length + seed.length]; buffer.blockcopy(label, 0, a, 0, label.length); buffer.blockcopy(seed, 0, a, label.length, seed.length); byte[] tmp = new byte[md5_iterations * 16]; //a(1) ? //a = p_md5(secret_1, a); (int = 0; < md5_iterations; i++) { = p_md5(secret_1, a); buffer.blockcopy(a, 0, tmp, * a.length, a.length); } buffer.blockcopy(tmp, 0, md5_data, 0, md5_data.length); //output_size = md5_data.length tmp = new byte[sha1_iterations * 20]; //does have start a(1) ? //a = p_sha1(secret_2, a); (int = 0; < sha1_iterations; i++) { = p_sha1(secret_2, a); buffer.blockcopy(a, 0, tmp, * a.length, a.length); } buffer.blockcopy(tmp, 0, sha1_data, 0, sha1_data.length); //output_size = sha1_data.length (int = 0; < output_size; i++) md5_data[i] = (byte)(md5_data[i] ^ sha1_data[i]); return md5_data; } private byte[] p_md5(byte[] secret /*это ключ?*/, byte[] seed /*это дата?*/) { hmacmd5 hmd5 = new hmacmd5(secret); hmd5.computehash(seed); return hmd5.hash; } private byte[] p_sha1(byte[] secret /*это ключ?*/, byte[] seed /*это дата?*/) { hmacsha1 hsha1 = new hmacsha1(secret); hsha1.computehash(seed); return hsha1.hash; } private byte[] mac(byte[] secret, byte[] data) { byte[] secret_64 = new byte[64]; buffer.blockcopy(secret, 0, secret_64, 0, secret.length); (int = 0; < 64; i++) secret_64[i] = (byte)(secret_64[i] ^ (byte)54); byte[] xor_output_data = new byte[64 + data.length]; buffer.blockcopy(secret_64, 0, xor_output_data, 0, 64); buffer.blockcopy(data, 0, xor_output_data, 64, data.length); sha1 sha1 = sha1.create(); sha1.computehash(xor_output_data); secret_64 = new byte[64]; buffer.blockcopy(secret, 0, secret_64, 0, secret.length); (int = 0; < 64; i++) secret_64[i] = (byte)(secret_64[i] ^ (byte)92); xor_output_data = new byte[64 + sha1.hashsize / 8]; buffer.blockcopy(secret_64, 0, xor_output_data, 0, 64); buffer.blockcopy(sha1.hash, 0, xor_output_data, 64, sha1.hashsize / 8); sha1.computehash(xor_output_data); return sha1.hash; } public void rc4(ref byte[] bytes, byte[] key) { byte[] s = new byte[128]; byte[] k = new byte[128]; byte temp; int i, j; (i = 0; < 128; i++) { s[i] = (byte)i; k[i] = key[i % key.getlength(0)]; } j = 0; (i = 0; < 128; i++) { j = (j + s[i] + k[i]) % 128; temp = s[i]; s[i] = s[j]; s[j] = temp; } = j = 0; (int x = 0; x < bytes.getlength(0); x++) { = (i + 1) % 128; j = (j + s[i]) % 128; temp = s[i]; s[i] = s[j]; s[j] = temp; int t = (s[i] + s[j]) % 128; bytes[x] ^= s[t]; } }
obviously, reading such bunch of code ain't being best way spend time, i'd add questions that, hopefully, can lot:
hmac_md5 , hmac_sha1 - in .net implementation take key , take input byte[] compute hash from. according rfc4346: first, define data expansion function, p_hash(secret, data) uses single hash function expand secret , seed arbitrary quantity of output:
p_hash(secret, seed) = hmac_hash(secret, a(1) + seed) + hmac_hash(secret, a(2) + seed) + hmac_hash(secret, a(3) + seed) + ... + indicates concatenation. a() defined as: a(0) = seed a(i) = hmac_hash(secret, a(i-1))
what seed? data compute hash from? secret = key, far understand. also, start p_hash a(1)?
thanks in advance!
seed client_random + server_random. need preserve them preceding steps of handshake, client_hello , server_hello.
a(0) = seed a(i) = hmac_hash(secret,a(i-1)) i>0
the output of a() function consists of a(1), a(2), a(3)...
Comments
Post a Comment