C# 和 javascript 之间的 ECDH

我正在构建一个 Web API,我需要实现 ECDH 来执行端到端加密。在服务器端我有一个 C# 应用程序,在客户端我有一个 Javascript 应用程序。


我能够交换密钥、生成私钥并加密消息,但我在解密时遇到了问题。


我认为问题在于公钥的交换。在 javascript 中,键以字节“4”开头,.NET 键以 8 个字节开头,用于标识键的类型和大小,我需要更改这些字节以导入每个键(我在这里找到的信息)。也许这会导致一些不一致。


在客户端,我使用 Web Cryptography API 来处理 ECDH。我正在实施如下。


生成密钥


await window.crypto.subtle.generateKey(

        {

            name: "ECDH",

            namedCurve: "P-256",

        },

        false,

        ["deriveKey", "deriveBits"]

    );

像这样导出公钥:


await window.crypto.subtle.exportKey(

        "raw",

        publicKey

    );

导入外部公钥


await window.crypto.subtle.importKey(

        "raw",

        {

            name: "ECDH",

            namedCurve: "P-256",

        },

        false,

        ["deriveKey", "deriveBits"]

    )

最后推导出密钥


await window.crypto.subtle.deriveKey(

        {

            name: "ECDH",

            namedCurve: "P-256",

            public: publicKey,

        },

        privateKey,

        {

            name: "AES-CBC",

            length: 256,

        },

        false,

        ["encrypt", "decrypt"]

    )

在服务器端,我正在实施以下相同的步骤。生成公钥


private static ECDiffieHellmanCng ecdh = new ECDiffieHellmanCng(256);


public static void GeneratePublicKey()

{

    ecdh.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;

    ecdh.HashAlgorithm = CngAlgorithm.Sha256;

    publicKey = ecdh.PublicKey.ToByteArray();

}

导出公钥。请注意,我更改了第一个字节


public static byte[] GetPublicKey()

    {

        var auxKey = publicKey.Skip(7).ToArray();

        auxKey[0] = 4;


        return auxKey;

    }

导入公钥并导出私钥。请注意,我更改了第一个字节


public static void GerarChavePrivada(byte[] bobPublicKey)

{

    byte[] aux = new byte[bobPublicKey.Length + 7];


    aux[0] = 0x45;

    aux[1] = 0x43;

    aux[2] = 0x4B;

    aux[3] = 0x31;

    aux[4] = 0x20;

    aux[5] = 0x00;

    aux[6] = 0x00;

    aux[7] = 0x00;


    for (int i = 1; i < bobPublicKey.Length; i++)

    {

        aux[7 + i] = bobPublicKey[i];

    }


    var importedKey = CngKey.Import(aux, CngKeyBlobFormat.EccPublicBlob);

    privateKey = ecdh.DeriveKeyMaterial(importedKey);

}


元芳怎么了
浏览 421回答 3
3回答

qq_笑_17

我遇到过同样的问题。经过更多调试后,我意识到 C# 使用 DeriveKeyMaterial 生成的密钥然后使用 SHA-256 进行哈希处理。我的解决方案是在 javascript 上导出派生密钥,对其进行哈希处理,然后将其作为新密钥导入。cryptoApi().deriveKey(&nbsp; {&nbsp; &nbsp; &nbsp; name: "ECDH",&nbsp; &nbsp; &nbsp; namedCurve: "P-256", //can be "P-256", "P-384", or "P-521"&nbsp; &nbsp; &nbsp; public: ServerKey, //an ECDH public key from generateKey or importKey&nbsp; },&nbsp; ECkey.privateKey, //your ECDH private key from generateKey or importKey&nbsp; { //the key type you want to create based on the derived bits&nbsp; &nbsp; &nbsp; name: "AES-CBC", //can be any AES algorithm ("AES-CTR", "AES-CBC", "AES-CMAC", "AES-GCM", "AES-CFB", "AES-KW", "ECDH", "DH", or "HMAC")&nbsp; &nbsp; &nbsp; //the generateKey parameters for that type of algorithm&nbsp; &nbsp; &nbsp; length: 256, //can be&nbsp; 128, 192, or 256&nbsp; },&nbsp; true, //whether the derived key is extractable (i.e. can be used in exportKey)&nbsp; ["encrypt", "decrypt"] //limited to the options in that algorithm's importKey).then(function(AESKeyData){&nbsp; //returns the exported key data&nbsp; console.log(AESKeyData);&nbsp; cryptoApi().exportKey('raw',&nbsp; &nbsp; AESKeyData&nbsp; ).then(function (exportedAESKeyData) {&nbsp; &nbsp; &nbsp; cryptoApi().digest('SHA-256', exportedAESKeyData).then(function (HashedAESKeyValue) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.log(HashedAESKeyValue);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cryptoApi().importKey(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 'raw',&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; HashedAESKeyValue,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; { //the key type you want to create based on the derived bits&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name: "AES-CBC", //can be any AES algorithm ("AES-CTR", "AES-CBC", "AES-CMAC", "AES-GCM", "AES-CFB", "AES-KW", "ECDH", "DH", or "HMAC")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //the generateKey parameters for that type of algorithm&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; length: 256, //can be&nbsp; 128, 192, or 256&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; false,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ["encrypt", "decrypt"]&nbsp; &nbsp; &nbsp; &nbsp; ).then(function (TrueAESKey) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cryptoApi().decrypt(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;name: 'AES-CBC',&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;length: 256,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;iv: base64ToArrayBuffer(IV)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;},&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;TrueAESKey,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;base64ToArrayBuffer(EncryptedData)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;).then(function (decrypted) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;console.log(buf2hex(decrypted));&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;});&nbsp; &nbsp; &nbsp; &nbsp; })&nbsp; &nbsp; &nbsp; });&nbsp; });})

天涯尽头无女友

你见过PKI.js吗?在那里您可以找到 CMS Enveloped/Encrypted Data 的所有可能的密钥加密模式的完整实现。还有现场示例,这里是所有示例的源代码。请注意存在的wiki页面关于PKI.js.与CMS EnvelopedData工作

FFIVE

尝试这样做要点中的完整示例class Protector {&nbsp; ab2str(buffer) {&nbsp; &nbsp; return new TextDecoder().decode(buffer);&nbsp; }&nbsp; str2ab(text) {&nbsp; &nbsp; return new TextEncoder().encode(text);&nbsp; }&nbsp; generateIv() {&nbsp; &nbsp; return crypto.getRandomValues(new Uint8Array(16));&nbsp; }&nbsp; /**&nbsp; &nbsp;* @see https://github.com/mdn/dom-examples/blob/master/web-crypto/derive-bits/ecdh.js&nbsp; &nbsp;*/&nbsp; async generateKey() {&nbsp; &nbsp; this.key = await window.crypto.subtle.generateKey(&nbsp; &nbsp; &nbsp; { name: 'ECDH', namedCurve: 'P-256' },&nbsp; &nbsp; &nbsp; false,&nbsp; &nbsp; &nbsp; ['deriveBits']&nbsp; &nbsp; );&nbsp; }&nbsp; async encrypt(plaintext) {&nbsp; &nbsp; const counter = this.generateIv();&nbsp; &nbsp; const buffer = await crypto.subtle.decrypt({&nbsp; &nbsp; &nbsp; name: 'aes-ctr',&nbsp; &nbsp; &nbsp; counter: counter,&nbsp; &nbsp; &nbsp; length: 128&nbsp; &nbsp; }, this.importedKey, this.str2ab(plaintext));&nbsp; &nbsp; return { buffer, counter };&nbsp; }&nbsp; async decrypt(data) {&nbsp; &nbsp; const buffer = await crypto.subtle.decrypt({&nbsp; &nbsp; &nbsp; name: 'aes-ctr',&nbsp; &nbsp; &nbsp; counter: data.counter,&nbsp; &nbsp; &nbsp; length: 128&nbsp; &nbsp; }, this.importedKey, data.buffer);&nbsp; &nbsp; return this.ab2str(buffer);&nbsp; }&nbsp; getPublicKey() {&nbsp; &nbsp; return {publicKey: this.key.publicKey};&nbsp; }&nbsp; async setRemotePublicKey(key) {&nbsp; &nbsp; this.clientKey = key;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; this.sharedSecret = await window.crypto.subtle.deriveBits(&nbsp; &nbsp; &nbsp; { name: 'ECDH', namedCurve: 'P-256', public: this.clientKey.publicKey },&nbsp; &nbsp; &nbsp; this.key.privateKey,&nbsp; &nbsp; &nbsp; 256&nbsp; &nbsp; );&nbsp; &nbsp; this.importedKey = await crypto.subtle.importKey(&nbsp; &nbsp; &nbsp; 'raw',&nbsp; &nbsp; &nbsp; this.sharedSecret,&nbsp; &nbsp; &nbsp; 'aes-ctr',&nbsp; &nbsp; &nbsp; false,&nbsp; &nbsp; &nbsp; ['encrypt', 'decrypt']&nbsp; &nbsp; );&nbsp; }}如何使用:(async () => {&nbsp; // Generate Keys&nbsp; const pro1 = new Protector();&nbsp; await pro1.generateKey();&nbsp; const pub1 = pro1.getPublicKey();&nbsp; const pro2 = new Protector();&nbsp; await pro2.generateKey();&nbsp; const pub2 = pro2.getPublicKey();&nbsp; // Exchange Keys&nbsp; await pro1.setRemotePublicKey(pub2);&nbsp; await pro2.setRemotePublicKey(pub1);&nbsp; // Let`s Encrypt&nbsp; const crypted = await pro1.encrypt('Hello World');&nbsp; const descrypted = await pro2.decrypt(crypted);})();
打开App,查看更多内容
随时随地看视频慕课网APP