Tạo cặp khóa
Bahn yêu cầu hai cặp khóa P-256 (EC) cho mỗi merchant:
| Cặp khóa | use | Mục đích |
|---|---|---|
| Signing (Ký) | sig | Bạn ký mỗi request body bằng private key của mình; Bahn xác minh bằng public key đã đăng ký của bạn. |
| Encryption (Mã hóa) | enc | Bahn mã hóa response bằng public key của bạn; bạn giải mã bằng private key của mình. |
Hướng dẫn này tạo cả hai cặp khóa và ghi chúng vào các file JWK bằng Go và thư viện go-jose.
1. Thiết lập
Tạo một thư mục mới và cài đặt các dependency:
bash
mkdir keygen && cd keygen
go mod init keygen
go get github.com/go-jose/go-jose/v4
go get github.com/google/uuid2. Tạo khóa
Tạo file 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() {
// cặp khóa sig
sigKID := uuid.NewString()
sigPriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
log.Fatalf("failed to generate sig key: %v", err)
}
// cặp khóa enc
encKID := uuid.NewString()
encPriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
log.Fatalf("failed to generate enc key: %v", err)
}
// JWKS riêng tư
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"},
},
}
// JWKS công khai
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)
}Chạy một lần:
bash
go run main.go
# generated sig kid=00b06c00-..., enc kid=acb9b4c7-...3. Kết quả
Hai file được ghi vào thư mục hiện tại:
| File | Nội dung | Quyền |
|---|---|---|
private_key.json | Cả hai private key. Không bao giờ chia sẻ hoặc commit file này. | 0600 |
public_key.json | Cả hai public key. Gửi cho Bahn trong quá trình 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"
}
]
}Trường d là số vô hướng riêng tư (private scalar) — nó chỉ xuất hiện trong private_key.json và là thứ cho phép bạn ký và giải mã.
WARNING
private_key.json chứa vật liệu khóa nhạy cảm. Lưu trữ nó trong một secrets manager hoặc vault đã được mã hóa và thêm vào .gitignore. Không bao giờ chia sẻ nó với Bahn hoặc bất kỳ ai khác.
Bước tiếp theo
- Gửi
public_key.jsoncho Bahn và ghi lại cả hai giá trịkid— bạn sẽ sử dụng chúng trong mọi header JWS và JWE. - Tiếp tục với Thực hiện request đầu tiên để ký và gửi lệnh gọi API đầu tiên của bạn.