如何在TypeScript中复刻C#的ECDH密钥对生成及共享密钥计算代码?
TypeScript复刻C# ECDH密钥对生成与共享密钥计算功能问题
原C#代码功能
这段C#代码用于基于ECDH协议生成密钥对并计算共享密钥:输入64字节的productKey,输出32字节的共享密钥,同时返回本地生成的64字节公钥。
public static byte[] CreateECDHAddHockKey(byte[] productKey, ref byte[] publicKey) { //productKey is byte[64] //BCRYPT_ECDH_PUBLIC_P256_MAGIC = 0x314B4345, //var keyType = new byte[] { 0x45, 0x43, 0x53, 0x31 }; //var keyLength = new byte[] { 0x20, 0x00, 0x00, 0x00 }; byte[] PublicP256Header = new byte[] { 0x45, 0x43, 0x4B, 0x31, 0x20, 0x00, 0x00, 0x00 }; //8 bytes byte[] completeKey = new byte[productKey.Length + PublicP256Header.Length]; //8+64 = 72 bytes Array.Copy(PublicP256Header, completeKey, PublicP256Header.Length); Array.Copy(productKey, 0, completeKey, PublicP256Header.Length, productKey.Length); CngKey key = CngKey.Import(completeKey, CngKeyBlobFormat.EccPublicBlob); ECDiffieHellmanCng MyKey = new ECDiffieHellmanCng(256); MyKey.HashAlgorithm = CngAlgorithm.Sha256; MyKey.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash; var sharedSecret = MyKey.DeriveKeyMaterial(key); //32 bytes publicKey = MyKey.Key.Export(CngKeyBlobFormat.EccPublicBlob).Skip(8).ToArray(); //64 bytes return sharedSecret; }
当前问题
尝试用TypeScript的crypto模块复刻上述功能时,遇到格式错误和输出长度不符的问题,当前写出的代码如下:
import crypto, { KeyObject } from 'crypto'; function generateKeyPairSync(data: Uint8Array): { publicKey: KeyObject; sharedSecret: KeyObject; } { //generate key pair using ecc public blob const keyPair = crypto.generateKeyPairSync('ed25519', { publicKeyEncoding: { type: 'spki', format: 'der', data, }, }); return { publicKey: keyPair.publicKey, sharedSecret: keyPair.privateKey, }; }
正确实现方案
要复刻C#代码功能,需注意核心细节:C#使用P-256曲线(而非TS代码中的Ed25519签名曲线)、处理C#专属的EccPublicBlob格式转换、用SHA256作为密钥派生哈希算法。以下是符合要求的TypeScript实现:
import crypto from 'crypto'; function createECDHAdHocKey(productKey: Uint8Array): { sharedSecret: Buffer; publicKey: Buffer } { // 1. 构造C#格式的EccPublicBlob(头部+productKey) const publicP256Header = Buffer.from([0x45, 0x43, 0x4B, 0x31, 0x20, 0x00, 0x00, 0x00]); const completeKey = Buffer.concat([publicP256Header, productKey]); // 2. 解析EccPublicBlob为Node.js支持的SPKI格式公钥 const rawPublicKey = completeKey.subarray(8); const x = rawPublicKey.subarray(0, 32); const y = rawPublicKey.subarray(32, 64); const spkiDer = createP256SPKI(x, y); // 3. 生成本地P-256曲线ECDH密钥对 const ecdh = crypto.createECDH('prime256v1'); // prime256v1对应P-256曲线 const localPublicKeyBuffer = ecdh.generateKeys(); // 4. 计算共享密钥并通过SHA256哈希(匹配C#的密钥派生规则) const sharedSecretRaw = ecdh.computeSecret(spkiDer); const sharedSecret = crypto.createHash('sha256').update(sharedSecretRaw).digest(); // 5. 提取本地公钥的原始x+y字节(对应C#跳过头部后的输出) const localRawPublicKey = extractRawP256PublicKey(localPublicKeyBuffer); return { sharedSecret, publicKey: localRawPublicKey }; } // 辅助函数:从x、y构造P-256曲线的SPKI格式DER公钥 function createP256SPKI(x: Buffer, y: Buffer): Buffer { // P-256曲线的OID标识 const oid = Buffer.from([0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07]); // 未压缩公钥结构:0x04标识 + x + y const publicKeyData = Buffer.concat([Buffer.from([0x04]), x, y]); // 组装ASN.1 DER格式的SPKI公钥 return Buffer.concat([ Buffer.from([0x30, 0x59]), Buffer.from([0x30, 0x13]), oid, Buffer.from([0x05, 0x00]), Buffer.from([0x03, 0x42, 0x00]), publicKeyData ]); } // 辅助函数:从Node.js生成的公钥中提取原始x+y字节(64字节) function extractRawP256PublicKey(publicKeyBuffer: Buffer): Buffer { // 跳过SPKI格式的ASN.1头部,提取x+y部分并去掉0x04标识位 return publicKeyBuffer.subarray(26, 26 + 65).subarray(1); }
核心匹配点
- 曲线统一:使用
prime256v1对应C#的P-256 - 格式兼容:手动实现
EccPublicBlob与SPKI格式的转换 - 密钥派生:共享密钥生成后用SHA256哈希,与C#代码逻辑完全一致
- 输出对齐:本地公钥输出64字节的x+y原始数据,匹配C#跳过头部后的结果
内容的提问来源于stack exchange,提问作者omer5611
相关产品推荐
相关产品推荐

