Signing Requests

This guide walks through signing HTTP requests in all five languages.

Step 1: Create a Signing Key

The auto-detect constructors infer the algorithm from the key type. You can also use explicit per-algorithm constructors (see Key Management).

import (
    "crypto/ed25519"
    "github.com/zourzouvillys/httpsig/golang"
)

_, privateKey, _ := ed25519.GenerateKey(nil)
kp, _ := httpsig.NewKeyPair("my-key-id", privateKey)
key := kp.Signing // use kp.Verifying for verification
import * as crypto from 'node:crypto';
import { newKeyPair } from '@zourzouvillys/httpsig';

const { privateKey } = crypto.generateKeyPairSync('ed25519');
const kp = newKeyPair('my-key-id', privateKey);
const key = kp.signingKey; // use kp.verifyingKey for verification
import io.zrz.httpsig.Keys;
import java.security.KeyPairGenerator;

var jcaKp = KeyPairGenerator.getInstance("Ed25519").generateKeyPair();
var kp = Keys.keyPair("my-key-id", jcaKp);
var key = kp.signingKey(); // use kp.verifyingKey() for verification
import HTTPSig
import CryptoKit

let privateKey = Curve25519.Signing.PrivateKey()
let kp = KeyPair.ed25519(keyId: "my-key-id", privateKey: privateKey)
let key = kp.signingKey // use kp.verifyingKey for verification
import io.zrz.httpsig.Keys
import java.security.KeyPairGenerator

val jcaKp = KeyPairGenerator.getInstance("Ed25519").generateKeyPair()
val kp = Keys.keyPair("my-key-id", jcaKp)
val key = kp.signingKey // use kp.verifyingKey for verification

Step 2: Define Signature Parameters

Signature parameters control which parts of the message are signed and what metadata is attached.

params := httpsig.SignatureParameters{
    Components: []httpsig.ComponentIdentifier{
        httpsig.Component("@method"),
        httpsig.Component("@path"),
        httpsig.Component("@authority"),
        httpsig.Component("content-type"),
    },
    KeyID:   "my-key-id",
    Created: httpsig.Int64Ptr(time.Now().Unix()),
}
import { component } from '@zourzouvillys/httpsig';

const params = {
  components: [
    component('@method'),
    component('@path'),
    component('@authority'),
    component('content-type'),
  ],
  keyId: 'my-key-id',
  created: Math.floor(Date.now() / 1000),
};
import io.zrz.httpsig.SignatureParameters;
import java.time.Instant;

var params = SignatureParameters.builder()
    .component("@method")
    .component("@path")
    .component("@authority")
    .component("content-type")
    .keyId("my-key-id")
    .created(Instant.now())
    .build();
let params = SignatureParameters(
    components: [
        .init("@method"),
        .init("@path"),
        .init("@authority"),
        .init("content-type"),
    ],
    keyId: "my-key-id",
    created: Int64(Date().timeIntervalSince1970)
)
import io.zrz.httpsig.SignatureParameters
import java.time.Instant

val params = SignatureParameters.builder()
    .component("@method")
    .component("@path")
    .component("@authority")
    .component("content-type")
    .keyId("my-key-id")
    .created(Instant.now())
    .build()

Step 3: Sign the Message

msg := &httpsig.RequestMessage{Req: req}
result, err := httpsig.SignMessage(msg, "sig1", params, key, nil)
if err != nil {
    return err
}

req.Header.Set("Signature-Input", httpsig.SignatureInputHeader(result))
req.Header.Set("Signature", httpsig.SignatureHeader(result))
import { signMessage, signatureInputHeader, signatureHeader } from '@zourzouvillys/httpsig';

const msg = {
  isRequest: true,
  method: 'POST',
  url: new URL('https://example.com/api'),
  headerValues: (name: string) => {
    const headers: Record<string, string> = { 'content-type': 'application/json' };
    const v = headers[name.toLowerCase()];
    return v ? [v] : [];
  },
};

const result = await signMessage(msg, 'sig1', params, key);

// Add to your fetch/axios/undici request headers:
headers['Signature-Input'] = signatureInputHeader(result);
headers['Signature'] = signatureHeader(result);
import io.zrz.httpsig.Signer;

Signer.SignResult result = Signer.sign(httpMessage, "sig1", params, key, null);

request.addHeader("Signature-Input", Signer.signatureInputHeader(result));
request.addHeader("Signature", Signer.signatureHeader(result));
let result = try Signer.sign(msg: httpMessage, label: "sig1", params: params, key: key)

request.addValue(Signer.signatureInputHeader(result), forHTTPHeaderField: "Signature-Input")
request.addValue(Signer.signatureHeader(result), forHTTPHeaderField: "Signature")
import io.zrz.httpsig.Signer

val result = Signer.sign(httpMessage, "sig1", params, key)

request.addHeader("Signature-Input", Signer.signatureInputHeader(result))
request.addHeader("Signature", Signer.signatureHeader(result))

Multiple Signatures

A message can carry multiple signatures. Sign with different labels:

result1, _ := httpsig.SignMessage(msg, "sig1", authParams, authKey, nil)
result2, _ := httpsig.SignMessage(msg, "audit", auditParams, auditKey, nil)

req.Header.Set("Signature-Input", httpsig.SignatureInputHeader(result1, result2))
req.Header.Set("Signature", httpsig.SignatureHeader(result1, result2))

Including Body Integrity

To protect the message body, compute a Content-Digest first, set the header, then include content-digest in your signed components:

body := []byte(`{"data": "value"}`)
digest, _ := httpsig.ContentDigest(body, httpsig.DigestSHA256)
req.Header.Set("Content-Digest", digest)

// Now include "content-digest" in params.Components and sign

See Content-Digest for the full details.