当前位置: 首页 > 知识库问答 >
问题:

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

巩选
2023-03-14

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

我正在创建一个简单的客户机/服务器应用程序,它使用Curve25519进行密钥交换。客户端用C实现,带有mbedtls,服务器用实现。使用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,

服务器密钥生成(弹跳城堡代码)

// 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,

服务器共享密钥生成(弹跳城堡代码

// 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替代品。

共有2个答案

梁渊
2023-03-14

曲线25519以小端序表示键。X25519(带曲线25519的ECDH)以小端序表示共享秘密。这不同于密码学中使用的大多数标准格式,特别是SECP/NIST和Brainpool曲线上的密钥,以及ECDH与Weierstrass曲线的共享密钥,后者表示大端数中的数字。因此,将对<code>mbedtls_。

或者,使用mbedtls_ecp_point_write_binary导出公钥,并使用mbedtls_ecdh_calc_secret来计算共享密钥:它们负责使用每条曲线的正确字节序来格式化数字。

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

楚宏胜
2023-03-14

在.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-停止作恶'的回答中已经指出的,必须考虑小端序,即在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()替换。

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

 类似资料:
  • 问题内容: 我已经读过Clojure vs. Scala的各种论述,而我意识到两者都有自己的位置。关于将Clojure和Scala进行比较时,有一些注意事项尚未得到完整的解释: 1.)两种语言中哪一种通常 更快 ?我意识到这从一种语言功能到另一种语言功能都会有所不同,但是对性能进行总体评估会有所帮助。例如:我知道Python字典的速度非常快。但作为一个整体,它是一个 多 比Java慢语。我不想和C

  • OAuth 2.0提供了丰富的具有明确的安全性质的授权框架。然而,尽管在其自身看来是一个带有许多可选择组件的丰富且高度可扩展的框架,本规范有可能产生许多非可互操作的实现。 此外,本规范中留下一些必需组件部分或完全没有定义(例如,客户端注册、授权服务器性能、端点发现等)。没有这些组件,客户端必须针对特定的授权服务器和资源服务器被手动并专门配置,以进行互操作。 本框架设计具有一个明确的期望,即未来工作

  • 我对这个项目很感兴趣,想了解更多关于RedisGgraph内部的信息,并寻找Redis-Streams和Redis-Ggraph模块之间互操作性的可行性。 因此,我想知道您在Redis的哪些本地数据结构中构建了您自己的“图形数据”数据结构,当我们运行TYPE myGgraph命令时,该数据结构会显示出来。 是RedisGgraph模块(或其子组件、节点、边、路径)内部的图,是建立在Redis的已知

  • 问题内容: 我正在与一个以Java中的图形渲染器为目标的小组合作。我试图弄清楚以 java.awt.Graphics2D 为目标是否会向前兼容。即,其他库(例如JavaFX)是否可以显示 Graphics2D 实例的渲染? java.awt.Graphics2D 和 javafx.scene.canvas.GraphicsContext 之间是否存在互操作性? 或者,如果不是直接在 Graphic

  • 问题内容: 我有一个Go函数,用于包装来自的函数。 这是完整的 C原型 : 可以在这里找到(至少在我的系统上)。 遵循Go代码: 除非删除对and 子句的调用,否则不会编译此代码。 该方法只能在 Mac OS X上使用, 并且如果从代码中删除, 则实际上可以 使用。 在收到以下错误消息后,以其实际形式显示:( 仅此而已,这就是整个编译器的输出)。 删除对我来说是不可接受的,我必须找到如何正确释放分

  • 本文向大家介绍微波访问的全球互操作性(WiMAX),包括了微波访问的全球互操作性(WiMAX)的使用技巧和注意事项,需要的朋友参考一下 全球微波访问互操作性(WiMAX)是一种基于IEEE 802.16标准的无线微波技术。它最初由WiMAX论坛于2001年发布,现在称为固定WiMAX版本。它与Wi-Fi相似,尽管即使在30英里的距离内也可提供高达1 Gbps的更高数据速率。 WiMAX论坛将WiM