2013-07-10 83 views
1

一段時間以來,我一直在拼命地嘗試在我的應用程序中實現TLS 1.1。這背後的原因是SocketType.Raw套接字的使用,所以沒有SslStream或其他更高級別的類可用於我。C#TLS 1.1實現

到目前爲止,我被TLS握手協議中的Finished消息卡住 - 在服務器響應中繼續接收bad_record_mac(20)。密碼套件是0x0005 - TLS_RSA_WITH_RC4_128_SHA。

這裏剛剛發生了一些示例代碼:

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 are added 
    rsa = (RSACryptoServiceProvider)certificates.First().PublicKey.Key; 

    private byte[] ClientKeyExchange() 
    { 
     pre_master_secret = new byte[48]; 
     (new Random()).NextBytes(pre_master_secret); 

     //version 0302 for 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[] A = 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); 

     for (int i = 0; i < md5_iterations; i++) 
     { 
      A = P_MD5(secret_1, A); 
      Buffer.BlockCopy(A, 0, tmp, i * 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 it have to start with A(1) ? 
     //A = P_SHA1(secret_2, A); 


     for (int i = 0; i < sha1_iterations; i++) 
     { 
      A = P_SHA1(secret_2, A); 
      Buffer.BlockCopy(A, 0, tmp, i * A.Length, A.Length); 
     } 
     Buffer.BlockCopy(tmp, 0, sha1_data, 0, sha1_data.Length); //output_size = sha1_data.Length 

     for (int i = 0; i < 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); 

     for (int i = 0; i < 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); 

     for (int i = 0; i < 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; 

     for (i = 0; i < 128; i++) 
     { 
      s[i] = (Byte)i; 
      k[i] = key[i % key.GetLength(0)]; 
     } 

     j = 0; 
     for (i = 0; i < 128; i++) 
     { 
      j = (j + s[i] + k[i]) % 128; 
      temp = s[i]; 
      s[i] = s[j]; 
      s[j] = temp; 
     } 

     i = j = 0; 
     for (int x = 0; x < bytes.GetLength(0); x++) 
     { 
      i = (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]; 
     } 
    } 

顯然,閱讀這樣一串代碼是不是花你的時間的最好辦法,所以我想補充一些問題,希望可以提供很多幫助:

HMAC_MD5和HMAC_SHA1 - 在.Net實現中,它們採用密鑰,它們用輸入字節[]來計算哈希。根據RFC4346: 首先,我們定義一個數據擴展功能,P_hash(祕密,數據) 使用單個散列函數來擴大一個祕密和種子轉換爲輸出的 任意數量:

P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) + 
          HMAC_hash(secret, A(2) + seed) + 
          HMAC_hash(secret, A(3) + seed) + ... 

    Where + indicates concatenation. 

    A() is defined as: 

    A(0) = seed 
    A(i) = HMAC_hash(secret, A(i-1)) 

什麼是種子?它是我們計算哈希的數據嗎?祕密=鑰匙,到目前爲止我明白。另外,我們是否用A(1)啓動P_hash?

在此先感謝!

回答

0

種子是Client_random + Server_random。您需要保留握手的前幾步,client_hello和server_hello。

A(0) = seed 
A(i) = HMAC_hash(secret,A(i-1)) for i>0 

的A()函數的輸出由A的(1),A(2),A(3)...