I am implementing a JWS based specification using openSSL. My code is below, in pascal. I'm trying to reproduce this test case here: https://datatracker.ietf.org/doc/html/rfc7515#appendix-A.3.1
--
I get a different outcome from EVP_DigestSignInit / EVP_DigestUpdate / EVP_DigestSignFinal from that specified (MEYCIQCf4hUhJvEFLZeOE4OPWrKT_LnyyNeU_1vdXgO5gqUK2AIhAILiDUd7i-FhbspRtlM90E6oSQD6eOBgiIylORcLhQbi instead of DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q
I presume that this is because of this text in the JWS spec: "The result of the digital signature is the Elliptic Curve (EC) point (R, S), where R and S are unsigned integers. The JWS Signature is the value R || S" - is that what EVP_DigestSignFinal should return? if not, how should I get R and S?
Or am I thinking about this wrong somehow?
thanks
Grahame
Code:
class function TJWTUtils.Sign_ES256(input: TBytes; key: TJWK): TBytes;
var
ctx : PEVP_MD_CTX;
keysize : integer;
len : Cardinal;
pkey: PEVP_PKEY;
rkey: PEC_KEY;
keys : TJWKList;
begin
check(key <> nil, 'A key must be provided for ES256');
// 1. Load the RSA private Key from FKey
rkey := key.LoadEC(true);
try
pkey := EVP_PKEY_new;
try
check(EVP_PKEY_set1_EC_KEY(pkey, rkey) = 1, 'openSSL EVP_PKEY_set1_RSA failed');
// 2. do the signing
keysize := EVP_PKEY_size(pkey);
SetLength(result, keysize);
ctx := EVP_MD_CTX_new;
try
check(EVP_DigestSignInit(ctx, nil, EVP_sha256, nil, pKey) = 1, 'openSSL EVP_DigestInit_ex failed');
check(EVP_DigestUpdate(ctx, @input[0], Length(input)) = 1, 'openSSL EVP_SignUpdate failed');
check(EVP_DigestSignFinal(ctx, @result[0], @len) = 1, 'openSSL EVP_SignFinal failed');
SetLength(result, len);
finally
EVP_MD_CTX_free(ctx);
end;
finally
EVP_PKEY_free(pKey);
end;
finally
EC_KEY_free(rkey);
end;
var
ctx : PEVP_MD_CTX;
keysize : integer;
len : Cardinal;
pkey: PEVP_PKEY;
rkey: PEC_KEY;
keys : TJWKList;
begin
check(key <> nil, 'A key must be provided for ES256');
// 1. Load the RSA private Key from FKey
rkey := key.LoadEC(true);
try
pkey := EVP_PKEY_new;
try
check(EVP_PKEY_set1_EC_KEY(pkey, rkey) = 1, 'openSSL EVP_PKEY_set1_RSA failed');
// 2. do the signing
keysize := EVP_PKEY_size(pkey);
SetLength(result, keysize);
ctx := EVP_MD_CTX_new;
try
check(EVP_DigestSignInit(ctx, nil, EVP_sha256, nil, pKey) = 1, 'openSSL EVP_DigestInit_ex failed');
check(EVP_DigestUpdate(ctx, @input[0], Length(input)) = 1, 'openSSL EVP_SignUpdate failed');
check(EVP_DigestSignFinal(ctx, @result[0], @len) = 1, 'openSSL EVP_SignFinal failed');
SetLength(result, len);
finally
EVP_MD_CTX_free(ctx);
end;
finally
EVP_PKEY_free(pKey);
end;
finally
EC_KEY_free(rkey);
end;
Signing the content:
Loading the key:
function TJWK.LoadEC(privkey: boolean): PEC_KEY;
var
pd, px, py : PBIGNUM;
pub : PEC_POINT;
grp : PEC_GROUP;
begin
check(keyType = 'EC', 'EC Key expected in JWK, but found '+KeyType);
check(hasX, 'EC Key needs an X');
check(hasY, 'EC Key needs an Y');
px := bn_decode_bin(x);
py := bn_decode_bin(y);
pd := bn_decode_bin(privateKey);
result := EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
check(result <> nil, 'EC_KEY_new_by_curve_name = nil');
grp := EC_KEY_get0_group(result);
pub := EC_POINT_new(grp);
check(EC_POINT_set_affine_coordinates_GFp(grp, pub, px, py, nil) = 1, 'EC_POINT_set_affine_coordinates_GFp failed');
EC_KEY_set_public_key(result, pub);
if (privkey) then
begin
check(hasPrivateKey, 'EC Key needs an private key');
EC_KEY_set_private_key(result, pd)
end;
end;
var
pd, px, py : PBIGNUM;
pub : PEC_POINT;
grp : PEC_GROUP;
begin
check(keyType = 'EC', 'EC Key expected in JWK, but found '+KeyType);
check(hasX, 'EC Key needs an X');
check(hasY, 'EC Key needs an Y');
px := bn_decode_bin(x);
py := bn_decode_bin(y);
pd := bn_decode_bin(privateKey);
result := EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
check(result <> nil, 'EC_KEY_new_by_curve_name = nil');
grp := EC_KEY_get0_group(result);
pub := EC_POINT_new(grp);
check(EC_POINT_set_affine_coordinates_GFp(grp, pub, px, py, nil) = 1, 'EC_POINT_set_affine_coordinates_GFp failed');
EC_KEY_set_public_key(result, pub);
if (privkey) then
begin
check(hasPrivateKey, 'EC Key needs an private key');
EC_KEY_set_private_key(result, pd)
end;
end;
-----
http://www.healthintersections.com.au / grahame@xxxxxxxxxxxxxxxxxxxxxxxxxx / +61 411 867 065
http://www.healthintersections.com.au / grahame@xxxxxxxxxxxxxxxxxxxxxxxxxx / +61 411 867 065
Benson & Grieve: Principles of Health Interoperability (Health Information Technology Standards), 4th ed - http://www.springer.com/978-3-030-56882-5