#c# #.net #cryptography #bouncycastle
#c# #.net #криптография #bouncycastle
Вопрос:
Я переношу код nodejs на .Net и я застрял в этой части, где мне нужно сгенерировать открытые и закрытые ключи.
Код Javascript:
const dh = crypto.createECDH('prime256v1');
let privk = dh.getPrivateKey();
let pubk = dh.getPublicKey();
Я попробовал то же самое с .Net C # с
var ecdh = new ECDiffieHellmanCng(CngKey.Create(CngAlgorithm.ECDiffieHellmanP256, null, new CngKeyCreationParameters { ExportPolicy = CngExportPolicies.AllowPlaintextExport }));
var privateKey = ecdh.Key.Export(CngKeyBlobFormat.EccPrivateBlob);
var publickey = ecdh.Key.Export(CngKeyBlobFormat.EccPublicBlob);
Однако, когда я попытался обменять эти ключи, сгенерированные с помощью C #, на сервер Google FCM, я получил ошибку недопустимого аргумента. Когда я копирую сгенерированный массив byte[] из nodejs в .Чистый код C # в качестве констант работает. Очевидно, что сгенерированные ключи не соответствуют требованиям сервера. Поскольку я работаю с недокументированным интерфейсом, я не могу сказать, почему ключи не принимаются. Я вижу, что ключи, сгенерированные с помощью nodejs, имеют длину 32 байта для закрытого ключа и 65 байт для открытого ключа. Ключи, сгенерированные на C #, имеют длину 140 и 96 байт. Как сгенерировать ключи на C #, чтобы они соответствовали свойствам ключей в nodejs?
Комментарии:
1. Ключи c #, вероятно, добавляют дополнение. Заполнение добавляет случайные биты в конец ключа, поэтому реальная длина ключа не определяется. См. docs.microsoft.com/en-us/dotnet/api /…
Ответ №1:
Я смог решить свою проблему, используя Bouncy Castle
ECKeyPairGenerator gen = new ECKeyPairGenerator("ECDH");
SecureRandom secureRandom = new SecureRandom();
X9ECParameters ecp = NistNamedCurves.GetByName("P-256");
ECDomainParameters ecSpec = new ECDomainParameters(ecp.Curve, ecp.G, ecp.N, ecp.H, ecp.GetSeed());
ECKeyGenerationParameters ecgp = new ECKeyGenerationParameters(ecSpec, secureRandom);
gen.Init(ecgp);
AsymmetricCipherKeyPair eckp = gen.GenerateKeyPair();
ECPublicKeyParameters ecPub = (ECPublicKeyParameters)eckp.Public;
ECPrivateKeyParameters ecPri = (ECPrivateKeyParameters)eckp.Private;
byte[] publicKeyBytes = ecPub.Q.GetEncoded();
Ответ №2:
Вы можете использовать ECDiffieHellman для шифрования сообщений. У вас есть два варианта: статический-статический ECDH и статический-эфемерный ECDH:
Для статически-статического ECDH получателю необходимо будет знать открытый ключ отправителя (это может быть или не быть опцией в вашем приложении). У вас также должны быть некоторые данные, уникальные для этого сообщения (это может быть серийный номер, который вы получаете откуда-то еще в протоколе или строке базы данных, или что-то еще, или это может быть одноразовый номер). Затем вы используете ECDH для генерации секретного ключа и использования его для шифрования ваших данных. Это даст вам желаемую длину зашифрованных данных в 16 байт, но она не является полностью асимметричной: шифровальщик также может расшифровывать сообщения (опять же: это может быть или не быть проблемой в вашем приложении).
Статический-эфемерный немного отличается: здесь шифровальщик генерирует временную (эфемерную) пару ключей EC. Затем он использует эту пару ключей вместе с открытым ключом получателя для генерации секретного ключа, который можно использовать для шифрования данных. Наконец, он отправляет открытый ключ пары эфемерных ключей получателю вместе с зашифрованными данными. Это может лучше вписаться в ваше приложение, но полные зашифрованные данные теперь будут 2*32 16 = 80 байт с использованием ECDH-256 и AES (как отмечает Грегс, вы можете сэкономить 32 байта, отправив только координату x открытого ключа, но я в это не верю.NET предоставляет функциональность для пересчета координаты y).
Вот небольшой класс, который будет выполнять статический-статический ECDH:
public static class StaticStaticDiffieHellman
{
private static Aes DeriveKeyAndIv(ECDiffieHellmanCng privateKey, ECDiffieHellmanPublicKey publicKey, byte[] nonce)
{
privateKey.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
privateKey.HashAlgorithm = CngAlgorithm.Sha256;
privateKey.SecretAppend = nonce;
byte[] keyAndIv = privateKey.DeriveKeyMaterial(publicKey);
byte[] key = new byte[16];
Array.Copy(keyAndIv, 0, key, 0, 16);
byte[] iv = new byte[16];
Array.Copy(keyAndIv, 16, iv, 0, 16);
Aes aes = new AesManaged();
aes.Key = key;
aes.IV = iv;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
return aes;
}
public static byte[] Encrypt(ECDiffieHellmanCng privateKey, ECDiffieHellmanPublicKey publicKey, byte[] nonce, byte[] data){
Aes aes = DeriveKeyAndIv(privateKey, publicKey, nonce);
return aes.CreateEncryptor().TransformFinalBlock(data, 0, data.Length);
}
public static byte[] Decrypt(ECDiffieHellmanCng privateKey, ECDiffieHellmanPublicKey publicKey, byte[] nonce, byte[] encryptedData){
Aes aes = DeriveKeyAndIv(privateKey, publicKey, nonce);
return aes.CreateDecryptor().TransformFinalBlock(encryptedData,0, encryptedData.Length);
}
}
// Usage:
ECDiffieHellmanCng key1 = new ECDiffieHellmanCng();
ECDiffieHellmanCng key2 = new ECDiffieHellmanCng();
byte[] data = Encoding.UTF8.GetBytes("TestTestTestTes");
byte[] nonce = Encoding.UTF8.GetBytes("whatever");
byte[] encryptedData = StaticStaticDiffieHellman.Encrypt(key1, key2.PublicKey, nonce, data);
Console.WriteLine(encryptedData.Length); // 16
byte[] decryptedData = StaticStaticDiffieHellman.Decrypt(key2, key1.PublicKey, nonce, encryptedData);
Console.WriteLine(Encoding.UTF8.GetString(decryptedData));