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.