ECDH между C # и JavaScript - PullRequest
       11

ECDH между C # и JavaScript

0 голосов
/ 31 августа 2018

Я создаю веб-API, и мне нужно реализовать ECDH для выполнения сквозной криптографии. На стороне сервера у меня есть приложение C #, а на стороне клиента - приложение Javascript.

Я могу обменяться ключами, сгенерировать закрытые ключи и зашифровать сообщения, но у меня возникают проблемы с расшифровкой.

Я думаю, что проблема в обмене открытыми ключами. В javascript ключи начинаются с байта «4», а ключи .NET начинаются с 8 байтов, определяющих тип и размер ключа, мне нужно изменить эти байты, чтобы импортировать каждый ключ (Информация, которую я нашел здесь ). Возможно, это вызывает некоторые несоответствия.

На стороне клиента я использую 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);
}

Полагаю, проблема в этих ключах. В любом случае это коды шифрования и дешифрования:

Javascript

async function encrypt2(iv, key, data){
    var mensagemCriptografada;

    await window.crypto.subtle.encrypt(
        {
            name: "AES-CBC",
            iv: iv,
        },
        key,
        str2ab(data) //Data is a string and I'm converting using str2ab method.
    )

    .then(function(encrypted){
        mensagemCriptografada = encrypted;
    })
    .catch(function(err){
        console.error(err);
    });

    return mensagemCriptografada;
}

function str2ab (str) {
    var array = new Uint8Array(str.length);     
    for(var i = 0; i < str.length; i++) {
        array[i] = str.charCodeAt(i);
    }
    return array.buffer
}

C #

string decMessage = "";

        using (Aes aes = new AesCryptoServiceProvider())
        {
            aes.Key = privateKey;
            aes.IV = iv; //IV is the same used by the javascript code
            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.None;

            var dec = aes.CreateDecryptor(privateKey, iv);

            var plain = dec.TransformFinalBlock(message, 0, message.Length);

            //I've tried every possible enconding.
            decMessage = Encoding.UTF8.GetString(plain); 
        }

        return decMessage;

Я действительно понятия не имею, как решить эту проблему.

Ответы [ 2 ]

0 голосов
/ 23 апреля 2019

У меня была такая же проблема. После дополнительной отладки я понял, что ключ, сгенерированный 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));
               });
        })
      });
  });


})
0 голосов
/ 01 сентября 2018

Вы видели PKI.js ? Там вы можете найти полную реализацию всех возможных схем шифрования ключей для CMS Enveloped / Encrypted Data. Также есть живых примеров и , здесь исходный код для всех примеров. Обратите внимание, что страница WiKi о работе с CMS EnvelopedData в PKI.js.

...