CryptographicException:错误的PKCS7填充
我看到一小部分生产用户随机报告这个与Xamarin.Android加密/解密字符串有关的异常,但不幸的是我无法复制它。
什么可能导致这种情况和/或如何重现异常情况,以便我可以找出修复/解决方法?
[CryptographicException: Bad PKCS7 padding. Invalid length 147.]
Mono.Security.Cryptography.SymmetricTransform.ThrowBadPaddingException(PaddingMode padding, Int32 length, Int32 position):0
Mono.Security.Cryptography.SymmetricTransform.FinalDecrypt(System.Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount):0
Mono.Security.Cryptography.SymmetricTransform.TransformFinalBlock(System.Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount):0
System.Security.Cryptography.CryptoStream.FlushFinalBlock():0
com.abc.mobile.shared.Security+PasswordEncoder.DecryptWithByteArray(System.String strText, System.String strEncrypt):0
编辑:这是我用来加密/解密的代码
private string EncryptWithByteArray(string inPassword, string inByteArray)
{
byte[] tmpKey = new byte[20];
tmpKey = System.Text.Encoding.UTF8.GetBytes(inByteArray.Substring(0, 8));
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
byte[] inputArray = System.Text.Encoding.UTF8.GetBytes(inPassword);
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(tmpKey, mInitializationVector), CryptoStreamMode.Write);
cs.Write(inputArray, 0, inputArray.Length);
cs.FlushFinalBlock();
return Convert.ToBase64String(ms.ToArray());
}
private string DecryptWithByteArray (string strText, string strEncrypt)
{
try
{
byte[] tmpKey = new byte[20];
tmpKey = System.Text.Encoding.UTF8.GetBytes (strEncrypt.Substring (0, 8));
DESCryptoServiceProvider des = new DESCryptoServiceProvider ();
Byte[] inputByteArray = Convert.FromBase64String (strText);
MemoryStream ms = new MemoryStream ();
CryptoStream cs = new CryptoStream (ms, des.CreateDecryptor (tmpKey, mInitializationVector), CryptoStreamMode.Write);
cs.Write (inputByteArray, 0, inputByteArray.Length);
try {
cs.FlushFinalBlock();
} catch (Exception ex) {
throw(ex);
}
System.Text.Encoding encoding = System.Text.Encoding.UTF8;
return encoding.GetString(ms.ToArray());
}
catch (Exception ex)
{
throw ex;
}
}
编辑2:
加密密钥始终是本地设备ID。 这是我得到这个的方式:
TelephonyManager telephonyMgr = Application.Context.GetSystemService(Context.TelephonyService) as TelephonyManager;
string deviceId = telephonyMgr.DeviceId == null ? "UNAVAILABLE" : telephonyMgr.DeviceId;
这是一个如何调用它的例子:
string mByteArray = GetDeviceId();
string mEncryptedString = EncryptWithByteArray(stringToEncrypt, mByteArray);
string mDecryptedString = DecryptWithByteArray(mEncryptedString, mByteArray);
您没有提供有关您的使用案例的详细信息,但是我会说这是因为您在加密和解密操作期间没有使用相同的密码设置。 对称密码要求您在数据加密和解密过程中使用完全相同的设置/参数。 例如,对于AES CBC,您需要在两台设备上使用完全相同的密钥,IV,密码模式和填充。 最好在代码中明确设置这些设置:
System.Security.Cryptography.RijndaelManaged aes = new System.Security.Cryptography.RijndaelManaged();
aes.Key = new byte[] { ... };
aes.IV = new byte[] { ... };
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
如果您确定您使用的是相同的设置,那么您还应该考虑在网络传输过程中某些数据被破坏或更改的情况。
在提供了一些代码片段后进行编辑:
您所提供的解密方法根本不适用于我,因此我将所有样本放在一起,并将它们转换为与您的代码完全相同的代码,但使用的是稍微更干净的方法。 例如,这段代码使用了更强大的“密钥派生”(请原谅我cryptoguys),它也通过了基本的代码分析。
您应该能够轻松使用公共方法来执行您所需的操作:
string plainData = "This information should be encrypted";
string encryptedData = EncryptStringified(plainData);
string decryptedData = DecryptStringified(encryptedData);
if (plainData != decryptedData)
throw new Exception("Decryption failed");
实施和私人方法如下:
/// <summary>
/// Encrypts string with the key derived from device ID
/// </summary>
/// <returns>Base64 encoded encrypted data</returns>
/// <param name="stringToEncrypt">String to encrypt</param>
public string EncryptStringified(string stringToEncrypt)
{
if (stringToEncrypt == null)
throw new ArgumentNullException("stringToEncrypt");
byte[] key = DeviceIdToDesKey();
byte[] plainData = Encoding.UTF8.GetBytes(stringToEncrypt);
byte[] encryptedData = Encrypt(key, plainData);
return Convert.ToBase64String(encryptedData);
}
/// <summary>
/// Decrypts Base64 encoded data with the key derived from device ID
/// </summary>
/// <returns>Decrypted string</returns>
/// <param name="b64DataToDecrypt">Base64 encoded data to decrypt</param>
public string DecryptStringified(string b64DataToDecrypt)
{
if (b64DataToDecrypt == null)
throw new ArgumentNullException("b64DataToDecrypt");
byte[] key = DeviceIdToDesKey();
byte[] encryptedData = Convert.FromBase64String(b64DataToDecrypt);
byte[] decryptedData = Decrypt(key, encryptedData);
return Encoding.UTF8.GetString(decryptedData);
}
private byte[] DeviceIdToDesKey()
{
TelephonyManager telephonyMgr = Application.Context.GetSystemService(Context.TelephonyService) as TelephonyManager;
string deviceId = telephonyMgr.DeviceId ?? "UNAVAILABLE";
// Compute hash of device ID so we are sure enough bytes have been gathered for the key
byte[] bytes = null;
using (SHA1 sha1 = SHA1.Create())
bytes = sha1.ComputeHash(Encoding.UTF8.GetBytes(deviceId));
// Get last 8 bytes from device ID hash as a key
byte[] desKey = new byte[8];
Array.Copy(bytes, bytes.Length - desKey.Length, desKey, 0, desKey.Length);
return desKey;
}
private byte[] Encrypt(byte[] key, byte[] plainData)
{
if (key == null)
throw new ArgumentNullException("key");
if (plainData == null)
throw new ArgumentNullException("plainData");
using (DESCryptoServiceProvider desProvider = new DESCryptoServiceProvider())
{
if (!desProvider.ValidKeySize(key.Length * 8))
throw new CryptographicException("Key with invalid size has been specified");
desProvider.Key = key;
// desProvider.IV should be automatically filled with random bytes when DESCryptoServiceProvider instance is created
desProvider.Mode = CipherMode.CBC;
desProvider.Padding = PaddingMode.PKCS7;
using (MemoryStream encryptedStream = new MemoryStream())
{
// Write IV at the beginning of memory stream
encryptedStream.Write(desProvider.IV, 0, desProvider.IV.Length);
// Perform encryption and append encrypted data to the memory stream
using (ICryptoTransform encryptor = desProvider.CreateEncryptor())
{
byte[] encryptedData = encryptor.TransformFinalBlock(plainData, 0, plainData.Length);
encryptedStream.Write(encryptedData, 0, encryptedData.Length);
}
return encryptedStream.ToArray();
}
}
}
private byte[] Decrypt(byte[] key, byte[] encryptedData)
{
if (key == null)
throw new ArgumentNullException("key");
if (encryptedData == null)
throw new ArgumentNullException("encryptedData");
using (DESCryptoServiceProvider desProvider = new DESCryptoServiceProvider())
{
if (!desProvider.ValidKeySize(key.Length * 8))
throw new CryptographicException("Key with invalid size has been specified");
desProvider.Key = key;
if (encryptedData.Length <= desProvider.IV.Length)
throw new CryptographicException("Too short encrypted data has been specified");
// Read IV from the beginning of encrypted data
// Note: New byte array needs to be created because data written to desprovider.IV are ignored
byte[] iv = new byte[desProvider.IV.Length];
Array.Copy(encryptedData, 0, iv, 0, iv.Length);
desProvider.IV = iv;
desProvider.Mode = CipherMode.CBC;
desProvider.Padding = PaddingMode.PKCS7;
// Remove IV from the beginning of encrypted data and perform decryption
using (ICryptoTransform decryptor = desProvider.CreateDecryptor())
return decryptor.TransformFinalBlock(encryptedData, desProvider.IV.Length, encryptedData.Length - desProvider.IV.Length);
}
}
真的很难分辨你的代码到底是什么问题,因为你的解密方法根本不适用于我 - 很可能是因为它使用CryptoStream以写模式进行解密,这对我来说似乎有点奇怪。
代码非常多。 现在我们来看看真正非常弱的加密。 这只是一个混淆,应该保护数据免于以纯文本形式意外显示(有些人使用BASE64编码进行同样的处理)。 造成这种情况的主要原因是相对较旧的加密算法和容易预测的加密密钥。 AFAIK运行在同一设备上的每个应用程序都可以读取设备ID,而无需任何权限。 这意味着任何应用程序都可以解密您的数据。 当然,您的SQLite数据库可能仅适用于您的应用程序,但如果您移除SD卡或根目录手机,则该数据库不再成立。 为了使这个更好一点,你可以例如要求用户提供一个密码,然后用它来派生唯一的加密密钥,但这是完全不同的问题。 无论如何,我不太清楚你想用这种加密技术实现什么 - 即使可以认为它很弱,它也可能完全满足你的需求。
希望这可以帮助。
链接地址: http://www.djcxy.com/p/24495.html