ECDH nodejs и обмен ключами C#

Я потерял себя, и мне нужна помощь, чтобы идти в правильном направлении :) У меня есть сервер nodejs, который должен обмениваться некоторыми важными данными с сервером, написанным на C #, поэтому в этом случае я хочу, чтобы мои данные были зашифрованы как-то. Я думал об AES, и для безопасного обмена ключами я хочу использовать ECDH, но я не знаю, как заставить его работать правильно... Если я правильно думаю, я могу сделать свою сторону C # "allice" следующим образом:

ECDiffieHellman alice = ECDiffieHellman.Create(ECCurve.NamedCurves.brainpoolP192t1);
var aliceKey = alice.PublicKey;
var ketyToExport = Convert.ToBase64String(aliceKey.ToByteArray());
//send ketyToExport to nodejs
//KEY FROM nodejs
var key1 = Convert.FromBase64String("BIzSJ1dmTXpSS8rqkVFISZ+vumhqXYMWfWoU5vB9GHhOtxxDInu/GZ92VJpqhrE3vw==").ToList();
var keyType = new byte[] { 0x45, 0x43, 0x4B, 0x31 };
var keyLength = new byte[] { 0x20, 0x00, 0x00, 0x00 };
key1.RemoveAt(0);
key1 = keyType.Concat(keyLength).Concat(key1).ToList();
byte[] bobKeyBytes = key1.ToArray();
//and I have a problem with that line bellow, I do not know how to make it work
var aliceSecret = alice.DeriveKeyMaterial(/*ECDiffieHellmanPublicKey bobKeyBytes*/);

И сторона nodejs «Боб» выглядит следующим образом:

const crypto = require("crypto");
const bob = crypto.createECDH('brainpoolP192t1')
const bobKey = bob.generateKeys('base64');
var bobLength = Buffer.from(bobKey, 'base64').length;
//send bobkey to c#
//recive alicekey
var tmp = "RUNLUBgAAAAR9C7kO2o+vxNT/UBvvEuJHNdI8NfU4fUxUT431ER1q3kJbeUVHepoG5SWUM2NHj8="
var aliceKeyBuffer = Buffer.from(tmp, 'base64');
var aliceKey = Buffer.alloc(bobLength)
aliceKeyBuffer.copy(aliceKey, 1, 8);
aliceKey[0] = 4;
bob.computeSecret(aliceKey);
//create aes 
//get mesage and iv ...

Итак, я внес некоторые коррективы во все это, но сейчас я не знаю, что делать с этой строкой, как заставить ее работать... var aliceSecret = alice.DeriveKeyMaterial(/*ECDiffieHellmanPublicKey bobKeyBytes*/);

#BIG EDIT Мне помогли ECDiffieHellmanPublicKey из ByteArray (используя ECDiffieHellman NamedCurves) и теперь у меня другая проблема -_- мой код узла js не изменился выше, но c# выглядит так:

using (ECDiffieHellman alice = ECDiffieHellman.Create(ECCurve.NamedCurves.brainpoolP256r1))
{
    var alicePublicKey = Convert.ToBase64String(alice.PublicKey.ToByteArray());
    //NODEJS brainpoolP256r1 publickey 
    var key1 = Convert.FromBase64String("BASsbkule53ARqlMBA8hYysyyoRi3xGxGnSzIJ2fS5FlLniQD/zYiiGUVydmO/BBkQwVTUo5f4OMCxVNtQ/LuMQ=");
    byte[] keyX = new byte[key1.Length / 2];
    byte[] keyY = new byte[keyX.Length];
    Buffer.BlockCopy(key1, 1, keyX, 0, keyX.Length);
    Buffer.BlockCopy(key1, 1 + keyX.Length, keyY, 0, keyY.Length);
    ECParameters parameters = new ECParameters
    {
        Curve = ECCurve.NamedCurves.brainpoolP256r1,
        Q =
        {
            X = keyX,
            Y = keyY,
        },
    };
    byte[] derivedKey;
    using (ECDiffieHellman bob = ECDiffieHellman.Create(parameters))
    using (ECDiffieHellmanPublicKey bobPublic = bob.PublicKey)
    {
        derivedKey = alice.DeriveKeyFromHash(bobPublic, HashAlgorithmName.SHA256);
    }
    var aliceKey = Convert.ToBase64String(derivedKey);
    byte[] encryptedMessage = null;
    byte[] iv = null;
    // Send(aliceKey, "Secret message", out encryptedMessage, out iv);
}

и это работает, но дает мне разные секретные ключи... из bob.computeSecret(aliceKey) я получил iIoH9aJoWf3666QQ6X+kj4iUKrk9j+hbRuXbhgs7YzM=, а из alice.DeriveKeyFromHash(bobPublic, HashAlgorithmName.SHA256); я получил wJ7O4Hm2Jxs1FcLx6KaMmENvqdTQJPZ/YNSs1+MQDOQ=, если я правильно думаю, они должны быть равны. я неправильно думаю?

#РЕДАКТИРОВАНИЕ ГОТОВО!!

Таким образом, добавление этого кода в конец файла js дало мне то, что мне было нужно.

const hash = crypto.createHash('sha256');
var tt = bob.computeSecret(aliceKey);
hash.update(tt);
console.log(hash.digest('base64'));

person Janso123    schedule 02.12.2018    source источник
comment
Вы должны использовать SSL/TLS. Каждая важная программная среда поддерживает его.   -  person President James K. Polk    schedule 02.12.2018
comment
У меня есть ограничение на то, чтобы не использовать ssl / tls ... другой вариант, который у меня есть, - это написать собственный аддон узла на C ++, который будет поддерживать шифрование RSA и некоторые странные вещи, которые были написаны более 10 лет назад, которые содержат magicInt и magicIntArray, которые подобен пакету данных, он содержит заголовок, информацию о данных и данные. и основан на смещении некоторых байтов и игре с потоками... Но если бы я мог использовать SSL/TSL, это было бы легко, как запустить https на сервере С#, потому что связь основана на JSON-RPC, а http-сервер работает...   -  person Janso123    schedule 02.12.2018
comment
Обычно открытые ключи кодируются как несжатые точки (начиная с байта со значением 04, за которым следуют X и Y статического размера, оба 24 байта. Как вы передаете значение открытого ключа, зависит от вас. Если у вас есть только сокет, вы можете в двоичный, нет необходимости в кодировании/декодировании.   -  person Maarten Bodewes    schedule 02.12.2018
comment
Хорошо. У меня есть calculateSecred в узле, но у меня проблема с добавлением информации С# к вопросу   -  person Janso123    schedule 03.12.2018


Ответы (2)


##РЕШЕНИЕ##

С#

class Program
{
    static void Main(string[] args)
    {
        using (ECDiffieHellman alice = ECDiffieHellman.Create(ECCurve.NamedCurves.brainpoolP256r1))
        {
            var alicePublicKey = Convert.ToBase64String(alice.PublicKey.ToByteArray());
            //send alicePublicKey
            var nodejsKey = ""; //NODEJS brainpoolP256r1 publickey  base64
            byte[] nodejsKeyBytes= Convert.FromBase64String(nodejsKey);

            var aliceKey = Convert.ToBase64String(getDeriveKey(nodejsKeyBytes,alice));
            byte[] encryptedMessage = null;
            byte[] iv = null;
            // Send(aliceKey, "Secret message", out encryptedMessage, out iv);
        }
    }
    static byte[] getDeriveKey(byte[] key1, ECDiffieHellman alice)
    {
        byte[] keyX = new byte[key1.Length / 2];
        byte[] keyY = new byte[keyX.Length];
        Buffer.BlockCopy(key1, 1, keyX, 0, keyX.Length);
        Buffer.BlockCopy(key1, 1 + keyX.Length, keyY, 0, keyY.Length);
        ECParameters parameters = new ECParameters
        {
            Curve = ECCurve.NamedCurves.brainpoolP256r1,
            Q =
            {
                X = keyX,
                Y = keyY,
            },
        };
        byte[] derivedKey;
        using (ECDiffieHellman bob = ECDiffieHellman.Create(parameters))
        using (ECDiffieHellmanPublicKey bobPublic = bob.PublicKey)
        {
            return derivedKey = alice.DeriveKeyFromHash(bobPublic, HashAlgorithmName.SHA256);
        }
    }
}

NODEJS

const crypto = require("crypto");
const bob = crypto.createECDH('brainpoolP256r1')
bob.generateKeys();
const bobKey = bob.getPublicKey('base64');
var bobLength = Buffer.from(bobKey, 'base64').length;
//send bobkey to c#
//recive alicekey

var alicePublicKey = "RUNLUCAAAAB/xP7JhSIhYIYAijyC2zHu7obB5CwfK/ynQPxcRAIhBI6OLRRcHyPo61AhfSZN3qA2vGDfWO2mrdWWvqqhVaDf";
var aliceKeyBuffer = Buffer.from(alicePublicKey, 'base64');
var aliceKey = Buffer.alloc(bobLength)
aliceKeyBuffer.copy(aliceKey, 1, 8);
aliceKey[0] = 4;
const hash = crypto.createHash('sha256');
var tt = bob.computeSecret(aliceKey);
var bobSecretKey = hash.update(tt).digest('base64');

большое спасибо за @bartonjs и @Мартен Бодевес

person Janso123    schedule 03.12.2018

Приведенный выше ответ мне очень помог. Я использую secp521r1/nistP521 для генерации ключей как в NodeJS, так и в C#. В моем случае вызов alice.PublicKey.ToByteArray() приведет к исключению:

Unhandled exception. System.PlatformNotSupportedException: Operation is not supported on this platform.
   at System.Security.Cryptography.ECDiffieHellmanImplementation.ECDiffieHellmanSecurityTransforms.ECDiffieHellmanSecurityTransformsPublicKey.ToByteArray()

Согласно проблеме, зарегистрированной группой выполнения dotnet здесь:

ECDiffieHellmanPublicKey.ToByteArray() не имеет определенного (стандартами) формата экспорта. Версия, используемая ECDiffieHellmanPublicKeyCng, специфична для Windows и работает только для NIST P-256 (secp256r1), NIST P-384 (secp384r1) и NIST P-521 (secp521r1).

Чтобы создать копию ключа, вы должны использовать метод ExportParameters() для получения расширенного объекта ECParameters; который затем можно отправить через ECDiffieHellman.Create(ECParameters) и прочитать свойство PublicKey этого объекта, чтобы получить второй экземпляр ECDiffieHellmanPublicKey.

Предполагая, что я отправляю свои X и Y от Алисы к Бобу (я использовал шестнадцатеричный код вместо base64), соответствующий процесс для получения того же общего секрета на стороне NodeJS выглядит следующим образом:

// Alice's public key X, Y coordinates from DotNet Core
const aliceHexX = '00248F624728B17196B22005742C13D80D3DFF75BCA74AF865195E5A29F41C013653B0931BC544245402EDD7922F38692F38DCCF780CF1A9E27D3CFB5E09E53883C0';
const aliceHexY = '0043517F3B2EF33ED70EFA2BC4163E9B99558A7C2ECB7C659A12EA4024633CFA8B9FC997F0A42D30759B4280FDADC13A67A3E7BB0227047C907FAAE92E7716E8A10D';

const alicePublicKeyXBytes = Buffer.from(aliceHexX, 'hex');
const alicePublicKeyYBytes = Buffer.from(aliceHexY, 'hex');
const alicePublicKey = Buffer.concat([Buffer.from([0x04]), alicePublicKeyXBytes, alicePublicKeyYBytes])
const bobSecret = bob.computeSecret(alicePublicKey);

const hash = crypto.createHash('sha256');
hash.update(bobSecret);
const sharedSecret = hash.digest('hex');
console.log(`Shared Secret: ${sharedSecret.toString('hex')}`);
person Matt Goodrich    schedule 24.04.2020