c# - C# System.Security.Cryptography - 使用不正确的密钥解密时引发异常 - 填充如何工作?
问题描述
我一直在尝试通过在线文档和示例在 c# 中了解更多关于 System.Security.Cryptography 的信息。到目前为止,我已经能够成功地加密/解密文本和文件;但是,当我尝试使用不正确的密钥解密时抛出异常。
Blockquote System.Security.Cryptography.CryptographicException:“填充无效,无法删除。”
加密
private static byte[] EncryptData(SymmetricAlgorithms symmetricAlgorithm, byte[] inputBytes, byte[] key)
{
byte[] output;
using (SymmetricAlgorithm algorithm = SymmetricAlgorithmFactory(symmetricAlgorithm))
{
byte[] encrypted;
byte[] salt = new byte[PBKDF2_SaltBytes];
int maxKeySize = GetLegalKeySizes(algorithm).Max();
_rng.GetBytes(salt); //
using (Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(key, salt, PBKDF2_Iterations))
{
algorithm.Key = pbkdf2.GetBytes(maxKeySize);
}
//algorithm.Padding = PaddingMode.PKCS7;
using (ICryptoTransform cryptoTransform = algorithm.CreateEncryptor())
{
using (MemoryStream inputStream = new MemoryStream(inputBytes), transformedStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(inputStream, cryptoTransform, CryptoStreamMode.Read))
{
cryptoStream.CopyTo(transformedStream);
}
encrypted = transformedStream.ToArray();
}
}
output = new byte[salt.Length + algorithm.IV.Length + encrypted.Length];
Buffer.BlockCopy(salt, 0, output, 0, salt.Length);
Buffer.BlockCopy(algorithm.IV, 0, output, salt.Length, algorithm.IV.Length);
Buffer.BlockCopy(encrypted, 0, output, salt.Length + algorithm.IV.Length, encrypted.Length);
}
return output;
}
解密
private static byte[] DecryptData(SymmetricAlgorithms symmetricAlgorithm, byte[] inputBytes, byte[] key)
{
using (SymmetricAlgorithm algorithm = SymmetricAlgorithmFactory(symmetricAlgorithm))
{
byte[] salt = new byte[PBKDF2_SaltBytes];
byte[] iv = new byte[algorithm.IV.Length];
byte[] encryptedData = new byte[inputBytes.Length - salt.Length - iv.Length];
int maxKeySize = GetLegalKeySizes(algorithm).Max();
Buffer.BlockCopy(inputBytes, 0, salt, 0, salt.Length);
Buffer.BlockCopy(inputBytes, salt.Length, iv, 0, iv.Length);
Buffer.BlockCopy(inputBytes, salt.Length + iv.Length, encryptedData, 0, encryptedData.Length);
algorithm.IV = iv;
//algorithm.Padding = PaddingMode.PKCS7;
using (Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(key, salt, PBKDF2_Iterations))
{
algorithm.Key = pbkdf2.GetBytes(maxKeySize);
}
using(ICryptoTransform cryptoTransform = algorithm.CreateDecryptor())
{
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write))
{
cryptoStream.Write(encryptedData, 0, encryptedData.Length);
cryptoStream.FlushFinalBlock();
}
return memoryStream.ToArray();
}
}
}
}
SymmetricAlgorithmFactory 返回一个 SymmetricAlgorithm 实例
private static SymmetricAlgorithm SymmetricAlgorithmFactory(SymmetricAlgorithms symmetricAlgorithm)
{
switch (symmetricAlgorithm)
{
case SymmetricAlgorithms.AES:
return new AesCryptoServiceProvider();
case SymmetricAlgorithms.DES:
return new DESCryptoServiceProvider();
case SymmetricAlgorithms.RC2:
return new RC2CryptoServiceProvider();
case SymmetricAlgorithms.Rijndael:
return new RijndaelManaged();
case SymmetricAlgorithms.TripleDES:
return new TripleDESCryptoServiceProvider();
default:
throw new Exception("The provided Symmetric algorithm is not supported.");
}
}
public enum SymmetricAlgorithms
{
AES,
DES,
RC2,
Rijndael,
TripleDES
}
额外类属性
private static readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create();
private const int PBKDF2_SaltBytes = 8; //PBKDF2 recommends 64 bits minimum. 64 / 8 = 8 bytes
private const int PBKDF2_Iterations = 10000;
private static Encoding encoding = Encoding.UTF32;
使用 AES 进行测试,然后我尝试更改填充属性并得到一些不同的消息
PaddingMode.None
- 加密方法抛出异常
Blockquote System.Security.Cryptography.CryptographicException: '输入数据不是一个完整的块。'
PaddingMode.PKCS7 + PaddingMode.ANSIX923 + PaddingMode.ISO10126
- 解密方法抛出异常
Blockquote System.Security.Cryptography.CryptographicException:“填充无效,无法删除。”
PaddingMode.Zeros
- 这似乎适用于正确和不正确的密钥
谁能帮助我理解填充以及为什么某些模式会失败?会不会是每个不同的对称算法都需要不同的填充?
解决方案
维基百科有一篇关于加密填充的文章可能会有所帮助。
块密码以块大小的块加密数据。大多数消息不是块的确切数量,因此需要填充以将最后一个块填充到正确的大小。解密时,填充会自动删除。
在您的情况下,解密无法识别填充和抱怨。
可能的原因有很多。不正确的键会破坏一切,包括填充。实际上,任何解密错误都会影响填充,从而导致此问题。
显然,如果加密函数应用一种类型的填充,而解密函数需要不同的类型,那么你会遇到这个问题。检查双方是否使用相同的填充。除非您有充分的理由,否则请使用 PKCS#7。
当您设置PaddingMode.None
加密方法时没有添加填充,因此最后一个块未填充,因此大小错误。因此,加密方对其进行了标记。该模式仅对已经是整数块的固定大小的消息有用。
PaddingMode.Zeros
如果最后一个块上有尾随零,则可能会起作用。问题是它会丢失原始消息中的所有尾随零(如果存在)。不建议。
使用多种填充模式是非常不寻常的;一个就足够了。怀疑是解密方式选错了三者的模式先试试。
推荐阅读
- algorithm - 检查着色图所需的最少颜色数(2-正则图中的色数)
- apache-kafka - 将总和存储在 KTable 中
- c# - .net 核心应用程序无法连接到 rabbitMQ(两者都通过 docker-compose 在 docker 网络中运行)
- python - Python 请求和我的浏览器没有给我相同的结果
- android - 用于改造服务的 RxJava2 错误处理包装器
- gdb - 在gdb中遇到断点后如何继续执行?
- java - 菜单项上的 NullPointerException
- amazon-web-services - 有没有直接的方法——一个 cft 来列出特定区域的所有 lambda 函数?
- ios - dynamicMemberLoookup ExpressibleByStringLiteral 参数 swift
- vb.net - 以非列表方式向 DataTable 添加大量列?