Skip to content
Advertisement

Verifying an ECDSA P-384 or P-256 signature with PHP 8

I would like to sign the message “hello” on the browser using SubtleCrypto with ECDSA curve P-384 or P-256, then verify the signature on the server with PHP 8.

On the browser, I generate a keypair and use it to sign the message:

JavaScript

I then send pk_hex and sign_hex to the server. This is what they look like:

JavaScript
JavaScript

On the server, I reconstruct the signature and public key:

JavaScript

I then use openssl_verify() to verify the signature:

JavaScript

But the result I get is -1, for both P-384 and P-256.

If I try doing the same operation using RSASSA-PKCS1-v1_5, the verification passes.

What am I doing wrong?

Could it be that the format of the public key that I’m creating is incorrect? If so, what can I do to format it correctly? Or maybe it’s something else?


openssl_error_string() returns:

JavaScript

openssl_get_curve_names() returns:

JavaScript

Solution

As the answer details, the problem is the fact that SubtleCrypto encodes the signature using the IEEE P1363 format, while OpenSSL expects it in the ASN.1/DER format.

I wrote the following function to convert between P1363 and ASN.1:

JavaScript

Usage example:

JavaScript

Advertisement

Answer

EC signatures can be specified in two formats: r|s (IEEE P1363) or ASN.1/DER. WebCrypto uses the r|s format, while PHP requires the ASN.1 format.

The ASN.1 format is explained in detail here. The posted signature in ASN.1 format is hex encoded:

JavaScript

With this, the verification in PHP is successful.


Both formats are defined in the context of ECC, while RSASSA-PKCS1-v1_5 is a signature scheme in the context of RSA that uses a uniform signature format, so a corresponding problem can generally not occur.


Edit: The ASN.1 format is explained in detail here:

JavaScript

Equally dividing the posted signature result in r:

JavaScript

and s:

JavaScript

Since in both cases the leading byte is larger than 0x7f, a leading 0x00 must be prepended in both cases.

b2 and b3 denote the lengths of r and s, respectively, and are both 0x31. b1 denotes the length of the subsequent data and is 0x66, which finally gives:

JavaScript
User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement