Ответ 1
Эта другая библиотека (python-jose) может помочь проверить.
Обратите внимание, что ключи должны быть JSON dict, которые должны быть переданы в decode
.
Я пытаюсь заставить PyJWT 1.1.0 проверять JWT с открытым ключом. Этими ключами являются по умолчанию, поставляемые с Keycloak. Скорее всего, проблема связана с созданием секретного ключа, но я не нашел рабочих примеров для создания ключа без сертификата с частным и открытым ключом.
Здесь мои попытки заставить его работать. Некоторые из приведенных ниже тестов жалуются на недопустимый ключ, и некоторые из них жалуются, что токен не проверен должным образом против ключа.
import jwt
from cryptography.hazmat.backends import default_backend
from itsdangerous import base64_decode
from Crypto.PublicKey import RSA
secret = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCIE6a1NyEFe7qCDFrvWFZiAlY1ttE5596w5dLjNSaHlKGv8AXbKg/f8yKY9fKAJ5BKoeWEkPPjpn1t9QQAZYzqH9KNOFigMU8pSaRUxjI2dDvwmu8ZH6EExY+RfrPjQGmeliK18iFzFgBtf0eH3NAW3Pf71OZZz+cuNnVtE9lrYQIDAQAB"
secretDer = base64_decode(secret)
sshrsaSecret = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCIE6a1NyEFe7qCDFrvWFZiAlY1ttE5596w5dLjNSaHlKGv8AXbKg/f8yKY9fKAJ5BKoeWEkPPjpn1t9QQAZYzqH9KNOFigMU8pSaRUxjI2dDvwmu8ZH6EExY+RfrPjQGmeliK18iFzFgBtf0eH3NAW3Pf71OZZz+cuNnVtE9lrYQ=="
secretPEM = "-----BEGIN PUBLIC KEY-----\n" + secret + "\n-----END PUBLIC KEY-----"
access_token = "eyJhbGciOiJSUzI1NiJ9.eyJqdGkiOiIzM2ZhZGYzMS04MzZmLTQzYWUtODM4MS01OGJhM2RhMDMwYTciLCJleHAiOjE0MjkwNzYyNTYsIm5iZiI6MCwiaWF0IjoxNDI5MDc2MTk2LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODEvYXV0aC9yZWFsbXMvbWFzdGVyIiwiYXVkIjoic2VjdXJpdHktYWRtaW4tY29uc29sZSIsInN1YiI6ImMzNWJlODAyLTcyOGUtNGMyNC1iMjQ1LTQxMWIwMDRmZTc2NSIsImF6cCI6InNlY3VyaXR5LWFkbWluLWNvbnNvbGUiLCJzZXNzaW9uX3N0YXRlIjoiYmRjOGM0ZDgtYzUwNy00MDQ2LWE4NDctYmRlY2QxNDVmZTNiIiwiY2xpZW50X3Nlc3Npb24iOiI0OTI5YmRjNi0xOWFhLTQ3MDYtYTU1Mi1lOWI0MGFhMDg5ZTYiLCJhbGxvd2VkLW9yaWdpbnMiOltdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiYWRtaW4iLCJjcmVhdGUtcmVhbG0iXX0sInJlc291cmNlX2FjY2VzcyI6eyJtYXN0ZXItcmVhbG0iOnsicm9sZXMiOlsibWFuYWdlLWV2ZW50cyIsIm1hbmFnZS1jbGllbnRzIiwidmlldy1yZWFsbSIsInZpZXctZXZlbnRzIiwibWFuYWdlLWlkZW50aXR5LXByb3ZpZGVycyIsInZpZXctaWRlbnRpdHktcHJvdmlkZXJzIiwidmlldy11c2VycyIsInZpZXctY2xpZW50cyIsIm1hbmFnZS11c2VycyIsIm1hbmFnZS1yZWFsbSJdfX0sIm5hbWUiOiIiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJhZG1pbiJ9.O7e8dkv0k-2HCjMdZFXIxLhypVyRPwIdrQsYTMwC1996wbsjIw1L3OjDSzJKXcx0U9YrVeRM4yMVlFg40uJDC-9IsKZ8nr5dl_da8SzgpAkempxpas3girST2U9uvY56m2Spp6-EFInvMSb6k4t1L49_Q7R2g0DOlKzxgQd87LY"
############### Test using PEM key (with ----- lines)
try:
access_token_json = jwt.decode(access_token, key=secretPEM)
except Exception as e:
print "Not working using PEM key with ----: ", e
else:
print "It worked!"
############### Test using PEM key (without ----- lines)
try:
access_token_json = jwt.decode(access_token, key=secret)
except Exception as e:
print "Not working using PEM key without ----: ", e
else:
print "It worked!"
############### Test using DER key
try:
access_token_json = jwt.decode(access_token, key=secretDer)
except Exception as e:
print "Not working using DER key: ", e
else:
print "It worked!"
############### Test using DER key #2
try:
public_key = default_backend().load_der_public_key(secretDer)
access_token_json = jwt.decode(access_token, key=public_key)
except Exception as e:
print "Not working using DER key #2: ", e
else:
print "It worked!"
############### Test using SSH style key
try:
access_token_json = jwt.decode(access_token, key=sshrsaSecret)
except Exception as e:
print "Not working using SSH style key: ", e
else:
print "It worked!"
############### Test using RSA numbers
class Numbers:
pass
numbers = Numbers()
public_key = RSA.importKey(secretDer)
numbers.e = public_key.key.e
numbers.n = public_key.key.n
# yet another way to generated valid key object
public_key = default_backend().load_rsa_public_numbers(numbers)
print public_key
try:
access_token_json = jwt.decode(access_token, key=public_key)
except Exception as e:
print "Not working using RSA numbers: ", e
else:
print "It worked!"
###############
Я проверил, что токен и ключ работают с реализацией Java, см. ниже.
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.security.jwt.crypto.sign.SignatureVerifier;
public class JWTTest {
public static final void main(String[] argv) {
String token = "eyJhbGciOiJSUzI1NiJ9.eyJqdGkiOiIzM2ZhZGYzMS04MzZmLTQzYWUtODM4MS01OGJhM2RhMDMwYTciLCJleHAiOjE0MjkwNzYyNTYsIm5iZiI6MCwiaWF0IjoxNDI5MDc2MTk2LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODEvYXV0aC9yZWFsbXMvbWFzdGVyIiwiYXVkIjoic2VjdXJpdHktYWRtaW4tY29uc29sZSIsInN1YiI6ImMzNWJlODAyLTcyOGUtNGMyNC1iMjQ1LTQxMWIwMDRmZTc2NSIsImF6cCI6InNlY3VyaXR5LWFkbWluLWNvbnNvbGUiLCJzZXNzaW9uX3N0YXRlIjoiYmRjOGM0ZDgtYzUwNy00MDQ2LWE4NDctYmRlY2QxNDVmZTNiIiwiY2xpZW50X3Nlc3Npb24iOiI0OTI5YmRjNi0xOWFhLTQ3MDYtYTU1Mi1lOWI0MGFhMDg5ZTYiLCJhbGxvd2VkLW9yaWdpbnMiOltdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiYWRtaW4iLCJjcmVhdGUtcmVhbG0iXX0sInJlc291cmNlX2FjY2VzcyI6eyJtYXN0ZXItcmVhbG0iOnsicm9sZXMiOlsibWFuYWdlLWV2ZW50cyIsIm1hbmFnZS1jbGllbnRzIiwidmlldy1yZWFsbSIsInZpZXctZXZlbnRzIiwibWFuYWdlLWlkZW50aXR5LXByb3ZpZGVycyIsInZpZXctaWRlbnRpdHktcHJvdmlkZXJzIiwidmlldy11c2VycyIsInZpZXctY2xpZW50cyIsIm1hbmFnZS11c2VycyIsIm1hbmFnZS1yZWFsbSJdfX0sIm5hbWUiOiIiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJhZG1pbiJ9.O7e8dkv0k-2HCjMdZFXIxLhypVyRPwIdrQsYTMwC1996wbsjIw1L3OjDSzJKXcx0U9YrVeRM4yMVlFg40uJDC-9IsKZ8nr5dl_da8SzgpAkempxpas3girST2U9uvY56m2Spp6-EFInvMSb6k4t1L49_Q7R2g0DOlKzxgQd87LY";
String key = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCHJUdDw1bPg/tZBY+kDDZZQnAp1mVr0CMyE+VzvJ+n2v6SHBdjjuWEw+LfLd69evg8ndr1RRPWZ1ryKgWS/NKTNqH+UhHkK9NToDucJI9Bi/scCpBps+/X/S7gZtcBMdfd4IB+LPCsP8v2RT/H9VjeCP4sWuqNwAMtCMyGr1Vw9wIDAQAB";
String verifierKey = "-----BEGIN PUBLIC KEY-----\n" + key + "\n-----END PUBLIC KEY-----";
SignatureVerifier verifier = new RsaVerifier(verifierKey);
System.out.println(JwtHelper.decodeAndVerify(token, verifier));
}
}
Обновление: Я могу правильно подписать маркер с помощью HS256 (с помощью http://jwt.io/), используя следующий код. Однако я не могу декодировать подписанный токен PyJWT с помощью PyJWT. Интерфейс действительно странный. Здесь пример (секрет такой же, как в приведенных выше примерах):
some_token = jwt.encode(access_token_json, secret)
# verified some_token to be valid with jwt.io
# the code below does not validate the token correctly
jwt.decode(some_token, key=secret)
Обновление 2: Это работает
from jwt.algorithms import HMACAlgorithm, RSAAlgorithm
access_token_json = jwt.decode(access_token, verify=False)
algo = HMACAlgorithm(HMACAlgorithm.SHA256)
shakey = algo.prepare_key(secret)
testtoken = jwt.encode(access_token_json, key=shakey, algorithm='HS256')
options={'verify_exp': False, # Skipping expiration date check
'verify_aud': False } # Skipping audience check
print jwt.decode(testtoken, key=shakey, options=options)
Однако это не
from jwt.algorithms import HMACAlgorithm, RSAAlgorithm
algo = RSAAlgorithm(RSAAlgorithm.SHA256)
shakey = algo.prepare_key(sshrsaSecret)
options={'verify_exp': False, # Skipping expiration date check
'verify_aud': False } # Skipping audience check
print jwt.decode(access_token, key=shakey, options=options)
Эта другая библиотека (python-jose) может помочь проверить.
Обратите внимание, что ключи должны быть JSON dict, которые должны быть переданы в decode
.
Я помещаю это здесь для следующего человека, подобного мне, который ищет его.
Мне было нужно:
Настройка:
# lets create a key to sign these tokens with
openssl genpkey -out mykey.pem -algorithm rsa -pkeyopt rsa_keygen_bits:2048
# lets generate a public key for it...
openssl rsa -in mykey.pem -out mykey.pub -pubout
# make another key so we can test that we cannot decode from it
openssl genpkey -out notmykey.pem -algorithm rsa -pkeyopt rsa_keygen_bits:2048
# this is really the key we would be using to try to check the signature
openssl rsa -in notmykey.pem -out notmykey.pub -pubout
код:
import jwt
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
# Load the key we created
with open("mykey.pem", "rb") as key_file:
private_key = serialization.load_pem_private_key(
key_file.read(),
password=None,
backend=default_backend()
)
# The data we're trying to pass along from place to place
data = {'user_id': 1}
# Lets create the JWT token -- this is a byte array, meant to be sent as an HTTP header
jwt_token = jwt.encode(data, key=private_key, algorithm='RS256')
print(f'data {data}')
print(f'jwt_token {jwt_token}')
# Load the public key to run another test...
with open("mykey.pub", "rb") as key_file:
public_key = serialization.load_pem_public_key(
key_file.read(),
backend=default_backend()
)
# This will prove that the derived public-from-private key is valid
print(f'decoded with public key (internal): {jwt.decode(jwt_token, private_key.public_key())}')
# This will prove that an external service consuming this JWT token can trust the token
# because this is the only key it will have to validate the token.
print(f'decoded with public key (external): {jwt.decode(jwt_token, public_key)}')
# Lets load another public key to see if we can load the data successfuly
with open("notmykey.pub", "rb") as key_file:
not_my_public_key = serialization.load_pem_public_key(
key_file.read(),
backend=default_backend()
)
# THIS WILL FAIL!!!!!!!!!!!!!!!!!!!!!!!
# Finally, this will not work and cause an exception
print(f'decoded with another public key: {jwt.decode(jwt_token, not_my_public_key)}')
Дополнительная информация здесь: https://gist.github.com/kingbuzzman/3912cc66896be0a06bf0eb23bb1e1999 - наряду с примером докера, как быстро запустить