首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >安全加密以存储信用卡数据

安全加密以存储信用卡数据
EN

Cryptography用户
提问于 2019-10-21 19:38:55
回答 1查看 236关注 0票数 1

我需要一个加密算法,这将是足够安全的存储信用卡数据。所以它应该是相当安全的。

这是我想出来的。如果有建设性的批评,我将不胜感激。够安全吗?能做得更好吗?

代码语言:javascript
复制
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,则代码可以工作。但我不清楚这会不会让事情好转。

EN

回答 1

Cryptography用户

回答已采纳

发布于 2019-10-21 20:20:10

作为你的网络档案显示,我假设您知道如何编写代码,并将完全集中在这里使用的密码学上。

因此,该代码目前所做的大致如下:

  1. 产生一种随机盐。
  2. 使用该salt和一个安全的PBKDF (PBKDF2)派生密钥。
  3. 另外,生成一个IV (技术上不是必要的,但也没有伤害)。
  4. 使用派生密钥和IV对数据进行AES-CBC加密。

这种方法显然是安全的。,对于“安全”的恰当和合理的定义。有更强大(更安全)的,可以很容易地通过切换构建块来实现。

特别是PBKDF可以切换出去,以及主要的加密算法。

PBKDF2本身是安全的,但并不能实现PBKDF的一个理想的安全目标:也就是说,对于强暴对手来说,最有效的策略是在专用专用硬件和GPU中使用与您正在使用的硬件相同的硬件,而GPU在计算PBKDF2时要节能得多。解决这个问题的方法是使用所谓的内存硬密码散列,比如Argon2id,这是经过优化的,在专用硬件上不能比标准的CPU便宜。

AES-CBC“只有”提供选择的明文安全,允许对手修改你的密文,而你没有注意到,并允许他们得到你精心制作的明文时,解密。在过去,这导致了像eFail这样的可利用的漏洞。这里的解决方法也很简单(概念上):使用选择的密文安全(CCA-安全)加密方案来添加身份验证数据,以防止这些精心编制的解密被忽略。这方面的例子包括AES-GCMChaCha20-Poly1305,这是TLS中常用的。

补充说明:通常,当人们谈论"Rijndael“时,他们谈论”Rijndael“,是因为他们想要前者提供的属性,而后者则不想。但当然,在AES标准化之后的几年里,没有人真正认真地研究过这些变体,因此使用它们有点风险,而且通常在需要更大块大小的时候需要其他工具(这些变体没有HW支持)。

票数 2
EN
页面原文内容由Cryptography提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://crypto.stackexchange.com/questions/75230

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档