Crowdhailer
Verifying web crypto signatures in erlang/Elixir
I’m experimenting with using the web crypto API. and want to verify signatures from the browser in Elixir (or erlang, so far I have used only modules that are also available in erlang).
The steps in the browser are:
- generate a key pair.
- export public-key in PEM format
- Sign message
- Base64 encode the signature
- Send the PEM public-key and encoded signature to the backend
The steps to verify on the backend:
- decode PEM
- decode signature
- use
:public_key.verify
The exact code I run is available in this gist.
This files can be used by copy pasting sign.js into the browser console and verify.ex into an iex session.
Doing this the verify function will return false.
I have tried several variations of how I encode/decode. I do need these encode steps because I eventually want to send the signature in the authorization header of a request.
Any help would be greatly appreciated.
Most Liked
voltone
The JS API returns a ‘raw’ signature, whereas OTP’s :public_key API expects a DER-encoded signature.
Try this:
message = "abc"
encoded_signature = "AOqjfvb1P0fYdbnq+f3XsbOR/Lylq4csC5e1Ks4cKRTcWKQyH4CVr/XRHCmZDEWOpaA8gwuM3Z3qD21vMaixySUPASTDnd1J8shNx51MCbONIReTNuH53kzxykLpf0riSyMbEBQtie4/pxlrolUOsDja2f/ikgM/lGXtCTXz2faV4m2Y"
public_PEM = "-----BEGIN PUBLIC KEY-----
MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQB7PNQVbADLNyobtijE5NVZUvHs74h
iMntCbp0C8pdU1IQRWlAfDeEs/iuxA32VARw9Q5/0mim8Si8JcpCJnhS0u8AESMf
Ux3WqzHhB33t4q3iPsJbM7zmN91QNnbYErrGqEDCmSruPpKw1iK5dJ3/xQZbkpmR
ztoVwrZoCoGUu+WTqEI=
-----END PUBLIC KEY-----"
raw_signature = Base.decode64!(encoded_signature)
size = div(byte_size(raw_signature), 2)
<<r::binary-size(size), s::binary-size(size)>> = raw_signature
signature = <<48, 129, 136, 2, size, r::binary, 2, size, s::binary>>
[key_entry] = :public_key.pem_decode(public_PEM)
public_key = :public_key.pem_entry_decode(key_entry)
:public_key.verify(
message,
:sha256,
signature,
public_key
)
# returns true
voltone
voltone
Correct
An RSA signature is just a single integer, which is simply encoded as a binary by all implementations. An ECDSA signature consists of two integers. Some implementations wrap those in an ASN.1 ‘Sequence’ and others just concatenate them (which may require zero-padding of the smaller of the two, to remove ambiguity about where to split).








