将密码从PasswordDerivedBytes转换为Rfc2898DerivedBytes,unicode

时间:2018-01-21 12:13:24

标签: c# cryptography

我正在尝试用Rfc2898DerivedBytes替换PasswordDerivedBytes,但是在获取unicode编码结果时遇到后者问题。

以此代码为例:

    [TestMethod]
    public void DerivedBytesTest()
    {
        string encrypted = "y4Ijqo9Ga/mHlFbLHDdDUkYZlyu7CHF4PVXGLnb8by7FAVtCgPLhFSiA9Et6hDac";
        string key = "{00B3403A-3C29-4f26-A9CC-14C411EA8547}";
        string salt = "gT5M07XB9hHl3l1s";
        string expected = "4552065703414505";
        string decrypted;

        decrypted = Decrypt(encrypted, key, salt, true);
        Assert.IsTrue(decrypted == expected); // Works

        decrypted = Decrypt(encrypted, key, salt, false);
        Assert.IsTrue(decrypted == expected); // Doesn't work, get wrong unicode characters in 24 character string
    }

    private string Decrypt(string encrypted, string key, string salt, bool legacy = false)
    {
        UnicodeEncoding encoding = new UnicodeEncoding();

        byte[] encryptedDataBytes = Convert.FromBase64String(encrypted);
        byte[] saltBytes = encoding.GetBytes(salt);

        RijndaelManaged encryption = new RijndaelManaged();
        DeriveBytes secretKey;

        if (legacy)
        {
            secretKey = new PasswordDeriveBytes(key, saltBytes) {IterationCount = 100};
            encryption.Padding = PaddingMode.PKCS7;
        }
        else
        {
            secretKey = new Rfc2898DeriveBytes(key, saltBytes, 100);
            encryption.Padding = PaddingMode.Zeros; // This is the only one that doesn't throw the "Padding is invalid and cannot be removed" exception, but gives me a non-ASCII result
        }

        ICryptoTransform decryptor = encryption.CreateDecryptor(secretKey.GetBytes(32), secretKey.GetBytes(16));

        string decryptedText = "";

        using (MemoryStream memoryStream = new MemoryStream(encryptedDataBytes))
        {
            using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
            {
                byte[] bytes = new byte[encryptedDataBytes.Length];
                int decryptedCount = cryptoStream.Read(bytes, 0, bytes.Length);
                decryptedText = encoding.GetString(bytes, 0, decryptedCount);

                if (!legacy)
                {
                    // Something more to do with result?
                }
            }
        }
        return decryptedText;
    }

我想知道是否有人可以告诉我哪里出错了?

1 个答案:

答案 0 :(得分:1)

PasswordDeriveBytes是PBKDF1的一个严格实现的扩展,而Rfc2898DeriveBytes是PBKDF2的实现。两者都从密码派生密钥,但它们是两种不同的算法,因此它们得出两种不同的结果。由于他们在下面使用加密安全哈希,因此无法将其转换为另一个。

如果您可以节省几个字节的存储空间,您仍然可以使用PKBDF1派生密钥,然后使用PBKDF2的结果加密该密钥。如果输出大小相同,你甚至可以使用XOR加密(一次性填充),但AES当然也可以工作。那么解密就变成:计算PBKDF2结果,解密数据密钥,使用数据密钥解密密文。

否则,您必须解密然后重新加密结果。

如果要比较解密结果,则比较生成的字节;不要先将其转换为字符串。强烈建议使用经过身份验证的加密或MAC,以便可以验证身份验证标记。通过使用零填充忽略填充异常是不可取的。这些填充错误发生在,因为密钥错误

通用说明:

  • PasswordDeriveBytes应该用于任何数量的字节> PBKDF1的Mickeysoft扩展20字节是非常不安全的,甚至在输出中重复字节(!)。如果你对PBKDF2做同样的事情,那么任何对手都必须完成你必须做的工作的一半,这也不是一个好主意。

  • 问题中的迭代计数非常低,但是您似乎使用高度随机的UID而不是密码应该没问题。