|
64 | 64 | #define JWT_CLAIM_EXPLICIT explicit
|
65 | 65 | #endif
|
66 | 66 |
|
| 67 | +#ifdef JWT_OPENSSL_3_0 |
| 68 | +#include <openssl/param_build.h> |
| 69 | +#endif |
| 70 | + |
67 | 71 | /**
|
68 | 72 | * \brief JSON Web Token
|
69 | 73 | *
|
@@ -3061,10 +3065,28 @@ namespace jwt {
|
3061 | 3065 |
|
3062 | 3066 | public:
|
3063 | 3067 | JWT_CLAIM_EXPLICIT jwk(const typename json_traits::string_type& str)
|
3064 |
| - : jwk_claims(details::map_of_claims<json_traits>::parse_claims(str)) {} |
| 3068 | + : jwk(details::map_of_claims<json_traits>::parse_claims(str)) {} |
| 3069 | + |
| 3070 | + JWT_CLAIM_EXPLICIT jwk(const typename json_traits::value_type& json) : jwk(json_traits::as_object(json)) {} |
3065 | 3071 |
|
3066 |
| - JWT_CLAIM_EXPLICIT jwk(const typename json_traits::value_type& json) |
3067 |
| - : jwk_claims(json_traits::as_object(json)) {} |
| 3072 | + JWT_CLAIM_EXPLICIT jwk(const typename json_traits::object_type& json) : jwk_claims(json) { |
| 3073 | + this->key = build_key(jwk_claims); |
| 3074 | + // https://datatracker.ietf.org/doc/html/rfc7518#section-6.1 |
| 3075 | + // * indicate required params |
| 3076 | + // "kty"* : "EC", "RSA", "oct" |
| 3077 | + |
| 3078 | + // if "EC", then "crv"*, then "x"*. if "crv" is any of "P-256", "P-384", "P-521", then "y"* |
| 3079 | + // if "EC" and private key, then "d"* |
| 3080 | + |
| 3081 | + // if "RSA", then "n"*, "e"* |
| 3082 | + // if "RSA" and private, then "d"* |
| 3083 | + // if "RSA" and any of the following is present, then all must be present |
| 3084 | + // "p", "q", "dp", "dq", "qi" |
| 3085 | + // "oth" - array of objects consisting of "r"*, "d"*, "t"* |
| 3086 | + |
| 3087 | + // if "oct", then "k"* |
| 3088 | + // if "oct", then SHOULD contain "alg" |
| 3089 | + } |
3068 | 3090 |
|
3069 | 3091 | /**
|
3070 | 3092 | * Get key type claim
|
@@ -3243,6 +3265,73 @@ namespace jwt {
|
3243 | 3265 | }
|
3244 | 3266 |
|
3245 | 3267 | bool empty() const noexcept { return jwk_claims.empty(); }
|
| 3268 | + |
| 3269 | + private: |
| 3270 | + static std::shared_ptr<EVP_PKEY> build_rsa_key(const details::map_of_claims<json_traits>& claims) { |
| 3271 | + EVP_PKEY* evp_key = nullptr; |
| 3272 | + auto n = jwt::helper::raw2bn( |
| 3273 | + base::decode<alphabet::base64url>(base::pad<alphabet::base64url>(claims.get_claim("n").as_string()))); |
| 3274 | + auto e = jwt::helper::raw2bn( |
| 3275 | + base::decode<alphabet::base64url>(base::pad<alphabet::base64url>(claims.get_claim("e").as_string()))); |
| 3276 | +#ifdef JWT_OPENSSL_3_0 |
| 3277 | + // https://www.openssl.org/docs/manmaster/man7/EVP_PKEY-RSA.html |
| 3278 | + // see https://www.openssl.org/docs/man3.0/man3/EVP_PKEY_fromdata.html |
| 3279 | + // and https://stackoverflow.com/questions/68465716/how-to-properly-create-an-rsa-key-from-raw-data-in-openssl-3-0-in-c-language |
| 3280 | + std::unique_ptr<EVP_PKEY_CTX, decltype(&EVP_PKEY_CTX_free)> ctx( |
| 3281 | + EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL), EVP_PKEY_CTX_free); |
| 3282 | + if (!ctx) { throw std::runtime_error("EVP_PKEY_CTX_new_from_name failed"); } |
| 3283 | + |
| 3284 | + std::unique_ptr<OSSL_PARAM_BLD, decltype(&OSSL_PARAM_BLD_free)> params_build(OSSL_PARAM_BLD_new(), |
| 3285 | + OSSL_PARAM_BLD_free); |
| 3286 | + OSSL_PARAM_BLD_push_BN(params_build.get(), "n", n.get()); |
| 3287 | + OSSL_PARAM_BLD_push_BN(params_build.get(), "e", e.get()); |
| 3288 | + |
| 3289 | + std::unique_ptr<OSSL_PARAM, decltype(&OSSL_PARAM_free)> params(OSSL_PARAM_BLD_to_param(params_build.get()), |
| 3290 | + OSSL_PARAM_free); |
| 3291 | + EVP_PKEY_fromdata_init(ctx.get()); |
| 3292 | + EVP_PKEY_fromdata(ctx.get(), &evp_key, EVP_PKEY_PUBLIC_KEY, params.get()); |
| 3293 | + return std::shared_ptr<EVP_PKEY>(evp_key, EVP_PKEY_free); |
| 3294 | +#else |
| 3295 | + RSA* rsa = RSA_new(); |
| 3296 | + evp_key = EVP_PKEY_new(); |
| 3297 | +#if defined(JWT_OPENSSL_1_0_0) && !defined(LIBWOLFSSL_VERSION_HEX) |
| 3298 | + rsa->e = e.release(); |
| 3299 | + rsa->n = n.release(); |
| 3300 | +#else |
| 3301 | + RSA_set0_key(rsa, n.release(), e.release(), nullptr); |
| 3302 | +#endif |
| 3303 | + EVP_PKEY_assign_RSA(evp_key, rsa); |
| 3304 | + return std::shared_ptr<EVP_PKEY>(evp_key, EVP_PKEY_free); |
| 3305 | +#endif |
| 3306 | + } |
| 3307 | + |
| 3308 | + static std::shared_ptr<EVP_PKEY> build_key(const details::map_of_claims<json_traits>& claims) { |
| 3309 | + if (!claims.has_claim("kty")) { |
| 3310 | + // TODO: custom exception or error code |
| 3311 | + throw std::runtime_error("missing required claim \"kty\""); |
| 3312 | + } |
| 3313 | + |
| 3314 | + if (claims.get_claim("kty").get_type() != json::type::string) { |
| 3315 | + // TODO: custom exception or error code |
| 3316 | + throw std::runtime_error("\"kty\" claim must be of type 'string'"); |
| 3317 | + } |
| 3318 | + |
| 3319 | + if (claims.get_claim("kty").as_string() == "RSA") { |
| 3320 | + // TODO: build RSA key |
| 3321 | + return build_rsa_key(claims); |
| 3322 | + } else if (claims.get_claim("kty").as_string() == "EC") { |
| 3323 | + // TODO: build EC key |
| 3324 | + } else if (claims.get_claim("kty").as_string() == "oct") { |
| 3325 | + // TODO: store in std::string or something more unsigned-ish? |
| 3326 | + } else { |
| 3327 | + // TODO: do not build error messages like this |
| 3328 | + throw std::runtime_error("unknown key type (\"kty\"):" + claims.get_claim("kty").as_string()); |
| 3329 | + } |
| 3330 | + |
| 3331 | + return nullptr; |
| 3332 | + } |
| 3333 | + |
| 3334 | + std::shared_ptr<EVP_PKEY> key; |
3246 | 3335 | };
|
3247 | 3336 |
|
3248 | 3337 | /**
|
|
0 commit comments