101 lines
2.6 KiB
Go
101 lines
2.6 KiB
Go
// A certificate database management functions
|
|
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
// _CAState represents the persisted CA state in JSON
|
|
// (matches the structure of example_ca.json)
|
|
type _CAState struct {
|
|
CreatedAt string `json:"createdAt"`
|
|
UpdatedAt string `json:"updatedAt"`
|
|
Serial int `json:"serial,omitempty"`
|
|
Certificates []CertificateRecord `json:"certificates"`
|
|
}
|
|
|
|
type CertificateRecord struct {
|
|
Name string `json:"name"`
|
|
Issued string `json:"issued"`
|
|
Expires string `json:"expires"`
|
|
Serial string `json:"serial"`
|
|
Valid bool `json:"valid"`
|
|
}
|
|
|
|
// LoadCAState loads the CA state from a JSON file
|
|
func LoadCAState(filename string) (*_CAState, error) {
|
|
f, err := os.Open(filename)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer f.Close()
|
|
var state _CAState
|
|
if err := json.NewDecoder(f).Decode(&state); err != nil {
|
|
return nil, err
|
|
}
|
|
return &state, nil
|
|
}
|
|
|
|
// SaveCAState saves the CA state to a JSON file
|
|
func SaveCAState(filename string, state *_CAState) error {
|
|
state.UpdatedAt = time.Now().UTC().Format(time.RFC3339)
|
|
f, err := os.Create(filename)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
enc := json.NewEncoder(f)
|
|
enc.SetIndent("", " ")
|
|
return enc.Encode(state)
|
|
}
|
|
|
|
// UpdateCAStateAfterIssue updates the CA state JSON after issuing a certificate
|
|
func UpdateCAStateAfterIssue(jsonFile, serialType, basename string, serialNumber any, validity time.Duration) error {
|
|
var err error
|
|
if CAState == nil {
|
|
CAState, err = LoadCAState(jsonFile)
|
|
if err != nil {
|
|
CAState = nil
|
|
}
|
|
}
|
|
if CAState == nil {
|
|
fmt.Fprintf(os.Stderr, "FATAL: CAState is nil in UpdateCAStateAfterIssue. This indicates a programming error.\n")
|
|
os.Exit(1)
|
|
}
|
|
issued := time.Now().UTC().Format(time.RFC3339)
|
|
expires := time.Now().Add(validity).UTC().Format(time.RFC3339)
|
|
serialStr := ""
|
|
switch serialType {
|
|
case "sequential":
|
|
serialStr = fmt.Sprintf("%d", CAState.Serial)
|
|
CAState.Serial++
|
|
case "random":
|
|
serialStr = fmt.Sprintf("%x", serialNumber)
|
|
default:
|
|
serialStr = fmt.Sprintf("%v", serialNumber)
|
|
}
|
|
AddCertificate(basename, issued, expires, serialStr, true)
|
|
return nil
|
|
}
|
|
|
|
// AddCertificate appends a new CertificateRecord to the CAState
|
|
func AddCertificate(name, issued, expires, serial string, valid bool) {
|
|
if CAState == nil {
|
|
fmt.Fprintf(os.Stderr, "FATAL: CAState is nil in AddCertificate. This indicates a programming error.\n")
|
|
os.Exit(1)
|
|
}
|
|
rec := CertificateRecord{
|
|
Name: name,
|
|
Issued: issued,
|
|
Expires: expires,
|
|
Serial: serial,
|
|
Valid: valid,
|
|
}
|
|
CAState.Certificates = append(CAState.Certificates, rec)
|
|
}
|
|
|
|
// No CAConfig references to update in this file
|