将密码保存到registry中时,最简单的encryption方法是什么?

目前我正在写明哎呀! ,这是一个内部程序,所以它不是那么糟糕,但我想做的权利。 在写入registry时,我应该如何去encryption这个文件?我该如何解密?

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

你不解密authentication密码!

使用类似SHA256提供程序的方式对其进行哈希处理,当您必须进行挑战时,对来自用户的input进行哈希处理,查看两个哈希值是否匹配。

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

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

编辑2:我以为我们只是在谈论前线authentication。 当然有些情况下,你想要encryption其他的东西需要是可逆的密码,但应该有一个单向locking(除极less数例外)。

我已经升级了哈希algorithm,但是为了尽可能保持私有盐的强度, 并哈希值之前将其添加到input中 。 当你比较你会再次这样做。 这增加了另一层,使得人们更难以扭转。

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

“ 盐值有助于减缓攻击者在您的凭证存储受到损害时执行字典攻击,给您额外的时间来检测并妥协。 ”

要存储密码哈希值:

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)将盐追加到结果散列。

 // 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)将结果存储在用户存储数据库中。

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

编辑 :随着原始计算能力变得更便宜,更快,哈希值或腌制哈希值下降。 杰夫阿特伍德有一个很好的2012年更新太长,以至于在这里完整重复,其中指出:

这(使用腌制的哈希)将比任何实际的安全提供安全的幻想。 既然你需要salt和哈希algorithm的select来生成哈希,并且检查哈希,攻击者不可能有一个,而不是另一个。 如果你已经被攻击到了攻击者拥有你的密码数据库的地步,那么可以假设他们已经或者可以得到你的秘密藏匿的盐。

安全的第一条规则是总是假设和计划最糟糕的。 你应该使用盐,理想的情况下,每个用户随机盐? 当然,这绝对是一个很好的做法,至less它可以让你消除具有相同密码的两个用户的歧义。 但是现在,仅靠盐就不能再把你从一个愿意在显卡硬件上花费几千美元的人那里解救出来,如果你认为他们可以的话,你就麻烦了。

这是你想要做的:

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

你可以用下面的类来做到这一点。 这个类是一个通用类,是客户端端点。 它使得使用Ninject的各种encryptionalgorithm的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); } } 

下一个类是允许你注入各种algorithm的ninject类:

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

这是任何algorithm需要实现的encryption/解密string的接口:

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

这是一个使用TripleDESalgorithm的实现:

 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()); } } } } 

您可以观看我的video并下载此代码: http : //www.wrightin.gs/2008/11/how-to-encryptdecrypt-sensitive-column-contents-in-nhibernateactive-record-video.html

一种select是存储密码的散列(SHA1,MD5)而不是明文密码,并且每当你想要查看密码是否好时,就把它和散列进行比较。

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

如果仅仅用于authentication,那么使用散列就足够了。

如果你想能够解密密码,我认为最简单的方法是使用DPAPI(用户存储模式)来encryption/解密。 这样,您就不必使用encryption密钥,将它们存储在某个地方,或者将它们硬编码到代码中 – 在这两种情况下,有人可以通过查看registry,用户设置或使用Reflector来发现它们。

否则使用散列SHA1或MD5像其他人在这里所说的。

就像ligget78说的那样,DPAPI将是存储密码的好方法。 例如,查看MSDN上的ProtectedData类 。

如果它是您的应用程序用于validation的密码,则按其他人的build议散列密码。

如果您为外部资源存储密码,则通常希望能够提示用户input这些凭据,并为其提供安全保存的机会。 Windows为此提供了凭证UI(CredUI) – 有许多示例显示了如何在.NET中使用它,包括MSDN上的这个 。

汤姆·斯科特(Tom Scott)正确地讲述了如何(而不是)在Computerphile上存储密码。

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

  1. 如果你完全可以避免它,不要尝试自己存储密码。 改为使用单独的,预先build立的,可靠的用户authentication平台(例如:OAuth提供商,您公司的Active Directory域等)。

  2. 如果您必须存储密码,请不要按照这里的任何指导。 至less,也不是没有咨询更适合您select的语言的最近的和有信誉的出版物。

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


存储密码的正确方式会随着时间而改变。

可能比有些人更换内衣更频繁。


所有这一切说,这里有一些一般的指导,希望有一段时间有用。

  1. 不要encryption密码。 任何允许恢复存储的数据的存储方法本质上都是不安全的,用于保存密码 – 包括所有forms的encryption。
  2. 按照用户在创build过程中input的内容准确处理密码。 在将密码发送给密码模块之前,您对密码所做的任何操作都可能会削弱密码。 进行以下任何操作也会增encryption码存储和validation过程的复杂性,这可能会导致其他问题(甚至可能引入漏洞)。

    • 不要转换为全部大写/全部小写。
    • 不要删除空格。
    • 不要剥夺不可接受的字符或string。
    • 不要更改文本编码。
    • 不要做任何字符或stringreplace。
    • 不要截断任何长度的密码。
  3. 拒绝创build任何未经修改就无法存储的密码。 加强以上。 如果您的密码存储机制无法正确处理某些字符,空格,string或密码长度,则返回错误并让用户知道系统的限制,以便可以使用适合其中的密码重试。 要获得更好的用户体验,请列出用户可以预先访问的限制。 不要担心,更不用说麻烦了,把攻击者的名单隐藏起来 – 无论如何,他们都会很容易地弄清楚。

  4. 为每个帐户使用一个长期的,随机的,独特的盐。 即使密码实际上是相同的,任何两个帐户的密码都不应该在存储中看起来相同。
  5. 使用caching和encryption强哈希algorithm,专门用于密码。 MD5肯定不在。 SHA-1 / SHA-2是不行的。 但是我也不会告诉你在这里要用什么。 (请参阅本文中的第一个#2项目。)
  6. 尽可能多地迭代你可以容忍的。 虽然你的系统可能比处理器周期要好一些,但整天密码都是散列密码,那么破解你的密码的人就会有系统没有的密码。 尽可能地对它们尽可能的努力,而不要让它“太难”在你身上。

最重要的是…

不要只听这里的人

去寻找一个有声望的和最近的出版物,为您select的语言正确的密码存储方法。 其实,你应该find多个最近的出版物,来自多个独立的来源,在你决定一种方法之前达成一致。

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

我已经看遍了encryption和解密过程的一个很好的例子,但大多数是过于复杂。

无论如何,有很多原因可能需要解密某些文本值(包括密码)。 我需要解密密码的原因是因为他们想要确保何时有人在密码过期时被迫更改密码,我们不让他们用相同密码的密码变更密码他们在过去的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); 

如果你需要比这更多的信息,例如保护一个连接string(用于连接数据库),请检查这篇文章 ,因为它提供了最好的“选项”。

Oli的回答也很好,因为它显示了如何为一个string创build一个散列。

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

而不是encryption/解密,你应该通过哈希algorithm,MD5 / SHA512或类似的密码。 你理想的做法是散列密码并存储散列,然后当需要密码时,散列条目并比较条目。 密码将永远不会被“解密”,只是散列,然后进行比较。