如何解密radius peap协议客户端完成握手消息
how to decrypt radius peap protocol client finish handshake message
我正在为 radius 服务器使用 TLS_RSA_WITH_3DES_EDE_CBC_SHA 密码套件,在客户端的 ChangeCipherSpec 之后立即收到加密的握手消息(40 字节),我曾尝试使用 3des 和 cbc 模式来解密这些字节,但有一个例外(坏数据),试图在 https://www.rfc-editor.org/rfc/rfc2246 上查找 peap tls v1.0 但是,没有找到很多关于完成握手 encryption/decryption 的详细信息。任何帮助都会很棒,非常感谢!!
这是我用来计算主密钥和密钥材料的代码。
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();
}
包含Finished握手消息的记录的authentication/encryption和decryption/verification与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) 对结果进行加密,其中第一个记录(Finished 是) 来自上面的密钥推导步骤,后续记录来自前一个记录的最后一个块。 (后者是 Rogaway 最先报告并被 BEAST 利用的漏洞。)
解密和验证以明显的方式逆转了这个过程。注意 rfc2246 没有指定接收方必须检查所有填充字节,但这显然是有意的; rfc4346 (1.1) 确实指定了它,但没有更改内容。 (rfc4346 确实 更改 IV 处理以修复 Rogaway 缺陷。SSLv3 指定了随机填充,但最终长度字节除外,因此它 不能 检查;这是 POODLE 缺陷。)
一些 libraries/APIs 为任意数据的分组密码(包括 3DES)提供 CBC 模式,默认为 PKCS5/7 填充。 TLS 使用的填充类似于 PKCS5/7 填充,但不兼容,因此使用这些库时,您可能必须按照 6.2.3.2 中的说明或大约十几个开源 TLS 中的任何代码自己处理填充和取消填充实现。
我正在为 radius 服务器使用 TLS_RSA_WITH_3DES_EDE_CBC_SHA 密码套件,在客户端的 ChangeCipherSpec 之后立即收到加密的握手消息(40 字节),我曾尝试使用 3des 和 cbc 模式来解密这些字节,但有一个例外(坏数据),试图在 https://www.rfc-editor.org/rfc/rfc2246 上查找 peap tls v1.0 但是,没有找到很多关于完成握手 encryption/decryption 的详细信息。任何帮助都会很棒,非常感谢!!
这是我用来计算主密钥和密钥材料的代码。
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();
}
包含Finished握手消息的记录的authentication/encryption和decryption/verification与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) 对结果进行加密,其中第一个记录(Finished 是) 来自上面的密钥推导步骤,后续记录来自前一个记录的最后一个块。 (后者是 Rogaway 最先报告并被 BEAST 利用的漏洞。)
解密和验证以明显的方式逆转了这个过程。注意 rfc2246 没有指定接收方必须检查所有填充字节,但这显然是有意的; rfc4346 (1.1) 确实指定了它,但没有更改内容。 (rfc4346 确实 更改 IV 处理以修复 Rogaway 缺陷。SSLv3 指定了随机填充,但最终长度字节除外,因此它 不能 检查;这是 POODLE 缺陷。)
一些 libraries/APIs 为任意数据的分组密码(包括 3DES)提供 CBC 模式,默认为 PKCS5/7 填充。 TLS 使用的填充类似于 PKCS5/7 填充,但不兼容,因此使用这些库时,您可能必须按照 6.2.3.2 中的说明或大约十几个开源 TLS 中的任何代码自己处理填充和取消填充实现。