zoukankan      html  css  js  c++  java
  • Microsoft.Identity的IPasswordHasher加密的默认实现与运用

     本文版权归博客园和作者吴双本人共同所有,转载和爬虫请注明原文地址  www.cnblogs.com/tdws

       相信了解了MS Identity认证体系的一定知道UserManager的作用,他是整个体系中的调度者,他定义了一套用户行为来帮助我们管理用户信息,角色信息,处理密码等。而其实现则在UserStore当中,我们可以实现其为我们定义的比如IUserStore,IUserPasswordStore,IRoleStore等等. 我们可以基于一整套用户行为,自定义自己的用户信息和数据结构以及数据存储。那么关于Password的Hasher,MS依然为我们提供了完整的行为定义,也由UserManager来调度。比如

    1 UserManager.PasswordHasher.HashPassword(password)
    PasswordHasher在UserManager接口中是这样定义的:

    我原本对其默认实现是没有兴趣的,出于独立多个应用的登陆认证的目的,所以需要一个独立的用户认证项目来作为认证服务,其仅生产token,认证成功后,用户的HTTP Request Header的Authorization带着 token来访问应用服务器上的各种资源。

    就是因为这样的原因,在多个应用的密码认证上出现了这样一个问题:

    比如应用A采用了实现IPasswordHasher来自定义加密方式——MD5+salt的形式,而应用B则采用了Identity默认的PasswordHasher来实现,通过反编译得到如下代码:

    所以为了兼容多个应用不同的加密方式,我不得不反编译出源码,拿到其默认加密方式,根据不同应用名称,来判断对密码加密或者解密,或者直接通过某种方式来对比数据库和用户输入的密码。先上MS默认的PasswordHasher具体实现

     1 // Decompiled with JetBrains decompiler
     2 // Type: Microsoft.AspNet.Identity.Crypto
     3 // Assembly: Microsoft.AspNet.Identity.Core, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
     4 // MVID: E3A10FFD-023A-4BC3-AD53-32D145ABF1C9
     5 // Assembly location: C:SportNewProjectV2.0ApiFantasy.SportpackagesMicrosoft.AspNet.Identity.Core.2.2.1lib
    et45Microsoft.AspNet.Identity.Core.dll
     6 
     7 using System;
     8 using System.Runtime.CompilerServices;
     9 using System.Security.Cryptography;
    10 
    11 namespace Microsoft.AspNet.Identity
    12 {
    13   internal static class Crypto
    14   {
    15     private const int PBKDF2IterCount = 1000;
    16     private const int PBKDF2SubkeyLength = 32;
    17     private const int SaltSize = 16;
    18 
    19     public static string HashPassword(string password)
    20     {
    21       if (password == null)
    22         throw new ArgumentNullException("password");
    23       byte[] salt;
    24       byte[] bytes;
    25       using (Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, 16, 1000))
    26       {
    27         salt = rfc2898DeriveBytes.Salt;
    28         bytes = rfc2898DeriveBytes.GetBytes(32);
    29       }
    30       byte[] inArray = new byte[49];
    31       Buffer.BlockCopy((Array) salt, 0, (Array) inArray, 1, 16);
    32       Buffer.BlockCopy((Array) bytes, 0, (Array) inArray, 17, 32);
    33       return Convert.ToBase64String(inArray);
    34     }
    35 
    36     public static bool VerifyHashedPassword(string hashedPassword, string password)
    37     {
    38       if (hashedPassword == null)
    39         return false;
    40       if (password == null)
    41         throw new ArgumentNullException("password");
    42       byte[] numArray = Convert.FromBase64String(hashedPassword);
    43       if (numArray.Length != 49 || (int) numArray[0] != 0)
    44         return false;
    45       byte[] salt = new byte[16];
    46       Buffer.BlockCopy((Array) numArray, 1, (Array) salt, 0, 16);
    47       byte[] a = new byte[32];
    48       Buffer.BlockCopy((Array) numArray, 17, (Array) a, 0, 32);
    49       byte[] bytes;
    50       using (Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, salt, 1000))
    51         bytes = rfc2898DeriveBytes.GetBytes(32);
    52       return Crypto.ByteArraysEqual(a, bytes);
    53     }
    54 
    55     [MethodImpl(MethodImplOptions.NoOptimization)]
    56     private static bool ByteArraysEqual(byte[] a, byte[] b)
    57     {
    58       if (object.ReferenceEquals((object) a, (object) b))
    59         return true;
    60       if (a == null || b == null || a.Length != b.Length)
    61         return false;
    62       bool flag = true;
    63       for (int index = 0; index < a.Length; ++index)
    64         flag &= (int) a[index] == (int) b[index];
    65       return flag;
    66     }
    67   }
    68 }

    有人可能会问,拿到了这些源码要如何应用呢。下面就是浅述到这个问题。

    一开始我天真的认为,不就是一个加密么,不用仔细看了,拿来用就好了?

    在注册用户和修改密码的时候,都是通过上面 HashPassword 方法来做的密码加密,那我在新的自定义PasswordHasher中,为应用B对比用户登录密码的时候,把用户输入直接通过HashPassword加密一边不就好了?所以我自定义的VerifyHashedPassword (Verify译为核实)方法,就是比较数据库中的Pwd和经过hasher处理的结果是否相等。 可结果是,每次相同的字符串,会产生不同的加密结果,和以前玩md5+salt不一样呀。所以我又想到了其默认实现的  VerifyHashedPassword 方法。

    所以最后要说的就是 你可以拿来微软Identity的加密方式(上面的Hasher)直接使用 , 在比较用户输入和数据库中已经经过hash的存储结果进行对比的时候,使用其 VerifyHashedPassword()方法。即使不使用Identity认证 也可以用此加密算法。

    如果需要更多了解Identity和OWIN,请看腾飞的如下四篇文章:

    http://www.cnblogs.com/jesse2013/category/551550.html

  • 相关阅读:
    HTML5
    js实现查找字符串中最多的字符的个数
    get和post的区别
    第十七篇 类的特殊成员
    第十八篇 面向对象修饰符
    MariaDB+Keepalived双主高可用配置MySQL-HA
    linux命令详解——crontab
    Java的内存泄漏
    jvm监控工具jconsole进行远程监控配置
    loadrunner执行场景时报Error -27040: Data Format Extension: Init: Internal error问题解决
  • 原文地址:https://www.cnblogs.com/tdws/p/6409650.html
Copyright © 2011-2022 走看看