C# 如果在C中使用PBKDF2或RFC2898代码加密或解密数据,是否不需要存储哈希密码#
最后附加了实际引用的代码。 从上面的链接中我可以理解到 我得出以下结论。如果我错了,请纠正我C# 如果在C中使用PBKDF2或RFC2898代码加密或解密数据,是否不需要存储哈希密码#,c#,encryption,cryptography,aes,pbkdf2,C#,Encryption,Cryptography,Aes,Pbkdf2,最后附加了实际引用的代码。 从上面的链接中我可以理解到 我得出以下结论。如果我错了,请纠正我 密码存储 pwd1 不是必需的 盐的储存** salt1 **是必要的 如果将密码散列为密钥“k1”,则将“data1”加密为“edata1”,甚至存储散列的密码=密钥 k1 ,不是必需的。我的想法背后的原因是加密数据“edata1”只能用密钥“k1”解密,并且只有用户输入正确的密码“pwd1”才能生成“k1” 结论:不需要存储密码“pwd1”。 无需存储散列密码,即“k1”。 如果我错了,请纠正我。多
using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;
public class rfc2898test
{
// Generate a key k1 with password pwd1 and salt salt1.
// Generate a key k2 with password pwd1 and salt salt1.
// Encrypt data1 with key k1 using symmetric encryption, creating edata1.
// Decrypt edata1 with key k2 using symmetric decryption, creating data2.
// data2 should equal data1.
private const string usageText = "Usage: RFC2898 <password>\nYou must specify the password for encryption.\n";
public static void Main(string[] passwordargs)
{
//If no file name is specified, write usage text.
if (passwordargs.Length == 0)
{
Console.WriteLine(usageText);
}
else
{
string pwd1 = passwordargs[0];
// Create a byte array to hold the random value.
byte[] salt1 = new byte[8];
using (RNGCryptoServiceProvider rngCsp = new
RNGCryptoServiceProvider())
{
// Fill the array with a random value.
rngCsp.GetBytes(salt1);
}
//data1 can be a string or contents of a file.
string data1 = "Some test data";
//The default iteration count is 1000 so the two methods use the same iteration count.
int myIterations = 1000;
try
{
Rfc2898DeriveBytes k1 = new Rfc2898DeriveBytes(pwd1, salt1,
myIterations);
Rfc2898DeriveBytes k2 = new Rfc2898DeriveBytes(pwd1, salt1);
// Encrypt the data.
Aes encAlg = Aes.Create();
encAlg.Key = k1.GetBytes(16);
MemoryStream encryptionStream = new MemoryStream();
CryptoStream encrypt = new CryptoStream(encryptionStream,
encAlg.CreateEncryptor(), CryptoStreamMode.Write);
byte[] utfD1 = new System.Text.UTF8Encoding(false).GetBytes(
data1);
encrypt.Write(utfD1, 0, utfD1.Length);
encrypt.FlushFinalBlock();
encrypt.Close();
byte[] edata1 = encryptionStream.ToArray();
k1.Reset();
// Try to decrypt, thus showing it can be round-tripped.
Aes decAlg = Aes.Create();
decAlg.Key = k2.GetBytes(16);
decAlg.IV = encAlg.IV;
MemoryStream decryptionStreamBacking = new MemoryStream();
CryptoStream decrypt = new CryptoStream(
decryptionStreamBacking, decAlg.CreateDecryptor(), CryptoStreamMode.Write);
decrypt.Write(edata1, 0, edata1.Length);
decrypt.Flush();
decrypt.Close();
k2.Reset();
string data2 = new UTF8Encoding(false).GetString(
decryptionStreamBacking.ToArray());
if (!data1.Equals(data2))
{
Console.WriteLine("Error: The two values are not equal.");
}
else
{
Console.WriteLine("The two values are equal.");
Console.WriteLine("k1 iterations: {0}", k1.IterationCount);
Console.WriteLine("k2 iterations: {0}", k2.IterationCount);
}
}
catch (Exception e)
{
Console.WriteLine("Error: {0}", e);
}
}
}
}
使用系统;
使用System.IO;
使用系统文本;
使用System.Security.Cryptography;
公共类RFC2898测试
{
//生成密码为pwd1和salt salt1的密钥k1。
//生成密码为pwd1和salt salt1的密钥k2。
//使用对称加密使用密钥k1加密数据1,创建edata1。
//使用对称解密使用密钥k2解密edata1,创建数据2。
//数据2应该等于数据1。
private const string usageText=“用法:RFC2898\n必须指定加密密码。\n”;
公共静态void Main(字符串[]passwordargs)
{
//如果未指定文件名,请写入用法文本。
if(passwordargs.Length==0)
{
控制台写入线(usageText);
}
其他的
{
字符串pwd1=passwordargs[0];
//创建一个字节数组来保存随机值。
字节[]salt1=新字节[8];
使用(RNGCryptoServiceProvider rngCsp=new)
RNGCryptoServiceProvider())
{
//用随机值填充数组。
rngCsp.GetBytes(salt1);
}
//数据1可以是字符串或文件内容。
string data1=“一些测试数据”;
//默认迭代计数为1000,因此这两个方法使用相同的迭代计数。
int=1000;
尝试
{
Rfc2898DeriveBytes k1=新的Rfc2898DeriveBytes(pwd1,salt1,
我的迭代);
Rfc2898DeriveBytes k2=新的Rfc2898DeriveBytes(pwd1,salt1);
//加密数据。
Aes encAlg=Aes.Create();
encAlg.Key=k1.GetBytes(16);
MemoryStream encryptionStream=新的MemoryStream();
CryptoStream encrypt=新加密流(encryptionStream,
encAlg.CreateEncryptor(),CryptoStreamMode.Write);
byte[]utfD1=新系统.Text.UTF8Encoding(false).GetBytes(
数据1);
加密.Write(utfD1,0,utfD1.Length);
encrypt.FlushFinalBlock();
encrypt.Close();
字节[]edata1=encryptionStream.ToArray();
k1.Reset();
//尝试解密,从而显示它可能是往返的。
Aes贴花=Aes.Create();
贴花键=k2.GetBytes(16);
贴花IV=包装IV;
MemoryStream DecryptionStreamBack=新建MemoryStream();
CryptoStream decrypt=新加密流(
DecryptionStreamBack,贴花.CreateDecryptor(),CryptoStreamMode.Write);
解密.写入(edata1,0,edata1.Length);
decrypt.Flush();
decrypt.Close();
k2.Reset();
string data2=新的UTF8Encoding(false).GetString(
decryptionstreamback.ToArray());
如果(!data1.Equals(data2))
{
WriteLine(“错误:两个值不相等。”);
}
其他的
{
WriteLine(“两个值相等”);
WriteLine(“k1迭代:{0}”,k1.IterationCount);
WriteLine(“k2迭代:{0}”,k2.IterationCount);
}
}
捕获(例外e)
{
WriteLine(“错误:{0}”,e);
}
}
}
}
通常您使用密码派生函数(如PBKDF2)获取密钥(此处为“k1”)进行加密和解密,因此对于解密,用户再次输入密码,并通过PBKDF2再次接收(相同)密钥进行解密。将钥匙(“k1”)存放在任何地方都是不安全的。只需记住一件事——您需要存储(随机)生成的salt和重建同一密钥的迭代次数。如果它运行时没有错误,那么它将演示新的Rfc2898DeriveBytes(pwd1,salt1,1000)代码>和新的Rfc2898DeriveBytes(pwd1,salt1)代码>是相等的。这意味着不指定迭代计数将导致迭代计数为1000。这本可以做得更简单,比如把这个默认行为放到文档中-@ArtjomB它被“隐藏”在文档示例代码中的注释中://默认迭代计数为1000,因此这两个方法使用相同的迭代计数。