https://github.com/form3tech-oss/go-http-message-signatures
Go implementation of the HTTP Message Signatures RFC
https://github.com/form3tech-oss/go-http-message-signatures
Last synced: about 1 year ago
JSON representation
Go implementation of the HTTP Message Signatures RFC
- Host: GitHub
- URL: https://github.com/form3tech-oss/go-http-message-signatures
- Owner: form3tech-oss
- License: apache-2.0
- Created: 2022-08-08T09:50:50.000Z (almost 4 years ago)
- Default Branch: master
- Last Pushed: 2023-06-12T07:59:20.000Z (almost 3 years ago)
- Last Synced: 2025-01-27T05:58:28.852Z (over 1 year ago)
- Language: Go
- Homepage:
- Size: 214 KB
- Stars: 0
- Watchers: 4
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Codeowners: .github/CODEOWNERS
Awesome Lists containing this project
README
# Go HTTP Message Signatures
Go implementation of the [draft HTTP Message Signatures RFC](https://www.ietf.org/archive/id/draft-cavage-http-signatures-12.txt).
**Please note the version of the draft this is based on as there are varying versions which are incompatible.**
## Usage
```go
import "github.com/form3tech-oss/go-http-message-signatures"
```
### Signing
A `MessageSigner` requires a `Signer` implementation to sign any messages. This library currently provides an `RSA` signer, or you can provide your own or contribute new signers back to this library.
```go
keyBytes, err := os.ReadFile("path/to/private.key")
if err != nil {
...
}
decoded, err := pem.Decode(keyBytes)
if err != nil {
...
}
privateKey, err := x509.ParsePKCS1PrivateKey(decoded.Bytes)
if err != nil {
...
}
signer, err := NewRSASigner(privateKey, crypto.SHA256)
if err != nil {
...
}
```
Once you have a `Signer`, you can use it in a `MessageSigner` to sign a request. If the request contains a non-empty `body`, then a `Digest` header will be generated with the given algorithm. This will only be used in the signature if provided in the list of headers to sign. You can also choose which header to populate the signature on, either `Authorization` or `Signature`.
```go
digestAlgorithm := crypto.SHA256
keyID := "id-that-maps-to-public-key-on-server"
messageSigner, err := NewMessageSigner(digestAlgorithm, signer, keyID, httpsignatures.Authorization)
if err != nil {
...
}
signatureHeaders := []string{httpsignature.RequestTarget, "date", "host", "digest"}
req := createHTTPRequest()
req, err = messageSigner.SignRequest(req, signatureHeaders)
if err != nil {
...
}
```
### Verifying
A `MessageVerifier` can be given a list of headers that are required in the message signature and a function that provides a public key and an optional expected hashing algorithm for a given `keyId`.
```go
func keyIDFetcher(keyID string) (crypto.PublicKey, crypto.Hash, error) {
keyBytes, err := os.ReadFile("path/to/public.key")
if err != nil {
...
}
decoded, err := pem.Decode(keyBytes)
if err != nil {
...
}
publicKey, err := x509.ParsePKIXPublicKey(decoded.Bytes)
if err != nil {
...
}
rsaPK, ok := publicKey.(*rsa.PublicKey)
if !ok {
...
}
return rsaPK, 0, nil
}
verifier := NewMessageVerifier(nil, keyIDFetcher)
```
Once you have a `MessageVerifier`, you can use it to verify an HTTP request. If the signature on the request contains the `digest` header, then the digest will be validated against the request `body`. If any `requiredHeaders` were provided, the signature headers will be validated against these. The `keyId` is then extracted and the signature is verified using the public key and optional hashing algorithm returned by the function provided to `MessageVerifier`.
```go
err := verifier.VerifyRequest(req)
if err != nil {
...
}
```