为了账号安全,请及时绑定邮箱和手机立即绑定

C# 和 javascript 之间的 ECDH

C# 和 javascript 之间的 ECDH

C#
元芳怎么了 2021-12-25 18:39:46
我正在构建一个 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);}
查看完整描述

3 回答

?
qq_笑_17

TA贡献1818条经验 获得超7个赞

我遇到过同样的问题。经过更多调试后,我意识到 C# 使用 DeriveKeyMaterial 生成的密钥然后使用 SHA-256 进行哈希处理。


我的解决方案是在 javascript 上导出派生密钥,对其进行哈希处理,然后将其作为新密钥导入。


cryptoApi().deriveKey(

  {

      name: "ECDH",

      namedCurve: "P-256", //can be "P-256", "P-384", or "P-521"

      public: ServerKey, //an ECDH public key from generateKey or importKey

  },

  ECkey.privateKey, //your ECDH private key from generateKey or importKey

  { //the key type you want to create based on the derived bits

      name: "AES-CBC", //can be any AES algorithm ("AES-CTR", "AES-CBC", "AES-CMAC", "AES-GCM", "AES-CFB", "AES-KW", "ECDH", "DH", or "HMAC")

      //the generateKey parameters for that type of algorithm

      length: 256, //can be  128, 192, or 256

  },

  true, //whether the derived key is extractable (i.e. can be used in exportKey)

  ["encrypt", "decrypt"] //limited to the options in that algorithm's importKey

)

.then(function(AESKeyData){

  //returns the exported key data

  console.log(AESKeyData);


  cryptoApi().exportKey('raw',

    AESKeyData

  ).then(function (exportedAESKeyData) {

      cryptoApi().digest('SHA-256', exportedAESKeyData).then(function (HashedAESKeyValue) {

          console.log(HashedAESKeyValue);


          cryptoApi().importKey(

            'raw',

            HashedAESKeyValue,

            { //the key type you want to create based on the derived bits

                name: "AES-CBC", //can be any AES algorithm ("AES-CTR", "AES-CBC", "AES-CMAC", "AES-GCM", "AES-CFB", "AES-KW", "ECDH", "DH", or "HMAC")

                //the generateKey parameters for that type of algorithm

                length: 256, //can be  128, 192, or 256

            },

            false,

            ["encrypt", "decrypt"]

        ).then(function (TrueAESKey) {


            cryptoApi().decrypt(

               {

                   name: 'AES-CBC',

                   length: 256,

                   iv: base64ToArrayBuffer(IV)

               },

               TrueAESKey,

               base64ToArrayBuffer(EncryptedData)

               ).then(function (decrypted) {

                   console.log(buf2hex(decrypted));

               });

        })

      });

  });



})


查看完整回答
反对 回复 2021-12-25
?
天涯尽头无女友

TA贡献1831条经验 获得超9个赞

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


查看完整回答
反对 回复 2021-12-25
?
FFIVE

TA贡献1797条经验 获得超6个赞

尝试这样做


要点中的完整示例


class Protector {

  ab2str(buffer) {

    return new TextDecoder().decode(buffer);

  }


  str2ab(text) {

    return new TextEncoder().encode(text);

  }


  generateIv() {

    return crypto.getRandomValues(new Uint8Array(16));

  }


  /**

   * @see https://github.com/mdn/dom-examples/blob/master/web-crypto/derive-bits/ecdh.js

   */

  async generateKey() {

    this.key = await window.crypto.subtle.generateKey(

      { name: 'ECDH', namedCurve: 'P-256' },

      false,

      ['deriveBits']

    );

  }


  async encrypt(plaintext) {

    const counter = this.generateIv();

    const buffer = await crypto.subtle.decrypt({

      name: 'aes-ctr',

      counter: counter,

      length: 128

    }, this.importedKey, this.str2ab(plaintext));

    return { buffer, counter };

  }


  async decrypt(data) {

    const buffer = await crypto.subtle.decrypt({

      name: 'aes-ctr',

      counter: data.counter,

      length: 128

    }, this.importedKey, data.buffer);

    return this.ab2str(buffer);

  }


  getPublicKey() {

    return {publicKey: this.key.publicKey};

  }


  async setRemotePublicKey(key) {

    this.clientKey = key;

    

    this.sharedSecret = await window.crypto.subtle.deriveBits(

      { name: 'ECDH', namedCurve: 'P-256', public: this.clientKey.publicKey },

      this.key.privateKey,

      256

    );


    this.importedKey = await crypto.subtle.importKey(

      'raw',

      this.sharedSecret,

      'aes-ctr',

      false,

      ['encrypt', 'decrypt']

    );

  }

}

如何使用:


(async () => {

  // Generate Keys

  const pro1 = new Protector();

  await pro1.generateKey();

  const pub1 = pro1.getPublicKey();


  const pro2 = new Protector();

  await pro2.generateKey();

  const pub2 = pro2.getPublicKey();


  // Exchange Keys

  await pro1.setRemotePublicKey(pub2);

  await pro2.setRemotePublicKey(pub1);


  // Let`s Encrypt

  const crypted = await pro1.encrypt('Hello World');

  const descrypted = await pro2.decrypt(crypted);

})();


查看完整回答
反对 回复 2021-12-25
  • 3 回答
  • 0 关注
  • 420 浏览

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号