Skip to content

Repository files navigation

swift-httpsig

Swift implementation of RFC 9421 HTTP Message Signatures with the Signature-Key header extension.

Tracks draft-hardt-httpbis-signature-key-08.

LineImplements
2.x-08
1.x-05 era, plus alg emission in 1.2.0

-08 is not wire compatible with earlier revisions in either direction, and the protocol has no version negotiation, so both ends of a deployment move together. See MIGRATING-2.0.md.

Schemes

SchemeStatus
hwkfull
jkt-jwtfull
jwtassertion claims and cnf.jwk; the issuer's signature is the caller's to check
jwks_uriparameters, discovery URL, and metadata issuer validation; the fetch is the caller's
jwksparameters and URL; the fetch is the caller's
self-jwtclaims and JWT signature verification; the fetch is the caller's
x509not implemented

This library performs no network I/O. Discovery schemes parse and expose what is needed, and validate what comes back; the caller owns the fetch and its egress admission.

Requirements

  • iOS 17.4+ / macOS 14+
  • Swift 5.9+
  • No external dependencies (uses CryptoKit and Security frameworks)

Installation

Add to your Package.swift:

dependencies:[.package(url:"https://github.com/hellocoop/swift-httpsig.git", from:"2.0.0"),],targets:[.target(
name:"YourTarget",
dependencies:[.product(name:"HTTPMessageSignatures",package:"swift-httpsig"),]),]

Usage

Signing a Request

import HTTPMessageSignatures
// Create a signing key (Secure Enclave on device, CryptoKit for testing)
letkey=CryptoKitP256SigningKey()
// Create a signer with default components
letsigner=HTTPMessageSigner(
key: key,
label:"sig",
components:["@method","@authority","@path","signature-key"])
// Sign a request (adds Signature-Input, Signature, and Signature-Key headers)
varrequest=URLRequest(url:URL(string:"https://wallet.hello.coop/api/v1/mobile/register")!)
request.httpMethod ="POST"letsignedRequest=try signer.sign(request)

Important: @authority Component

The @authority component in the signature base is derived from the request URL. When verifying on the server, do not use the Host header from the incoming request — reverse proxies (nginx, ALB, CloudFront) commonly rewrite the Host header to the internal upstream hostname.

Instead, use a server-side environment constant for the expected authority:

// Node.js server — CORRECTconstAUTHORITY=`${HOST}.${DOMAIN}`// e.g. "wallet.hello.coop"// In signature base construction:case'@authority':
value=AUTHORITY// NOT req.headers.host
// Swift client — the URL already contains the correct authority
// No special handling needed; URLRequest.url.host() is used automatically

Verifying a Request

letresult=tryHTTPMessageVerifier.verify(request: signedRequest)
// result.jwk - the public key that verified the signature
// result.parameters.created - when the signature was created
// result.components - which components were covered

Secure Enclave Key (iOS)

letkey=trySecureEnclaveSigningKey()
// Persist the key handle for later use
letkeyData= key.dataRepresentation
UserDefaults.standard.set(keyData, forKey:"deviceKey")
// Restore later
letrestored=trySecureEnclaveSigningKey(dataRepresentation: keyData)

JWK Thumbprint

letthumbprint=tryJWKThumbprint.compute(key.publicKeyJWK)

Signature-Key Schemes

SchemeFormatUse Case
hwkInline JWK parametersDevice-bound keys
jwtJWT with cnf.jwkDelegated/attested keys
jwks_uriJWKS discovery URIServer keys

License

MIT

About

Swift implementation of HTTP Message Signatures (RFC 9421) with Signature-Key header (draft-hardt-httpbis-signature-key)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages