我需要一个加密算法,这将是足够安全的存储信用卡数据。所以它应该是相当安全的。
这是我想出来的。如果有建设性的批评,我将不胜感激。够安全吗?能做得更好吗?
public class Encryption
{
// Algorithm meta sizes (in bits)
private const int SaltSize = 256;
private const int BlockSize = 256;
private const int KeySize = 256;
// Number of iterations for password bytes generation
private const int DerivationIterations = 1000;
/// <summary>
/// Encrypts a string using the given password.
/// </summary>
/// <param name="s">String to encrypt.</param>
/// <param name="password">Encryption password.</param>
/// <returns>Encrypted string.</returns>
public string Encrypt(string s, string password)
{
if (s == null)
throw new ArgumentNullException(nameof(s));
if (password == null)
throw new ArgumentNullException(nameof(password));
if (password.Length == 0)
throw new ArgumentException("Password cannot be empty", nameof(password));
var salt = GetRandomBytes(SaltSize);
var iv = GetRandomBytes(BlockSize);
var data = Encoding.UTF8.GetBytes(s);
using (Rfc2898DeriveBytes derivedKey = new Rfc2898DeriveBytes(password, salt, DerivationIterations))
{
var keyBytes = derivedKey.GetBytes(KeySize / 8);
using (var encrypt = new RijndaelManaged())
{
encrypt.BlockSize = BlockSize;
encrypt.Mode = CipherMode.CBC;
encrypt.Padding = PaddingMode.PKCS7;
using (var encryptor = encrypt.CreateEncryptor(keyBytes, iv))
{
using (var stream = new MemoryStream())
{
stream.Write(salt, 0, salt.Length);
stream.Write(iv, 0, iv.Length);
using (var cryptoStream = new CryptoStream(stream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(data, 0, data.Length);
cryptoStream.FlushFinalBlock();
}
return Convert.ToBase64String(stream.ToArray());
}
}
}
}
}
/// <summary>
/// Decrypts a string previously encrypted by <see cref="Encrypt"></see>.
/// </summary>
/// <param name="s">The text to decrypt.</param>
/// <param name="password">The decryption password.</param>
/// <returns>The decrypted string.</returns>
public string Decrypt(string s, string password)
{
if (s == null)
throw new ArgumentNullException(nameof(s));
if (password == null)
throw new ArgumentNullException(nameof(password));
if (password.Length == 0)
throw new ArgumentException("Password cannot be empty", nameof(password));
try
{
// Get data plus salt and IV
byte[] allData = Convert.FromBase64String(s);
byte[] salt = new byte[SaltSize / 8];
byte[] iv = new byte[BlockSize / 8];
byte[] data = new byte[allData.Length - (salt.Length + iv.Length)];
Buffer.BlockCopy(allData, 0, salt, 0, salt.Length);
Buffer.BlockCopy(allData, salt.Length, iv, 0, iv.Length);
Buffer.BlockCopy(allData, salt.Length + iv.Length, data, 0, data.Length);
using (Rfc2898DeriveBytes derivedBytes = new Rfc2898DeriveBytes(password, salt, DerivationIterations))
{
var keyBytes = derivedBytes.GetBytes(KeySize / 8);
using (var encrypt = new RijndaelManaged())
{
encrypt.BlockSize = BlockSize;
encrypt.Mode = CipherMode.CBC;
encrypt.Padding = PaddingMode.PKCS7;
using (var decryptor = encrypt.CreateDecryptor(keyBytes, iv))
{
using (var memoryStream = new MemoryStream(data))
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
var plainTextBytes = new byte[data.Length];
var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
}
}
}
}
}
catch (Exception)
{
return string.Empty;
}
}
private byte[] GetRandomBytes(int bits)
{
byte[] bytes = new byte[bits / 8];
using (var provider = new RNGCryptoServiceProvider())
{
provider.GetBytes(bytes);
}
return bytes;
}
}注意:根据Rijndael类的文档,“Rijndael类是Aes算法的前身,您应该使用Aes算法而不是Rijndael。”我发现,如果我将RijndaelManaged更改为AesCryptoServiceProvider,并将BlockSize的值更改为128,则代码可以工作。但我不清楚这会不会让事情好转。
发布于 2019-10-21 20:20:10
作为你的网络档案显示,我假设您知道如何编写代码,并将完全集中在这里使用的密码学上。
因此,该代码目前所做的大致如下:
这种方法显然是安全的。,对于“安全”的恰当和合理的定义。有更强大(更安全)的,可以很容易地通过切换构建块来实现。
特别是PBKDF可以切换出去,以及主要的加密算法。
PBKDF2本身是安全的,但并不能实现PBKDF的一个理想的安全目标:也就是说,对于强暴对手来说,最有效的策略是在专用专用硬件和GPU中使用与您正在使用的硬件相同的硬件,而GPU在计算PBKDF2时要节能得多。解决这个问题的方法是使用所谓的内存硬密码散列,比如Argon2id,这是经过优化的,在专用硬件上不能比标准的CPU便宜。
AES-CBC“只有”提供选择的明文安全,允许对手修改你的密文,而你没有注意到,并允许他们得到你精心制作的明文时,解密。在过去,这导致了像eFail这样的可利用的漏洞。这里的解决方法也很简单(概念上):使用选择的密文安全(CCA-安全)加密方案来添加身份验证数据,以防止这些精心编制的解密被忽略。这方面的例子包括AES-GCM和ChaCha20-Poly1305,这是TLS中常用的。
补充说明:通常,当人们谈论"Rijndael“时,他们谈论”Rijndael“,是因为他们想要前者提供的属性,而后者则不想。但当然,在AES标准化之后的几年里,没有人真正认真地研究过这些变体,因此使用它们有点风险,而且通常在需要更大块大小的时候需要其他工具(这些变体没有HW支持)。
https://crypto.stackexchange.com/questions/75230
复制相似问题