C# 盐中的什么导致解密失败
下面和小提琴中的代码不是用于生产,而是用于教育目的。我不想修复任何东西,因为我有一个可行的解决方案。然而,我想知道为什么:C# 盐中的什么导致解密失败,c#,asp.net,cryptography,C#,Asp.net,Cryptography,下面和小提琴中的代码不是用于生产,而是用于教育目的。我不想修复任何东西,因为我有一个可行的解决方案。然而,我想知道为什么: var password = "password"; var salt = Encoding.ASCII.GetBytes(password.Length.ToString()); var secret = new PasswordDeriveBytes(password, salt); 当实现上述功能时,在以下方法中,FixedEncryptor将起作用 // Vali
var password = "password";
var salt = Encoding.ASCII.GetBytes(password.Length.ToString());
var secret = new PasswordDeriveBytes(password, salt);
当实现上述功能时,在以下方法中,FixedEncryptor
将起作用
// Valid:
public static string FixedEncryptor(string content)
{
var cipher = new RijndaelManaged();
var plain = Encoding.Unicode.GetBytes(content);
var key = new PasswordDeriveBytes(password, salt);
using (var encrypt = cipher.CreateEncryptor(key.GetBytes(32), key.GetBytes(16)))
using (var stream = new MemoryStream())
using (var crypto = new CryptoStream(stream, encrypt, CryptoStreamMode.Write))
{
crypto.Write(plain, 0, plain.Length);
crypto.FlushFinalBlock();
return Convert.ToBase64String(stream.ToArray());
}
}
但是,如果您实施:
var secret = new PasswordDeriveBytes("password",
Encoding.ASCII.GetBytes("password"));
代码将突然产生:
运行时异常(第70行):填充无效,无法删除
删除
堆栈跟踪:
[System.Security.Cryptography.CryptographyException:填充为
无效,无法删除。]在Crypt.Decryptor(字符串内容):
Program.Main()处的第70行:第17行
如以下方法所示:
// Invalid:
public static string Encryptor(string content)
{
var cipher = new RijndaelManaged();
var plain = Encoding.Unicode.GetBytes(content);
var key = new PasswordDeriveBytes("password", Encoding.ASCII.GetBytes("password"));
using (var encrypt = cipher.CreateEncryptor(key.GetBytes(32), key.GetBytes(16)))
using (var stream = new MemoryStream())
using (var crypto = new CryptoStream(stream, encrypt, CryptoStreamMode.Write))
{
crypto.Write(plain, 0, plain.Length);
crypto.FlushFinalBlock();
return Convert.ToBase64String(stream.ToArray());
}
}
那么,为什么一个能成功解密,而另一个不能正确解密并产生上述错误呢
一个很小的例子是。首先,生成盐的方法根本不安全;其次,
PasswordDerivedBytes
已被弃用,您应该看看它的后续版本,Rfc2898DeriveBytes
尝试以下操作-注意,这需要使用一些语句:System
、System.IO
、System.Security.Cryptography
和System.Text
只需使用encrypt(明文,密码)
加密数据,然后使用decrypt(EncryptedData,密码)
再次解密即可。salt作为前16个字节滚入加密数据,并且对于每一轮加密/解密,它是完全随机的
这段代码是我自己的开源密码管理器的一部分
/*
* Encryption/Decryption, based on AES256 and PBKDF2
*/
public string Encrypt (string plainText, string passPhrase, bool fast_encrypt = false)
{
string result;
using (Rijndael algR = Rijndael.Create ()) {
RNGCryptoServiceProvider rngC = new RNGCryptoServiceProvider ();
byte[] iv = new byte[16];
rngC.GetBytes (iv);
Rfc2898DeriveBytes derived = new Rfc2898DeriveBytes (passPhrase, iv, fast_encrypt ? 10 : 3000);
algR.KeySize = 256;
algR.BlockSize = 128;
algR.Key = derived.GetBytes (32);
algR.IV = iv;
using (MemoryStream memoryStream = new MemoryStream ()) {
memoryStream.Write (iv, 0, 16);
using (CryptoStream cryptoStreamEncrypt = new CryptoStream (memoryStream, algR.CreateEncryptor (algR.Key, algR.IV), CryptoStreamMode.Write)) {
using (StreamWriter streamWriterEncrypt = new StreamWriter (cryptoStreamEncrypt)) {
streamWriterEncrypt.Write (plainText);
}
}
result = Convert.ToBase64String (memoryStream.ToArray ());
}
}
return result;
}
public string Decrypt (string cipherText, string passPhrase, bool fast_decrypt = false)
{
string result;
using (Rijndael algR = Rijndael.Create ()) {
using (MemoryStream memoryStream = new MemoryStream (Convert.FromBase64String (cipherText))) {
byte[] iv = new byte[16];
memoryStream.Read (iv, 0, 16);
Rfc2898DeriveBytes derived = new Rfc2898DeriveBytes (passPhrase, iv, fast_decrypt ? 10 : 3000);
algR.KeySize = 256;
algR.BlockSize = 128;
algR.Key = derived.GetBytes (32);
algR.IV = iv;
using (CryptoStream cryptoStreamDecrypt = new CryptoStream (memoryStream, algR.CreateDecryptor (algR.Key, algR.IV), CryptoStreamMode.Read)) {
using (StreamReader streamReaderDecrypt = new StreamReader (cryptoStreamDecrypt)) {
result = streamReaderDecrypt.ReadToEnd ();
}
}
}
}
return result;
}
从您发布的代码示例中,您的问题来自于您正在使用两种不同的盐
在FixedEncryptor中,您使用
Encoding.ASCII.GetBytes(password.Length.ToString());
编码为等于{56}
的字节数组,这是因为Length
返回8
,然后调用ToString()返回字符串“8”,并将其转换为ascii值56
在加密机中,您使用的是
Encoding.ASCII.GetBytes("password")
它编码为字节数组,等于{112、97、115、115、119、111、114、100}
,这是字符“p”、“a”、“s”、“s”、“w”、“o”、“r”和“d”的ascii值
您遇到的问题是您只尝试在解密函数中使用{56}
,因此您的问题归结为您的加密函数和解密函数使用了两种不同的盐
如果我制作了一个新的解密程序
以使用与加密程序
相同的密码和密码,然后制作一个单独的固定加密程序
以匹配固定加密程序
的密码,一切都会正常工作
public class Program
{
public static void Main()
{
var message = "Hello World!";
var fixedCipherText = Crypt.FixedEncryptor(message);
var cipherText = Crypt.Encryptor(message);
Console.WriteLine(cipherText);
Console.WriteLine(fixedCipherText);
var plainText = Crypt.Decryptor(cipherText);
var fixedPlainText = Crypt.FixedDecryptor(fixedCipherText);
Console.WriteLine(plainText);
Console.WriteLine(fixedPlainText);
}
}
public static class Crypt
{
private const string password = "password";
private readonly static byte[] salt = Encoding.ASCII.GetBytes(password.Length.ToString());
public static string FixedEncryptor(string content)
{
var cipher = new RijndaelManaged();
var plain = Encoding.Unicode.GetBytes(content);
var key = new PasswordDeriveBytes(password, salt);
using (var encrypt = cipher.CreateEncryptor(key.GetBytes(32), key.GetBytes(16)))
using (var stream = new MemoryStream())
using (var crypto = new CryptoStream(stream, encrypt, CryptoStreamMode.Write))
{
crypto.Write(plain, 0, plain.Length);
crypto.FlushFinalBlock();
return Convert.ToBase64String(stream.ToArray());
}
}
public static string Encryptor(string content)
{
var cipher = new RijndaelManaged();
var plain = Encoding.Unicode.GetBytes(content);
var key = new PasswordDeriveBytes("password", Encoding.ASCII.GetBytes("password"));
using (var encrypt = cipher.CreateEncryptor(key.GetBytes(32), key.GetBytes(16)))
using (var stream = new MemoryStream())
using (var crypto = new CryptoStream(stream, encrypt, CryptoStreamMode.Write))
{
crypto.Write(plain, 0, plain.Length);
crypto.FlushFinalBlock();
return Convert.ToBase64String(stream.ToArray());
}
}
public static string FixedDecryptor(string content)
{
var cipher = new RijndaelManaged();
var encrypted = Convert.FromBase64String(content);
var key = new PasswordDeriveBytes(password, salt);
using (var decryptor = cipher.CreateDecryptor(key.GetBytes(32), key.GetBytes(16)))
using (var stream = new MemoryStream(encrypted))
using (var crypto = new CryptoStream(stream, decryptor, CryptoStreamMode.Read))
{
byte[] plain = new byte[encrypted.Length];
int decrypted = crypto.Read(plain, 0, plain.Length);
string data = Encoding.Unicode.GetString(plain, 0, decrypted);
return data;
}
}
public static string Decryptor(string content)
{
var cipher = new RijndaelManaged();
var encrypted = Convert.FromBase64String(content);
var key = new PasswordDeriveBytes("password", Encoding.ASCII.GetBytes("password"));
using (var decryptor = cipher.CreateDecryptor(key.GetBytes(32), key.GetBytes(16)))
using (var stream = new MemoryStream(encrypted))
using (var crypto = new CryptoStream(stream, decryptor, CryptoStreamMode.Read))
{
byte[] plain = new byte[encrypted.Length];
int decrypted = crypto.Read(plain, 0, plain.Length);
string data = Encoding.Unicode.GetString(plain, 0, decrypted);
return data;
}
}
}
这是守则的一部分
然而,这仍然不是“正确”的做事方式。请参阅答案这两段代码不相等-第一段代码缺少ToString()
call。@Greg将重新创建问题的代码最小化,并将其发布到此处。你现在的问题不太可能得到帮助。@ScottChamberlain我得到了。上面的代码不是我在问题中所说的产品,而是用于教育目的。我想知道的是:为什么使用这两种不同的方法来创建salt
,为什么一种方法在解密时会导致堆栈跟踪异常,而另一种不会。那么,不使用PasswordDerivedBytes这一点就更为重要了;从教育角度讲,学习正确的做事方法是更好的实践。就像在你最喜欢的书桌上修一个凹痕;当然,你可以在它上面画画,使它看起来光滑,但你不更愿意正确地修复它吗?@Greg,因为你解密时只使用一种方法来创建盐。你的两种方法产生了两种不同的盐,但是你在解密过程中只使用了这两种不同的盐中的一种。我知道这不是正确的方法,我看到了那段代码,对它很感兴趣,然后我注意到了它的特殊性。所以我想我会问为什么不一致。你说的盐是不同的,这是有道理的。