欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Asp.net中Microsoft.Identity的IPasswordHasher加密的默认实现与运用

程序员文章站 2022-06-29 17:41:14
相信了解了ms identity认证体系的一定知道usermanager的作用,他是整个体系中的调度者,他定义了一套用户行为来帮助我们管理用户信息,角色信息,处理密码等。而...

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

usermanager.passwordhasher.hashpassword(password)

passwordhasher在usermanager接口中是这样定义的:

Asp.net中Microsoft.Identity的IPasswordHasher加密的默认实现与运用

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

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

比如应用a采用了实现ipasswordhasher来自定义加密方式——md5+salt的形式,而应用b则采用了identity默认的passwordhasher来实现,通过反编译得到如下代码:

Asp.net中Microsoft.Identity的IPasswordHasher加密的默认实现与运用

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

// decompiled with jetbrains decompiler
// type: microsoft.aspnet.identity.crypto
// assembly: microsoft.aspnet.identity.core, version=2.0.0.0, culture=neutral, publickeytoken=31bf3856ad364e35
// mvid: e3a10ffd-023a-4bc3-ad53-32d145abf1c9
// assembly location: c:\sport\newproject\v2.0\api\fantasy.sport\packages\microsoft.aspnet.identity.core.2.2.1\lib\net45\microsoft.aspnet.identity.core.dll
using system;
using system.runtime.compilerservices;
using system.security.cryptography;
namespace microsoft.aspnet.identity
{
 internal static class crypto
 {
 private const int pbkdf2itercount = 1000;
 private const int pbkdf2subkeylength = 32;
 private const int saltsize = 16;
 public static string hashpassword(string password)
 {
  if (password == null)
  throw new argumentnullexception("password");
  byte[] salt;
  byte[] bytes;
  using (rfc2898derivebytes rfc2898derivebytes = new rfc2898derivebytes(password, 16, 1000))
  {
  salt = rfc2898derivebytes.salt;
  bytes = rfc2898derivebytes.getbytes(32);
  }
  byte[] inarray = new byte[49];
  buffer.blockcopy((array) salt, 0, (array) inarray, 1, 16);
  buffer.blockcopy((array) bytes, 0, (array) inarray, 17, 32);
  return convert.tobase64string(inarray);
 }
 public static bool verifyhashedpassword(string hashedpassword, string password)
 {
  if (hashedpassword == null)
  return false;
  if (password == null)
  throw new argumentnullexception("password");
  byte[] numarray = convert.frombase64string(hashedpassword);
  if (numarray.length != 49 || (int) numarray[0] != 0)
  return false;
  byte[] salt = new byte[16];
  buffer.blockcopy((array) numarray, 1, (array) salt, 0, 16);
  byte[] a = new byte[32];
  buffer.blockcopy((array) numarray, 17, (array) a, 0, 32);
  byte[] bytes;
  using (rfc2898derivebytes rfc2898derivebytes = new rfc2898derivebytes(password, salt, 1000))
  bytes = rfc2898derivebytes.getbytes(32);
  return crypto.bytearraysequal(a, bytes);
 }
 [methodimpl(methodimploptions.nooptimization)]
 private static bool bytearraysequal(byte[] a, byte[] b)
 {
  if (object.referenceequals((object) a, (object) b))
  return true;
  if (a == null || b == null || a.length != b.length)
  return false;
  bool flag = true;
  for (int index = 0; index < a.length; ++index)
  flag &= (int) a[index] == (int) b[index];
  return flag;
 }
 }
}

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

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

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

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

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持!