Generate Key Pairs
Bahn requires two P-256 (EC) key pairs per merchant:
| Key pair | use | Purpose |
|---|---|---|
| Signing | sig | You sign every request body with your private key; Bahn verifies it with your registered public key. |
| Encryption | enc | Bahn encrypts responses with your public key; you decrypt them with your private key. |
This guide generates both pairs and writes them to JWK files using Go and the go-jose library.
1. Set Up
Create a new directory and install dependencies:
bash
mkdir keygen && cd keygen
go mod init keygen
go get github.com/go-jose/go-jose/v4
go get github.com/google/uuid2. Generate
Create main.go:
go
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"encoding/json"
"log"
"os"
"github.com/go-jose/go-jose/v4"
"github.com/google/uuid"
)
func main() {
// sig keypair
sigKID := uuid.NewString()
sigPriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
log.Fatalf("failed to generate sig key: %v", err)
}
// enc keypair
encKID := uuid.NewString()
encPriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
log.Fatalf("failed to generate enc key: %v", err)
}
// private JWKS
privSet := jose.JSONWebKeySet{
Keys: []jose.JSONWebKey{
{Key: sigPriv, KeyID: sigKID, Algorithm: string(jose.ES256), Use: "sig"},
{Key: encPriv, KeyID: encKID, Algorithm: string(jose.ES256), Use: "enc"},
},
}
// public JWKS
sigPub := sigPriv.PublicKey
encPub := encPriv.PublicKey
pubSet := jose.JSONWebKeySet{
Keys: []jose.JSONWebKey{
{Key: &sigPub, KeyID: sigKID, Algorithm: string(jose.ES256), Use: "sig"},
{Key: &encPub, KeyID: encKID, Algorithm: string(jose.ES256), Use: "enc"},
},
}
privBytes, err := json.MarshalIndent(privSet, "", " ")
if err != nil {
log.Fatalf("failed to marshal private jwks: %v", err)
}
pubBytes, err := json.MarshalIndent(pubSet, "", " ")
if err != nil {
log.Fatalf("failed to marshal public jwks: %v", err)
}
if err := os.WriteFile("private_key.json", privBytes, 0600); err != nil {
log.Fatalf("failed to write private_key.json: %v", err)
}
if err := os.WriteFile("public_key.json", pubBytes, 0644); err != nil {
log.Fatalf("failed to write public_key.json: %v", err)
}
log.Printf("generated sig kid=%s, enc kid=%s", sigKID, encKID)
}Run it once:
bash
go run main.go
# generated sig kid=00b06c00-..., enc kid=acb9b4c7-...3. Output
Two files are written to the current directory:
| File | Contents | Permission |
|---|---|---|
private_key.json | Both private keys. Never share or commit this file. | 0600 |
public_key.json | Both public keys. Submit to Bahn during onboarding. | 0644 |
public_key.json
json
{
"keys": [
{
"use": "sig",
"kty": "EC",
"kid": "00b06c00-3278-4f09-bf9b-6825cf889131",
"crv": "P-256",
"alg": "ES256",
"x": "S1sGGCWsPQW8PDLKSZn1zOUzWH0GDGaHXFjeTphY5WA",
"y": "pf74XAcJ33zwp_w1O57qJylhlej3mBvFOzwe29s6Jsg"
},
{
"use": "enc",
"kty": "EC",
"kid": "acb9b4c7-aa0c-450b-9ebf-a0dc10a5d4b4",
"crv": "P-256",
"alg": "ES256",
"x": "ge6PdsBMu9y9x_bvMg1g4K8CJ8j6xR_2_jyb0xYn5ss",
"y": "rxE1YRebjiKyPHBRCbLrNHjTei42b2nasfOvhnvvk78"
}
]
}private_key.json
json
{
"keys": [
{
"use": "sig",
"kty": "EC",
"kid": "00b06c00-3278-4f09-bf9b-6825cf889131",
"crv": "P-256",
"alg": "ES256",
"x": "S1sGGCWsPQW8PDLKSZn1zOUzWH0GDGaHXFjeTphY5WA",
"y": "pf74XAcJ33zwp_w1O57qJylhlej3mBvFOzwe29s6Jsg",
"d": "h6s7t1LWWSb_-P2H25dZ_T-EyxCkZdDSVSg-BhXYKNs"
},
{
"use": "enc",
"kty": "EC",
"kid": "acb9b4c7-aa0c-450b-9ebf-a0dc10a5d4b4",
"crv": "P-256",
"alg": "ES256",
"x": "ge6PdsBMu9y9x_bvMg1g4K8CJ8j6xR_2_jyb0xYn5ss",
"y": "rxE1YRebjiKyPHBRCbLrNHjTei42b2nasfOvhnvvk78",
"d": "Uv_5PRnWdrtQQzv_nzowArZ3m2y7pbpeOrc7abkaqbk"
}
]
}The d field is the private scalar — it is present only in private_key.json and is what allows you to sign and decrypt.
WARNING
private_key.json contains sensitive key material. Store it in a secrets manager or encrypted vault and add it to .gitignore. Never share it with Bahn or anyone else.
Next Steps
- Submit
public_key.jsonto Bahn and note bothkidvalues — you will use them in every JWS and JWE header. - Continue to Make Your First Request to sign and send your first API call.