.NET 中的密码加密/解密代码
Posted
技术标签:
【中文标题】.NET 中的密码加密/解密代码【英文标题】:Password encryption/decryption code in .NET 【发布时间】:2010-12-13 07:23:58 【问题描述】:我想要在 C# 中对密码进行简单的加密和解密。如何将密码以加密格式保存在数据库中,并通过解密恢复为原始格式?
【问题讨论】:
有什么特别的原因需要检索它吗?大多数时候,您可以简单地使用散列函数来存储它。然后,当他们输入密码时,您对他们的输入进行哈希处理并进行比较。 密码解密是一个非常严重的安全漏洞。密码应该是散列的,而不是加密的。 【参考方案1】:给你。我在互联网的某个地方找到了它。很适合我。
/// <summary>
/// Encrypts a given password and returns the encrypted data
/// as a base64 string.
/// </summary>
/// <param name="plainText">An unencrypted string that needs
/// to be secured.</param>
/// <returns>A base64 encoded string that represents the encrypted
/// binary data.
/// </returns>
/// <remarks>This solution is not really secure as we are
/// keeping strings in memory. If runtime protection is essential,
/// <see cref="SecureString"/> should be used.</remarks>
/// <exception cref="ArgumentNullException">If <paramref name="plainText"/>
/// is a null reference.</exception>
public string Encrypt(string plainText)
if (plainText == null) throw new ArgumentNullException("plainText");
//encrypt data
var data = Encoding.Unicode.GetBytes(plainText);
byte[] encrypted = ProtectedData.Protect(data, null, Scope);
//return as base64 string
return Convert.ToBase64String(encrypted);
/// <summary>
/// Decrypts a given string.
/// </summary>
/// <param name="cipher">A base64 encoded string that was created
/// through the <see cref="Encrypt(string)"/> or
/// <see cref="Encrypt(SecureString)"/> extension methods.</param>
/// <returns>The decrypted string.</returns>
/// <remarks>Keep in mind that the decrypted string remains in memory
/// and makes your application vulnerable per se. If runtime protection
/// is essential, <see cref="SecureString"/> should be used.</remarks>
/// <exception cref="ArgumentNullException">If <paramref name="cipher"/>
/// is a null reference.</exception>
public string Decrypt(string cipher)
if (cipher == null) throw new ArgumentNullException("cipher");
//parse base64 string
byte[] data = Convert.FromBase64String(cipher);
//decrypt data
byte[] decrypted = ProtectedData.Unprotect(data, null, Scope);
return Encoding.Unicode.GetString(decrypted);
【讨论】:
也许我弄错了,但看起来这个解决方案不能跨用户/机器移植,因此不适合存储加密数据。来自文档:“DPAPI 将密钥数据存储在用户配置文件中”。如果您使用这种方法来加密和存储数据,那么您将无法从其他机器上检索和解密数据,或者,如果您重建您的服务器,那么天赐良机。 Visual Studio 无法识别“Scope”? @DanFromGermany 添加对 System.Security 程序集的引用。见msdn.microsoft.com/en-us/library/… Scope 必须是 DataProtectionScope.LocalMachine(加密/解密必须在同一台机器上完成)或 DataProtectionScope.CurrentUser(加密/解密必须由同一用户完成) 这是个玩笑吧?因为这是一个获得很多观点的问题的公认答案,而且这是任何人永远都不应该做的事情。【参考方案2】:编辑:这是一个非常古老的答案。 SHA1 在 2011 年被弃用,现在已经在实践中被打破。 https://shattered.io/ 改用较新的标准(例如 SHA256、SHA512 等)。
如果您在我的评论中对问题的回答是“否”,那么这就是我使用的:
public static byte[] HashPassword(string password)
var provider = new SHA1CryptoServiceProvider();
var encoding = new UnicodeEncoding();
return provider.ComputeHash(encoding.GetBytes(password));
【讨论】:
SHA1 已被入侵,如下所示:okami-infosec.blogspot.com/2007/01/hash-sha-1-compromised.html 我不相信任何 SHA-2 系列已被破坏,您可能想要使用其中之一:en.wikipedia.org/wiki/SHA_hash_functions#SHA-2_family 但是,这取决于您的安全程度或偏执程度。在大学里我很偏执。 不要使用快速散列来散列密码。不是 SHA-1,也不是 SHA-2。使用 scrypt、bcrypt 或 PBKDF2。这也不是问题的答案,因为 OP 需要可逆性。【参考方案3】:我使用 RC2CryptoServiceProvider。
public static string EncryptText(string openText)
RC2CryptoServiceProvider rc2CSP = new RC2CryptoServiceProvider();
ICryptoTransform encryptor = rc2CSP.CreateEncryptor(Convert.FromBase64String(c_key), Convert.FromBase64String(c_iv));
using (MemoryStream msEncrypt = new MemoryStream())
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
byte[] toEncrypt = Encoding.Unicode.GetBytes(openText);
csEncrypt.Write(toEncrypt, 0, toEncrypt.Length);
csEncrypt.FlushFinalBlock();
byte[] encrypted = msEncrypt.ToArray();
return Convert.ToBase64String(encrypted);
public static string DecryptText(string encryptedText)
RC2CryptoServiceProvider rc2CSP = new RC2CryptoServiceProvider();
ICryptoTransform decryptor = rc2CSP.CreateDecryptor(Convert.FromBase64String(c_key), Convert.FromBase64String(c_iv));
using (MemoryStream msDecrypt = new MemoryStream(Convert.FromBase64String(encryptedText)))
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
List<Byte> bytes = new List<byte>();
int b;
do
b = csDecrypt.ReadByte();
if (b != -1)
bytes.Add(Convert.ToByte(b));
while (b != -1);
return Encoding.Unicode.GetString(bytes.ToArray());
【讨论】:
如果您想知道使用随机有效数据填充c_iv
和c_key
,在RC2CryptoServiceProvider
的实例中有一个属性Key
和IV
。跨度>
您的 IV 使用不正确。 IV 的全部意义在于每次加密都不同。不要使用常量 IV。
我必须先将字符串转换为base64字符串***.com/a/57293370/5237614【参考方案4】:
首先创建一个类:
public class Encryption
public static string Encrypt(string clearText)
string EncryptionKey = "MAKV2SPBNI99212";
byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
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();
clearText = Convert.ToBase64String(ms.ToArray());
return clearText;
public static string Decrypt(string cipherText)
string EncryptionKey = "MAKV2SPBNI99212";
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;
**在控制器中**
为此加密类添加参考:
using testdemo.Models
public ActionResult Index()
return View();
[HttpPost]
public ActionResult Index(string text)
if (Request["txtEncrypt"] != null)
string getEncryptionCode = Request["txtEncrypt"];
string DecryptCode = Encryption.Decrypt(HttpUtility.UrlDecode(getEncryptionCode));
ViewBag.GetDecryptCode = DecryptCode;
return View();
else
string getDecryptCode = Request["txtDecrypt"];
string EncryptionCode = HttpUtility.UrlEncode(Encryption.Encrypt(getDecryptCode));
ViewBag.GetEncryptionCode = EncryptionCode;
return View();
可见
<h2>Decryption Code</h2>
@using (Html.BeginForm())
<table class="table-bordered table">
<tr>
<th>Encryption Code</th>
<td><input type="text" id="txtEncrypt" name="txtEncrypt" placeholder="Enter Encryption Code" /></td>
</tr>
<tr>
<td colspan="2">
<span style="color:red">@ViewBag.GetDecryptCode</span>
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" id="btnEncrypt" name="btnEncrypt"value="Decrypt to Encrypt code" />
</td>
</tr>
</table>
<br />
<br />
<br />
<h2>Encryption Code</h2>
@using (Html.BeginForm())
<table class="table-bordered table">
<tr>
<th>Decryption Code</th>
<td><input type="text" id="txtDecrypt" name="txtDecrypt" placeholder="Enter Decryption Code" /></td>
</tr>
<tr>
<td colspan="2">
<span style="color:red">@ViewBag.GetEncryptionCode</span>
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" id="btnDecryt" name="btnDecryt" value="Encrypt to Decrypt code" />
</td>
</tr>
</table>
【讨论】:
【参考方案5】:这个问题将回答如何加密/解密: Encrypt and decrypt a string in C#?
您没有指定数据库,但您希望使用 Convert.toBase64String 对其进行 base-64 编码。例如,您可以使用: http://www.opinionatedgeek.com/Blog/blogentry=000361/BlogEntry.aspx
然后,您可以将其保存在 varchar 或 blob 中,具体取决于加密消息的长度,但对于密码 varchar 应该可以工作。
上面的例子也包括解码base64后的解密。
更新:
实际上,您可能不需要使用 base64 编码,但我发现它很有帮助,以防我想打印它或通过网络发送它。如果消息足够长,最好先压缩,然后加密,因为当消息已经是二进制形式时,更难使用暴力破解,因此很难判断您何时成功破解加密。
【讨论】:
除了可移植性之外,使用 base-64 而不是二进制(如果数据库支持的话)是否有任何实质性的好处? 没有任何好处,除了它更容易存储、作为字符串处理之外,如果你需要移动它,我发现它更容易。我不喜欢使用 blob,主要是因为我在 mysql 上花了很长时间,他们真的没有支持,所以我就是这么想的。此外,如果你想打印出加密的消息,那么它是一种可打印的方式,它可以很方便,只是为了看看发生了什么。 好点...我在调试期间将哈希转换为字符串一次并比较了生成的汉字> 【参考方案6】:最简单的加密方法之一(如果您绝对必须自己制作,因为 .NET 已经拥有如此出色的加密库 [正如我之前的 Cogwheel 提供的那样])是对输入的每个字符的 ASCII 值进行异或对已知“键”值的字符串。我相信 C# 中的 XOR 功能是使用 ^ 键完成的。
然后您可以将值从 XOR 的结果转换回 ASCII 字符,并将它们存储在数据库中。这不是很安全,但它是最简单的加密方法之一。
另外,如果使用 access 数据库,我发现在打开数据库本身时,将某些字符放在字符串前面会使整个字段不可读。但是,即使恶意用户该字段是空白的,您的应用程序仍然可以读取该字段。但是谁再使用访问权呢?
【讨论】:
我知道,对吧? 天真地环顾四周(其实我正在写一个摆脱我继承的系统并进入21世纪的提案) 我感觉到他的痛苦。我知道有关访问的唯一原因是因为我正在处理一个使用旧访问数据库的项目。当时它已经在升级,但这并没有阻止客户请求旧版本的新功能。 密钥重用对于流密码来说是绝对致命的罪过。让它们真的很容易被打破。【参考方案7】: string clearText = txtPassword.Text;
string EncryptionKey = "MAKV2SPBNI99212";
byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
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();
clearText = Convert.ToBase64String(ms.ToArray());
【讨论】:
【参考方案8】:不要加密/解密密码,这是一个重大的安全漏洞。 HASH 密码,使用强哈希算法,例如 PBKDF2、bcrypt、scrypts 或 Argon。
当用户设置他们的密码时,对其进行散列,并存储散列(和盐)。
当用户登录时,重新散列他们提供的密码,并将其与数据库中的散列进行比较。
【讨论】:
【参考方案9】:您可以使用托管的 .Net 密码库,然后将加密的字符串保存到数据库中。当您想要验证密码时,您可以将存储的数据库字符串与用户输入的哈希值进行比较。有关SHA512Managed的更多信息,请参见此处
使用 System.Security.Cryptography;
public static string EncryptSHA512Managed(string password)
UnicodeEncoding uEncode = new UnicodeEncoding();
byte[] bytPassword = uEncode.GetBytes(password);
SHA512Managed sha = new SHA512Managed();
byte[] hash = sha.ComputeHash(bytPassword);
return Convert.ToBase64String(hash);
【讨论】:
1) 错误的方法名称,因为您散列而不加密。 2) 不要使用快速散列来散列密码。 SHA-2 的单次迭代还不够好。使用 scrypt、bcrypt 或 PBKDF2。这也不是问题的答案,因为 OP 需要可逆性。以上是关于.NET 中的密码加密/解密代码的主要内容,如果未能解决你的问题,请参考以下文章