You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#服务器与Python客户端Diffie-Hellman密钥交换报错求助

问题根源:算法不兼容导致公钥格式不匹配

你在C#中使用的ECDiffieHellmanCng是椭圆曲线Diffie-Hellman(ECDH)实现,而Python端的pyDH库提供的是传统的模素数Diffie-Hellman(DH)——这两种算法的公钥结构、交换逻辑完全不同:

  • ECDH的公钥包含椭圆曲线标识符、X/Y坐标等信息,C#的EccPublicBlob格式是专门为EC公钥设计的;
  • pyDH发送的只是一个大整数(即g^x mod p,传统DH的公钥),完全不符合EccPublicBlob的格式要求,自然会触发CryptographicException。

下面提供两种解决思路,你可以根据需求选择:


方案一:Python切换到ECDH,和C#的ECDiffieHellmanCng匹配

如果想要更安全、高效的椭圆曲线算法,推荐Python改用cryptography库实现ECDH,生成C#能直接识别的EccPublicBlob格式公钥:

Python代码(使用cryptography库)

from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import serialization
import base64

# 选择和C# ECDiffieHellmanCng默认一致的曲线(nistP256/SECP256R1)
private_key = ec.generate_private_key(ec.SECP256R1())
public_key = private_key.public_key()
public_numbers = public_key.public_numbers()

# 将X/Y坐标转换为32字节的大端字节数组(SECP256R1坐标长度为256位)
x_bytes = public_numbers.x.to_bytes(32, byteorder='big')
y_bytes = public_numbers.y.to_bytes(32, byteorder='big')

# 构造C#兼容的EccPublicBlob格式:
# 0x45 = 公钥标识 | 0x0000000E = nistP256曲线ID | X坐标 | Y坐标
ecc_blob = b'\x45' + b'\x0E\x00\x00\x00' + x_bytes + y_bytes

# 转换为base64字符串发送给C#(避免字节传输的编码问题)
pubkey_str = base64.b64encode(ecc_blob).decode('utf-8')

对应C#代码

using System.Security.Cryptography;
using System.Text;

// 接收Python发送的base64格式公钥
string receivedPubKey = "...";
byte[] eccBlob = Convert.FromBase64String(receivedPubKey);

using (ECDiffieHellmanCng dh = new ECDiffieHellmanCng())
{
    dh.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
    dh.HashAlgorithm = CngAlgorithm.Sha256;
    
    // 导入EC公钥Blob,此时不会再报错
    CngKey foreignKey = CngKey.Import(eccBlob, CngKeyBlobFormat.EccPublicBlob);
    byte[] sharedKey = dh.DeriveKeyMaterial(foreignKey);
    
    // 后续用sharedKey作为AES密钥(建议再做一次哈希确保长度符合AES要求)
}

方案二:C#改用传统Diffie-Hellman,适配pyDH

如果不想修改Python的现有代码,可以让C#切换到传统DH实现,匹配pyDH的参数(pyDH默认使用RFC3526的2048位素数,生成元g=2):

C#代码

using System.Security.Cryptography;
using System.Numerics;

// 接收pyDH发送的公钥字符串
string receivedPubKeyStr = "...";
BigInteger pubKeyInt = BigInteger.Parse(receivedPubKeyStr);
// 转换为大端字节数组(pyDH的公钥是大端存储的整数)
byte[] pubKeyBytes = pubKeyInt.ToByteArray(isBigEndian: true);

// pyDH默认的2048位素数(十六进制)
byte[] pBytes = Convert.FromHexString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF");
// 生成元g=2
byte[] gBytes = new byte[] { 0x02 };

using (DiffieHellman dh = DiffieHellman.Create())
{
    dh.KeySize = 2048;
    // 配置pyDH对应的素数和生成元
    dh.ImportParameters(new DiffieHellmanParameters
    {
        P = pBytes,
        G = gBytes,
        Q = null // pyDH不使用Q参数,设为null即可
    });

    // 导入客户端的公钥(RawPublicKeyBlob格式直接匹配大整数字节数组)
    DiffieHellmanPublicKey foreignPubKey = dh.ImportPublicKey(pubKeyBytes, DiffieHellmanKeyBlobFormat.RawPublicKey);
    byte[] sharedKey = dh.DeriveKeyMaterial(foreignPubKey);
    
    // 后续处理AES密钥逻辑不变
}

内容的提问来源于stack exchange,提问作者Jan Bellenberg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 20:12:57