SRP 认证过程
SRP(Security Remote Password,安全远程密码) 实际上是一个 PAKE(Password-Authenticated Key Agreement,密码验证的密钥协议) 协议的实现,可以解决在一个非安全网络上安全的交换密钥的问题。它是所谓零信息证明协议,在服务端不需要密码或者密码的等效信息(如 hash 等),而是通过服务端对客户端进行挑战来完成验证过程。这种模式可以抵抗几乎所有已知的攻击方式,从而提供更好的安全性。
SRP 基于开放的密码学算法和实现,不需要任何私有协议和软件包,理论上可以实现并运行在任何操作系统和环境当中。SRP 和相关过程使用的算法包括 random、sha256、hmac,特别是大整数的加、减、乘、幂、模和表示转换操作等运算方法。
SRP 核心的实现要点和特点如下:
- 验证器 - 服务不保存密码和密码摘要
- 挑战响应的验证模式
- 验证完成同时生成协商密钥
本例中 SRP 的 nodejs 实现,详见项目代码 srp.js。本实现需要一个第三方库 bn.js。理论上而言,nodejs10 以上的版本,原生支持 bigint,但不知道什么原因,它无法处理特别大的整数,所以只能使用第三方实现。此实现在 nodejs 和浏览器中都可以使用。
原理和流程
SRP 实现的基本认证流程如下图所示。
相关的要素项目和相关参数,按阶段和类别,可以分为算法、账号、客户端和服务端四种类型,具体如下:
算法参数
下面是约定的计算函数:
- H- 摘要计算方法
- M- Hmac 方法,包括 data 和 key
- R- 随机数产生方法
此外,SRP 基本上基于一个 DH 密钥机制,实现客户端和服务器的认证过程,所以在认证前,需要事先在两端共享一套算法参数 param。包括以下内容
- N- 一个预配置的大质数(1024、2048 位或以上)
- G- 一个素数的原根 G(通常为 2)
- H- 一个摘要算法,通常为 sha256
- h- 算法因子参数,初始或配置变化时生成
h = H(G+N)
下面是一个参数配置样例:
2048: { L: 2048, N: bn(hformat(` AC6BDB41 324A9A9B F166DE5E 1389582F AF72B665 1987EE07 FC319294 3DB56050 A37329CB B4A099ED 8193E075 7767A13D D52312AB 4B03310D CD7F48A9 DA04FD50 E8083969 EDB767B0 CF609517 9A163AB3 661A05FB D5FAAAE8 2918A996 2F0B93B8 55F97993 EC975EEA A80D740A DBF4FF74 7359D041 D5C33EA7 1D281E44 6B14773B CA97B43A 23FB8016 76BD207A 436C6481 F1D2B907 8717461A 5B9D32E6 88F87748 544523B5 24B0D57D 5EA77A27 75D2ECFA 032CFBDB F52FB378 61602790 04E57AE6 AF874E73 03CE5329 9CCC041C 7BC308D8 2A5698F3 A8D0C382 71AE35F8 E9DBFBB6 94B5C803 D89F7AE4 35DE236D 525F5475 9B65E372 FCD68EF2 0FA7111F 9E4AFF73` ),16), G: bn(2), H: 'sha256' },
用户账号参数
- I- 用户标识,如用户名、电子邮件、电话号码等等。
- P- 用户密码,原文,UTF8 编码
- s- 用户密码盐,8 位,在注册阶段提供给服务服务器
- x- 用户参数摘要
// s 和 x 的生成公式如下 s = R(8) x = H(s+H(I+":"+P))
- v- 用户验证器,由 t、x 计算而来,在注册阶段会提供给服务器
// v 的生成公式如下 v = G ^ x % N
客户端过程参数
- o- 登录会话随机数,由客户端发起,在客户端保存(后期计算 u),并在认证启动时提交给服务端
- a- 登录会话客户端私钥,登录时随机生成 16 位,认证过程中需要保存
- A- 登录会话客户端公钥,由私钥计算而来,认证过程中需要保存,并在登录阶段提交给服务端
o = R(16) a = R(16) A = G ^ a % N
- u- 登录会话参数,由 o、A、B 计算而来,并提供给密钥协商计算使用
u = M(A+B,o)
- Kc- 客户端协商计算出的密钥,由 x、a、B、u 计算而来,在响应阶段完成,并用于计算 M1
Kc = H([B - h * (G ^ x % N)] ^ ( a + u * x) % N )
- M1- 客户端认证签名信息,由 KC、A、B 计算而来
M1 = M(A+B, Kc)
服务端过程参数
- b- 登录会话服务端私钥,登录时随机生成,16 位,认证过程中需要保存
- B- 登录会话服务端公钥,由私钥和 v 计算而来,认证过程中需要保存
b = R(16) B = (h * v) + (G ^ b % N)
- u- 登录会话参数,由 o、A、B 计算而来,并提供给密钥协商计算使用
u = M(A+B,o)
- Ks- 服务端协商计算出来的密钥,由 x、A、b、u 计算而来
KS = [A * (v ^ u % N)] ^ b % N
- M2- 服务端认证签名信息,由 KS、A、M1 计算而来
M2 = M(A+M1, Ks)
- k - 服务端业务数据令牌,格式和内容由业务需求决定,认证成功后发放给客户端
注册和认证全流程
全流程包括包括登记 C0、认证 C1、挑战 S1、响应 C1、验证 S2、和完成 CF 等阶段。
登记 C0
在登记流程,主要在客户端生成盐,基于用户标识、密码和盐计算认证器参数,并将其提交给服务器。典型的提交信息包括 I、t、v,服务端应当基于这些建立和更新用户认证记录。
客户端认证 C1
在认证阶段,客户端向服务端提供 I。
服务端挑战 S1
服务器接收客户端认证请求,基于 I 查询 s、v; 随机生成 u;随机生成 b 和 B,计算并记录 KS;向客户端响应 u,B,s,v。
客户端响应 C2
客户端收到服务器端的挑战,使用保存和 v、A 和从服务器来的 s、u、B,来计算 x,并进一步计算 KC。
得到 KC 后,使用 A、B 和 KC 计算 M1;再次向服务端请求作为挑战响应,内容包括 I、M1。
服务端验证 S2
服务端收到 I,M1,可以通过 I 查询 KS、A 和 B,并计算 M1S,和 M1 进行比较得到结果 r,如果匹配成功,使用 M1、A 和 KS 计算 M2;
在这个阶段如果认证通过,可以根据业务和应用需求,生成相关业务数据如访问 token k。
随后,向客户端响应 r,可能包括 M2 和 k。
客户端验证和完成 CF
客户端得到验证结果,如果成功,提取 M2,可以进一步使用 A、M1 和 KC 来验证 M2。如果验证成功,则使用 k 开展后续业务。
附加信息
bigint 库功能
对于大整数,不能直接使用系统的操作符,而需要使用 bigint 库提供的对象 bn,应该提供如下功能
- 将 int、hex、buffer 转为 bn 对象
- 提供大数的相加功能, add
- 提供大数的相见功能, minus
- 大数相乘, multiply
- 大数乘方, pow
- 大数取模, mod
- 大数乘方取模组合, modPow
客户端实现
本案中的示例代码,均是 nodejs 的实现。在实际的应用场景中,实际上是分为客户端和服务器运行的环境。由于都是 javascript,技术上的差异并不是很大,如所使用的 bn 库,都是第三方外置库,在两端的使用方式都是一样的。
客户端部分示例代码如下,需要引用 bn 和自己的 webcrypto(详见 webcrypt 文档) 库,其他阶段的计算,基本上和服务端无异:
// 引用 bn 库 // <script src="/js/bn.js"></script> const bn = bigInt // 密码盐 let salt = wcrypto.random(8); // compute x let uhash = await wcrypto.hash(user.id+":"+user.password) let x = await wcrypto.hash(Uint8Array.from([...salt,...wcrypto.hexArray(uhash)])) // compute v from x let v = P_SRP.G.modPow(bn(x,16),P_SRP.N).toString(36) salt = await wcrypto.toHex(salt)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: SJCL 技术解读
下一篇: 彻底找到 Tomcat 启动速度慢的元凶
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论