关于OAuth2.1 PKCE利用crypto的sha256哈希结果转换base64url计算结果不一致的问题(Flutter)

前言

最近在写第三方flutter app并抓包分析某原生app登录逻辑的时候,遇到了使用OAuth2.1 PKCE授权码模式的登录方式,该模式下需要将code_verifier先进行sha256哈希后再进行base64(URL-Save) 编码成最终的code_challenge。

OAuth2.1 PKCE授权码模式详解:https://shanhy.blog.csdn.net/article/details/114080598

code_verifier转code_challenge的工具:https://tonyxu-io.github.io/pkce-generator/

问题详情

  1. 在对code_verifier进行sha256哈希+base64URL编码后,所得结果与预期的完全不一致;在进行初步对比排查后发现是在base64URL这一步骤出现的差异。
  2. 并且在使用各种线上工具对已经sha256哈希后的字符串进行base64URL编码,也与预期结果完全不一致。

核心要点(太长不看版)

编码不同的问题。sha256哈希后的结果数据是32个byte(字节编码),PKCE模式下的base64应该基于这32个byte进行编码;而将哈希后的结果转换成十六进制字符串后进行base64编码会不一样。

分析

在搜索科普了不少文章后才意识到问题所在。

进行数据观测的时候我分别打印出了以下三种数据格式:

// Flutter,使用了crypto插件
var codeVerifier = "Tl-HbpKwvp54QYiUeYOKrvE8jO9ZUX54L-6VwkgiWzU";
var sha256Result = sha256.convert(utf8.encode(codeVerifier));
print(sha256Result.bytes); // 1、sha256哈希后,打印字节编码
print(sha256Result.toString()); // 2、sha256哈希后,打印成字符串
print(utf8.encode(sha256Result.toString())); // 3、sha256哈希后,打印字符串的utf8编码
print(base64UrlEncode(utf8.encode(sha256Result.toString()))); // 4、打印base64Url编码结果(与正常情况不一致)

输出结果分别为:

  1. [93, 154, 247, 89, 229, 56, 246, 48, 54, 31, 68, 109, 191, 35, 46, 82, 100, 162, 83, 19, 141, 204, 139, 6, 104, 112, 146, 122, 62, 224, 232, 60]

  2. 5d9af759e538f630361f446dbf232e5264a253138dcc8b066870927a3ee0e83c

  3. [53, 100, 57, 97, 102, 55, 53, 57, 101, 53, 51, 56, 102, 54, 51, 48, 51, 54, 49, 102, 52, 52, 54, 100, 98, 102, 50, 51, 50, 101, 53, 50, 54, 52, 97, 50, 53, 51, 49, 51, 56, 100, 99, 99, 56, 98, 48, 54, 54, 56, 55, 48, 57, 50, 55, 97, 51, 101, 101, 48, 101, 56, 51, 99]

  4. NWQ5YWY3NTllNTM4ZjYzMDM2MWY0NDZkYmYyMzJlNTI2NGEyNTMxMzhkY2M4YjA2Njg3MDkyN2EzZWUwZTgzYw==

其实根据这个结果其实可发现,结果一开头的十进制字节编码93对应结果二开头的十六进制字符串5d,而5d又分别对应十进制UTF8/ASCII编码的53100(结果三开头)。而结果四与在线字符串转base64结果一样(都是不正确的)。正确结果应该是XZr3WeU49jA2H0RtvyMuUmSiUxONzIsGaHCSej7g6Dw=

查阅资料得知,sha256哈希后的结果数据是32个字节编码(例如结果一),而PKCE模式下的base64应该基于这32个byte进行编码。结果四之所以与正确结果完全不同,是因为将哈希后的结果转换成十六进制字符串后(例如结果二)又转换成utf8编码(例如结果三)进行base64URl编码,导致结果大不相同。线上的字符串与base64转换工具也是相似的原因导致的结果不一致。

解决方法(Flutter)

改动前的错误的flutter dart代码:

// 注:使用了插件包 crypto: ^3.0.1
String str = sha256.convert(utf8.encode(codeVerifier)).toString();
return base64UrlEncode(utf8.encode(str));

改正后:

var str = sha256.convert(utf8.encode(codeVerifier)).bytes;
return base64UrlEncode(str);

PS:(果然理论知识还是挺重要的啊,不然就不会犯这种错。)

额外参考资料:

ascii 和 byte以及UTF-8的转码规则(还有base64)

一文读懂SHA256算法原理及其实现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@月琳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值