我知道其他问题已经被问到,但到目前为止还没有提供解决方案,或者正是我的问题。
下面的类处理字符串的加密和解密,传入的密钥和向量总是相同的。
加密和解密的字符串总是数字,大多数工作但偶尔会在解密时失败(但仅在生产服务器上)。我应该提到本地和生产环境都在Windows Server 2003上的IIS6中,使用该类的代码位于.ashx处理程序中。生产服务器上失败的示例是“0000232668”
错误消息是
System.Security.Cryptography.CryptographicException:填充无效且无法删除。 在System.Security.Cryptography.RijndaelManagedTransform.DecryptData(Byte [] inputBuffer,Int32 inputOffset,Int32 inputCount,Byte []& outputBuffer,Int32 outputOffset,PaddingMode paddingMode,Boolean fLast)
代码
public class Aes
{
private byte[] Key;
private byte[] Vector;
private ICryptoTransform EncryptorTransform, DecryptorTransform;
private System.Text.UTF8Encoding UTFEncoder;
public Aes(byte[] key, byte[] vector)
{
this.Key = key;
this.Vector = vector;
// our encyption method
RijndaelManaged rm = new RijndaelManaged();
rm.Padding = PaddingMode.PKCS7;
// create an encryptor and decyptor using encryption method. key and vector
EncryptorTransform = rm.CreateEncryptor(this.Key, this.Vector);
DecryptorTransform = rm.CreateDecryptor(this.Key, this.Vector);
// used to translate bytes to text and vice versa
UTFEncoder = new System.Text.UTF8Encoding();
}
/// Encrypt some text and return a string suitable for passing in a URL.
public string EncryptToString(string TextValue)
{
return ByteArrToString(Encrypt(TextValue));
}
/// Encrypt some text and return an encrypted byte array.
public byte[] Encrypt(string TextValue)
{
//Translates our text value into a byte array.
Byte[] bytes = UTFEncoder.GetBytes(TextValue);
Byte[] encrypted = null;
//Used to stream the data in and out of the CryptoStream.
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(memoryStream, EncryptorTransform, CryptoStreamMode.Write))
{
cs.Write(bytes, 0, bytes.Length);
}
encrypted = memoryStream.ToArray();
}
return encrypted;
}
/// The other side: Decryption methods
public string DecryptString(string EncryptedString)
{
return Decrypt(StrToByteArray(EncryptedString));
}
/// Decryption when working with byte arrays.
public string Decrypt(byte[] EncryptedValue)
{
Byte[] decryptedBytes = null;
using (MemoryStream encryptedStream = new MemoryStream())
{
using (CryptoStream decryptStream = new CryptoStream(encryptedStream, DecryptorTransform, CryptoStreamMode.Write))
{
decryptStream.Write(EncryptedValue, 0, EncryptedValue.Length);
}
decryptedBytes = encryptedStream.ToArray();
}
return UTFEncoder.GetString(decryptedBytes);
}
/// Convert a string to a byte array. NOTE: Normally we'd create a Byte Array from a string using an ASCII encoding (like so).
// System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
// return encoding.GetBytes(str);
// However, this results in character values that cannot be passed in a URL. So, instead, I just
// lay out all of the byte values in a long string of numbers (three per - must pad numbers less than 100).
public byte[] StrToByteArray(string str)
{
if (str.Length == 0)
throw new Exception("Invalid string value in StrToByteArray");
byte val;
byte[] byteArr = new byte[str.Length / 3];
int i = 0;
int j = 0;
do
{
val = byte.Parse(str.Substring(i, 3));
byteArr[j++] = val;
i += 3;
}
while (i < str.Length);
return byteArr;
}
// Same comment as above. Normally the conversion would use an ASCII encoding in the other direction:
// System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
// return enc.GetString(byteArr);
public string ByteArrToString(byte[] byteArr)
{
byte val;
string tempStr = "";
for (int i = 0; i <= byteArr.GetUpperBound(0); i++)
{
val = byteArr[i];
if (val < (byte)10)
tempStr += "00" + val.ToString();
else if (val < (byte)100)
tempStr += "0" + val.ToString();
else
tempStr += val.ToString();
}
return tempStr;
}
编辑:感谢您的所有帮助,但是您的答案并没有解决问题,结果证明是非常简单的。我在一台服务器上生成一个加密的字符串,并将其交给另一台服务器上的处理程序进行decrpytion和处理,但事实证明,当在不同的服务器上运行时,加密结果会有所不同,因此接收服务器无法对其进行解密。其中一个答案偶然发现了这个暗示,这就是我接受它的原因
答案 0 :(得分:12)
当加密和解密因任何原因未使用相同的密钥或初始化向量时,有时会收到有关无效填充的消息。填充是添加到明文末尾的一些字节,以使密码可以处理完整数量的块。在PKCS7填充中,每个字节等于添加的字节数,因此在解密后总是可以删除它。你的解密导致了一个字符串,其中最后一个 n 字节不等于最后一个字节的值 n (希望句子有意义)。所以我会仔细检查你所有的钥匙。
或者,在您的情况下,我建议您确保为每个加密和解密操作创建并配置RijndaelManagedTransform
的实例,并使用密钥和向量对其进行初始化。重新使用此转换对象可能会导致此问题,这意味着在第一次使用后,它不再处于正确的初始状态。
答案 1 :(得分:12)
我倾向于在关闭之前在CryptoStream上显式调用FlushFinalBlock方法。这意味着在您的加密方法中执行以下操作:
using (CryptoStream cs = new CryptoStream(memoryStream, EncryptorTransform, CryptoStreamMode.Write))
{
cs.Write(bytes, 0, bytes.Length);
cs.FlushFinalBlock();
}
如果不这样做,可能是加密数据被截断 - 这将导致“无效填充”方案。使用PKCS7时始终存在填充,即使加密的数据与密码的块长度对齐。
答案 2 :(得分:2)
这导致无法在URL中传递的字符值
您是否有理由使用自己的编码StrToByteArray
而不是Base64编码?
如果您进行了这些更改:
public string EncryptToString(string TextValue)
{
return Convert.ToBase64String(Encrypt(TextValue));
}
public string DecryptToString(string TextValue)
{
return Decrypt(Convert.FromBase64String(TextValue));
}
那么事情应该会好很多。
修改强>
关于ToBase64String和QueryString的问题:
如果您自己进行QueryString解析,那么您需要确保在第一个= -sign上只拆分。
var myURL = "http://somewhere.com/default.aspx?encryptedID=s9W/h7Sls98sqw==&someKey=someValue";
var myQS = myURL.SubString(myURL.IndexOf("?") + 1);
var myKVPs = myQS.Split("&");
foreach (var kvp in myKVPs) {
// It is important you specify a maximum number of 2 elements
// since the Base64 encoded string might contain =-signs.
var keyValue = kvp.Split("=", 2);
var key = keyValue[0];
var value = keyValue[1];
if (key == "encryptedID")
var decryptedID = myAES.DecryptToString(value);
}
这样,当它的Base64编码时,你不需要替换QueryString中的任何字符。