pythonrsapublic-key-encryptionencryption-asymmetricjwk

Generate a public key with a predefined modulus and exponent


"n": "rKZ-1zdz_CoLekSynOtyWv6cPSSkV28Kb9kZZHyYL-yhkKnH_bHl8OpWiGxQiKP0ulLRIaq1IhSMetkZ8FfXH-iptIDu4lPb8gt0HQYkjcy3HoaKRXBw2F8fJQO4jQ-ufR4l-E0HRqwLywzdtAImNWmju3A4kx8s0iSGHGSHyE4EUdh5WKt-NMtfUPfB5v9_2bC-w6wH7zAEsI5nscMXnvz1u8w7g2_agyhKSK0D9OkJ02w3I4xLMlrtKEv2naoBGerWckKcQ1kBYUh6WASPdvTqX4pcAJi7Tg6jwQXIP1aEq0JU8C0zE3d33kaMoCN3SenIxpRczRzUHpbZ-gk5PQ",
"e": "AQAB",

How can I generate a public key from these values? Preferable via python or Linux programs. I'm sorry if the question is nonsense because of invalid values.

The source is here.


Solution

  • In Python, you can use Python-JOSE

    What you got there in your link is a JSON Web Key Set (JWKS), a JSON formated array of JSON Web Keys (JWK).

    n and e are the modulus and exponent of a RSA public key.

    The function jwk.construct can directly create a key from a JWK. The key can then be used e.g. to verify a JWT (key.verify).

    You can also use jwt.decode like shown in the code below and pass the JWK directly, or in PEM format.

    from jose import jwk
    from jose.utils import base64url_decode
    
    token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IlctNjduZWt0WVRjOEpWWVBlV0g1c1dlN1JZVm5uMFN5NzQxZjhUT0pfQWMifQ.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.hiKxeC66LIyVKOXjiOk7iScFPy_5-ATw7hEfqGij8sBZmwXAeTPT5BRFYHitFKSXomGqmy_63LLvg4zbhcTTmNf8XIeDAuLsC32soO5woSByisswWHVf8BgxMkI_FPW_oEtEQ8Xv3FL_1rF9j9Oy3jIjgjqhFhXUtsSQWAeuGYH-OQljFwiuO5Bqexcw-H71OEWvQLQof_6KJ0viJyte8QEwEVridyO834-ppHzeaoW2sTvZ22ZNfxPCew0Ul2V_TxHTtO7ZuJCZ81EmeIV6dYJ2GrYh3UN1x1PHy4-tEn-PL4otlaO3PYOcXfCHxHa6xtPsquzPZJnB1Vq8zULLfQ"
    
    rsa_key = {
        "kty": "RSA",
        "kid": "W-67nektYTc8JVYPeWH5sWe7RYVnn0Sy741f8TOJ_Ac",
        "use": "sig",
        "alg": "RS256",
        "n": "kFpGoVmBmmKepvBQiwq3hU9lIAuGsAPda4AVk712d3Z_QoS-5veGp4yltnyEFYyX867GOKDpbH7OF2uIjDg4-FPZwbuhiMscbkZzh25SQmfRtCT5ocUloQiopBcNAE-sd1p-ayUJWjhPrFoBrBLZHYxVEjY4JrWevQDj7kSeX7eJpud_VuZ77TNoIzj7d_iUuJUUlqF1ZF540igHKoVJJ6ujQLHh4ob8_izUuxX2iDq4h0VN3-uer59GsWw6OHgkOt85TsjMwYbeN9iw_7cNfLEYpSiH-sVHBCyKYQw7f8bKaChLxDRhUUTIEUUjGT9Ub_A3gOXq9TIi8BmbzrzVKQ",
        "e": "AQAB"
    }
    
    key = jwk.construct(rsa_key)
    
    message, encoded_sig = token.rsplit('.', 1)
    decoded_sig = base64url_decode(encoded_sig + '=' * (4 - len(encoded_sig) % 4)) # looks weird, but without added padding I got errors
    res = key.verify(bytes(message, "UTF-8"), decoded_sig)
    # jwt.decode(token=token, key=key.to_pem().decode(), algorithms= 'RS256') # with PEM key
    payload = jwt.decode(token=token, rsa_key, algorithms= 'RS256') # with JWK
    print(res)
    print(payload)
    

    The result will be:

    True {'sub': '1234567890', 'name': 'John Doe', 'admin': True, 'iat': 1516239022}

    which means the token could be verified with that key.