1

我正在为radius服务器使用TLS_RSA_WITH_3DES_EDE_CBC_SHA密码套件,在从客户端ChangeCipherSpec之后立即收到加密的握手消息(40字节),我曾尝试使用3des和cbc模式来解密这些字节,但有一个例外(坏数据),试图在https://www.rfc-editor.org/rfc/rfc2246上查找 peap tls v1.0但是,没有找到很多关于完成握手加密/解密的详细信息。任何帮助都会很棒,非常感谢!!

这是我用来计算主密钥和密钥材料的代码。

    public static byte[] ComputeMasterSecret(byte[] pre_master_secret, byte[] client_random, byte[] server_random)
    {
        byte[] label = Encoding.ASCII.GetBytes("master secret");

        var seed = new List<byte>();
        seed.AddRange(client_random);
        seed.AddRange(server_random);

        var master_secret = PRF(pre_master_secret, label, seed.ToArray(), 48);

        return master_secret;
    }

    public static KeyMaterial ComputeKeys(byte[] master_secret, byte[] client_random, byte[] server_random)
    {
        /*
         * The cipher spec which is defined in this document which requires
        the most material is 3DES_EDE_CBC_SHA: it requires 2 x 24 byte
        keys, 2 x 20 byte MAC secrets, and 2 x 8 byte IVs, for a total of
 
        104 bytes of key material.
        */

 
        byte[] label = Encoding.ASCII.GetBytes("key expansion");

        var seed = new List<byte>();
        seed.AddRange(client_random);
        seed.AddRange(server_random);

        byte[] key_material = PRF(master_secret, label, seed.ToArray(), 104);  //need 104 for TLS_RSA_WITH_3DES_EDE_CBC_SHA cipher suite


        var km = new KeyMaterial();
        int idx = 0;
        km.ClientWriteMACSecret = Utils.CopyArray(key_material, idx, 20);
        idx += 20;

        km.ServerWriteMACSecret = Utils.CopyArray(key_material, idx, 20);
        idx += 20;

        km.ClientWriteKey = Utils.CopyArray(key_material, idx, 24);
        idx += 24;

        km.ServerWriteKey = Utils.CopyArray(key_material, idx, 24);
        idx += 24;

        km.ClientWriteIV = Utils.CopyArray(key_material, idx, 8);
        idx += 8;

        km.ServerWriteIV = Utils.CopyArray(key_material, idx, 8);

        return km;
    }

    public static byte[] PRF(byte[] secret, byte[] label, byte[] seed, int outputLength)
    {
        List<byte> s1 = new List<byte>();
        List<byte> s2 = new List<byte>();

        int size = (int)Math.Ceiling((double)secret.Length / 2);
        
        for(int i=0;i < size; i++)
        {
            s1.Add(secret[i]);
            s2.Insert(0, secret[secret.Length - i - 1]);
        }

        var tbc = new List<byte>();
        tbc.AddRange(label);
        tbc.AddRange(seed);

        var md5Result = MD5Hash(s1.ToArray(), tbc.ToArray(), outputLength);

        var sha1Result = SHA1Hash(s2.ToArray(), tbc.ToArray(), outputLength);
        

        var result = new List<byte>();
        for (int i = 0; i < outputLength; i++)
            result.Add((byte)(md5Result[i] ^ sha1Result[i]));

        return result.ToArray();
    }

    /// <summary>
    /// 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))
    /// </summary>
    /// <param name="secret"></param>
    /// <param name="seed"></param>
    /// <param name="iterations"></param>
    /// <returns></returns>
    private static byte[] MD5Hash(byte[] secret, byte[] seed, int outputLength)
    {
        int iterations = (int)Math.Ceiling((double)outputLength / 16);

        HMACMD5 HMD5 = new HMACMD5(secret);

        var result = new List<byte>();
        byte[] A = null;
        for (int i = 0; i <= iterations; i++)
            if (A == null)
                A = seed;
            else
            {
                A = HMD5.ComputeHash(A);

                var tBuff = new List<byte>();
                tBuff.AddRange(A);
                tBuff.AddRange(seed);

                var tb = HMD5.ComputeHash(tBuff.ToArray());

                result.AddRange(tb);
            }

        return result.ToArray();
    }

    private static byte[] SHA1Hash(byte[] secret, byte[] seed, int outputLength)
    {
        int iterations = (int)Math.Ceiling((double)outputLength / 20);

        HMACSHA1 HSHA1 = new HMACSHA1(secret);
        var result = new List<byte>();
        byte[] A = null;

        for (int i = 0; i <= iterations; i++)
            if (A == null)
                A = seed;
            else
            {
                A = HSHA1.ComputeHash(A);

                var tBuff = new List<byte>();
                tBuff.AddRange(A);
                tBuff.AddRange(seed);

                var tb = HSHA1.ComputeHash(tBuff.ToArray());

                result.AddRange(tb);
            }

        return result.ToArray();
    }
4

1 回答 1

1

包含 Finished handshake 消息的记录的认证/加密和解密/验证与 SSL/TLS 中的所有其他记录相同,只是它是 CCS 之后的第一个。

首先(在握手期间)来自密钥交换的预主密钥用于派生主密钥和多个工作密钥和 IV,具体取决于套件。这会随着协议版本而有所不同;对于 TLS1.0,请参阅 rfc2246 第 8.1.1 节(对于普通 RSA)、6.3(对于所有密钥交换)和第 5 节。

使用 'GenericBlock' (CBC) 密码 - 这是 TLS1.0 和 1.1 中除 RC4 之外的唯一选项 - 使用 6.2.3.1 分段(此记录不需要) 6.2.3.2 可选压缩(现在通常不使用,因为像 CRIME 这样的攻击,无论如何对 EAP 流量都毫无用处)和 6.2.3.2。

具体来说,首先添加一个 HMAC(对于这个套件 HMAC-SHA1),然后填充,然后使用带有 IV 的数据密码(3DES-CBC)对结果进行加密,该 IV 对于第一条记录(已完成)来自密钥上面的推导步骤和后续记录来自上一条记录的最后一个块。(后者是 Rogaway 首次报告并被 BEAST 利用的漏洞。)

解密和验证以明显的方式逆转了这个过程。注意 rfc2246 没有指定接收者必须检查所有的填充字节,但这显然是有意的;rfc4346 (1.1) 确实指定了它,而不更改内容。(rfc4346确实更改了 IV 处理以修复 Rogaway 缺陷。SSLv3 指定了除了最终长度字节之外的随机填充,因此无法检查;这是 POODLE 缺陷。)

一些为任意数据的分组密码(包括 3DES)提供 CBC 模式的库/API 默认为 PKCS5/7 填充。填充 TLS 使用类似于但不兼容 PKCS5/7 填充,因此使用这些库您可能必须按照 6.2.3.2 中的说明自行处理填充和取消填充 - 或大约十几个开源 TLS 实现中的任何一个中的代码。

于 2017-12-27T04:59:49.157 回答