OpenID Connect Discovery issuer里面有一个jwks_uri,里面包含了验证id_token的公钥,这些公钥都是JWK格式的。一般情况下,证书使用pem格式保存,可以将pem转换成jwk格式。jwt.io验证id token居然需要pem格式的公钥,不支持jwk这样跟jwt有亲戚关系的格式真的是匪夷所思。
我比较好奇如何使用这个公钥校验id_token。查了一下资料,找到lokey这个工具,可以很方便将pem和jwk格式互相转换。
首先安装一下lokey这个工具。
$ sudo pip install lokey
#如果遇到six的问题,重装一下six就好。
$ sudo pip install --ignore-installed six
Collecting six
Downloading six-1.10.0-py2.py3-none-any.whl
Installing collected packages: six
Successfully installed six-1.10.0
通过Google OAuth Playground去拿到一个合法id_token。scope记得加上profile,才会有id_token。
把id_token放到https://jwt.io/校验一下。发现kid是b2719f31c6ba1e5fe664fbb1bf0f7c05b3d3a0a1
。顶部ALGORITHM选择RS256
。因为没有设置公钥,所以底部会提示无效的签名。
打开Google jwks_uri,可以看到有这个kid。
通过lokey去获取这个kid的信息,并将其转换成pem格式。
$ lokey fetch jwk accounts.google.com --key-id b2719f31c6ba1e5fe664fbb1bf0f7c05b3d3a0a1 | lokey to pem
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAovtSwEWv9Q97JbB5Knfq
4iAn8gl+ONzsFoxEasbh9+l4CgeTImIXH31cOxu5tjVjAxeFifPW9w8EdEa+o8kU
SJ40Fp2qMRN9wFAHmu5pmS70Vlm4brg4Rc2jk8XMahNKoZ6AY1sIs/6e/JsiLMvE
P8btu1iOpZri5fS9MjbWC9IOW1Vpy9MB8hp7IUZ4nwWBcWmT4NIUmR1QF9+fDmWs
F0JUXfjAm1//cAJN7K3EcMOhLwWkVvrr1U0bpeRLprTUKpRNER+nq+pA0b2t5U56
rFAAws+5ydNw4coa2dS3AJrmnaCSS4BZubpRYGh8ScTBxQlZm74f1Sty8H7gUeys
FwIDAQAB
-----END PUBLIC KEY-----
#lokey也支持把pem转换成jwk格式,所以可以这样转回去。
$ lokey fetch jwk accounts.google.com --key-id b2719f31c6ba1e5fe664fbb1bf0f7c05b3d3a0a1 | lokey to pem | lokey to jwk
{"e": "AQAB", "kty": "RSA", "n": "ovtSwEWv9Q97JbB5Knfq4iAn8gl-ONzsFoxEasbh9-l4CgeTImIXH31cOxu5tjVjAxeFifPW9w8EdEa-o8kUSJ40Fp2qMRN9wFAHmu5pmS70Vlm4brg4Rc2jk8XMahNKoZ6AY1sIs_6e_JsiLMvEP8btu1iOpZri5fS9MjbWC9IOW1Vpy9MB8hp7IUZ4nwWBcWmT4NIUmR1QF9-fDmWsF0JUXfjAm1__cAJN7K3EcMOhLwWkVvrr1U0bpeRLprTUKpRNER-nq-pA0b2t5U56rFAAws-5ydNw4coa2dS3AJrmnaCSS4BZubpRYGh8ScTBxQlZm74f1Sty8H7gUeysFw"}
lokey的kid参数居然要求是一个int类型的,而通过shell传入的kid会被当做一个字符串,然后提示错误,所以我把代码接受的类型改成了字符串。
#/Library/Python/2.7/site-packages/lokey/__init__.py
#@click.option('--kid', '--key-id', type=int, help="Key ID ('kid') to print.")
@click.option('--kid', '--key-id', type=str, help="Key ID ('kid') to print.")
把pem输入到公钥里面,可以发现id_token验证通过了。