sign in with apple - (ES256 using OpenSSL) 'invalid_client' error reason

There are a lot of libraries(java-jwt, OpenSSL, chilkat.. etc) to make ES256 signature value of JWT(client_secret).


According to the RFC 7515, the result of the digital signature is the Elliptic Curve (EC) point (R, S), where R and S are unsigned integers. The Signature is the value R || S. When the algorithm is ES256, the length of R || S is 64.


But, OpenSSL's ES256 signature result is a DER-encoded ASN.1 structure (it's size exceed 64). (not a raw R || S value),

like the following

ECDSASignature ::= SEQUENCE {
r INTEGER,
s INTEGER
}

--> recv 'invalid_client' error from apple.

--> some libraries will return an exception for the length if they are larger than 64. (ex: java-jwt)


So I tested it like this,

1) decode ES256 result(DER-encoded ASN.1 structure) of OpenSSL,

2) parse R, S value,

3) make a raw R || S value,

4) JWT signature value = Base64UrlEncode(raw R || S value)

--> recv success from apple.


Share for other users using OpenSSL.


Apple Push Service succeeded using the same OpenSSL, but it doesn't seem to be available for Sign in with Apple yet.

The best thing seems to be support from Sign in with Apple : )

Replies

Hi hwcho,


do you have any indications about how decode the ES256 result extracting the R and S values?


Thank you

Hi biotto,

The answer seems to be a lot late.


Almost all of the official c ++ libraries specified on https://jwt.io/ use openssl.

Their implementation first,

1) obtains openssl's es256 signature.

2) And they use some function and structure to get the values of r and s from 1)'s es256 signature.


In my case, I got the values of r and s with the code below.


byte * signaturePtr = signature.Ptr(); // 1)'s es256 signature result string

std::unique_ptr<ECDSA_SIG, decltype(&ECDSA_SIG_free)> esSig( // convert to ECDSA_SIG sturture

d2i_ECDSA_SIG(nullptr, (const unsigned char**)&signaturePtr, signature.Count()),

ECDSA_SIG_free

);

if (!esSig)

return false;

*out = EncodeBase64Url(Bignumber2Raw(esSig->r) + Bignumber2Raw(esSig->s)); // base64 encode

// BigNumber2Raw is custom func

Hi hwco,


yes I solved the problem in similar manner: https://stackoverflow.com/questions/59737488/apple-sign-in-invalid-client-sign-jwt-for-authentication-using-php-and-openss


thank you anyway for your help 😉

Does anyone knows how to convert ES256 signature result into a raw concatenation of the R and S value ?? (using python)
Hi hwcho.

I follow your steps to get r and s from ECDSA_SIG structure. And use BN_bn2bin to get the binary of r and s.

But sometimes the length of result BN_bn2bin will be 31 instead of 32.

Should I append 0x00 if the length is 31?