PS256 签入 Flutter / Dart

问题描述

我需要在 Flutter 应用程序中使用 PS256 算法对 JWT 进行编码,但是搜索了一段时间后,我找不到任何支持 PS256 算法或至少支持 RSASSA-PSS(PS256 中使用的签名算法)的库。我想从头开始编码它,但我的密码学知识太少,我找不到关于算法如何工作的任何全面信息。 有人能帮我解决这个问题吗?也许有算法的例子或在 dart 中使用其他语言库的方法

解决方法

PS256 在 RFC7518,Sec. 3.1 中有描述:RSA-256 的 RSASSA-PSS 和带有 SHA-256 的 MGF1。如果您找不到支持 PS256 的 Dart JWT 库,您可以使用 RSASSA-PSS 显式进行签名,无需太多努力。

对于以下实现,我选择了支持 RSASSA-PSS 的库 fast_rsa 1.3.5。作为测试,我在选择 PS256 作为算法后使用了网站 https://jwt.io/ 的密钥和 JWT。

下面的 Dart 代码生成签名和签名的 JWT:

Future<void> test() async {

  var privateKey = """-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAnzyis1ZjfNB0bBgKFMSvvkTtwlvBsaJq7S5wA+kzeVOVpVWw
kWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHcaT92whREFpLv9cj5lTeJSibyr/Mr
m/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIytvHWTxZYEcXLgAXFuUuaS3uF9gEi
NQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0e+lf4s4OxQawWD79J9/5d3Ry0vbV
3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWbV6L11BWkpzGXSW4Hv43qa+GSYOD2
QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9MwIDAQABAoIBACiARq2wkltjtcjs
kFvZ7w1JAORHbEufEO1Eu27zOIlqbgyAcAl7q+/1bip4Z/x1IVES84/yTaM8p0go
amMhvgry/mS8vNi1BN2SAZEnb/7xSxbflb70bX9RHLJqKnp5GZe2jexw+wyXlwaM
+bclUCrh9e1ltH7IvUrRrQnFJfh+is1fRon9Co9Li0GwoN0x0byrrngU8Ak3Y6D9
D8GjQA4Elm94ST3izJv8iCOLSDBmzsPsXfcCUZfmTfZ5DbUDMbMxRnSo3nQeoKGC
0Lj9FkWcfmLcpGlSXTO+Ww1L7EGq+PT3NtRae1FZPwjddQ1/4V905kyQFLamAA5Y
lSpE2wkCgYEAy1OPLQcZt4NQnQzPz2SBJqQN2P5u3vXl+zNVKP8w4eBv0vWuJJF+
hkGNnSxXQrTkvDOIUddSKOzHHgSg4nY6K02ecyT0PPm/UZvtRpWrnBjcEVtHEJNp
bU9pLD5iZ0J9sbzPU/LxPmuAP2Bs8JmTn6aFRspFrP7W0s1Nmk2jsm0CgYEAyH0X
+jpoqxj4efZfkUrg5GbSEhf+dZglf0tTOA5bVg8IYwtmNk/pniLG/zI7c+GlTc9B
BwfMr59EzBq/eFMI7+LgXaVUsM/sS4Ry+yeK6SJx/otIMWtDfqxsLD8CPMCRvecC
2Pip4uSgrl0MOebl9XKp57GoaUWRWRHqwV4Y6h8CgYAZhI4mh4qZtnhKjY4TKDjx
QYufXSdLAi9v3FxmvchDwOgn4L+PRVdMwDNms2bsL0m5uPn104EzM6w1vzz1zwKz
5pTpPI0OjgWN13Tq8+PKvm/4Ga2MjgOgPWQkslulO/oMcXbPwWC3hcRdr9tcQtn9
Imf9n2spL/6EDFId+Hp/7QKBgAqlWdiXsWckdE1Fn91/NGHsc8syKvjjk1onDcw0
NvVi5vcba9oGdElJX3e9mxqUKMrw7msJJv1MX8LWyMQC5L6YNYHDfbPF1q5L4i8j
8mRex97UVokJQRRA452V2vCO6S5ETgpnad36de3MUxHgCOX3qL382Qx9/THVmbma
3YfRAoGAUxL/Eu5yvMK8SAt/dJK6FedngcM3JEFNplmtLYVLWhkIlNRGDwkg3I5K
y18Ae9n7dHVueyslrb6weq7dTkYDi3iOYRW8HRkIQh06wEdbxt0shTzAJvvCQfrB
jg/3747WSsf/zBTcHihTRBdAv6OmdhV4/dD5YBfLAkLrd+mX7iE=
-----END RSA PRIVATE KEY-----""";

  var unsignedJWT = "eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0";
  var signature = await RSA.signPSS(unsignedJWT,Hash.HASH_SHA256,SaltLength.SALTLENGTH_EQUALS_HASH,privateKey);
  var signatureB64url = signature.replaceAll ("+","-").replaceAll ("/","_").replaceAll ("=","");
  var signedJWT = unsignedJWT + "." + signatureB64url;

  print(signedJWT);
}

如果代码被执行,例如以下签名的 JWT 结果:

eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.Yn5adhSVLL1PZShH16gFYAv1LxmShAro0-2lv9wPea8Vc-6LbvT91xhvLzRve6f5ShKCVQRAIWP3Iqip6cF8q4M25ro7_g5FnWPXK5NoMQnf_08gCizsIrZXmpyLLpCYKds38UYcpni5IgcaFgoqjJlbtPGVf5utdIB1CaZHP_OkwpL0hHTCv4n9qlwLqmiTMV0XCN6OIBq4XIzh4MGJa9xCzNgHnRXBQbygxB5yaoK__f6DUKKJNa9mcII5cQOWNHrI7PGClRr4QYxv0pRjCvaRkjpCSJxJQSw1MapV5JDxanE3XORnOZXbh_BHgNlQX9S6NxjnRlxYiV04VV0Nhw

请注意 PSS 是概率性的,因此每次相同的输入都会产生不同的签名。

签名后的 JWT 可以通过公钥成功验证:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSv
vkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHc
aT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIy
tvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0
e+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWb
V6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9
MwIDAQAB
-----END PUBLIC KEY-----

例如https://jwt.io/