Skip to content
GitHub

HTTP message signatures

HTTP message signatures are cryptographic digital signatures used by the Open Payments API to secure HTTP messages exchanged between sender, receiver, or third-party initiating payment systems.

The Open Payments API implements the HTTP Signatures section of the GNAP (Grant Negotiation and Authorization Protocol) specification.

Purpose

The use of digital signatures allow the Open Payments API to address two key aspects of message security:

  • Authenticity of any system that requests access to specific resources
  • Integrity of specific message fields to guard against message tampering

Part of how Open Payments-enabled systems control access to protected resources is by generating or verifying the digital signature of each HTTP message.

Signature algorithms

To generate message signatures, the Open Payments API implements the Ed25519 variant of the EdDSA (Edwards-curve Digital Signature Algorithm). EdDSA is an elliptic curve cryptographic algorithm that offers advantages over previous generations of public key cryptography algorithms.

The main advantages for using this digital signature algorithm include:

  • Good hash function collision resilience.
  • Speed and efficiency for signature generation and verification.
  • Guarding against the risk of an encryption key downgrade attack.
  • Relatively efficient security offered with smaller key sizes. Earlier public-key cryptographic algorithms, such as RSA, offer comparable security with notably larger key sizes.

For more information about the EdDSA and its variants, refer to RFC8032.

Signature creation

The example below illustrates creating the signature, starting with the original HTTP message.

Example: message before signature

POST HTTP/1.1
Host: example.com
Content-Type: application/json
Content-Digest: sha-512=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:
Content-Length: 18
Authorization: GNAP 123454321
{
"hello";"world"
}

Create the http signature

Signature base

The signature creation process begins with identifying the fields of the original message to use when creating the signature. These are called the covered components of the message. The covered components make up the signature base, together with the signing algorithm, and an identifier for the signer’s public key.

The final sub-field of the signature base is an HTTP structured field called signature params, an ordered list of components that make up the signature base.

Example: signature base

"content-type": application/json
"content-digest": sha-512=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:
"content-length": 18
"authorization": GNAP 123454321
"@method": POST
"@target-uri": https://example.com/
"@signature-params": ("content-type" "content-digest" "content-length" "authorization" "@method" "@target-uri");alg="ed25519";keyid="eddsa_key_1";created=1704722601

Generate signature

To generate the http signature:

  1. The signature base gets hashed (using SHA-512), producing a digest.
  2. The digest gets signed with the signer’s private key, producing the signature as a byte string.
  3. The byte string gets Base64 encoded, and this results in the final signature value.

Signed HTTP message

The original message gets signed by adding uniquely labelled signature headers to the original message: Signature-Input and Signature.

Example: signed message

POST HTTP/1.1
Host: https://example.com
Content-Type: application/json
Content-Length: 18
Authorization: "GNAP 123454321"
Signature-Input: sig1=("content-type" "content-digest" "content-length" "authorization" "@method" "@target-uri");alg="ed25519";keyid="eddsa_key_1";created=1704722601
Signature: sig1=:EiCdZMbyXj6pN59g+mh3mY/Q6DlSBrCL7CJM4OZ550+d2MZhfdDKrOJU/ugeRdwd1KYyd1wA/VA7J2fi9YehCA==:
{
"hello";"world"
}