Need a little help, need to sign a message, don't know how

I’m at a loss in the mbedtls API.
I’m writing a small ACME protocol client for embedded systems, in C / C++ and I can’t seem to figure out how to “sign” the message I’m supposed to send.

A linux script based application (certbot) does the same, and produced the valid test JSON message below.
I should “sign” a concatenation of the protected and payload fields, evidently the result is a signature of some 340 bytes (after base64).

The mbedtls calls that I’ve looked into don’t seem fit for that. Also examples such as programs/pkey/rsa_encrypt are limited to 100 characters of input, I have >700 characters of input.

Can you point me to an example that shows what to do, or which calls I should be using ? Stuff like mbedtls_rsa_pkcs1_sign() doesn’t look like the right answer.


Notes : ACME v2 = RFC8555
The signing I need is JWS, see RFC 7515 (

Working sample JSON (tell me how to build the contents of the signature field from the two others, plus my RSA key of course) :
“signature”: “bK93X6ER88u-tUrEo7ycf-Ncm2Ex3RhIg3ZFzNJeAARenE2_KXtnqj2-HNOAGH7ESrrClX-zgRHd4JJ0856lQsbbS1QoGS-cLGzAyx4uA5cIdzddc1zHmKf_v_ScN1XBJLhkid_xy0coSSYSSGIwHV6TVkidhnq1nPVBIDhcmQD02UFkjIvKRp7uNqym7kNSAim7ruuxpE6NY3XxCep1-GPCojykEovz82kld_VG8M9f15vP40mdubCIaijpIhRT0h8i0rScghwfesfDVRab2yy9lLnVeQHy4Fvso569CDrqeb5nTL_vthcYcU-QhbIN7XBH-P_vQ7uCf-wE67Wujw”,
“payload”: “ewogICJ0ZXJtc09mU2VydmljZUFncmVlZCI6IHRydWUsCiAgInJlc291cmNlIjogIm5ldy1yZWciCn0”

Hi @DannyBackx
Decoding the protected field, you will see:

{"alg": "RS256", "jwk": {"n": "tcqDzqO6Nr9A1xCNkGMefusd0yUD0oGHL4SWHW0tz3QoZrsxsPIwtqeBVfWRBDbjvU69eKetIWHC06MsW_eOZDHIJ64ueMwdzpGghvAeZNeFCScBllXiPG2M2g-vszQTbZLG2KUHpkPOhhGOx470Qbe1v-rOSFJHde-bFdBpPOQfp4qRo2GLk7TjBEcSD8xn3N1zhLxgbb7Ds5yxDNc_iJDeZoOfVj017FSckSiGOPkzFKNLX3mpgmNMpBIo4UHNe2F5I1v7taeH4L7mTeZqFuiEpRGFO7py8y32c8-zjQZ4-Tfgu2GrUd3WiR76yWVKIZUNDVbQTcnkPP6U1c4Spw", "e": "AQAB", "kty": "RSA"}, "nonce": "CeBTanEJIgYz1U-r_eSxUQ", "url": "https://localhost:14000/sign-me-up"}

This includes the public key (modulus n and exponent e, probably in Base64 format), and the algorithm used.
According to this page, RS256 algorithm is RSA asymmetric encryption and private key signature and HS256 . (HMAC SHA256 symmetric encryption)
The Mbed TLS RSA API expects the payload to sign to be the hash of the payload. This means you will need to first do Hmac256 on the payload, and the RSA verify.
As mentioned in the RFC you referenced, section 5.2:

Validate the JWS Signature against the JWS Signing Input
ASCII(BASE64URL(UTF8(JWS Protected Header)) || ‘.’ ||
BASE64URL(JWS Payload)) in the manner defined for the algorithm
being used, which MUST be accurately represented by the value of
the “alg” (algorithm) Header Parameter, which MUST be present.

So, you should probably do the following, in your case:

rsa_verify( HMAC256("eyJhbGciOiAiUlMyNTYiLCAiandrIjogeyJuIjogInRjcUR6cU82TnI5QTF4Q05rR01lZnVzZDB5VUQwb0dITDRTV0hXMHR6M1FvWnJzeHNQSXd0cWVCVmZXUkJEYmp2VTY5ZUtldElXSEMwNk1zV19lT1pESElKNjR1ZU13ZHpwR2dodkFlWk5lRkNTY0JsbFhpUEcyTTJnLXZzelFUYlpMRzJLVUhwa1BPaGhHT3g0NzBRYmUxdi1yT1NGSkhkZS1iRmRCcFBPUWZwNHFSbzJHTGs3VGpCRWNTRDh4bjNOMXpoTHhnYmI3RHM1eXhETmNfaUpEZVpvT2ZWajAxN0ZTY2tTaUdPUGt6RktOTFgzbXBnbU5NcEJJbzRVSE5lMkY1STF2N3RhZUg0TDdtVGVacUZ1aUVwUkdGTzdweTh5MzJjOC16alFaNC1UZmd1MkdyVWQzV2lSNzZ5V1ZLSVpVTkRWYlFUY25rUFA2VTFjNFNwdyIsICJlIjogIkFRQUIiLCAia3R5IjogIlJTQSJ9LCAibm9uY2UiOiAiQ2VCVGFuRUpJZ1l6MVUtcl9lU3hVUSIsICJ1cmwiOiAiaHR0cHM6Ly9sb2NhbGhvc3Q6MTQwMDAvc2lnbi1tZS11cCJ9.ewogICJ0ZXJtc09mU2VydmljZUFncmVlZCI6IHRydWUsCiAgInJlc291cmNlIjogIm5ldy1yZWciCn0"))

and extract the public key from the protected field, as mentioned.
Please try the former and update on results
Mbed TLS Support

Hi Ron,
Thanks. What you didn’t write was that I was completely off track with the examples, so thanks for that.
Your answer got me closer to working software.
I’m now somewhat puzzled that the server appears to reply differently to subsequent runs of my application.
On some occasions it replies (to new-account queries) with
failure 400 urn:ietf:params:acme:error:malformed JWS verification error
while other runs give
failure 400 urn:ietf:params:acme:error:unsupportedContact contact method “” is not supported.
That’s against a local Pebble, not the staging API. What could cause that ?


I (9290) Acme: RequestNewAccount( msg {
“termsOfServiceAgreed”: true,
“contact”: [

Hi Danny,

It is highly recommended that you consult in JWS related forums, as this forum is more dedicated to Mbed TLS and TLS and Cryptography.

However, according to this link,
the errors you are encountering are:

  • ‘malformed’: 'The request message was malformed
  • ‘unsupportedContact’: ‘A contact URL for an account used an unsupported protocol scheme’

I can only assume that the data was not properly generated and signed.
Do you have any logs from the local Pebble?