快精灵印艺坊 您身边的文印专家
广州名片 深圳名片 会员卡 贵宾卡 印刷 设计教程
产品展示 在线订购 会员中心 产品模板 设计指南 在线编辑
 首页 名片设计   CorelDRAW   Illustrator   AuotoCAD   Painter   其他软件   Photoshop   Fireworks   Flash  

 » 彩色名片
 » PVC卡
 » 彩色磁性卡
 » 彩页/画册
 » 个性印务
 » 彩色不干胶
 » 明信片
   » 明信片
   » 彩色书签
   » 门挂
 » 其他产品与服务
   » 创业锦囊
   » 办公用品
     » 信封、信纸
     » 便签纸、斜面纸砖
     » 无碳复印纸
   » 海报
   » 大篇幅印刷
     » KT板
     » 海报
     » 横幅

详解对密码执行散列和 salt 运算方式

  大家对密码执行散列和Salt运算一定不生疏。两个Visual Studio企业版示例都是用的这个方式来加密这个方式的。结合示例代码,我总结了一个包含对密码进行加密,比较等静态方式的类。
  使用说明:先用HashAndSalt方式对密码进行加密,然后存储到数据库中。 在用户登录时用ComparePasswords方式在对用户输入的密码和用户注册时存储在数据库中的密码进行比较,判定用户输入的密码是否准确。

 

Credentials.cs

using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;
 
namespace BookStore.Common
{
       /// <summary>
       /// Credentials 的摘要说明。
       /// 原理:
       /// 对密码执行散列运算
       /// 若要避免以明文形式存储密码,一种常见的安全做法是对密码执行散列运算。如以下代码所示,使用 System.Security.Cryptography 命名空间(它实现 160 位 SHA-1 标准)对密码进行散列运算。有关更多信息,请参见 SHA1 成员。
       /// 对散列执行 Salt 运算
       /// 虽然对密码执行散列运算的一个好的开端,但若要增加免受潜在攻击的安全性,则可以对密码散列执行 Salt 运算。Salt 就是在已执行散列运算的密码中插入的一个随机数字。这一策略有助于阻止潜在的攻击者利用预先计算的字典攻击。字典攻击是攻击者使用密钥的所有可能组合来破解密码的攻击。当您使用 Salt 值使散列运算进一步随机化后,攻击者将需要为每个 Salt 值创建一个字典,这将使攻击变得异常复杂且成本极高。
       /// Salt 值随散列存储在一起,并且未经过加密。所存储的 Salt 值可以在随后用于密码验证。
       /// </summary>
       public class Credentials
       {
              private static string key = \"!48%0d-F=cj>,s&2\";  //密钥(增加密码复杂度,似乎比较多余)
              private const int saltLength = 4;                         //定义salt值的长度
 
              /// <summary>
              /// 对密码进行Hash 和 Salt
              /// </summary>
              /// <param name=\"Password\">用户输入的密码</param>
              /// <returns></returns>
              public static byte[] HashAndSalt(string Password)
              {
                     return CreateDbPassword(HashPassword(Password));
              }
 
              /// <summary>
              /// 对用户输入的密码加上密钥key后进行SHA1散列
              /// </summary>
              /// <param name=\"Password\">用户输入的密码</param>
              /// <returns>返回 160 位 SHA-1 散列后的的byte[](160位对应20个字节)</returns>
              private static byte[] HashPassword( string Password )
              {
                     //创建SHA1的对象实例sha1
                     SHA1 sha1 = SHA1.Create();
                     //计算输入数据的哈希值
                     return sha1.ComputeHash( Encoding.Unicode.GetBytes( Password + key ) );
              }
             
              /// <summary>
              /// 比较数据库中的密码和所输入的密码是否一样
              /// </summary>
              /// <param name=\"storedPassword\">数据库中的密码</param>
              /// <param name=\"Password\">用户输入的密码</param>
              /// <returns>true:相等/false:不等</returns>
              public static bool ComparePasswords(byte[] storedPassword, string Password)
              {
                     //首先将用户输入的密码进行Hash散列
                     byte[] hashedPassword = HashPassword(Password);
 
                     if (storedPassword == null || hashedPassword == null || hashedPassword.Length != storedPassword.Length - saltLength)
                     {
                            return false;
                     }
 
                     //获取数据库中的密码的salt 值,数据库中的密码的后4个字节为salt 值
                     byte[] saltValue = new byte[saltLength];
                     int saltOffset = storedPassword.Length - saltLength;
                     for (int i = 0; i < saltLength; i++){
                            saltValue[i] = storedPassword[saltOffset + i];
                     }
                    
                     //用户输入的密码用户输入的密码加上salt 值,进行salt
                     byte[] saltedPassword = CreateSaltedPassword(saltValue, hashedPassword);
             
                     //比较数据库中的密码和经过salt的用户输入密码是否相等
                     return CompareByteArray(storedPassword, saltedPassword);
              }
 
              /// <summary>
              /// 比较两个ByteArray,看是否相等
              /// </summary>
              /// <param name=\"array1\"></param>
              /// <param name=\"array2\"></param>
              /// <returns>true:相等/false:不等</returns>
              private static bool CompareByteArray(byte[] array1, byte[] array2)
              {
                     if (array1.Length != array2.Length)
                     {
                            return false;
                     }
                     for (int i = 0; i < array1.Length; i++)
                     {
                            if (array1[i] != array2[i])
                            {
                                   return false;
                            }
                     }
                     return true;
              }
 
              /// <summary>
              /// 对要存储的密码进行salt运算
              /// </summary>
              /// <param name=\"unsaltedPassword\">没有进行过salt运算的hash散列密码</param>
              /// <returns>经过salt的密码(经过salt的密码长度为:20+4=24,存储密码的字段为Binary(24))</returns>
              private static byte[] CreateDbPassword(byte[] unsaltedPassword)
              {
                     //获得 salt 值
                     byte[] saltValue = new byte[saltLength];
                     RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
                     rng.GetBytes(saltValue);
                    
                     return CreateSaltedPassword(saltValue, unsaltedPassword);
              }
             
              /// <summary>
              /// 创建一个经过salt的密码
              /// </summary>
              /// <param name=\"saltValue\">salt 值</param>
              /// <param name=\"unsaltedPassword\">没有进行过salt运算的hash散列密码</param>
              /// <returns>经过salt的密码</returns>
              private static byte[] CreateSaltedPassword(byte[] saltValue, byte[] unsaltedPassword)
              {
                     //将salt值数组添加到hash散列数组后拼接成rawSalted数组中
                     byte[] rawSalted  = new byte[unsaltedPassword.Length + saltValue.Length];
                     unsaltedPassword.CopyTo(rawSalted,0);
                     saltValue.CopyTo(rawSalted,unsaltedPassword.Length);
                    
                     //将合并后的rawSalted数组再进行SHA1散列的到saltedPassword数组(长度为20字节)
                     SHA1 sha1 = SHA1.Create();
                     byte[] saltedPassword = sha1.ComputeHash(rawSalted);
 
                     //将salt值数组在添加到saltedPassword数组后拼接成dbPassword数组(长度为24字节)
                     byte[] dbPassword  = new byte[saltedPassword.Length + saltValue.Length];
                     saltedPassword.CopyTo(dbPassword,0);
                     saltValue.CopyTo(dbPassword,saltedPassword.Length);
 
                     return dbPassword;
              }
 
       }
}


返回类别: 教程
上一教程: 如何得到一个汉字和字母组合的字符串的正确的长度(asp.net 版本的)
下一教程: 如何用asp+获取post的页面的数据

您可以阅读与"详解对密码执行散列和 salt 运算方式"相关的教程:
· 在.net执行sql脚本的简朴实现
· C# 3.0语言详解之基本的语言增强
· [ASP.NET] Session 详解
· 密码系统与.NET Framework
· NET框架程序设计读书笔记(三)--执行程序集代码
    微笑服务 优质保证 索取样品