如何创建 JWKS 公钥/私钥对,类似于可以在 https://mkjwk.org/ 手动创建的密钥对,其中包括 密钥 ID (
kid
) 和 密钥使用 (use
)?我使用 cryptography
模块生成 RSA 密钥对,并使用 python-jose
将密钥提取为 JWK,但创建的密钥不包括 kid
和 use
(毫不奇怪,因为它们尚未在任何地方指定) .
代码:
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from jose import jwk, constants
import json
key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
public_key = key.public_key().public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
private_key = key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption()
)
print(json.dumps(jwk.RSAKey(algorithm=constants.Algorithms.RS256, key=public_key.decode('utf-8')).to_dict()))
print(json.dumps(jwk.RSAKey(algorithm=constants.Algorithms.RS256, key=private_key.decode('utf-8')).to_dict()))
通过上述代码片段生成公钥(无
kid
或 use
属性):
{
"alg": "RS256",
"kty": "RSA",
"n": "tqbcR_6JC....OKQ",
"e": "AQAB"
}
使用
jwcrypto
库生成 JWKS 公钥/私钥对的另一种方法:
from jwcrypto import jwk
key = jwk.JWK.generate(kty='RSA', size=2048, alg='RSA-OAEP-256', use='enc', kid='12345')
public_key = key.export_public()
private_key = key.export_private()
通过上面的代码片段生成公钥:
{
"kty": "RSA",
"alg": "RSA-OAEP-256",
"kid": "12345",
"use": "enc",
"e": "AQAB",
"n": "0YclBn...vV7y7w"
}
我相信
kid
只是一段元数据(任何字符串),在生成密钥的过程中没有使用。
对于
use
来说,它可能有点相似,但根据您想要不同的非对称加密方案的用途(您可以参考最知名的非对称加密系统 RSA 的加密和签名方案)描述)。
总而言之,您很可能可以根据上述信息向 JSON 字典添加适当的键来重新创建 JSON 的精确结构。
您可以使用以下代码生成 JWKS,其中包括有关证书的信息(例如 x5t、x5c)。 由于证书中已经包含了公钥的信息,所以不需要单独传递公钥。
要了解有关如何为 JWT 生成数字签名 RSA 对的更多信息,请参阅此处
import base64
import json
from cryptography.hazmat.primitives.hashes import SHA1
from jose import constants, jwk
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
class JWKSGenerator:
def __init__(
self,
*,
file_path: str,
kid: str,
use: str = 'sig',
) -> None:
with open(file_path, 'rb') as f:
self._cert_pem_raw_data: bytes = f.read().strip()
self._cert = x509.load_pem_x509_certificate(self._cert_pem_raw_data, default_backend())
self._kid = kid
self._use = use
def _create_x5t(self, ):
digest = self._cert.fingerprint(algorithm=SHA1())
return base64.b64encode(digest).decode('utf-8')
def _create_x5c(self, ):
cert_val = self._cert.public_bytes(serialization.Encoding.DER)
return base64.b64encode(cert_val).decode('utf-8')
def process(self):
jwks = jwk.RSAKey(algorithm=constants.Algorithms.RS256, key=self._cert_pem_raw_data).to_dict()
jwks.update({
"x5t": self._create_x5t(),
"x5c": [
self._create_x5c()
],
"kid": self._kid,
"use": self._use,
})
print(json.dumps(jwks, indent=4))
return jwks
JWKSGenerator(
file_path='<complete path to certificate pem file>',
kid="key_id_123",
).process()
如果您尚未进行数字签名,您可以使用@Anand Jayabalan 提供的