如何在python中创建JWKS公钥/私钥对?



如何创建一个JWKS公钥/私钥对,类似于可以在 https://mkjwk.org/手动创建的公钥对,其中包括密钥ID(kid)和密钥使用(use)?我使用cryptography模块生成 RSA 密钥对,python-jose将密钥提取为 JWK,但创建的密钥不包括kiduse(不出所料,因为它们没有在任何地方指定)。

法典:

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()))

通过上述代码片段生成的公钥(无kiduse属性):

{
"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()

如果您没有证书,则可以使用以下代码:

jwks = jwk.RSAKey(algorithm=constants.Algorithms.RS256, key=open('<path_to_public_key>'>, 'rb')).to_dict()
jwks.update({
"kid": '<key_id>',
"use": 'sig',
})
print(json.dumps(jwks, indent=4))

最新更新