2025-02-28 10:11:08 -05:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
2025-03-17 12:34:24 -04:00
|
|
|
#include <haproxy/jwt-t.h>
|
|
|
|
|
|
2025-02-28 10:11:08 -05:00
|
|
|
#include <haproxy/base64.h>
|
|
|
|
|
#include <haproxy/chunk.h>
|
2025-02-28 16:27:28 -05:00
|
|
|
#include <haproxy/init.h>
|
2025-02-28 10:11:08 -05:00
|
|
|
#include <haproxy/openssl-compat.h>
|
2025-04-02 13:34:09 -04:00
|
|
|
#include <haproxy/ssl_utils.h>
|
2025-02-28 10:11:08 -05:00
|
|
|
|
|
|
|
|
#if defined(HAVE_JWS)
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Convert an OpenSSL BIGNUM to a base64url representation
|
|
|
|
|
* Fill a buffer <dst> of <dsize> max size
|
|
|
|
|
*
|
|
|
|
|
* Return the size of the data dumped in <dst>
|
|
|
|
|
*/
|
2025-09-11 08:25:03 -04:00
|
|
|
size_t bn2base64url(const BIGNUM *bn, char *dst, size_t dsize)
|
2025-02-28 10:11:08 -05:00
|
|
|
{
|
|
|
|
|
struct buffer *bin;
|
|
|
|
|
int binlen;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
if ((bin = get_trash_chunk()) == NULL)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
binlen = BN_num_bytes(bn);
|
|
|
|
|
if (binlen > bin->size)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if (BN_bn2bin(bn, (unsigned char *)bin->area) != binlen)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
ret = a2base64url(bin->area, binlen, dst, dsize);
|
|
|
|
|
out:
|
2025-09-11 08:25:03 -04:00
|
|
|
if (ret > 0)
|
|
|
|
|
return ret;
|
|
|
|
|
return 0;
|
2025-02-28 10:11:08 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Convert a EC <pkey> to a public key JWK
|
|
|
|
|
* Fill a buffer <dst> of <dsize> max size
|
|
|
|
|
*
|
|
|
|
|
* Return the size of the data or 0
|
|
|
|
|
*/
|
2025-09-11 08:25:03 -04:00
|
|
|
static size_t EVP_PKEY_EC_to_pub_jwk(EVP_PKEY *pkey, char *dst, size_t dsize)
|
2025-02-28 10:11:08 -05:00
|
|
|
{
|
|
|
|
|
BIGNUM *x = NULL, *y = NULL;
|
|
|
|
|
struct buffer *str_x = NULL, *str_y = NULL;
|
|
|
|
|
int ret = 0;
|
2025-03-01 17:58:48 -05:00
|
|
|
const char *crv = NULL;
|
2025-02-28 10:11:08 -05:00
|
|
|
|
|
|
|
|
#if HA_OPENSSL_VERSION_NUMBER > 0x30000000L
|
2025-03-01 17:58:48 -05:00
|
|
|
char curve[32] = {};
|
|
|
|
|
size_t curvelen;
|
|
|
|
|
int nid;
|
2025-02-28 10:11:08 -05:00
|
|
|
|
|
|
|
|
EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_X, &x);
|
|
|
|
|
EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_Y, &y);
|
|
|
|
|
|
2025-03-01 17:58:48 -05:00
|
|
|
if (EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME, curve, sizeof(curve), &curvelen) == 0)
|
2025-02-28 10:11:08 -05:00
|
|
|
goto out;
|
2025-03-01 17:58:48 -05:00
|
|
|
|
|
|
|
|
crv = curve;
|
|
|
|
|
|
|
|
|
|
/* convert to NIST format */
|
|
|
|
|
nid = curves2nid(curve);
|
|
|
|
|
if (nid > 0) {
|
|
|
|
|
crv = nid2nist(nid);
|
|
|
|
|
if (crv == NULL)
|
|
|
|
|
crv = curve;
|
|
|
|
|
}
|
2025-02-28 10:11:08 -05:00
|
|
|
#else
|
|
|
|
|
const EC_KEY *ec = NULL;
|
|
|
|
|
const EC_GROUP *ec_group = NULL;
|
|
|
|
|
const EC_POINT *ec_point = NULL;
|
|
|
|
|
|
|
|
|
|
/* get EC from EVP */
|
|
|
|
|
if ((ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL)
|
|
|
|
|
goto out;
|
|
|
|
|
if ((ec_group = EC_KEY_get0_group(ec)) == NULL)
|
|
|
|
|
goto out;
|
|
|
|
|
if ((ec_point = EC_KEY_get0_public_key(ec)) == NULL)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
/* get group, point, x, y */
|
|
|
|
|
if ((x = BN_new()) == NULL)
|
|
|
|
|
goto out;
|
|
|
|
|
if ((y = BN_new()) == NULL)
|
|
|
|
|
goto out;
|
|
|
|
|
if ((EC_POINT_get_affine_coordinates(ec_group, ec_point, x, y, NULL)) == 0)
|
|
|
|
|
goto out;
|
|
|
|
|
if ((crv = EC_curve_nid2nist(EC_GROUP_get_curve_name(ec_group))) == NULL)
|
|
|
|
|
goto out;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* allocate trash */
|
|
|
|
|
if ((str_x = alloc_trash_chunk()) == NULL)
|
|
|
|
|
goto out;
|
|
|
|
|
if ((str_y = alloc_trash_chunk()) == NULL)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
/* convert x, y to base64url */
|
|
|
|
|
str_x->data = bn2base64url(x, str_x->area, str_x->size);
|
|
|
|
|
str_y->data = bn2base64url(y, str_y->area, str_y->size);
|
|
|
|
|
if (str_x->data == 0 || str_y->data == 0)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2025-03-25 10:21:50 -04:00
|
|
|
ret = snprintf(dst, dsize, "{"
|
|
|
|
|
"\"crv\":\"%s\","
|
|
|
|
|
"\"kty\":\"%s\","
|
|
|
|
|
"\"x\":\"%s\","
|
|
|
|
|
"\"y\":\"%s\""
|
|
|
|
|
"}",
|
|
|
|
|
crv, "EC", str_x->area, str_y->area);
|
2025-02-28 10:11:08 -05:00
|
|
|
if (ret >= dsize)
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
free_trash_chunk(str_x);
|
|
|
|
|
free_trash_chunk(str_y);
|
|
|
|
|
|
|
|
|
|
BN_free(x);
|
|
|
|
|
BN_free(y);
|
|
|
|
|
|
2025-09-11 08:25:03 -04:00
|
|
|
if (ret > 0)
|
|
|
|
|
return ret;
|
|
|
|
|
return 0;
|
2025-02-28 10:11:08 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Convert a RSA <pkey> to a public key JWK
|
|
|
|
|
* Fill a buffer <dst> of <dsize> max size
|
|
|
|
|
*
|
|
|
|
|
* Return the size of the data or 0
|
|
|
|
|
*/
|
2025-09-11 08:25:03 -04:00
|
|
|
static size_t EVP_PKEY_RSA_to_pub_jwk(EVP_PKEY *pkey, char *dst, size_t dsize)
|
2025-02-28 10:11:08 -05:00
|
|
|
{
|
|
|
|
|
BIGNUM *n = NULL, *e = NULL;
|
|
|
|
|
struct buffer *str_n = NULL, *str_e = NULL;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
#if HA_OPENSSL_VERSION_NUMBER > 0x30000000L
|
|
|
|
|
|
|
|
|
|
if ((EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_N, &n)) == 0)
|
|
|
|
|
goto out;
|
|
|
|
|
if ((EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_E, &e)) == 0)
|
|
|
|
|
goto out;
|
|
|
|
|
#else
|
|
|
|
|
const RSA *rsa;
|
|
|
|
|
|
|
|
|
|
if ((rsa = EVP_PKEY_get0_RSA(pkey)) == NULL)
|
|
|
|
|
goto out;
|
|
|
|
|
if ((n = (BIGNUM *)BN_dup(RSA_get0_n(rsa))) == NULL)
|
|
|
|
|
goto out;
|
|
|
|
|
if ((e = (BIGNUM *)BN_dup(RSA_get0_e(rsa))) == NULL)
|
|
|
|
|
goto out;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* allocate trash */
|
|
|
|
|
if ((str_n = alloc_trash_chunk()) == NULL)
|
|
|
|
|
goto out;
|
|
|
|
|
if ((str_e = alloc_trash_chunk()) == NULL)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
/* convert n, e to base64url */
|
|
|
|
|
str_n->data = bn2base64url(n, str_n->area, str_n->size);
|
|
|
|
|
str_e->data = bn2base64url(e, str_e->area, str_e->size);
|
|
|
|
|
if (str_n->data == 0 || str_e->data == 0)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2025-03-25 10:21:50 -04:00
|
|
|
ret = snprintf(dst, dsize, "{"
|
|
|
|
|
"\"e\":\"%s\","
|
|
|
|
|
"\"kty\":\"%s\","
|
|
|
|
|
"\"n\":\"%s\""
|
|
|
|
|
"}",
|
|
|
|
|
str_e->area, "RSA", str_n->area );
|
2025-02-28 10:11:08 -05:00
|
|
|
if (ret >= dsize)
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
BN_free(n);
|
|
|
|
|
BN_free(e);
|
|
|
|
|
free_trash_chunk(str_n);
|
|
|
|
|
free_trash_chunk(str_e);
|
|
|
|
|
|
2025-09-11 08:25:03 -04:00
|
|
|
if (ret > 0)
|
|
|
|
|
return ret;
|
|
|
|
|
return 0;
|
2025-02-28 10:11:08 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Convert an EVP_PKEY to a public key JWK
|
|
|
|
|
* Fill a buffer <dst> of <dsize> max size
|
|
|
|
|
*
|
|
|
|
|
* Return the size of the data or 0
|
|
|
|
|
*/
|
2025-09-11 08:25:03 -04:00
|
|
|
size_t EVP_PKEY_to_pub_jwk(EVP_PKEY *pkey, char *dst, size_t dsize)
|
2025-02-28 10:11:08 -05:00
|
|
|
{
|
2025-09-11 08:25:03 -04:00
|
|
|
size_t ret = 0;
|
2025-02-28 10:11:08 -05:00
|
|
|
|
|
|
|
|
switch (EVP_PKEY_base_id(pkey)) {
|
|
|
|
|
case EVP_PKEY_RSA:
|
|
|
|
|
ret = EVP_PKEY_RSA_to_pub_jwk(pkey, dst, dsize);
|
|
|
|
|
break;
|
|
|
|
|
case EVP_PKEY_EC:
|
|
|
|
|
ret = EVP_PKEY_EC_to_pub_jwk(pkey, dst, dsize);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
MINOR: jws: implement JWS signing
This commits implement JWS signing, this is divided in 3 parts:
- jws_b64_protected() creates a JWS "protected" header, which takes the
algorithm, kid or jwk, nonce and url as input, and fill a destination
buffer with the base64url version of the header
- jws_b64_payload() just encode a payload in base64url
- jws_b64_signature() generates a signature using as input the protected
header and the payload, it supports ES256, ES384 and ES512 for ECDSA
keys, and RS256 for RSA ones. The RSA signature just use the
EVP_DigestSign() API with its result encoded in base64url. For ECDSA
it's a little bit more complicated, and should follow section 3.4 of
RFC7518, R and S should be padded to byte size.
Then the JWS can be output with jws_flattened() which just formats the 3
base64url output in a JSON representation with the 3 fields, protected,
payload and signature.
2025-03-06 11:14:26 -05:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Generate the JWS payload and converts it to base64url.
|
|
|
|
|
* Use either <kid> or <jwk>, but won't use both
|
|
|
|
|
*
|
|
|
|
|
* Return the size of the data or 0
|
|
|
|
|
*/
|
|
|
|
|
|
2025-09-11 08:25:03 -04:00
|
|
|
size_t jws_b64_protected(enum jwt_alg alg, char *kid, char *jwk, char *nonce, char *url,
|
|
|
|
|
char *dst, size_t dsize)
|
MINOR: jws: implement JWS signing
This commits implement JWS signing, this is divided in 3 parts:
- jws_b64_protected() creates a JWS "protected" header, which takes the
algorithm, kid or jwk, nonce and url as input, and fill a destination
buffer with the base64url version of the header
- jws_b64_payload() just encode a payload in base64url
- jws_b64_signature() generates a signature using as input the protected
header and the payload, it supports ES256, ES384 and ES512 for ECDSA
keys, and RS256 for RSA ones. The RSA signature just use the
EVP_DigestSign() API with its result encoded in base64url. For ECDSA
it's a little bit more complicated, and should follow section 3.4 of
RFC7518, R and S should be padded to byte size.
Then the JWS can be output with jws_flattened() which just formats the 3
base64url output in a JSON representation with the 3 fields, protected,
payload and signature.
2025-03-06 11:14:26 -05:00
|
|
|
{
|
|
|
|
|
char *acc;
|
|
|
|
|
char *acctype;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
struct buffer *json = NULL;
|
2025-03-17 12:34:24 -04:00
|
|
|
const char *algstr;
|
|
|
|
|
|
|
|
|
|
switch (alg) {
|
|
|
|
|
case JWS_ALG_RS256: algstr = "RS256"; break;
|
|
|
|
|
case JWS_ALG_RS384: algstr = "RS384"; break;
|
|
|
|
|
case JWS_ALG_RS512: algstr = "RS512"; break;
|
|
|
|
|
case JWS_ALG_ES256: algstr = "ES256"; break;
|
|
|
|
|
case JWS_ALG_ES384: algstr = "ES384"; break;
|
|
|
|
|
case JWS_ALG_ES512: algstr = "ES512"; break;
|
|
|
|
|
default:
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
MINOR: jws: implement JWS signing
This commits implement JWS signing, this is divided in 3 parts:
- jws_b64_protected() creates a JWS "protected" header, which takes the
algorithm, kid or jwk, nonce and url as input, and fill a destination
buffer with the base64url version of the header
- jws_b64_payload() just encode a payload in base64url
- jws_b64_signature() generates a signature using as input the protected
header and the payload, it supports ES256, ES384 and ES512 for ECDSA
keys, and RS256 for RSA ones. The RSA signature just use the
EVP_DigestSign() API with its result encoded in base64url. For ECDSA
it's a little bit more complicated, and should follow section 3.4 of
RFC7518, R and S should be padded to byte size.
Then the JWS can be output with jws_flattened() which just formats the 3
base64url output in a JSON representation with the 3 fields, protected,
payload and signature.
2025-03-06 11:14:26 -05:00
|
|
|
|
|
|
|
|
if ((json = alloc_trash_chunk()) == NULL)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
/* kid or jwk ? */
|
|
|
|
|
acc = kid ? kid : jwk;
|
|
|
|
|
acctype = kid ? "kid" : "jwk";
|
|
|
|
|
|
|
|
|
|
ret = snprintf(json->area, json->size, "{\n"
|
|
|
|
|
" \"alg\": \"%s\",\n"
|
|
|
|
|
" \"%s\": %s%s%s,\n"
|
|
|
|
|
" \"nonce\": \"%s\",\n"
|
|
|
|
|
" \"url\": \"%s\"\n"
|
|
|
|
|
"}\n",
|
2025-03-17 12:34:24 -04:00
|
|
|
algstr, acctype, kid ? "\"" : "", acc, kid ? "\"" : "", nonce, url);
|
MINOR: jws: implement JWS signing
This commits implement JWS signing, this is divided in 3 parts:
- jws_b64_protected() creates a JWS "protected" header, which takes the
algorithm, kid or jwk, nonce and url as input, and fill a destination
buffer with the base64url version of the header
- jws_b64_payload() just encode a payload in base64url
- jws_b64_signature() generates a signature using as input the protected
header and the payload, it supports ES256, ES384 and ES512 for ECDSA
keys, and RS256 for RSA ones. The RSA signature just use the
EVP_DigestSign() API with its result encoded in base64url. For ECDSA
it's a little bit more complicated, and should follow section 3.4 of
RFC7518, R and S should be padded to byte size.
Then the JWS can be output with jws_flattened() which just formats the 3
base64url output in a JSON representation with the 3 fields, protected,
payload and signature.
2025-03-06 11:14:26 -05:00
|
|
|
if (ret >= json->size) {
|
|
|
|
|
ret = 0;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
json->data = ret;
|
|
|
|
|
|
|
|
|
|
ret = a2base64url(json->area, json->data, dst, dsize);
|
|
|
|
|
out:
|
|
|
|
|
free_trash_chunk(json);
|
2025-09-11 08:25:03 -04:00
|
|
|
if (ret > 0)
|
|
|
|
|
return ret;
|
|
|
|
|
return 0;
|
MINOR: jws: implement JWS signing
This commits implement JWS signing, this is divided in 3 parts:
- jws_b64_protected() creates a JWS "protected" header, which takes the
algorithm, kid or jwk, nonce and url as input, and fill a destination
buffer with the base64url version of the header
- jws_b64_payload() just encode a payload in base64url
- jws_b64_signature() generates a signature using as input the protected
header and the payload, it supports ES256, ES384 and ES512 for ECDSA
keys, and RS256 for RSA ones. The RSA signature just use the
EVP_DigestSign() API with its result encoded in base64url. For ECDSA
it's a little bit more complicated, and should follow section 3.4 of
RFC7518, R and S should be padded to byte size.
Then the JWS can be output with jws_flattened() which just formats the 3
base64url output in a JSON representation with the 3 fields, protected,
payload and signature.
2025-03-06 11:14:26 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Converts the JWS payload to base64url
|
|
|
|
|
*
|
|
|
|
|
* Return the size of the data or 0
|
|
|
|
|
*/
|
|
|
|
|
|
2025-09-11 08:25:03 -04:00
|
|
|
size_t jws_b64_payload(char *payload, char *dst, size_t dsize)
|
MINOR: jws: implement JWS signing
This commits implement JWS signing, this is divided in 3 parts:
- jws_b64_protected() creates a JWS "protected" header, which takes the
algorithm, kid or jwk, nonce and url as input, and fill a destination
buffer with the base64url version of the header
- jws_b64_payload() just encode a payload in base64url
- jws_b64_signature() generates a signature using as input the protected
header and the payload, it supports ES256, ES384 and ES512 for ECDSA
keys, and RS256 for RSA ones. The RSA signature just use the
EVP_DigestSign() API with its result encoded in base64url. For ECDSA
it's a little bit more complicated, and should follow section 3.4 of
RFC7518, R and S should be padded to byte size.
Then the JWS can be output with jws_flattened() which just formats the 3
base64url output in a JSON representation with the 3 fields, protected,
payload and signature.
2025-03-06 11:14:26 -05:00
|
|
|
{
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
ret = a2base64url(payload, strlen(payload), dst, dsize);
|
|
|
|
|
|
2025-09-11 08:25:03 -04:00
|
|
|
if (ret > 0)
|
|
|
|
|
return ret;
|
|
|
|
|
return 0;
|
MINOR: jws: implement JWS signing
This commits implement JWS signing, this is divided in 3 parts:
- jws_b64_protected() creates a JWS "protected" header, which takes the
algorithm, kid or jwk, nonce and url as input, and fill a destination
buffer with the base64url version of the header
- jws_b64_payload() just encode a payload in base64url
- jws_b64_signature() generates a signature using as input the protected
header and the payload, it supports ES256, ES384 and ES512 for ECDSA
keys, and RS256 for RSA ones. The RSA signature just use the
EVP_DigestSign() API with its result encoded in base64url. For ECDSA
it's a little bit more complicated, and should follow section 3.4 of
RFC7518, R and S should be padded to byte size.
Then the JWS can be output with jws_flattened() which just formats the 3
base64url output in a JSON representation with the 3 fields, protected,
payload and signature.
2025-03-06 11:14:26 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2025-03-17 12:34:24 -04:00
|
|
|
* Return a JWS algorithm compatible with a Private KEY, or JWS_ALG_NONE.
|
MINOR: jws: implement JWS signing
This commits implement JWS signing, this is divided in 3 parts:
- jws_b64_protected() creates a JWS "protected" header, which takes the
algorithm, kid or jwk, nonce and url as input, and fill a destination
buffer with the base64url version of the header
- jws_b64_payload() just encode a payload in base64url
- jws_b64_signature() generates a signature using as input the protected
header and the payload, it supports ES256, ES384 and ES512 for ECDSA
keys, and RS256 for RSA ones. The RSA signature just use the
EVP_DigestSign() API with its result encoded in base64url. For ECDSA
it's a little bit more complicated, and should follow section 3.4 of
RFC7518, R and S should be padded to byte size.
Then the JWS can be output with jws_flattened() which just formats the 3
base64url output in a JSON representation with the 3 fields, protected,
payload and signature.
2025-03-06 11:14:26 -05:00
|
|
|
*/
|
2025-03-17 12:34:24 -04:00
|
|
|
enum jwt_alg EVP_PKEY_to_jws_alg(EVP_PKEY *pkey)
|
MINOR: jws: implement JWS signing
This commits implement JWS signing, this is divided in 3 parts:
- jws_b64_protected() creates a JWS "protected" header, which takes the
algorithm, kid or jwk, nonce and url as input, and fill a destination
buffer with the base64url version of the header
- jws_b64_payload() just encode a payload in base64url
- jws_b64_signature() generates a signature using as input the protected
header and the payload, it supports ES256, ES384 and ES512 for ECDSA
keys, and RS256 for RSA ones. The RSA signature just use the
EVP_DigestSign() API with its result encoded in base64url. For ECDSA
it's a little bit more complicated, and should follow section 3.4 of
RFC7518, R and S should be padded to byte size.
Then the JWS can be output with jws_flattened() which just formats the 3
base64url output in a JSON representation with the 3 fields, protected,
payload and signature.
2025-03-06 11:14:26 -05:00
|
|
|
{
|
2025-03-17 12:34:24 -04:00
|
|
|
enum jwt_alg alg = JWS_ALG_NONE;
|
MINOR: jws: implement JWS signing
This commits implement JWS signing, this is divided in 3 parts:
- jws_b64_protected() creates a JWS "protected" header, which takes the
algorithm, kid or jwk, nonce and url as input, and fill a destination
buffer with the base64url version of the header
- jws_b64_payload() just encode a payload in base64url
- jws_b64_signature() generates a signature using as input the protected
header and the payload, it supports ES256, ES384 and ES512 for ECDSA
keys, and RS256 for RSA ones. The RSA signature just use the
EVP_DigestSign() API with its result encoded in base64url. For ECDSA
it's a little bit more complicated, and should follow section 3.4 of
RFC7518, R and S should be padded to byte size.
Then the JWS can be output with jws_flattened() which just formats the 3
base64url output in a JSON representation with the 3 fields, protected,
payload and signature.
2025-03-06 11:14:26 -05:00
|
|
|
|
|
|
|
|
if (EVP_PKEY_base_id(pkey) == EVP_PKEY_EC) {
|
|
|
|
|
#if HA_OPENSSL_VERSION_NUMBER > 0x30000000L
|
|
|
|
|
char curve[32] = {};
|
|
|
|
|
size_t curvelen;
|
|
|
|
|
int nid;
|
|
|
|
|
|
|
|
|
|
if (EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME, curve, sizeof(curve), &curvelen) == 0)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
nid = curves2nid(curve);
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
const EC_KEY *ec = NULL;
|
|
|
|
|
const EC_GROUP *ec_group = NULL;
|
|
|
|
|
int nid = -1;
|
|
|
|
|
|
|
|
|
|
if ((ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL)
|
|
|
|
|
goto out;
|
|
|
|
|
if ((ec_group = EC_KEY_get0_group(ec)) == NULL)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
nid = EC_GROUP_get_curve_name(ec_group);
|
|
|
|
|
#endif
|
|
|
|
|
switch (nid) {
|
|
|
|
|
/* ES256: ECDSA using P-256 and SHA-256 */
|
|
|
|
|
case NID_X9_62_prime256v1:
|
2025-03-17 12:34:24 -04:00
|
|
|
alg = JWS_ALG_ES256;
|
MINOR: jws: implement JWS signing
This commits implement JWS signing, this is divided in 3 parts:
- jws_b64_protected() creates a JWS "protected" header, which takes the
algorithm, kid or jwk, nonce and url as input, and fill a destination
buffer with the base64url version of the header
- jws_b64_payload() just encode a payload in base64url
- jws_b64_signature() generates a signature using as input the protected
header and the payload, it supports ES256, ES384 and ES512 for ECDSA
keys, and RS256 for RSA ones. The RSA signature just use the
EVP_DigestSign() API with its result encoded in base64url. For ECDSA
it's a little bit more complicated, and should follow section 3.4 of
RFC7518, R and S should be padded to byte size.
Then the JWS can be output with jws_flattened() which just formats the 3
base64url output in a JSON representation with the 3 fields, protected,
payload and signature.
2025-03-06 11:14:26 -05:00
|
|
|
break;
|
|
|
|
|
/* ES384: ECDSA using P-384 and SHA-384 */
|
|
|
|
|
case NID_secp384r1:
|
2025-03-17 12:34:24 -04:00
|
|
|
alg = JWS_ALG_ES384;
|
MINOR: jws: implement JWS signing
This commits implement JWS signing, this is divided in 3 parts:
- jws_b64_protected() creates a JWS "protected" header, which takes the
algorithm, kid or jwk, nonce and url as input, and fill a destination
buffer with the base64url version of the header
- jws_b64_payload() just encode a payload in base64url
- jws_b64_signature() generates a signature using as input the protected
header and the payload, it supports ES256, ES384 and ES512 for ECDSA
keys, and RS256 for RSA ones. The RSA signature just use the
EVP_DigestSign() API with its result encoded in base64url. For ECDSA
it's a little bit more complicated, and should follow section 3.4 of
RFC7518, R and S should be padded to byte size.
Then the JWS can be output with jws_flattened() which just formats the 3
base64url output in a JSON representation with the 3 fields, protected,
payload and signature.
2025-03-06 11:14:26 -05:00
|
|
|
break;
|
|
|
|
|
/* ES512: ECDSA using P-521 and SHA-512 */
|
|
|
|
|
case NID_secp521r1:
|
2025-03-17 12:34:24 -04:00
|
|
|
alg = JWS_ALG_ES512;
|
MINOR: jws: implement JWS signing
This commits implement JWS signing, this is divided in 3 parts:
- jws_b64_protected() creates a JWS "protected" header, which takes the
algorithm, kid or jwk, nonce and url as input, and fill a destination
buffer with the base64url version of the header
- jws_b64_payload() just encode a payload in base64url
- jws_b64_signature() generates a signature using as input the protected
header and the payload, it supports ES256, ES384 and ES512 for ECDSA
keys, and RS256 for RSA ones. The RSA signature just use the
EVP_DigestSign() API with its result encoded in base64url. For ECDSA
it's a little bit more complicated, and should follow section 3.4 of
RFC7518, R and S should be padded to byte size.
Then the JWS can be output with jws_flattened() which just formats the 3
base64url output in a JSON representation with the 3 fields, protected,
payload and signature.
2025-03-06 11:14:26 -05:00
|
|
|
break;
|
|
|
|
|
default:
|
2025-03-17 12:34:24 -04:00
|
|
|
alg = JWS_ALG_NONE;
|
MINOR: jws: implement JWS signing
This commits implement JWS signing, this is divided in 3 parts:
- jws_b64_protected() creates a JWS "protected" header, which takes the
algorithm, kid or jwk, nonce and url as input, and fill a destination
buffer with the base64url version of the header
- jws_b64_payload() just encode a payload in base64url
- jws_b64_signature() generates a signature using as input the protected
header and the payload, it supports ES256, ES384 and ES512 for ECDSA
keys, and RS256 for RSA ones. The RSA signature just use the
EVP_DigestSign() API with its result encoded in base64url. For ECDSA
it's a little bit more complicated, and should follow section 3.4 of
RFC7518, R and S should be padded to byte size.
Then the JWS can be output with jws_flattened() which just formats the 3
base64url output in a JSON representation with the 3 fields, protected,
payload and signature.
2025-03-06 11:14:26 -05:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
2025-03-17 12:34:24 -04:00
|
|
|
alg = JWS_ALG_RS256;
|
MINOR: jws: implement JWS signing
This commits implement JWS signing, this is divided in 3 parts:
- jws_b64_protected() creates a JWS "protected" header, which takes the
algorithm, kid or jwk, nonce and url as input, and fill a destination
buffer with the base64url version of the header
- jws_b64_payload() just encode a payload in base64url
- jws_b64_signature() generates a signature using as input the protected
header and the payload, it supports ES256, ES384 and ES512 for ECDSA
keys, and RS256 for RSA ones. The RSA signature just use the
EVP_DigestSign() API with its result encoded in base64url. For ECDSA
it's a little bit more complicated, and should follow section 3.4 of
RFC7518, R and S should be padded to byte size.
Then the JWS can be output with jws_flattened() which just formats the 3
base64url output in a JSON representation with the 3 fields, protected,
payload and signature.
2025-03-06 11:14:26 -05:00
|
|
|
}
|
2025-03-17 12:34:24 -04:00
|
|
|
out:
|
|
|
|
|
return alg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Generate a JWS signature using the base64url protected buffer and the base64url payload buffer
|
|
|
|
|
*
|
|
|
|
|
* For RSA it uses the RS256 algorithm (EVP_sha256)
|
|
|
|
|
* For ECDSA, the ES256, ES384 or ES512 is chosen depending on the curves of the key
|
|
|
|
|
*
|
|
|
|
|
* Return the size of the data or 0
|
|
|
|
|
*/
|
2025-09-11 08:25:03 -04:00
|
|
|
size_t jws_b64_signature(EVP_PKEY *pkey, enum jwt_alg alg, char *b64protected, char *b64payload, char *dst, size_t dsize)
|
2025-03-17 12:34:24 -04:00
|
|
|
{
|
|
|
|
|
EVP_MD_CTX *ctx;
|
|
|
|
|
const EVP_MD *evp_md = NULL;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
struct buffer *sign = NULL;
|
|
|
|
|
size_t out_sign_len = 0;
|
|
|
|
|
|
|
|
|
|
switch (alg) {
|
|
|
|
|
case JWS_ALG_ES256:
|
|
|
|
|
case JWS_ALG_RS256:
|
|
|
|
|
evp_md = EVP_sha256();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JWS_ALG_ES384:
|
|
|
|
|
case JWS_ALG_RS384:
|
|
|
|
|
evp_md = EVP_sha384();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JWS_ALG_ES512:
|
|
|
|
|
case JWS_ALG_RS512:
|
|
|
|
|
evp_md = EVP_sha512();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
evp_md = NULL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (evp_md == NULL)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if ((sign = alloc_trash_chunk()) == NULL)
|
|
|
|
|
goto out;
|
MINOR: jws: implement JWS signing
This commits implement JWS signing, this is divided in 3 parts:
- jws_b64_protected() creates a JWS "protected" header, which takes the
algorithm, kid or jwk, nonce and url as input, and fill a destination
buffer with the base64url version of the header
- jws_b64_payload() just encode a payload in base64url
- jws_b64_signature() generates a signature using as input the protected
header and the payload, it supports ES256, ES384 and ES512 for ECDSA
keys, and RS256 for RSA ones. The RSA signature just use the
EVP_DigestSign() API with its result encoded in base64url. For ECDSA
it's a little bit more complicated, and should follow section 3.4 of
RFC7518, R and S should be padded to byte size.
Then the JWS can be output with jws_flattened() which just formats the 3
base64url output in a JSON representation with the 3 fields, protected,
payload and signature.
2025-03-06 11:14:26 -05:00
|
|
|
|
|
|
|
|
if ((ctx = EVP_MD_CTX_new()) == NULL)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if (EVP_DigestSignInit(ctx, NULL, evp_md, NULL, pkey) == 0)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if (EVP_DigestSignUpdate(ctx, b64protected, strlen(b64protected)) == 0)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if (EVP_DigestSignUpdate(ctx, ".", 1) == 0)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if (EVP_DigestSignUpdate(ctx, b64payload, strlen(b64payload)) == 0)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if (EVP_DigestSignFinal(ctx, NULL, &out_sign_len) == 0)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if (out_sign_len > sign->size)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if (EVP_DigestSignFinal(ctx, (unsigned char *)sign->area, &out_sign_len) == 0)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
sign->data = out_sign_len;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (EVP_PKEY_base_id(pkey) == EVP_PKEY_EC) {
|
|
|
|
|
/* Convert the DigestSign output to an ECDSA_SIG (R and S parameters concatenatedi,
|
|
|
|
|
* see section 3.4 of RFC7518), and output R and S padded.
|
|
|
|
|
*/
|
|
|
|
|
ECDSA_SIG *sig = NULL;
|
|
|
|
|
const BIGNUM *r = NULL, *s = NULL;
|
|
|
|
|
int bignum_len;
|
|
|
|
|
|
2025-04-01 16:44:54 -04:00
|
|
|
/* need to pad to byte size, essentially for P-521 */
|
MINOR: jws: implement JWS signing
This commits implement JWS signing, this is divided in 3 parts:
- jws_b64_protected() creates a JWS "protected" header, which takes the
algorithm, kid or jwk, nonce and url as input, and fill a destination
buffer with the base64url version of the header
- jws_b64_payload() just encode a payload in base64url
- jws_b64_signature() generates a signature using as input the protected
header and the payload, it supports ES256, ES384 and ES512 for ECDSA
keys, and RS256 for RSA ones. The RSA signature just use the
EVP_DigestSign() API with its result encoded in base64url. For ECDSA
it's a little bit more complicated, and should follow section 3.4 of
RFC7518, R and S should be padded to byte size.
Then the JWS can be output with jws_flattened() which just formats the 3
base64url output in a JSON representation with the 3 fields, protected,
payload and signature.
2025-03-06 11:14:26 -05:00
|
|
|
bignum_len = (EVP_PKEY_bits(pkey) + 7) / 8;
|
|
|
|
|
|
|
|
|
|
if ((sig = d2i_ECDSA_SIG(NULL, (const unsigned char **)&sign->area, sign->data)) == NULL)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if ((r = ECDSA_SIG_get0_r(sig)) == NULL)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if ((s = ECDSA_SIG_get0_s(sig)) == NULL)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if (BN_bn2binpad(r, (unsigned char *)sign->area, bignum_len) != bignum_len)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if (BN_bn2binpad(s, (unsigned char *)sign->area + bignum_len, bignum_len) != bignum_len)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
sign->data = bignum_len * 2;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Then encode the whole thing in base64url */
|
|
|
|
|
ret = a2base64url(sign->area, sign->data, dst, dsize);
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
free_trash_chunk(sign);
|
|
|
|
|
|
2025-09-11 08:25:03 -04:00
|
|
|
if (ret > 0)
|
|
|
|
|
return ret;
|
|
|
|
|
return 0;
|
MINOR: jws: implement JWS signing
This commits implement JWS signing, this is divided in 3 parts:
- jws_b64_protected() creates a JWS "protected" header, which takes the
algorithm, kid or jwk, nonce and url as input, and fill a destination
buffer with the base64url version of the header
- jws_b64_payload() just encode a payload in base64url
- jws_b64_signature() generates a signature using as input the protected
header and the payload, it supports ES256, ES384 and ES512 for ECDSA
keys, and RS256 for RSA ones. The RSA signature just use the
EVP_DigestSign() API with its result encoded in base64url. For ECDSA
it's a little bit more complicated, and should follow section 3.4 of
RFC7518, R and S should be padded to byte size.
Then the JWS can be output with jws_flattened() which just formats the 3
base64url output in a JSON representation with the 3 fields, protected,
payload and signature.
2025-03-06 11:14:26 -05:00
|
|
|
}
|
|
|
|
|
|
2025-03-25 10:21:50 -04:00
|
|
|
/*
|
|
|
|
|
* Fill a <dst> buffer of <dsize> size with a jwk thumbprint from a pkey
|
|
|
|
|
*
|
|
|
|
|
* Return the size of the data or 0
|
|
|
|
|
*/
|
2025-09-11 08:25:03 -04:00
|
|
|
size_t jws_thumbprint(EVP_PKEY *pkey, char *dst, size_t dsize)
|
2025-03-25 10:21:50 -04:00
|
|
|
{
|
|
|
|
|
int ret = 0;
|
|
|
|
|
struct buffer *jwk = NULL;
|
|
|
|
|
unsigned char md[EVP_MAX_MD_SIZE];
|
|
|
|
|
unsigned int size;
|
|
|
|
|
|
|
|
|
|
if ((jwk = alloc_trash_chunk()) == NULL)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
switch (EVP_PKEY_base_id(pkey)) {
|
|
|
|
|
case EVP_PKEY_RSA:
|
|
|
|
|
jwk->data = EVP_PKEY_RSA_to_pub_jwk(pkey, jwk->area, jwk->size);
|
|
|
|
|
break;
|
|
|
|
|
case EVP_PKEY_EC:
|
|
|
|
|
jwk->data = EVP_PKEY_EC_to_pub_jwk(pkey, jwk->area, jwk->size);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (EVP_Digest(jwk->area, jwk->data, md, &size, EVP_sha256(), NULL) == 0)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
ret = a2base64url((const char *)md, size, dst, dsize);
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
free_trash_chunk(jwk);
|
2025-09-11 08:25:03 -04:00
|
|
|
if (ret > 0)
|
|
|
|
|
return ret;
|
|
|
|
|
return 0;
|
2025-03-25 10:21:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-09-11 08:25:03 -04:00
|
|
|
size_t jws_flattened(char *protected, char *payload, char *signature, char *dst, size_t dsize)
|
MINOR: jws: implement JWS signing
This commits implement JWS signing, this is divided in 3 parts:
- jws_b64_protected() creates a JWS "protected" header, which takes the
algorithm, kid or jwk, nonce and url as input, and fill a destination
buffer with the base64url version of the header
- jws_b64_payload() just encode a payload in base64url
- jws_b64_signature() generates a signature using as input the protected
header and the payload, it supports ES256, ES384 and ES512 for ECDSA
keys, and RS256 for RSA ones. The RSA signature just use the
EVP_DigestSign() API with its result encoded in base64url. For ECDSA
it's a little bit more complicated, and should follow section 3.4 of
RFC7518, R and S should be padded to byte size.
Then the JWS can be output with jws_flattened() which just formats the 3
base64url output in a JSON representation with the 3 fields, protected,
payload and signature.
2025-03-06 11:14:26 -05:00
|
|
|
{
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
ret = snprintf(dst, dsize, "{\n"
|
|
|
|
|
" \"protected\": \"%s\",\n"
|
|
|
|
|
" \"payload\": \"%s\",\n"
|
|
|
|
|
" \"signature\": \"%s\"\n"
|
|
|
|
|
"}\n",
|
|
|
|
|
protected, payload, signature);
|
|
|
|
|
|
|
|
|
|
if (ret >= dsize)
|
|
|
|
|
ret = 0;
|
2025-09-11 08:25:03 -04:00
|
|
|
|
|
|
|
|
if (ret > 0)
|
|
|
|
|
return ret;
|
|
|
|
|
return 0;
|
MINOR: jws: implement JWS signing
This commits implement JWS signing, this is divided in 3 parts:
- jws_b64_protected() creates a JWS "protected" header, which takes the
algorithm, kid or jwk, nonce and url as input, and fill a destination
buffer with the base64url version of the header
- jws_b64_payload() just encode a payload in base64url
- jws_b64_signature() generates a signature using as input the protected
header and the payload, it supports ES256, ES384 and ES512 for ECDSA
keys, and RS256 for RSA ones. The RSA signature just use the
EVP_DigestSign() API with its result encoded in base64url. For ECDSA
it's a little bit more complicated, and should follow section 3.4 of
RFC7518, R and S should be padded to byte size.
Then the JWS can be output with jws_flattened() which just formats the 3
base64url output in a JSON representation with the 3 fields, protected,
payload and signature.
2025-03-06 11:14:26 -05:00
|
|
|
}
|
|
|
|
|
|
2025-03-06 11:15:23 -05:00
|
|
|
|
|
|
|
|
int jws_debug(int argc, char **argv)
|
|
|
|
|
{
|
|
|
|
|
FILE *f = NULL;
|
|
|
|
|
EVP_PKEY *pkey = NULL;
|
|
|
|
|
char jwk[1024];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
char b64prot[4096];
|
|
|
|
|
char b64payload[4096];
|
|
|
|
|
char b64sign[4096];
|
|
|
|
|
char output[16384];
|
|
|
|
|
|
|
|
|
|
int ret = 1;
|
|
|
|
|
const char *filename = NULL;
|
|
|
|
|
char *payload = NULL;
|
2025-03-17 12:34:24 -04:00
|
|
|
enum jwt_alg alg = JWS_ALG_NONE;
|
2025-03-06 11:15:23 -05:00
|
|
|
char *nonce = NULL;
|
|
|
|
|
char *url = NULL;
|
|
|
|
|
|
|
|
|
|
if (argc < 5) {
|
|
|
|
|
fprintf(stderr, "error: -U jws <pkey> <payload> <nonce> <url>!\n");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
filename = argv[1];
|
|
|
|
|
payload = argv[2];
|
|
|
|
|
nonce = argv[3];
|
|
|
|
|
url = argv[4];
|
|
|
|
|
|
|
|
|
|
if ((f = fopen(filename, "r")) == NULL) {
|
|
|
|
|
fprintf(stderr, "fopen!\n");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
if ((pkey = PEM_read_PrivateKey(f, NULL, NULL, NULL)) == NULL) {
|
|
|
|
|
fprintf(stderr, "PEM_read_PrivateKey!\n");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = !EVP_PKEY_to_pub_jwk(pkey, jwk, sizeof(jwk));
|
|
|
|
|
|
|
|
|
|
fprintf(stderr, "JWK: %s\n", jwk);
|
|
|
|
|
|
2025-03-17 12:34:24 -04:00
|
|
|
alg = EVP_PKEY_to_jws_alg(pkey);
|
2025-03-06 11:15:23 -05:00
|
|
|
jws_b64_protected(alg, NULL, jwk, nonce, url, b64prot, sizeof(b64prot));
|
|
|
|
|
jws_b64_payload(payload, b64payload, sizeof(b64payload));
|
2025-03-17 12:34:24 -04:00
|
|
|
jws_b64_signature(pkey, alg, b64prot, b64payload, b64sign, sizeof(b64sign));
|
2025-03-06 11:15:23 -05:00
|
|
|
jws_flattened(b64prot, b64payload, b64sign, output, sizeof(output));
|
|
|
|
|
|
|
|
|
|
fprintf(stdout, "%s", output);
|
|
|
|
|
|
|
|
|
|
EVP_PKEY_free(pkey);
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-02-28 16:27:28 -05:00
|
|
|
int jwk_debug(int argc, char **argv)
|
|
|
|
|
{
|
|
|
|
|
FILE *f = NULL;
|
|
|
|
|
EVP_PKEY *pkey = NULL;
|
|
|
|
|
char msg[1024];
|
|
|
|
|
int ret = 1;
|
|
|
|
|
const char *filename;
|
|
|
|
|
|
|
|
|
|
if (argc < 1)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
filename = argv[1];
|
|
|
|
|
|
|
|
|
|
if ((f = fopen(filename, "r")) == NULL) {
|
|
|
|
|
fprintf(stderr, "fopen!\n");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
if ((pkey = PEM_read_PrivateKey(f, NULL, NULL, NULL)) == NULL) {
|
|
|
|
|
fprintf(stderr, "PEM_read_PrivateKey!\n");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = !EVP_PKEY_to_pub_jwk(pkey, msg, sizeof(msg));
|
|
|
|
|
fprintf(stdout, "%s\n", msg);
|
|
|
|
|
|
|
|
|
|
EVP_PKEY_free(pkey);
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void __jws_init(void)
|
|
|
|
|
{
|
|
|
|
|
hap_register_unittest("jwk", jwk_debug);
|
2025-03-06 11:15:23 -05:00
|
|
|
hap_register_unittest("jws", jws_debug);
|
2025-02-28 16:27:28 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
INITCALL0(STG_REGISTER, __jws_init);
|
|
|
|
|
|
2025-02-28 10:11:08 -05:00
|
|
|
#endif /* HAVE_JWS */
|
|
|
|
|
|