Skip to content

denpeshkov/httpsign

Repository files navigation

httpsign

CI Go Reference

httpsign provides utilities for creating, encoding, and verifying signatures within HTTP requests. Library provides both the transport to create digital signatures or message authentication codes (MACs), and a middleware to verify such signatures.

Overview

The library provides the following signature algorithms:

The API is based on four interfaces: Signer, Verifier and SignerSource, VerifierSource.

Signer is essentially a wrapper around the signature algorithm's private key. Because the private key also contains the corresponding public key, Signer can be used for verification as well. SignerSource abstracts the retrieval of Signer based on the provided key ID.

Verifier uses the public key for verification. It is useful in situations where the user only has access to the public key and not the private key. VerifierSource abstracts the retrieval of Verifier based on the provided key ID.

The HMAC algorithm is an exception, as it uses the same shared secret key for both signing and verification. Therefore, the API provides a single structure, HMAC, for both signing and verification.

Usage

Here is an example using HMAC-SHA-256 algorithm:

type staticSource struct{ h *hmac.HMAC }

func (s staticSource) Signer(ctx context.Context, kid string) (Signer, error) {
	return s.h, nil
}
func (s staticSource) Verifier(ctx context.Context, kid string) (Verifier, error) {
	return s.h, nil
}

const (
	secret = "shared-secret"
	kid    = "key-id"
)

// Create the signer using the shared secret key.
sgn, err := hmac.New([]byte(secret), crypto.SHA256)
if err != nil {
	log.Fatal(err)
}

// Create the source given the signer.
src := staticSource{sgn}

// Create the Transport.
tr := NewTransport(src, kid)

// Create an HTTP client using our transport to sign outgoing requests.
c := &http.Client{Transport: tr}

// Create the Middleware to verify incoming requests signatures.
m := Middleware(src, DefaultErrorHandler)

// Wrap the handler.
var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Hello")
})
handler = m(handler)

http.Handle("/api/foo", handler)

Here is an example using RSASSA-PKCS1-v1.5 SHA-256 algorithm:

type staticSource struct{ h *rsa.PKCSSigner }

func (s staticSource) Signer(ctx context.Context, kid string) (Signer, error) {
	return s.h, nil
}
func (s staticSource) Verifier(ctx context.Context, kid string) (Verifier, error) {
	return s.h, nil
}

const kid = "key-id"

privateKey, err := stdrsa.GenerateKey(rand.Reader, 2048)
hash := crypto.SHA256

// Create the signer using the shared secret key.
sgn, err := rsa.NewPKCSSigner(privateKey, hash)
if err != nil {
	log.Fatal(err)
}

// Create the source given the signer.
src := staticSource{sgn}

// Create the Transport.
tr := NewTransport(src, kid)

// Create an HTTP client using our transport to sign outgoing requests.
c := &http.Client{Transport: tr}

// Create the Middleware to verify incoming requests signatures.
m := Middleware(src, DefaultErrorHandler)

// Alternatively, we can explicitly create a Verifier using the public key.
vrf, err := hsrsa.NewPKCSVerifier(&privateKey.PublicKey, hash)
if err != nil {
	log.Fatal(err)
}

// Wrap the handler.
var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Hello")
})
handler = m(handler)

http.Handle("/api/foo", handler)