c-MbedTLS和.NET BouncyCastle与Curve25519的互操作性问题



编辑:更改代码以提供更简单的测试用例

我正在创建一个简单的客户端/服务器应用程序,它使用Curve25519进行密钥交换。客户端是用mbedtls的C语言实现的,服务器是用实现的。NET与BouncyCastle的结合。

不幸的是,生成的共享机密在客户端和服务器上并不相同。下面显示了生成公钥/私钥的代码摘录(我硬编码了一些值以便于调试(。

客户端密钥生成(mbedtls代码,主要从https://github.com/ARMmbed/mbedtls/blob/development/programs/pkey/ecdh_curve25519.c和https://github.com/google/eddystone/blob/bb8738d7ddac0ddd3dfa70e594d011a0475e763d/implementations/mbed/source/EIDFrame.cpp#L144)

void generate_curve25519_keys() {
uint8_t my_pubkey[32] = { 0 };
uint8_t my_privkey[32] = { 0 };
mbedtls_ecdh_context ctx;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
// generate the keys and save to buffer
mbedtls_ctr_drbg_init(&ctr_drbg);
mbedtls_entropy_init(&entropy);
mbedtls_ecdh_init(&ctx);
mbedtls_ctr_drbg_seed(
&ctr_drbg, 
mbedtls_entropy_func, 
&entropy, 
0, 
0
);

mbedtls_ecp_group_load(&ctx.grp, MBEDTLS_ECP_DP_CURVE25519);        
mbedtls_ecdh_gen_public(
&ctx.grp, 
&ctx.d, 
&ctx.Q,
mbedtls_ctr_drbg_random, 
&ctr_drbg
);
mbedtls_mpi_write_binary(&ctx.Q.X, my_pubkey, sizeof(my_pubkey));   
printf("Pub: ");
for (size_t i = 0; i < sizeof(my_pubkey); i++)
printf("0x%02X, ", my_pubkey[i]);
printf("n");

mbedtls_mpi_write_binary(&ctx.d, my_privkey, sizeof(my_privkey));
printf("Priv: ");
for (size_t i = 0; i < sizeof(my_privkey); i++)
printf("0x%02X, ", my_privkey[i]);
printf("n");
}

执行的输出为:

Pub: 0x36, 0x4B, 0x8E, 0x89, 0x31, 0x18, 0xA4, 0x32, 0xE3, 0x5B, 0xB1, 0x70, 0x69, 0x55, 0xFE, 0x42, 0x8C, 0x48, 0x8C, 0xC9, 0x0E, 0x2C, 0xA2, 0x1A, 0x66, 0x6A, 0x26, 0x7B, 0xD0, 0xDA, 0x88, 0x5C,
Priv: 0x6E, 0xCF, 0x6C, 0xBD, 0x9C, 0xDE, 0xDC, 0xBF, 0xD3, 0xB3, 0x82, 0x9A, 0x7D, 0xA7, 0x27, 0x50, 0xA2, 0xA0, 0x47, 0x64, 0x14, 0xC7, 0xD8, 0x90, 0xFC, 0xCD, 0x11, 0xC3, 0x5C, 0x37, 0xFB, 0xB0,

服务器密钥生成(BouncyCastle代码(

// generate public and private key
let keyGenerator = new X25519KeyPairGenerator()
keyGenerator.Init(new X25519KeyGenerationParameters(new SecureRandom()))
let keys = keyGenerator.GenerateKeyPair()
let publicKey = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keys.Public)
let x25519PublicKey = new X25519PublicKeyParameters(publicKey.GetEncoded(), 0)
Console.WriteLine("PUB: {{0x{0}}}", BitConverter.ToString(x25519PublicKey.GetEncoded()).Replace("-", ", 0x"))
let privateKey = ECPrivateKeyStructure.GetInstance(PrivateKeyInfoFactory.CreatePrivateKeyInfo(keys.Private))
let x25519PrivateKey = new X25519PrivateKeyParameters(privateKey.GetEncoded(), 0)
Console.WriteLine("PRIV: {{0x{0}}}", BitConverter.ToString(x25519PrivateKey.GetEncoded()).Replace("-", ", 0x"))

执行的输出为:

PUB: {0x30, 0x2A, 0x30, 0x05, 0x06, 0x03, 0x2B, 0x65, 0x6E, 0x03, 0x21, 0x00, 0xD2, 0x86, 0xF9, 0x67, 0xAB, 0xF8, 0x8C, 0x8C, 0x4E, 0xC9, 0xF9, 0xFC, 0x29, 0xE2, 0xC2, 0xD2, 0x3B, 0x8E, 0x1E, 0x3D}
PRIV: {0x30, 0x51, 0x02, 0x01, 0x01, 0x30, 0x05, 0x06, 0x03, 0x2B, 0x65, 0x6E, 0x04, 0x22, 0x04, 0x20, 0x78, 0xF3, 0xC9, 0xBE, 0xB5, 0x74, 0x5A, 0x63, 0x99, 0x5C, 0xCB, 0x82, 0xD7, 0x0C, 0xBC, 0x37}

有了这些信息,我继续生成具有以下代码的共享密钥:

客户端共享密钥生成(mbedtls代码(

void generate_curve25519_shared_secret() {
uint8_t my_privkey[] =      { 0x6E, 0xCF, 0x6C, 0xBD, 0x9C, 0xDE, 0xDC, 0xBF, 0xD3, 0xB3, 0x82, 0x9A, 0x7D, 0xA7, 0x27, 0x50, 0xA2, 0xA0, 0x47, 0x64, 0x14, 0xC7, 0xD8, 0x90, 0xFC, 0xCD, 0x11, 0xC3, 0x5C, 0x37, 0xFB, 0xB0 };
uint8_t server_pubkey[] =   { 0x30, 0x2A, 0x30, 0x05, 0x06, 0x03, 0x2B, 0x65, 0x6E, 0x03, 0x21, 0x00, 0xD2, 0x86, 0xF9, 0x67, 0xAB, 0xF8, 0x8C, 0x8C, 0x4E, 0xC9, 0xF9, 0xFC, 0x29, 0xE2, 0xC2, 0xD2, 0x3B, 0x8E, 0x1E, 0x3D };
uint8_t shared_secret[32] = { 0 };
mbedtls_ecdh_context ctx;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
// generate the keys and save to buffer
mbedtls_ctr_drbg_init(&ctr_drbg);
mbedtls_entropy_init(&entropy);
mbedtls_ecdh_init(&ctx);
mbedtls_ctr_drbg_seed(
&ctr_drbg,
mbedtls_entropy_func,
&entropy,
0,
0
);
mbedtls_ecp_group_load(&ctx.grp, MBEDTLS_ECP_DP_CURVE25519);    

// read my private key
mbedtls_mpi_read_binary(&ctx.d, my_privkey, sizeof(my_privkey));
mbedtls_mpi_lset(&ctx.Qp.Z, 1);

// read server key
mbedtls_mpi_read_binary(&ctx.Qp.X, server_pubkey, sizeof(server_pubkey));
// generate shared secret
size_t olen;
mbedtls_ecdh_calc_secret(
&ctx,
&olen,
shared_secret,
32,
0,
0
);
printf("Secret :");
for (size_t i = 0; i < sizeof(shared_secret); i++)
printf("0x%02X, ", shared_secret[i]);
printf("n");
}

客户端上执行的输出为:

Secret :0x3D, 0xF3, 0xD3, 0x88, 0xAB, 0xD7, 0x31, 0xA4, 0x1E, 0x52, 0xFB, 0x9A, 0x28, 0x82, 0xBF, 0x9C, 0xA9, 0x45, 0xB0, 0x6C, 0xC7, 0xD7, 0x20, 0xAC, 0x7E, 0xCB, 0x51, 0x50, 0x84, 0x2C, 0x25, 0x57,

服务器共享密钥生成(BouncyCastle代码(

// compute shared secret
let rawAgentPubKey = [|0x36uy; 0x4Buy; 0x8Euy; 0x89uy; 0x31uy; 0x18uy; 0xA4uy; 0x32uy; 0xE3uy; 0x5Buy; 0xB1uy; 0x70uy; 0x69uy; 0x55uy; 0xFEuy; 0x42uy; 0x8Cuy; 0x48uy; 0x8Cuy; 0xC9uy; 0x0Euy; 0x2Cuy; 0xA2uy; 0x1Auy; 0x66uy; 0x6Auy; 0x26uy; 0x7Buy; 0xD0uy; 0xDAuy; 0x88uy; 0x5Cuy|]
let rawPrivKey =     [|0x30uy; 0x51uy; 0x02uy; 0x01uy; 0x01uy; 0x30uy; 0x05uy; 0x06uy; 0x03uy; 0x2Buy; 0x65uy; 0x6Euy; 0x04uy; 0x22uy; 0x04uy; 0x20uy; 0x78uy; 0xF3uy; 0xC9uy; 0xBEuy; 0xB5uy; 0x74uy; 0x5Auy; 0x63uy; 0x99uy; 0x5Cuy; 0xCBuy; 0x82uy; 0xD7uy; 0x0Cuy; 0xBCuy; 0x37uy|]
let agentPubKey = new X25519PublicKeyParameters(rawAgentPubKey, 0)
let secret = Array.zeroCreate<Byte>(32)        
let privateKey = new X25519PrivateKeyParameters(rawPrivKey, 0)
privateKey.GenerateSecret(agentPubKey, secret, 0)
Console.WriteLine("SECRET: {{0x{0}}}", BitConverter.ToString(secret).Replace("-", ", 0x"))

在服务器上执行的输出是:

SECRET: {0xE2, 0x2B, 0xC6, 0x3A, 0xA0, 0x75, 0x83, 0x60, 0xB8, 0xE1, 0x47, 0xD6, 0x66, 0x24, 0x14, 0xC2, 0x99, 0x51, 0x05, 0x3C, 0xDC, 0x96, 0x2B, 0xC4, 0xE2, 0x10, 0x7C, 0x77, 0xC0, 0xA2, 0xD1, 0x77}

生成的两个秘密明显不同。通过阅读各种示例,这可能是由于不同的字节顺序编码。我试着使用mbedtls_mpi_read_binary_le

mbedtls_mpi_write_binary_le作为替代解决方案,我可以更改。NET库,如果此更改可以解决问题,请移动到另一个库。不幸的是,这个时候我没能找到一个好的。NET替代方案。

在上。NET端,密钥对生成正确,但公钥和私钥派生错误。它们必须按照以下方式确定(在C#中(:

// generate public and private key
var keyGenerator = new X25519KeyPairGenerator();
keyGenerator.Init(new X25519KeyGenerationParameters(new SecureRandom()));
var keys = keyGenerator.GenerateKeyPair();
var publicKey = (X25519PublicKeyParameters)keys.Public;
Console.WriteLine("PUB: {{0x{0}}}", BitConverter.ToString(publicKey.GetEncoded()).Replace("-", ", 0x"));
var privateKey = (X25519PrivateKeyParameters)keys.Private;
Console.WriteLine("PRIV: {{0x{0}}}", BitConverter.ToString(privateKey.GetEncoded()).Replace("-", ", 0x"));

正如Gilles的回答"SO-stop be evil"中已经指出的那样,必须考虑小端序,即在generate_curve25519_keys()中的C/C++代码中,mbedtls_mpi_write_binary()必须被mbedtls_mpi_write_binary_le()取代。类似地,在generate_curve25519_shared_secret()中,两个mbedtls_mpi_read_binary()都必须被mbedtls_mpi_read_binary_le()替换。

通过这些更改,两个代码在我的机器上生成了相同的共享机密。

Curve25519以小端序表示键。X25519(带有Curve25519的ECDH(以小端顺序表示共享秘密。这与密码学中使用的大多数标准格式不同,特别是SECP/NIST和Brainpool曲线上的密钥,以及ECDH与Weierstrass曲线的共享秘密,Weierstras曲线表示大端数中的数字。因此,将两个对mbedtls_mpi_write_binary的调用都更改为mbedtls_mpi_write_binary_le

或者,使用mbedtls_ecp_point_write_binary导出公钥,mbedtls_ecdh_calc_secret计算共享密钥:它们负责为每条曲线设置具有正确endianness的数字格式。

我还没有证实这是唯一的问题。

相关内容

  • 没有找到相关文章

最新更新