I serialize the ECDiffieHellmanPublicKey key BLOB to a byte array by ToByteArray() method but this method is obsolete.
ECDiffieHellmanPublicKey.ToByteArray() and the associated constructor do not have a consistent and interoperable implementation on all platforms. Use ECDiffieHellmanPublicKey.ExportSubjectPublicKeyInfo() instead.
Microsoft suggest ExportSubjectPublicKeyInfo() method instead. when I replace ToByteArray to ExportSubjectPublicKeyInfo() method CngKey.Import makes an error:
System.Security.Cryptography.CryptographicException: 'The parameter is incorrect.'
How can I fix the code:
public static void Main()
{
var client = new ECDiffieHellmanCng();
client.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
client.HashAlgorithm = CngAlgorithm.Sha256;
// var clientPublicKey = client.PublicKey.ToByteArray();
var clientPublicKey = client.PublicKey.ExportSubjectPublicKeyInfo();
var server = new ECDiffieHellmanCng();
server.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
server.HashAlgorithm = CngAlgorithm.Sha256;
// var serverPublicKey = server.PublicKey.ToByteArray();
var serverPublicKey = server.PublicKey.ExportSubjectPublicKeyInfo();
var enc = Encrypt(client, serverPublicKey, "Hello");
Console.WriteLine(Convert.ToBase64String(enc.Item1));
var dec = Decrypt(server, clientPublicKey, enc.Item2, enc.Item1);
Console.WriteLine(Encoding.UTF8.GetString(dec));
}
public static (byte[], byte[]) Encrypt(ECDiffieHellmanCng client, byte[] serverPublicKey, string plainText)
{
var cngKey = CngKey.Import(serverPublicKey, CngKeyBlobFormat.EccPublicBlob);
var sharedSecret = client.DeriveKeyMaterial(cngKey);
using var aes = Aes.Create();
aes.Key = sharedSecret;
aes.GenerateIV();
aes.Padding = PaddingMode.PKCS7;
var plainBytes = Encoding.UTF8.GetBytes(plainText);
using var encryptor = aes.CreateEncryptor();
var cipherBytes = encryptor.TransformFinalBlock(plainBytes, 0, plainBytes.Length);
return (cipherBytes, aes.IV);
}
public static byte[] Decrypt(ECDiffieHellmanCng server, byte[] clientPublicKey, byte[] iv, byte[] cipher)
{
var cngKey = CngKey.Import(clientPublicKey, CngKeyBlobFormat.EccPublicBlob);
var sharedSecret = server.DeriveKeyMaterial(cngKey);
using var aes = Aes.Create();
aes.Padding = PaddingMode.PKCS7;
aes.IV = iv;
aes.Key = sharedSecret;
using var decryptor = aes.CreateDecryptor();
var plain = decryptor.TransformFinalBlock(cipher, 0, cipher.Length);
return plain;
}
One option is: Instead of CngKey.Import()
an ECDiffieHellmanCng()
instance is to be created in the Encrypt()
and Decrypt()
methods.
Into this instance the passed public SPKI key is imported with ImportSubjectPublicKeyInfo()
.
To DeriveKeyMaterial()
the Key
property of the ECDiffieHellmanCng()
instance is to be passed.
E.g. for Encrypt()
:
public static (byte[], byte[]) Encrypt(ECDiffieHellmanCng client, byte[] serverPublicKey, string plainText)
{
var server = new ECDiffieHellmanCng();
server.ImportSubjectPublicKeyInfo(serverPublicKey, out _);
var sharedSecret = client.DeriveKeyMaterial(server.Key);
...
Note that ECDiffieHellmanCng
is only supported on Windows, s. here.