C# - PBDKF2,空值
C# - PBDKF2, null values
我在 C# 中实现 AES,对于 IV 和密钥,我通过 Rfc2898DeriveBytes Class 使用 PBDKF2。当我 运行 它时,输入文本是加密的,当我查看 "key" 变量中的数据时,有一个错误说 "Hash = 'key.m_hmacsha1.Hash' threw an exception of type 'System.NullReferenceException'",以及其他 null values/0s其他方法。我怀疑我没有在我的代码中正确实现它,而且我在尝试诊断它时遇到了麻烦。我希望从在 C# 中实现 AES 的人那里获得见解。另外,如果用户输入了错误的密钥,我将如何在异常中捕获它?代码贴在下面。
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace AES
{
class Program
{
public static byte[] salt;
public static byte[] saltBytes;
static void Main(string[] args)
{
var encrypted = secure.EncryptText("abc", "123");
Console.WriteLine(encrypted);
Console.WriteLine(secure.DecryptText(encrypted, "123"));
}
public string EncryptText(string input, string password)
{
// Get the bytes of the string
byte[] bytesToBeEncrypted = Encoding.UTF8.GetBytes(input);
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
// Hash the password with SHA256
passwordBytes = SHA256.Create().ComputeHash(passwordBytes);
byte[] bytesEncrypted = AES_Encrypt(bytesToBeEncrypted, passwordBytes);
string result = Convert.ToBase64String(bytesEncrypted);
return result;
}
public string DecryptText(string input, string password)
{
// Get the bytes of the string
byte[] bytesToBeDecrypted = Convert.FromBase64String(input);
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
passwordBytes = SHA256.Create().ComputeHash(passwordBytes);
byte[] bytesDecrypted = AES_Decrypt(bytesToBeDecrypted, passwordBytes);
string result = Encoding.UTF8.GetString(bytesDecrypted);
return result;
}
public byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
byte[] encryptedBytes = null;
// Set your salt here, change it to meet your flavor:
// The salt bytes must be at least 8 bytes.
new RNGCryptoServiceProvider().GetBytes(salt = new byte[32]);
saltBytes = salt;
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
var iv = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.IV = iv.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
cs.Close();
}
encryptedBytes = ms.ToArray();
}
}
return encryptedBytes;
}
public byte[] AES_Decrypt(byte[] bytesToBeDecrypted, byte[] passwordBytes)
{
byte[] decryptedBytes = null;
// Set your salt here, change it to meet your flavor:
// The salt bytes must be at least 8 bytes.
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
var iv = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.IV = iv.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
cs.Close();
}
decryptedBytes = ms.ToArray();
}
}
return decryptedBytes;
}
}
}
when I view the data in the "key" variable, there's an error that says "Hash = 'key.m_hmacsha1.Hash' threw an exception of type 'System.NullReferenceException'", along with other null values/0s in other methods.
您正在检查不属于您的 class 的私有字段。这并不总能带来明智的结果。相反,您应该只依赖 public API.
if a user enters in the wrong key, how would I catch this in an exception?
您将获得指示 "the padding is invalid and cannot be removed" 的异常的可能性 (~15/16)。但实际上你应该通过
在加密之上执行此操作
a) 在密文上计算 HMAC(例如 HMACSHA256),然后在解密时验证 HMAC(验证失败表示密码错误或数据已被篡改),或
b) 明文中有一段格式良好的数据可用于验证。
(A) 是更好的答案,原因有很多。
你有类似
的代码
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
var iv = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.IV = iv.GetBytes(AES.BlockSize / 8);
由于 key
和 iv
的密码、salt 和迭代次数相同,因此您的实际 IV 是加密密钥的前 128 位。如果您希望它是一个计算值,您需要改变盐值(例如,将盐值的最后一个字节设置为 0
用于加密密钥,1
用于 IV,以及 2
用于 HMAC 密钥),或将其作为下一个输出字节执行(要求 32 + 16 + 32 字节并相应地将其切碎......或者,给定 .NET 实现,调用 GetBytes(32), GetBytes(16 ), GetBytes(32)(Rfc2898DeriveBytes.GetBytes 是一个流API))。
或者,对于IV,使用加密时随机生成的IV,与密文一起传输即可。 (如果这样做,请务必将其包含在 HMAC 计算中)
您将 salt
和 saltBytes
存储在字段中(尽管您将一个设置为另一个,所以不清楚为什么有两个字段)。您需要将盐值与密文一起传输,并让解密器接受它作为输入。
基本上,您的函数应该是 Encrypt(passphrase, data) => <salt, hmac, ciphertext>
(或 <salt, iv, hmac, ciphertext>
)和 Decrypt(passphrase, salt, [iv,] hmac, ciphertext) => data
。 (取决于 IV 是传输还是导出)
我在 C# 中实现 AES,对于 IV 和密钥,我通过 Rfc2898DeriveBytes Class 使用 PBDKF2。当我 运行 它时,输入文本是加密的,当我查看 "key" 变量中的数据时,有一个错误说 "Hash = 'key.m_hmacsha1.Hash' threw an exception of type 'System.NullReferenceException'",以及其他 null values/0s其他方法。我怀疑我没有在我的代码中正确实现它,而且我在尝试诊断它时遇到了麻烦。我希望从在 C# 中实现 AES 的人那里获得见解。另外,如果用户输入了错误的密钥,我将如何在异常中捕获它?代码贴在下面。
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace AES
{
class Program
{
public static byte[] salt;
public static byte[] saltBytes;
static void Main(string[] args)
{
var encrypted = secure.EncryptText("abc", "123");
Console.WriteLine(encrypted);
Console.WriteLine(secure.DecryptText(encrypted, "123"));
}
public string EncryptText(string input, string password)
{
// Get the bytes of the string
byte[] bytesToBeEncrypted = Encoding.UTF8.GetBytes(input);
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
// Hash the password with SHA256
passwordBytes = SHA256.Create().ComputeHash(passwordBytes);
byte[] bytesEncrypted = AES_Encrypt(bytesToBeEncrypted, passwordBytes);
string result = Convert.ToBase64String(bytesEncrypted);
return result;
}
public string DecryptText(string input, string password)
{
// Get the bytes of the string
byte[] bytesToBeDecrypted = Convert.FromBase64String(input);
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
passwordBytes = SHA256.Create().ComputeHash(passwordBytes);
byte[] bytesDecrypted = AES_Decrypt(bytesToBeDecrypted, passwordBytes);
string result = Encoding.UTF8.GetString(bytesDecrypted);
return result;
}
public byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
byte[] encryptedBytes = null;
// Set your salt here, change it to meet your flavor:
// The salt bytes must be at least 8 bytes.
new RNGCryptoServiceProvider().GetBytes(salt = new byte[32]);
saltBytes = salt;
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
var iv = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.IV = iv.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
cs.Close();
}
encryptedBytes = ms.ToArray();
}
}
return encryptedBytes;
}
public byte[] AES_Decrypt(byte[] bytesToBeDecrypted, byte[] passwordBytes)
{
byte[] decryptedBytes = null;
// Set your salt here, change it to meet your flavor:
// The salt bytes must be at least 8 bytes.
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
var iv = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.IV = iv.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
cs.Close();
}
decryptedBytes = ms.ToArray();
}
}
return decryptedBytes;
}
}
}
when I view the data in the "key" variable, there's an error that says "Hash = 'key.m_hmacsha1.Hash' threw an exception of type 'System.NullReferenceException'", along with other null values/0s in other methods.
您正在检查不属于您的 class 的私有字段。这并不总能带来明智的结果。相反,您应该只依赖 public API.
if a user enters in the wrong key, how would I catch this in an exception?
您将获得指示 "the padding is invalid and cannot be removed" 的异常的可能性 (~15/16)。但实际上你应该通过
在加密之上执行此操作a) 在密文上计算 HMAC(例如 HMACSHA256),然后在解密时验证 HMAC(验证失败表示密码错误或数据已被篡改),或
b) 明文中有一段格式良好的数据可用于验证。
(A) 是更好的答案,原因有很多。
你有类似
的代码var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
var iv = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.IV = iv.GetBytes(AES.BlockSize / 8);
由于 key
和 iv
的密码、salt 和迭代次数相同,因此您的实际 IV 是加密密钥的前 128 位。如果您希望它是一个计算值,您需要改变盐值(例如,将盐值的最后一个字节设置为 0
用于加密密钥,1
用于 IV,以及 2
用于 HMAC 密钥),或将其作为下一个输出字节执行(要求 32 + 16 + 32 字节并相应地将其切碎......或者,给定 .NET 实现,调用 GetBytes(32), GetBytes(16 ), GetBytes(32)(Rfc2898DeriveBytes.GetBytes 是一个流API))。
或者,对于IV,使用加密时随机生成的IV,与密文一起传输即可。 (如果这样做,请务必将其包含在 HMAC 计算中)
您将 salt
和 saltBytes
存储在字段中(尽管您将一个设置为另一个,所以不清楚为什么有两个字段)。您需要将盐值与密文一起传输,并让解密器接受它作为输入。
基本上,您的函数应该是 Encrypt(passphrase, data) => <salt, hmac, ciphertext>
(或 <salt, iv, hmac, ciphertext>
)和 Decrypt(passphrase, salt, [iv,] hmac, ciphertext) => data
。 (取决于 IV 是传输还是导出)