将密码保存到注册表时,加密密码的最简单方法是什么?

时间:2008-10-17 14:55:48

标签: c# encryption passwords

目前我用明文 oops编写它!,这是一个内部程序,所以它并没有那么糟糕,但我想做得对。在写入注册表时我应该如何加密这个?如何解密?

OurKey.SetValue("Password", textBoxPassword.Text);

12 个答案:

答案 0 :(得分:121)

您不解密验证密码!

使用类似SHA256提供程序的东西对它们进行哈希处理,当你必须挑战时,哈希来自用户的输入并查看这两个哈希是否匹配。

byte[] data = System.Text.Encoding.ASCII.GetBytes(inputString);
data = new System.Security.Cryptography.SHA256Managed().ComputeHash(data);
String hash = System.Text.Encoding.ASCII.GetString(data);

让密码可逆是一个真的可怕的模型。

Edit2:我以为我们只是在谈论一线认证。当然,有些情况下你想要为其他需要可逆的东西加密密码,但是应该有一个单向锁定(除了极少数例外)。

我已经升级了散列算法但是要保持a private salt and add that to your input before hashing it的最佳强度。比较时你会再次这样做。这增加了另一层,使得某人更难以逆转。

答案 1 :(得分:23)

还请考虑“腌制”你的哈希(不是烹饪概念!)。基本上,这意味着在散列之前将一些随机文本附加到密码。

The salt value helps to slow an attacker perform a dictionary attack should your credential store be compromised, giving you additional time to detect and react to the compromise.

存储密码哈希:

a)生成随机盐值:

byte[] salt = new byte[32];
System.Security.Cryptography.RNGCryptoServiceProvider.Create().GetBytes(salt);

b)将盐附加到密码。

// Convert the plain string pwd into bytes
byte[] plainTextBytes = System.Text UnicodeEncoding.Unicode.GetBytes(plainText);
// Append salt to pwd before hashing
byte[] combinedBytes = new byte[plainTextBytes.Length + salt.Length];
System.Buffer.BlockCopy(plainTextBytes, 0, combinedBytes, 0, plainTextBytes.Length);
System.Buffer.BlockCopy(salt, 0, combinedBytes, plainTextBytes.Length, salt.Length);

c)哈希组合密码&盐:

// Create hash for the pwd+salt
System.Security.Cryptography.HashAlgorithm hashAlgo = new System.Security.Cryptography.SHA256Managed();
byte[] hash = hashAlgo.ComputeHash(combinedBytes);

d)将salt附加到生成的哈希值。

// Append the salt to the hash
byte[] hashPlusSalt = new byte[hash.Length + salt.Length];
System.Buffer.BlockCopy(hash, 0, hashPlusSalt, 0, hash.Length);
System.Buffer.BlockCopy(salt, 0, hashPlusSalt, hash.Length, salt.Length);

e)将结果存储在用户商店数据库中。

这种方法意味着您不需要单独存储salt,然后使用从用户获得的salt值和明文密码值重新计算哈希值。

编辑:随着原始计算能力变得越来越便宜和快速,散列值 - 或者腌制哈希值 - 已经下降。 Jeff Atwood has an excellent 2012 update过于冗长,不能在此全文重复:

  

这(使用盐渍哈希)将提供比任何实际安全更多的安全幻觉。既然你需要salt和hash算法的选择来生成散列,并检查散列,那么攻击者不太可能拥有一个而不是另一个。如果你已被攻击者攻击者拥有你的密码数据库,那么可以合理地假设他们拥有或者可以获得你隐藏的秘密盐。

     

安全的第一条规则是始终承担并计划最坏的情况。   您是否应该使用盐,理想情况下每个用户使用盐?当然,是的   绝对是一个很好的做法,至少它可以让你   消除具有相同密码的两个用户的歧义。但是现在,   单独的盐就不能再让你从一个愿意花钱的人身上拯救出来   在视频卡硬件上几千美元,如果你认为他们   可以,你有麻烦。

答案 2 :(得分:12)

汤姆斯科特在关于如何(不)存储密码的报道中,在Computerphile上做得对。

https://www.youtube.com/watch?v=8ZtInClXe1Q

  1. 如果您完全可以避免,请不要尝试自己存储密码。使用单独的,预先建立的,值得信赖的用户身份验证平台(例如:OAuth提供商,您公司和# 39; s Active Directory域等)。

  2. 如果您必须存储密码,请不要遵循此处的任何指导。至少,并非没有咨询适用于您选择的语言的更新近且声誉良好的出版物。

  3. 这里肯定有很多聪明人,甚至可能给出一些很好的指导。但是,当你读到这篇文章的时候,这里的所有答案(包括这个答案)都已经过时了。

    存储密码的正确方法会随着时间的推移而变化。

    可能比一些人更换内衣的频率更高。

    所有这些都说,这里有一些一般指导意见,希望有一段时间有用。

    1. 不要加密密码。 任何允许恢复存储数据的存储方法本身就不安全以保存密码 - 包括所有形式的加密
    2. 在创建过程中完全按照用户输入的密码处理。 在将密码发送到加密模块之前对密码所做的任何操作都可能只是削弱它。执行以下任何操作也会增加密码存储的复杂性。验证过程可能导致其他问题(甚至可能引入漏洞)。

      • 不要转换为全大写/全小写。
      • 不要删除空格。
      • 不要剥夺不可接受的字符或字符串。
      • 不要更改文字编码。
      • 不要做任何字符或字符串替换。
      • 不要截断任何长度的密码。
    3. 拒绝创建任何无需修改即可存储的密码。 强化上述内容。如果由于某种原因您的密码存储机制无法正确处理某些字符,空格,字符串或密码长度,则返回错误并让用户了解系统的限制,以便他们可以使用适合其中的密码重试。为了获得更好的用户体验,请预先列出用户可以访问的那些限制。不要担心,更不用说为攻击者隐藏列表而烦恼 - 无论如何,他们都可以很容易地解决这个问题。

    4. 为每个帐户使用长而随机且唯一的盐。 没有两个帐户'密码在存储中应该看起来一样,即使密码实际上是相同的。
    5. 使用设计用于密码的慢速和加密强哈希算法。 MD5肯定是不合适的。 SHA-1 / SHA-2是禁止的。但我也不会告诉你应该在这里使用什么。 (参见本文中的第一个#2子弹。)
    6. 尽可能多地迭代。 虽然你的系统可能有更好的处理周期而不是哈希密码整天,但是会破解的人你的密码有不合适的系统。尽可能地让它们变得坚硬,而不是让它变得更加坚硬"在你身上。
    7. 最重要的是......

      不要只听这里的任何人。

      请查找有关您所选语言的正确密码存储方法的信誉良好且最新的出版物。实际上,在解决一种方法之前,你应该找到多个来自多个单独来源的最新出版物。

      极有可能的是,这里的每个人(包括我自己)所说的一切都已经被更好的技术所取代,或者被新开发的攻击方法变得不安全。去找一些可能不是的东西。

答案 3 :(得分:11)

这就是您想要做的事情:

OurKey.SetValue("Password", StringEncryptor.EncryptString(textBoxPassword.Text));
OurKey.GetValue("Password", StringEncryptor.DecryptString(textBoxPassword.Text));

您可以使用以下类来完成此操作。 这个类是泛型类,是客户端端点。它使用Ninject为IOC提供各种加密算法。

public class StringEncryptor
{
    private static IKernel _kernel;

    static StringEncryptor()
    {
        _kernel = new StandardKernel(new EncryptionModule());
    }

    public static string EncryptString(string plainText)
    {
        return _kernel.Get<IStringEncryptor>().EncryptString(plainText);
    }

    public static string DecryptString(string encryptedText)
    {
        return _kernel.Get<IStringEncryptor>().DecryptString(encryptedText);
    }
}

下一个类是ninject类,它允许您注入各种算法:

public class EncryptionModule : StandardModule
{
    public override void Load()
    {
        Bind<IStringEncryptor>().To<TripleDESStringEncryptor>();
    }
}

这是任何算法需要实现的用于加密/解密字符串的接口:

public interface IStringEncryptor
{
    string EncryptString(string plainText);
    string DecryptString(string encryptedText);
}

这是使用TripleDES算法的实现:

public class TripleDESStringEncryptor : IStringEncryptor
{
    private byte[] _key;
    private byte[] _iv;
    private TripleDESCryptoServiceProvider _provider;

    public TripleDESStringEncryptor()
    {
        _key = System.Text.ASCIIEncoding.ASCII.GetBytes("GSYAHAGCBDUUADIADKOPAAAW");
        _iv = System.Text.ASCIIEncoding.ASCII.GetBytes("USAZBGAW");
        _provider = new TripleDESCryptoServiceProvider();
    }

    #region IStringEncryptor Members

    public string EncryptString(string plainText)
    {
        return Transform(plainText, _provider.CreateEncryptor(_key, _iv));
    }

    public string DecryptString(string encryptedText)
    {
        return Transform(encryptedText, _provider.CreateDecryptor(_key, _iv));
    }

    #endregion

    private string Transform(string text, ICryptoTransform transform)
    {
        if (text == null)
        {
            return null;
        }
        using (MemoryStream stream = new MemoryStream())
        {
            using (CryptoStream cryptoStream = new CryptoStream(stream, transform, CryptoStreamMode.Write))
            {
                byte[] input = Encoding.Default.GetBytes(text);
                cryptoStream.Write(input, 0, input.Length);
                cryptoStream.FlushFinalBlock();

                return Encoding.Default.GetString(stream.ToArray());
            }
        }
    }
}

您可以在http://www.wrightin.gs/2008/11/how-to-encryptdecrypt-sensitive-column-contents-in-nhibernateactive-record-video.html

观看我的视频并下载相应的代码

答案 4 :(得分:10)

一种选择是存储密码的哈希值(SHA1,MD5)而不是明文密码,每当你想知道密码是否合适时,只需将其与该哈希值进行比较即可。

如果您需要安全存储(例如,用于连接服务的密码),则问题会更复杂。

如果只是用于身份验证,那么使用哈希就足够了。

答案 5 :(得分:9)

如果您希望能够解密密码,我认为最简单的方法是使用DPAPI(用户存储模式)加密/解密。这样您就不必使用加密密钥,将它们存储在某处或在代码中对其进行硬编码 - 在这两种情况下,有人可以通过查看注册表,用户设置或使用Reflector来发现它们。

否则请像其他人一样使用哈希SHA1或MD5。

答案 6 :(得分:6)

就像ligget78所说,DPAPI是存储密码的好方法。 Check out the ProtectedData class on MSDN例如用法。

答案 7 :(得分:5)

如果它是您的应用程序用于身份验证的密码,则按其他人的建议散列密码。

如果您要存储外部资源的密码,您通常希望能够提示用户输入这些凭据,并让他有机会安全地保存这些凭据。 Windows为此提供了凭据UI(CredUI) - 有许多示例显示如何在.NET中使用它,包括this one on MSDN

答案 8 :(得分:5)

我一直在寻找一个加密和解密过程的好例子,但大多数都过于复杂。

无论如何,有些人可能想要解密包括密码在内的某些文本值。我需要在我正在处理的网站上解密密码的原因是因为他们希望确保当某人在到期时被迫更改密码时我们不会让他们使用相同密码的密码变体来更改密码他们在过去的x个月中使用过

所以我编写了一个以简化方式完成此操作的流程。我希望这段代码对某人有益。据我所知,我可能会在另一个时间用于另一家公司/网站。

public string GenerateAPassKey(string passphrase)
    {
        // Pass Phrase can be any string
        string passPhrase = passphrase;
        // Salt Value can be any string(for simplicity use the same value as used for the pass phrase)
        string saltValue = passphrase;
        // Hash Algorithm can be "SHA1 or MD5"
        string hashAlgorithm = "SHA1";
        // Password Iterations can be any number
        int passwordIterations = 2;
        // Key Size can be 128,192 or 256
        int keySize = 256;
        // Convert Salt passphrase string to a Byte Array
        byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);
        // Using System.Security.Cryptography.PasswordDeriveBytes to create the Key
        PasswordDeriveBytes pdb = new PasswordDeriveBytes(passPhrase, saltValueBytes, hashAlgorithm, passwordIterations);
        //When creating a Key Byte array from the base64 string the Key must have 32 dimensions.
        byte[] Key = pdb.GetBytes(keySize / 11);
        String KeyString = Convert.ToBase64String(Key);

        return KeyString;
    }

 //Save the keystring some place like your database and use it to decrypt and encrypt
//any text string or text file etc. Make sure you dont lose it though.

 private static string Encrypt(string plainStr, string KeyString)        
    {            
        RijndaelManaged aesEncryption = new RijndaelManaged();
        aesEncryption.KeySize = 256;
        aesEncryption.BlockSize = 128;
        aesEncryption.Mode = CipherMode.ECB;
        aesEncryption.Padding = PaddingMode.ISO10126;
        byte[] KeyInBytes = Encoding.UTF8.GetBytes(KeyString);
        aesEncryption.Key = KeyInBytes;
        byte[] plainText = ASCIIEncoding.UTF8.GetBytes(plainStr);
        ICryptoTransform crypto = aesEncryption.CreateEncryptor();
        byte[] cipherText = crypto.TransformFinalBlock(plainText, 0, plainText.Length);
        return Convert.ToBase64String(cipherText);
    }

 private static string Decrypt(string encryptedText, string KeyString) 
    {
        RijndaelManaged aesEncryption = new RijndaelManaged(); 
        aesEncryption.KeySize = 256;
        aesEncryption.BlockSize = 128; 
        aesEncryption.Mode = CipherMode.ECB;
        aesEncryption.Padding = PaddingMode.ISO10126;
        byte[] KeyInBytes = Encoding.UTF8.GetBytes(KeyString);
        aesEncryption.Key = KeyInBytes;
        ICryptoTransform decrypto = aesEncryption.CreateDecryptor(); 
        byte[] encryptedBytes = Convert.FromBase64CharArray(encryptedText.ToCharArray(), 0, encryptedText.Length); 
        return ASCIIEncoding.UTF8.GetString(decrypto.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length)); 
    }

 String KeyString = GenerateAPassKey("PassKey");
 String EncryptedPassword = Encrypt("25Characterlengthpassword!", KeyString);
 String DecryptedPassword = Decrypt(EncryptedPassword, KeyString);

答案 9 :(得分:1)

如果您需要更多,例如保护连接字符串(用于连接到数据库),请选中此article,因为它为此提供了最佳“选项”。

Oli的答案也很好,因为它显示了如何为字符串创建哈希值。

答案 10 :(得分:0)

.. NET提供了包含在类中的密码服务 System.Security.Cryptography名称空间。

答案 11 :(得分:-1)

您应该通过散列算法md5 / sha512或类似方法传递密码,而不是加密/解密。您理想的做法是散列密码并存储散列,然后在需要密码时,您对条目进行散列并比较条目。然后,密码将永远不会被“解密”,只需进行哈希处理然后进行比较。