Skip to content
GitHub

HTTP message signatures

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

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

Purpose

The use of digital signatures allow the Open Payments APIs 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 APIs implement 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 fields are referred to as 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, which contains 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

For more information about required components, see Section 7.3.1 HTTP Message Signatures in the GNAP specification.

Generate signature

To generate the http signature:

  1. The signature base is hashed using SHA-512, producing a digest.
  2. The digest is signed with the signer’s private key, producing the signature as a byte string.
  3. The byte string is Base64-encoded, resulting 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"
}