如何在 C#.NET 中创建持久的 AesCng 密钥或 TripleDesCng 密钥?
How to create a persisted AesCng key or TripleDesCng Key In C#.NET?
4.6.2版本时。 .NET Framework 发布后,release notes 中包含的功能之一是 'Support for Persisted-Key Symmetric Encryption'。此功能描述如下:
Windows 加密库 (CNG) 支持在软件和硬件设备上存储永久对称密钥。 .NET Framework 现在公开了此 CNG 功能,您可以在下面的示例中看到这一点。
您需要使用具体实现 classes,例如 AesCng 才能使用此新功能,而不是更常见的工厂方法 Aes.Create()。此要求是由于密钥名称和密钥提供程序是特定于实现的
已分别在 AesCng TripleDESCng classes 中为 AES 和 3DES 算法添加持久密钥对称加密。
查看 AesCng and TripleDESCng classes 的文档后,我注意到每个 class 中都有许多构造函数允许 class 从 'existing persisted AES/TripleDES key' 创建 - 但没有机制可以在 class 本身中保留密钥。
经过一些研究,我遇到了 CngKey class that appears to offer this functionality - however, neither AES or TripleDES are listed in the suite of algorithms outlined in the CngAlgortithm class。
本质上,我的问题是如何在 C#.NET 中创建持久的 AesCng 密钥或 TripleDesCng 密钥?
我注意到之前在 Whosebug 上有人问过 ;然而,在尝试了标记为正确的答案后,我的代码崩溃并出现以下异常:System.Security.Cryptography.CryptographicException:'不支持请求的操作。
'
works on my machine with .NET Framework 4.6.2 and above. Another helpful answer on this topic (but for RSA) can be found .
以下方法创建一个持久的 32 字节密钥 (AES-256),如果它不存在,或者加载一个持久密钥,如果它已经存在,并执行加密:
private static string encryptWithKey(string plaintext, string keyName, string iv)
{
CngProvider keyStorageProvider = CngProvider.MicrosoftSoftwareKeyStorageProvider;
if (!CngKey.Exists(keyName, keyStorageProvider))
{
CngKeyCreationParameters keyCreationParameters = new CngKeyCreationParameters()
{
Provider = keyStorageProvider
};
CngKey.Create(new CngAlgorithm("AES"), keyName, keyCreationParameters);
}
Aes aes = new AesCng(keyName, keyStorageProvider);
aes.IV = Encoding.UTF8.GetBytes(iv);
var encryptor = aes.CreateEncryptor();
byte[] plaintextBytes = Encoding.UTF8.GetBytes(plaintext);
byte[] ciphertextBytes = encryptor.TransformFinalBlock(plaintextBytes, 0, plaintextBytes.Length);
aes.Dispose();
return Convert.ToBase64String(ciphertextBytes);
}
请注意,key默认是exportable,所以aes.Key
会生成一个System.Security.Cryptography.CryptographicException:请求的操作是不支持,即例如:
var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
会抛出那个异常。也许这个或类似的东西是您环境中出现异常的原因。
对应的解密对应为:
private static string decryptWithKey(string ciphertext, string keyName, string iv)
{
CngProvider keyStorageProvider = CngProvider.MicrosoftSoftwareKeyStorageProvider;
if (!CngKey.Exists(keyName, keyStorageProvider))
{
throw new Exception("Error: key doesn't exist...");
}
Aes aes = new AesCng(keyName, keyStorageProvider);
aes.IV = Encoding.UTF8.GetBytes(iv);
var decryptor = aes.CreateDecryptor();
byte[] ciphertextBytes = Convert.FromBase64String(ciphertext);
byte[] plaintextBytes = decryptor.TransformFinalBlock(ciphertextBytes, 0, ciphertextBytes.Length);
aes.Dispose();
return Encoding.UTF8.GetString(plaintextBytes);
}
以下代码:
string plaintext = "The quick brown fox jumps over the lazy dog";
string iv = "0123456789012345";
string keyName = "keyName";
string ciphertext1 = encryptWithKey(plaintext, keyName, iv);
Console.WriteLine(ciphertext1);
string ciphertext2 = encryptWithKey(plaintext, keyName, iv);
Console.WriteLine(ciphertext2);
string decryptedText1 = decryptWithKey(ciphertext1, keyName, iv);
Console.WriteLine(decryptedText1);
执行两次加密,在两种情况下生成 相同的 密文:第一次调用创建并保存密钥,第二次调用加载保存的密钥。相同的密文证明使用了相同的密钥进行加密
为了也能导出key,需要做如下修改:
CngKeyCreationParameters keyCreationParameters = new CngKeyCreationParameters()
{
ExportPolicy = CngExportPolicies.AllowPlaintextExport,
Provider = keyStorageProvider
};
现在可以访问密钥,例如aes.Key
.
请注意,静态 IV 仅用于比较两种加密的密文。当然,实际上,每次加密都必须应用随机 IV。
为了完整起见,应该提到代码与 .NET Core 兼容(已针对 .NET Core 3.1 进行测试)
密钥持久性与 3DES 类似,即 TripleDESCng
。由于必须使用标识符 3DES
(无效的标识符也会触发上面发布的 CryptographicException),即:
CngKey.Create(new CngAlgorithm("3DES"), keyName, keyCreationParameters);
这将创建并保留一个 24 字节的密钥 (3TDEA)。但是请注意,与当前标准 AES 相比,过时且性能相对较差的 3DES 不应再应用。
4.6.2版本时。 .NET Framework 发布后,release notes 中包含的功能之一是 'Support for Persisted-Key Symmetric Encryption'。此功能描述如下:
Windows 加密库 (CNG) 支持在软件和硬件设备上存储永久对称密钥。 .NET Framework 现在公开了此 CNG 功能,您可以在下面的示例中看到这一点。 您需要使用具体实现 classes,例如 AesCng 才能使用此新功能,而不是更常见的工厂方法 Aes.Create()。此要求是由于密钥名称和密钥提供程序是特定于实现的 已分别在 AesCng TripleDESCng classes 中为 AES 和 3DES 算法添加持久密钥对称加密。
查看 AesCng and TripleDESCng classes 的文档后,我注意到每个 class 中都有许多构造函数允许 class 从 'existing persisted AES/TripleDES key' 创建 - 但没有机制可以在 class 本身中保留密钥。
经过一些研究,我遇到了 CngKey class that appears to offer this functionality - however, neither AES or TripleDES are listed in the suite of algorithms outlined in the CngAlgortithm class。
本质上,我的问题是如何在 C#.NET 中创建持久的 AesCng 密钥或 TripleDesCng 密钥?
我注意到之前在 Whosebug 上有人问过
以下方法创建一个持久的 32 字节密钥 (AES-256),如果它不存在,或者加载一个持久密钥,如果它已经存在,并执行加密:
private static string encryptWithKey(string plaintext, string keyName, string iv)
{
CngProvider keyStorageProvider = CngProvider.MicrosoftSoftwareKeyStorageProvider;
if (!CngKey.Exists(keyName, keyStorageProvider))
{
CngKeyCreationParameters keyCreationParameters = new CngKeyCreationParameters()
{
Provider = keyStorageProvider
};
CngKey.Create(new CngAlgorithm("AES"), keyName, keyCreationParameters);
}
Aes aes = new AesCng(keyName, keyStorageProvider);
aes.IV = Encoding.UTF8.GetBytes(iv);
var encryptor = aes.CreateEncryptor();
byte[] plaintextBytes = Encoding.UTF8.GetBytes(plaintext);
byte[] ciphertextBytes = encryptor.TransformFinalBlock(plaintextBytes, 0, plaintextBytes.Length);
aes.Dispose();
return Convert.ToBase64String(ciphertextBytes);
}
请注意,key默认是exportable,所以aes.Key
会生成一个System.Security.Cryptography.CryptographicException:请求的操作是不支持,即例如:
var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
会抛出那个异常。也许这个或类似的东西是您环境中出现异常的原因。
对应的解密对应为:
private static string decryptWithKey(string ciphertext, string keyName, string iv)
{
CngProvider keyStorageProvider = CngProvider.MicrosoftSoftwareKeyStorageProvider;
if (!CngKey.Exists(keyName, keyStorageProvider))
{
throw new Exception("Error: key doesn't exist...");
}
Aes aes = new AesCng(keyName, keyStorageProvider);
aes.IV = Encoding.UTF8.GetBytes(iv);
var decryptor = aes.CreateDecryptor();
byte[] ciphertextBytes = Convert.FromBase64String(ciphertext);
byte[] plaintextBytes = decryptor.TransformFinalBlock(ciphertextBytes, 0, ciphertextBytes.Length);
aes.Dispose();
return Encoding.UTF8.GetString(plaintextBytes);
}
以下代码:
string plaintext = "The quick brown fox jumps over the lazy dog";
string iv = "0123456789012345";
string keyName = "keyName";
string ciphertext1 = encryptWithKey(plaintext, keyName, iv);
Console.WriteLine(ciphertext1);
string ciphertext2 = encryptWithKey(plaintext, keyName, iv);
Console.WriteLine(ciphertext2);
string decryptedText1 = decryptWithKey(ciphertext1, keyName, iv);
Console.WriteLine(decryptedText1);
执行两次加密,在两种情况下生成 相同的 密文:第一次调用创建并保存密钥,第二次调用加载保存的密钥。相同的密文证明使用了相同的密钥进行加密
为了也能导出key,需要做如下修改:
CngKeyCreationParameters keyCreationParameters = new CngKeyCreationParameters()
{
ExportPolicy = CngExportPolicies.AllowPlaintextExport,
Provider = keyStorageProvider
};
现在可以访问密钥,例如aes.Key
.
请注意,静态 IV 仅用于比较两种加密的密文。当然,实际上,每次加密都必须应用随机 IV。
为了完整起见,应该提到代码与 .NET Core 兼容(已针对 .NET Core 3.1 进行测试)
密钥持久性与 3DES 类似,即 TripleDESCng
。由于必须使用标识符 3DES
(无效的标识符也会触发上面发布的 CryptographicException),即:
CngKey.Create(new CngAlgorithm("3DES"), keyName, keyCreationParameters);
这将创建并保留一个 24 字节的密钥 (3TDEA)。但是请注意,与当前标准 AES 相比,过时且性能相对较差的 3DES 不应再应用。