替换ToByteArray为ExportSubjectPublicKeyInfo后CngKey.Import报错如何修复?
修复ECDiffieHellman公钥导出导入的参数错误问题
你遇到的错误核心是公钥格式不匹配:
ExportSubjectPublicKeyInfo()导出的是标准X.509 SubjectPublicKeyInfo(SPKI)格式的公钥,属于跨平台通用格式。- 而
CngKey.Import指定的CngKeyBlobFormat.EccPublicBlob是Windows CNG框架特有的公钥BLOB格式,两者结构完全不同,因此导入时会触发参数错误。
以下是两种可行的修复方案:
方案一:直接使用ECDH标准导入方法(推荐,跨平台)
无需借助CngKey,直接用ECDiffieHellmanCng.ImportSubjectPublicKeyInfo导入SPKI格式公钥,再派生共享密钥。修改后代码如下:
public static void Main() { var client = new ECDiffieHellmanCng(); client.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash; client.HashAlgorithm = CngAlgorithm.Sha256; var clientPublicKey = client.PublicKey.ExportSubjectPublicKeyInfo(); var server = new ECDiffieHellmanCng(); server.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash; server.HashAlgorithm = CngAlgorithm.Sha256; 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) { // 直接导入SPKI格式公钥 using var serverEc = ECDiffieHellmanCng.ImportSubjectPublicKeyInfo(serverPublicKey, out _); var sharedSecret = client.DeriveKeyMaterial(serverEc.PublicKey); 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) { // 直接导入SPKI格式公钥 using var clientEc = ECDiffieHellmanCng.ImportSubjectPublicKeyInfo(clientPublicKey, out _); var sharedSecret = server.DeriveKeyMaterial(clientEc.PublicKey); 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; }
方案二:将SPKI格式转换为CNG BLOB格式(仅Windows适用)
如果必须保留CngKey.Import的逻辑,可以先把SPKI格式公钥导入临时ECDH对象,再导出为CNG的EccPublicBlob格式后再导入:
public static (byte[], byte[]) Encrypt(ECDiffieHellmanCng client, byte[] serverPublicKey, string plainText) { // 先导入SPKI,再导出为CNG BLOB格式 using var tempEc = ECDiffieHellmanCng.ImportSubjectPublicKeyInfo(serverPublicKey, out _); byte[] cngBlob = tempEc.PublicKey.ExportECDHPublicKey(); var cngKey = CngKey.Import(cngBlob, CngKeyBlobFormat.EccPublicBlob); var sharedSecret = client.DeriveKeyMaterial(cngKey); // AES加密逻辑不变 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); } // Decrypt方法做同样修改 public static byte[] Decrypt(ECDiffieHellmanCng server, byte[] clientPublicKey, byte[] iv, byte[] cipher) { using var tempEc = ECDiffieHellmanCng.ImportSubjectPublicKeyInfo(clientPublicKey, out _); byte[] cngBlob = tempEc.PublicKey.ExportECDHPublicKey(); var cngKey = CngKey.Import(cngBlob, CngKeyBlobFormat.EccPublicBlob); var sharedSecret = server.DeriveKeyMaterial(cngKey); // AES解密逻辑不变 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; }
方案说明
- 方案一符合微软跨平台建议,代码简洁且兼容性更强,优先推荐。
- 方案二仅适用于Windows环境,适合必须依赖
CngKey的特定场景。
内容的提问来源于stack exchange,提问作者ArMaN
相关产品推荐
相关产品推荐

