【问题标题】:How to properly decrypt passwords stored in SQL Server database in C#如何在 C# 中正确解密存储在 SQL Server 数据库中的密码
【发布时间】:2021-08-17 06:48:20
【问题描述】:

我是一个相当新的开发人员,所以请耐心等待,这让我很头疼。

我一直在尝试将我正在开发的 Winforms 应用程序的密码以加密格式存储在数据库中。首次打开应用时有登录和注册界面。

我已经设法加密了注册期间提供的密码,它在数据库中不是明文,我为此使用了一个类Cryptography。但是,当我尝试解密密码以授予用户对应用程序的访问权限时,我收到异常未处理错误:

输入不是有效的 Base-64 字符串,因为它包含非 base-64 字符、两个以上的填充字符或填充字符中的非法字符。

有没有办法解决这个问题?我想不通。

进行加密的代码:

public static string Encrypt(string encryptString)
{
        string EncryptionKey = "djknh46hdkkjsdvvjjsijeykskerfubb1906234575";      
        byte[] clearBytes = Encoding.Unicode.GetBytes(encryptString);

        using (Aes encryptor = Aes.Create())
        {
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] {
            0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76});
            encryptor.Key = pdb.GetBytes(32);
            encryptor.IV = pdb.GetBytes(16);

            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), 
                CryptoStreamMode.Write))
                {
                    cs.Write(clearBytes, 0, clearBytes.Length);
                    cs.Close();
                }

                encryptString = Convert.ToBase64String(ms.ToArray());
            }
        }

        return encryptString;
}

应该进行解密的代码:

public static string Decrypt(string cipherText)
{
        string EncryptionKey = "djknh46hdkkjsdvvjjsijeykskerfubb1906234575";      
        cipherText = cipherText.Replace(" ", "+");
        byte[] cipherBytes = Convert.FromBase64String(cipherText);

        using (Aes encryptor = Aes.Create())
        {
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] {
            0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76});
            encryptor.Key = pdb.GetBytes(32);
            encryptor.IV = pdb.GetBytes(16);

            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), 
                CryptoStreamMode.Write))
                {
                    cs.Write(cipherBytes, 0, cipherBytes.Length);
                    cs.Close();
                }

                cipherText = Encoding.Unicode.GetString(ms.ToArray());
            }
        }

        return cipherText;
}

这样做的代码应该从数据库中验证密码:

string Password = "";
bool IsExist = false;           

SqlCommand command = new SqlCommand("select * from LibraryUser where UserName='" + 
        txtUsernameLogin.Text + "'", connection1);

SqlDataReader dataReader = command.ExecuteReader();

if (dataReader.Read())
{
    Password = dataReader.GetString(4);   
    IsExist = true;
}

connection1.Close();

if (IsExist)   
{
    if (Cryptography.Decrypt(Password).Equals(txtPasswordLogin.Text))
    {
        this.Hide();
        new LibraryForm().Show();                 
    }
    else
    {
        MessageBox.Show("The password you have entered is incorrect, please try again.", 
                        "Incorrect Password", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}
else  
{
    MessageBox.Show("Please enter the valid credentials.", "Error", 
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
}

感谢任何人提供的任何帮助,在此先感谢。

【问题讨论】:

  • 您不需要解密密码,因为它们应该只存储为加盐哈希。
  • 这不是最佳实践。始终使用如上所述的单向加密,然后以相同的方式加密用户输入以进行匹配。
  • 原谅我,什么意思?这是否意味着我应该删除解密的代码?什么是盐渍哈希?我该如何使用它们?
  • 是的,从不解密。谷歌将帮助您解决第二个问题。密码管理方面肯定有很多很好的例子。
  • SQL Injection alert - 您应该将您的 SQL 语句连接在一起 - 使用 参数化查询 来避免 SQL 注入 - 查看 Little Bobby Tables

标签: c# sql-server encryption


【解决方案1】:

以上关于最佳实践(单向加密、加盐哈希、SQL 注入)的 cmets 是有效的,我完全赞同它们。

然而,实际的问题是:为什么 Base64 解码失败?

答案是您从数据库中读取的值不是有效的 Base64 编码。这行可疑:

cipherText = cipherText.Replace(" ", "+");

空格不是 Base64 编码中的有效字符。您使用什么数据类型来存储字符串?它是否作为带有空格填充的固定长度字符串返回?如果是这样,您可能需要在cipherText 上调用.Trim() 而不是上述行。

如果这不起作用,请从您的 Decrypt() 方法中发布导致此异常的 cipherText 示例,以便我们尝试回答您的问题。

同样,这不是最佳做法。请注意上面 Mitch Wheat、Peter 和 marc_s 的 cmets。

【讨论】:

    【解决方案2】:

    对于密码,您不解密它们,您只比较加密的密码。

    新用户:

    var hash = Encrypt(password);
    DbInsert(userId, hash);
    

    验证用户身份:

    var hash = Encrypt(password);
    var dbHash = GetPasswordFromDb(userId);
    if (dbHash == hash) {
        // authenticated
    } else {
        // wrong password
    }
    

    您比较散列值,因为密码应该无法解密,因此发现密码的唯一方法是猜测。

    通常我使用BCrypt.Net-Next NuGet。它使用简单且足够安全。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-26
      • 1970-01-01
      • 1970-01-01
      • 2011-07-15
      • 1970-01-01
      • 2016-12-25
      • 2020-03-20
      • 2011-02-27
      相关资源
      最近更新 更多