Improve configuration, implement setup mode
- implement a dedicated setup mode for creating CA certificates that is triggered by the '-setup' command line flag - switch to YAML configuration for comment support and more human readable syntax. Format documentation is in docs/config.sample.yaml - move HSM related code to pkg/hsm - improve consistency checks in pkg/config
This commit is contained in:
parent
24f9ef297c
commit
47d5b2afff
16 changed files with 657 additions and 236 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,4 +3,5 @@
|
|||
*.pub
|
||||
.idea/
|
||||
ca-hierarchy.json
|
||||
config.yaml
|
||||
dist/
|
|
@ -1,5 +0,0 @@
|
|||
//go:build linux && amd64
|
||||
|
||||
package main
|
||||
|
||||
const defaultPkcs11Module = "/usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so"
|
|
@ -1,5 +0,0 @@
|
|||
//go:build linux && arm64
|
||||
|
||||
package main
|
||||
|
||||
const defaultPkcs11Module = "/usr/lib/aarch64-linux-gnu/softhsm/libsofthsm2.so"
|
|
@ -1,5 +0,0 @@
|
|||
//go:build linux && arm
|
||||
|
||||
package main
|
||||
|
||||
const defaultPkcs11Module = "/usr/lib/arm-linux-gnueabihf/softhsm/libsofthsm2.so"
|
|
@ -2,16 +2,10 @@ package main
|
|||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"git.cacert.org/cacert-gosigner/pkg/config"
|
||||
"github.com/ThalesIgnite/crypto11"
|
||||
"golang.org/x/term"
|
||||
|
||||
"git.cacert.org/cacert-gosigner/pkg/hsm"
|
||||
)
|
||||
|
||||
|
@ -22,23 +16,23 @@ var (
|
|||
)
|
||||
|
||||
const (
|
||||
defaultTokenLabel = "localhsm"
|
||||
defaultSignerConfigFile = "ca-hierarchy.json"
|
||||
defaultSignerConfigFile = "config.yaml"
|
||||
)
|
||||
|
||||
func main() {
|
||||
p11Config := &crypto11.Config{}
|
||||
var (
|
||||
showVersion bool
|
||||
signerConfigFile string
|
||||
setupMode bool
|
||||
)
|
||||
|
||||
log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile | log.LUTC)
|
||||
|
||||
log.Printf("cacert-gosigner %s (%s) - built %s\n", version, commit, date)
|
||||
|
||||
flag.StringVar(&p11Config.Path, "module", defaultPkcs11Module, "PKCS#11 module")
|
||||
flag.StringVar(&p11Config.TokenLabel, "token", defaultTokenLabel, "PKCS#11 token label")
|
||||
flag.StringVar(&signerConfigFile, "caconfig", defaultSignerConfigFile, "signer configuration file")
|
||||
flag.BoolVar(&showVersion, "version", false, "show version")
|
||||
flag.BoolVar(&setupMode, "setup", false, "setup mode")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
|
@ -46,9 +40,6 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
log.Printf("using PKCS#11 module %s", p11Config.Path)
|
||||
log.Printf("looking for token with label %s", p11Config.TokenLabel)
|
||||
|
||||
configFile, err := os.Open(signerConfigFile)
|
||||
if err != nil {
|
||||
log.Fatalf("could not open singer configuration file %s: %v", signerConfigFile, err)
|
||||
|
@ -59,41 +50,20 @@ func main() {
|
|||
log.Fatalf("could not load CA hierarchy: %v", err)
|
||||
}
|
||||
|
||||
getPin(p11Config)
|
||||
|
||||
p11Context, err := crypto11.Configure(p11Config)
|
||||
if err != nil {
|
||||
log.Fatalf("could not configure PKCS#11 library: %v", err)
|
||||
if setupMode {
|
||||
log.Print("running in setup mode")
|
||||
}
|
||||
|
||||
defer func(p11Context *crypto11.Context) {
|
||||
err := p11Context.Close()
|
||||
if err != nil {
|
||||
log.Printf("could not close PKCS#11 library context: %v", err)
|
||||
}
|
||||
}(p11Context)
|
||||
ctx := hsm.SetupContext(caConfig, setupMode)
|
||||
|
||||
err = hsm.EnsureCAKeysAndCertificates(p11Context, caConfig)
|
||||
err = hsm.EnsureCAKeysAndCertificates(ctx)
|
||||
if err != nil {
|
||||
log.Fatalf("could not ensure CA keys and certificates exist: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func getPin(p11Config *crypto11.Config) {
|
||||
pin, found := os.LookupEnv("TOKEN_PIN")
|
||||
if !found {
|
||||
log.Printf("environment variable TOKEN_PIN has not been set")
|
||||
if !term.IsTerminal(syscall.Stdin) {
|
||||
log.Fatal("stdin is not a terminal")
|
||||
}
|
||||
fmt.Print("Enter PIN: ")
|
||||
bytePin, err := term.ReadPassword(syscall.Stdin)
|
||||
if err != nil {
|
||||
log.Fatalf("could not read PIN")
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
pin = string(bytePin)
|
||||
if setupMode {
|
||||
return
|
||||
}
|
||||
p11Config.Pin = strings.TrimSpace(pin)
|
||||
|
||||
log.Print("setup complete, starting signer operation")
|
||||
}
|
||||
|
|
131
docs/config.sample.yaml
Normal file
131
docs/config.sample.yaml
Normal file
|
@ -0,0 +1,131 @@
|
|||
---
|
||||
# configuration example for the CAcert signer software
|
||||
#
|
||||
# each directive is described at its first occurrence. If a description is
|
||||
# missing please file a bug report.
|
||||
|
||||
# Settings defines global settings for the signer
|
||||
Settings:
|
||||
# define address information for the CA operator, this will be used for
|
||||
# the SubjectDN of CA certificates
|
||||
organization:
|
||||
country: [ "CH" ]
|
||||
organization: [ "CAcert Inc." ]
|
||||
locality: [ "Genève" ]
|
||||
street-address: [ "Clos Belmont 2" ]
|
||||
postal-code: [ "1208" ]
|
||||
# define how long CA certificates should be valid
|
||||
validity-years:
|
||||
root: 20
|
||||
intermediary: 5
|
||||
# URL patterns used for certificate fields. The first %s is replaced with
|
||||
# the identifier of a CA certificate
|
||||
url-patterns:
|
||||
ocsp: "http://ocsp.cacert.org/"
|
||||
crl: "http://crl.cacert.org/%s.crl"
|
||||
issuer: "http://www.cacert.org/certs/%s.crt"
|
||||
|
||||
# KeyStorage defines PKCS#11 tokens, a token named 'default' must be present
|
||||
KeyStorage:
|
||||
default:
|
||||
# HSM type, softhsm support is builtin
|
||||
type: softhsm
|
||||
# token label of the PKCS#11 token
|
||||
label: localhsm
|
||||
offline:
|
||||
# HSM type, p11module requires a module parameter
|
||||
type: p11module
|
||||
# path to a PKCS#11 shared library module
|
||||
module: /usr/lib/x86_64-linux-gnu/pkcs11/onepin-opensc-pkcs11.so
|
||||
label: smartcard
|
||||
|
||||
# CAs defines the CA hierarchy of root and intermediary CA certificates
|
||||
CAs:
|
||||
# a root CA, the map key will be used as a label for PKCS11 and URLs
|
||||
ecc_root_2022:
|
||||
# information about the private key
|
||||
key-info:
|
||||
# key algorithm EC (for elliptic curve) or RSA are supported
|
||||
algorithm: "EC"
|
||||
# elliptic curve name P-224, P-256, P-384 and P-521 are supported
|
||||
ecc-curve: "P-521"
|
||||
# common name for the SubjectDN of the CA certificate
|
||||
common-name: "CAcert ECC Root CA 2022"
|
||||
# storage can be any label from the KeyStorage configuration
|
||||
storage: offline
|
||||
rsa_root_2022:
|
||||
key-info:
|
||||
algorithm: "RSA"
|
||||
# RSA key length in bits (> 3072 is recommended by NIST, BSI and others)
|
||||
rsa-bits: 4096
|
||||
common-name: "CAcert RSA Root CA 2022"
|
||||
storage: offline
|
||||
ecc_person_2022:
|
||||
key-info:
|
||||
algorithm: "EC"
|
||||
ecc-curve: "P-384"
|
||||
# parent CA, must be any of the other defined CAs
|
||||
parent: "ecc_root_2022"
|
||||
common-name: "CAcert ECC Person CA 2022"
|
||||
# extended key usage, only makes sense for non root CAs, supported values are
|
||||
#
|
||||
# - client for client authentication
|
||||
# - code for code signing
|
||||
# - email for email protection (aka S/MIME)
|
||||
# - ocsp for OCSP signing
|
||||
# - server for server authentication
|
||||
#
|
||||
# CAs should only sign endpoint certificates for one of the extended key usages
|
||||
# defined in the CA certificate itself
|
||||
ext-key-usages:
|
||||
- client
|
||||
- code
|
||||
- email
|
||||
- ocsp
|
||||
rsa_person_2022:
|
||||
key-info:
|
||||
algorithm: "RSA"
|
||||
rsa-bits: 3072
|
||||
parent: "rsa_root_2022"
|
||||
common-name: "CAcert RSA Person CA 2022"
|
||||
ext-key-usages:
|
||||
- client
|
||||
- code
|
||||
- email
|
||||
- ocsp
|
||||
ecc_client_2022:
|
||||
key-info:
|
||||
algorithm: "EC"
|
||||
ecc-curve: "P-384"
|
||||
parent: "ecc_root_2022"
|
||||
common-name: "CAcert ECC Client CA 2022"
|
||||
ext-key-usages:
|
||||
- client
|
||||
- ocsp
|
||||
rsa_client_2022:
|
||||
key-info:
|
||||
algorithm: "RSA"
|
||||
rsa-bits: 3072
|
||||
parent: "rsa_root_2022"
|
||||
common-name: "CAcert RSA Client CA 2022"
|
||||
ext-key-usages:
|
||||
- client
|
||||
- ocsp
|
||||
ecc_server_2022:
|
||||
key-info:
|
||||
algorithm: "EC"
|
||||
ecc-curve: "P-384"
|
||||
parent: "ecc_root_2022"
|
||||
common-name: "CAcert ECC Server CA 2022"
|
||||
ext-key-usages:
|
||||
- server
|
||||
- ocsp
|
||||
rsa_server_2022:
|
||||
key-info:
|
||||
algorithm: "RSA"
|
||||
rsa-bits: 3072
|
||||
parent: "rsa_root_2022"
|
||||
common-name: "CAcert RSA Server CA 2022"
|
||||
ext-key-usages:
|
||||
- server
|
||||
- ocsp
|
2
go.mod
2
go.mod
|
@ -6,6 +6,7 @@ require (
|
|||
github.com/ThalesIgnite/crypto11 v1.2.5
|
||||
github.com/stretchr/testify v1.7.1
|
||||
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
)
|
||||
|
||||
require (
|
||||
|
@ -15,5 +16,4 @@ require (
|
|||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/thales-e-security/pool v0.0.2 // indirect
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
)
|
||||
|
|
5
pkg/config/amd64.go
Normal file
5
pkg/config/amd64.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
//go:build linux && amd64
|
||||
|
||||
package config
|
||||
|
||||
const softHsmModule = "/usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so"
|
5
pkg/config/arm64.go
Normal file
5
pkg/config/arm64.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
//go:build linux && arm64
|
||||
|
||||
package config
|
||||
|
||||
const softHsmModule = "/usr/lib/aarch64-linux-gnu/softhsm/libsofthsm2.so"
|
5
pkg/config/armhf.go
Normal file
5
pkg/config/armhf.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
//go:build linux && arm
|
||||
|
||||
package config
|
||||
|
||||
const softHsmModule = "/usr/lib/arm-linux-gnueabihf/softhsm/libsofthsm2.so"
|
|
@ -5,71 +5,183 @@ import (
|
|||
"crypto/elliptic"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type Settings struct {
|
||||
Organization pkix.Name `json:"organization"`
|
||||
RootYears int `json:"root-years"`
|
||||
IntermediaryYears int `json:"intermediary-years"`
|
||||
Organization struct {
|
||||
Country []string `yaml:"country"`
|
||||
Organization []string `yaml:"organization"`
|
||||
Locality []string `yaml:"locality"`
|
||||
StreetAddress []string `yaml:"street-address"`
|
||||
PostalCode []string `yaml:"postal-code"`
|
||||
} `yaml:"organization"`
|
||||
ValidityYears struct {
|
||||
Root int `yaml:"root"`
|
||||
Intermediary int `yaml:"intermediary"`
|
||||
} `yaml:"validity-years"`
|
||||
URLPatterns struct {
|
||||
Ocsp string `yaml:"ocsp"`
|
||||
CRL string `yaml:"crl"`
|
||||
Issuer string `yaml:"issuer"`
|
||||
} `yaml:"url-patterns"`
|
||||
}
|
||||
|
||||
func (s *Settings) CalculateValidity(cert *CaCertificateEntry) (time.Time, time.Time) {
|
||||
var notBefore, notAfter time.Time
|
||||
notBefore = time.Now()
|
||||
type KeyStorage struct {
|
||||
Label string
|
||||
Module string
|
||||
}
|
||||
|
||||
if cert.Parent == nil {
|
||||
notAfter = notBefore.AddDate(s.RootYears, 0, 0)
|
||||
} else {
|
||||
notAfter = notBefore.AddDate(s.IntermediaryYears, 0, 0)
|
||||
func (k *KeyStorage) UnmarshalYAML(n *yaml.Node) error {
|
||||
ks := struct {
|
||||
TokenType string `yaml:"type"`
|
||||
Label string `yaml:"label"`
|
||||
Module string `yaml:"module"`
|
||||
}{}
|
||||
|
||||
err := n.Decode(&ks)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not decode YAML: %w", err)
|
||||
}
|
||||
|
||||
return notBefore, notAfter
|
||||
}
|
||||
switch ks.TokenType {
|
||||
case "softhsm":
|
||||
k.Module = softHsmModule
|
||||
case "p11module":
|
||||
if ks.Module == "" {
|
||||
return errors.New("specify a 'module' field when using the 'p11module' type")
|
||||
}
|
||||
k.Module = ks.Module
|
||||
default:
|
||||
return fmt.Errorf("unsupported KeyStorage type '%s'", ks.TokenType)
|
||||
}
|
||||
|
||||
func (s *Settings) CalculateSubject(cert *CaCertificateEntry) pkix.Name {
|
||||
subject := s.Organization
|
||||
subject.CommonName = cert.CommonName
|
||||
k.Label = ks.Label
|
||||
|
||||
return subject
|
||||
}
|
||||
|
||||
func (s *Settings) BuildIssuerURL(parentCA *CaCertificateEntry) string {
|
||||
return fmt.Sprintf("http://www.example.org/%s.crt", parentCA.Label)
|
||||
}
|
||||
|
||||
func (s *Settings) BuildOCSPURL(_ *CaCertificateEntry) string {
|
||||
return "http://ocsp.example.org/"
|
||||
}
|
||||
|
||||
func (s *Settings) BuildCRLUrl(parentCA *CaCertificateEntry) string {
|
||||
return fmt.Sprintf("http://crl.cacert.org/%s.crl", parentCA.Label)
|
||||
return nil
|
||||
}
|
||||
|
||||
type SignerConfig struct {
|
||||
Global *Settings `json:"Settings"`
|
||||
CAs []*CaCertificateEntry `json:"CAs"`
|
||||
global *Settings `yaml:"Settings"`
|
||||
caMap map[string]*CaCertificateEntry `yaml:"CAs"`
|
||||
KeyStorage map[string]*KeyStorage `yaml:"KeyStorage"`
|
||||
}
|
||||
|
||||
// LoadConfiguration reads JSON configuration from the given reader as a SignerConfig structure
|
||||
func (c *SignerConfig) GetCADefinition(label string) (*CaCertificateEntry, error) {
|
||||
entry, ok := c.caMap[label]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no CA definition found for label %s", label)
|
||||
}
|
||||
return entry, nil
|
||||
}
|
||||
|
||||
func (c *SignerConfig) CalculateValidity(cert *CaCertificateEntry) (time.Time, time.Time) {
|
||||
var notBefore, notAfter time.Time
|
||||
notBefore = time.Now()
|
||||
|
||||
if cert.IsRoot() {
|
||||
notAfter = notBefore.AddDate(c.global.ValidityYears.Root, 0, 0)
|
||||
} else {
|
||||
notAfter = notBefore.AddDate(c.global.ValidityYears.Intermediary, 0, 0)
|
||||
}
|
||||
return notBefore, notAfter
|
||||
}
|
||||
|
||||
func (c *SignerConfig) CalculateSubject(cert *CaCertificateEntry) pkix.Name {
|
||||
subject := pkix.Name{
|
||||
Country: c.global.Organization.Country,
|
||||
Organization: c.global.Organization.Organization,
|
||||
Locality: c.global.Organization.Locality,
|
||||
StreetAddress: c.global.Organization.StreetAddress,
|
||||
PostalCode: c.global.Organization.PostalCode,
|
||||
}
|
||||
subject.CommonName = cert.CommonName
|
||||
return subject
|
||||
}
|
||||
|
||||
func (c *SignerConfig) CertificateFileName(label string) string {
|
||||
return fmt.Sprintf("%s.crt", label)
|
||||
}
|
||||
|
||||
func (c *SignerConfig) BuildIssuerURL(cert *CaCertificateEntry) string {
|
||||
return fmt.Sprintf(c.global.URLPatterns.Issuer, cert.parent)
|
||||
}
|
||||
|
||||
func (c *SignerConfig) BuildOCSPURL(cert *CaCertificateEntry) string {
|
||||
// in case the configuration specifies a placeholder
|
||||
if strings.Count(c.global.URLPatterns.Ocsp, "%s") == 1 {
|
||||
return fmt.Sprintf(c.global.URLPatterns.Ocsp, cert.parent)
|
||||
}
|
||||
return c.global.URLPatterns.Ocsp
|
||||
}
|
||||
|
||||
func (c *SignerConfig) BuildCRLUrl(cert *CaCertificateEntry) string {
|
||||
return fmt.Sprintf(c.global.URLPatterns.CRL, cert.parent)
|
||||
}
|
||||
|
||||
func (c *SignerConfig) GetParentCA(label string) (*CaCertificateEntry, error) {
|
||||
entry, ok := c.caMap[label]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no CA definition for %s", label)
|
||||
}
|
||||
|
||||
if entry.IsRoot() {
|
||||
return nil, fmt.Errorf("CA %s is a root CA and has no parent", label)
|
||||
}
|
||||
|
||||
return c.caMap[entry.parent], nil
|
||||
}
|
||||
|
||||
// RootCAs returns the labels of all configured root CAs
|
||||
func (c *SignerConfig) RootCAs() []string {
|
||||
roots := make([]string, 0)
|
||||
for label, entry := range c.caMap {
|
||||
if entry.IsRoot() {
|
||||
roots = append(roots, label)
|
||||
}
|
||||
}
|
||||
|
||||
return roots
|
||||
}
|
||||
|
||||
// IntermediaryCAs returns the labels of all configured intermediary CAs
|
||||
func (c *SignerConfig) IntermediaryCAs() []string {
|
||||
intermediaries := make([]string, 0)
|
||||
for label, entry := range c.caMap {
|
||||
if !entry.IsRoot() {
|
||||
intermediaries = append(intermediaries, label)
|
||||
}
|
||||
}
|
||||
|
||||
return intermediaries
|
||||
}
|
||||
|
||||
// LoadConfiguration reads YAML configuration from the given reader as a SignerConfig structure
|
||||
func LoadConfiguration(r io.Reader) (*SignerConfig, error) {
|
||||
data, err := io.ReadAll(r)
|
||||
config := struct {
|
||||
Global *Settings `yaml:"Settings"`
|
||||
CAs map[string]*CaCertificateEntry `yaml:"CAs"`
|
||||
KeyStorage map[string]*KeyStorage `yaml:"KeyStorage"`
|
||||
}{}
|
||||
|
||||
decoder := yaml.NewDecoder(r)
|
||||
err := decoder.Decode(&config)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not load configuration: %w", err)
|
||||
return nil, fmt.Errorf("could not parse YAML configuration: %w", err)
|
||||
}
|
||||
|
||||
config := &SignerConfig{}
|
||||
|
||||
err = json.Unmarshal(data, config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse JSON configuration: %w", err)
|
||||
}
|
||||
|
||||
return config, nil
|
||||
return &SignerConfig{
|
||||
global: config.Global,
|
||||
caMap: config.CAs,
|
||||
KeyStorage: config.KeyStorage,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type PrivateKeyInfo struct {
|
||||
|
@ -78,15 +190,15 @@ type PrivateKeyInfo struct {
|
|||
RSABits int
|
||||
}
|
||||
|
||||
func (p *PrivateKeyInfo) UnmarshalJSON(data []byte) error {
|
||||
func (p *PrivateKeyInfo) UnmarshalYAML(value *yaml.Node) error {
|
||||
internalStructure := struct {
|
||||
Label string `json:"label"`
|
||||
Algorithm string `json:"algorithm"`
|
||||
EccCurve string `json:"ecc-curve,omitempty"`
|
||||
RSABits *int `json:"rsa-bits,omitempty"`
|
||||
Label string `yaml:"label"`
|
||||
Algorithm string `yaml:"algorithm"`
|
||||
EccCurve string `yaml:"ecc-curve,omitempty"`
|
||||
RSABits *int `yaml:"rsa-bits,omitempty"`
|
||||
}{}
|
||||
|
||||
err := json.Unmarshal(data, &internalStructure)
|
||||
err := value.Decode(&internalStructure)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not unmarshal private key info: %w", err)
|
||||
}
|
||||
|
@ -111,11 +223,11 @@ func (p *PrivateKeyInfo) UnmarshalJSON(data []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *PrivateKeyInfo) MarshalJSON() ([]byte, error) {
|
||||
func (p *PrivateKeyInfo) MarshalYAML() (interface{}, error) {
|
||||
internalStructure := struct {
|
||||
Algorithm string `json:"algorithm"`
|
||||
EccCurve string `json:"ecc-curve,omitempty"`
|
||||
RSABits *int `json:"rsa-bits,omitempty"`
|
||||
Algorithm string `yaml:"algorithm"`
|
||||
EccCurve string `yaml:"ecc-curve,omitempty"`
|
||||
RSABits *int `yaml:"rsa-bits,omitempty"`
|
||||
}{}
|
||||
switch p.Algorithm {
|
||||
case x509.RSA:
|
||||
|
@ -130,12 +242,7 @@ func (p *PrivateKeyInfo) MarshalJSON() ([]byte, error) {
|
|||
internalStructure.EccCurve = curveName
|
||||
}
|
||||
|
||||
data, err := json.Marshal(internalStructure)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not marshal private key info: %w", err)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
return internalStructure, nil
|
||||
}
|
||||
|
||||
func curveToName(curve elliptic.Curve) (string, error) {
|
||||
|
@ -170,45 +277,47 @@ func nameToCurve(name string) (elliptic.Curve, error) {
|
|||
}
|
||||
|
||||
type CaCertificateEntry struct {
|
||||
Label string
|
||||
KeyInfo *PrivateKeyInfo
|
||||
CommonName string
|
||||
SubCAs []*CaCertificateEntry
|
||||
MaxPathLen int `json:"max-path-len"` // maximum path length should be 0 for CAs that issue end entity certificates
|
||||
ExtKeyUsage []x509.ExtKeyUsage `json:"ext-key-usage"`
|
||||
MaxPathLen int // maximum path length should be 0 for CAs that issue end entity certificates
|
||||
ExtKeyUsage []x509.ExtKeyUsage
|
||||
Certificate *x509.Certificate
|
||||
KeyPair crypto.Signer
|
||||
Parent *CaCertificateEntry
|
||||
parent string
|
||||
Storage string
|
||||
}
|
||||
|
||||
func (c *CaCertificateEntry) CertificateFileName() string {
|
||||
return c.Label + ".crt"
|
||||
}
|
||||
|
||||
func (c *CaCertificateEntry) UnmarshalJSON(data []byte) error {
|
||||
func (c *CaCertificateEntry) UnmarshalYAML(value *yaml.Node) error {
|
||||
var m struct {
|
||||
Label string
|
||||
KeyInfo *PrivateKeyInfo `json:"key-info"`
|
||||
CommonName string `json:"common-name"`
|
||||
SubCAs []*CaCertificateEntry `json:"sub-cas,omitempty"`
|
||||
MaxPathLen int `json:"max-path-len,omitempty"` // maximum path length should be 0 for CAs that issue end entity certificates
|
||||
ExtKeyUsage []string `json:"ext-key-usage,omitempty"`
|
||||
KeyInfo *PrivateKeyInfo `yaml:"key-info"`
|
||||
CommonName string `yaml:"common-name"`
|
||||
MaxPathLen int `yaml:"max-path-len,omitempty"` // maximum path length should be 0 for CAs that issue end entity certificates
|
||||
ExtKeyUsage []string `yaml:"ext-key-usages,omitempty"`
|
||||
Parent string `yaml:"parent"`
|
||||
Storage string `yaml:"storage"`
|
||||
}
|
||||
|
||||
err := json.Unmarshal(data, &m)
|
||||
err := value.Decode(&m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Label = m.Label
|
||||
c.KeyInfo = m.KeyInfo
|
||||
c.CommonName = m.CommonName
|
||||
c.SubCAs = m.SubCAs
|
||||
c.MaxPathLen = m.MaxPathLen
|
||||
|
||||
if m.ExtKeyUsage != nil {
|
||||
c.ExtKeyUsage, err = mapExtKeyUsageNames(m.ExtKeyUsage)
|
||||
}
|
||||
|
||||
c.parent = m.Parent
|
||||
|
||||
if m.Storage != "" {
|
||||
c.Storage = m.Storage
|
||||
} else {
|
||||
c.Storage = "default"
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -216,6 +325,10 @@ func (c *CaCertificateEntry) UnmarshalJSON(data []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *CaCertificateEntry) IsRoot() bool {
|
||||
return c.parent == ""
|
||||
}
|
||||
|
||||
func mapExtKeyUsageNames(usages []string) ([]x509.ExtKeyUsage, error) {
|
||||
extKeyUsages := make([]x509.ExtKeyUsage, len(usages))
|
||||
|
||||
|
|
|
@ -3,13 +3,13 @@ package config
|
|||
import (
|
||||
"crypto/elliptic"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func TestPrivateKeyInfo_MarshalJSON(t *testing.T) {
|
||||
func TestPrivateKeyInfo_MarshalYAML(t *testing.T) {
|
||||
testData := []struct {
|
||||
name string
|
||||
pkInfo *PrivateKeyInfo
|
||||
|
@ -21,7 +21,9 @@ func TestPrivateKeyInfo_MarshalJSON(t *testing.T) {
|
|||
Algorithm: x509.RSA,
|
||||
RSABits: 3072,
|
||||
},
|
||||
`{"algorithm":"RSA","rsa-bits":3072}`,
|
||||
`algorithm: RSA
|
||||
rsa-bits: 3072
|
||||
`,
|
||||
},
|
||||
{
|
||||
"ECDSA",
|
||||
|
@ -29,13 +31,15 @@ func TestPrivateKeyInfo_MarshalJSON(t *testing.T) {
|
|||
Algorithm: x509.ECDSA,
|
||||
EccCurve: elliptic.P224(),
|
||||
},
|
||||
`{"algorithm":"EC","ecc-curve":"P-224"}`,
|
||||
`algorithm: EC
|
||||
ecc-curve: P-224
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, item := range testData {
|
||||
t.Run(item.name, func(t *testing.T) {
|
||||
data, err := json.Marshal(item.pkInfo)
|
||||
data, err := yaml.Marshal(item.pkInfo)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -45,16 +49,18 @@ func TestPrivateKeyInfo_MarshalJSON(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestPrivateKeyInfo_UnmarshalJSON(t *testing.T) {
|
||||
func TestPrivateKeyInfo_UnmarshalYAML(t *testing.T) {
|
||||
testData := []struct {
|
||||
name string
|
||||
json string
|
||||
yaml string
|
||||
expected *PrivateKeyInfo
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
"RSA",
|
||||
`{"label":"mykey","algorithm":"RSA","rsa-bits":2048}`,
|
||||
`label: "mykey"
|
||||
algorithm: "RSA"
|
||||
rsa-bits: 2048`,
|
||||
&PrivateKeyInfo{
|
||||
Algorithm: x509.RSA,
|
||||
RSABits: 2048,
|
||||
|
@ -63,7 +69,9 @@ func TestPrivateKeyInfo_UnmarshalJSON(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"ECDSA",
|
||||
`{"label":"mykey","algorithm":"EC","ecc-curve":"P-521"}`,
|
||||
`label: "mykey"
|
||||
algorithm: "EC"
|
||||
ecc-curve: "P-521"`,
|
||||
&PrivateKeyInfo{
|
||||
Algorithm: x509.ECDSA,
|
||||
EccCurve: elliptic.P521(),
|
||||
|
@ -72,19 +80,21 @@ func TestPrivateKeyInfo_UnmarshalJSON(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"no-algorithm",
|
||||
`{"label":"mykey"}`,
|
||||
`label: "mykey"`,
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"RSA-no-rsa-bits",
|
||||
`{"label":"mykey","algorithm":"RSA"}`,
|
||||
`label: "mykey"
|
||||
algorithm: "RSA"`,
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ECDSA-no-curve",
|
||||
`{"label":"mykey","algorithm":"EC"}`,
|
||||
`label: "mykey"
|
||||
algorithm: "EC"`,
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
|
@ -93,7 +103,7 @@ func TestPrivateKeyInfo_UnmarshalJSON(t *testing.T) {
|
|||
for _, item := range testData {
|
||||
t.Run(item.name, func(t *testing.T) {
|
||||
pkInfo := &PrivateKeyInfo{}
|
||||
err := json.Unmarshal([]byte(item.json), pkInfo)
|
||||
err := yaml.Unmarshal([]byte(item.yaml), pkInfo)
|
||||
if err != nil {
|
||||
if !item.expectErr {
|
||||
t.Fatal(err)
|
||||
|
@ -107,7 +117,7 @@ func TestPrivateKeyInfo_UnmarshalJSON(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCaCertificateEntry_UnmarshalJSON(t *testing.T) {
|
||||
func TestCaCertificateEntry_UnmarshalYAML(t *testing.T) {
|
||||
data := `{
|
||||
"label":"root",
|
||||
"key-info": {
|
||||
|
@ -120,7 +130,7 @@ func TestCaCertificateEntry_UnmarshalJSON(t *testing.T) {
|
|||
|
||||
entry := CaCertificateEntry{}
|
||||
|
||||
err := json.Unmarshal([]byte(data), &entry)
|
||||
err := yaml.Unmarshal([]byte(data), &entry)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
56
pkg/hsm/context.go
Normal file
56
pkg/hsm/context.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
package hsm
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/ThalesIgnite/crypto11"
|
||||
|
||||
"git.cacert.org/cacert-gosigner/pkg/config"
|
||||
)
|
||||
|
||||
type ctxKey int
|
||||
|
||||
const (
|
||||
ctxP11Contexts ctxKey = iota
|
||||
ctxSetupMode
|
||||
ctxSignerConfig
|
||||
)
|
||||
|
||||
// SetupContext sets global context for HSM operations.
|
||||
func SetupContext(
|
||||
signerConfig *config.SignerConfig,
|
||||
setupMode bool,
|
||||
) context.Context {
|
||||
ctx := context.Background()
|
||||
|
||||
ctx = context.WithValue(ctx, ctxP11Contexts, make(map[string]*crypto11.Context))
|
||||
ctx = context.WithValue(ctx, ctxSignerConfig, signerConfig)
|
||||
ctx = context.WithValue(ctx, ctxSetupMode, setupMode)
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
func GetSignerConfig(ctx context.Context) *config.SignerConfig {
|
||||
return ctx.Value(ctxSignerConfig).(*config.SignerConfig)
|
||||
}
|
||||
|
||||
func IsSetupMode(ctx context.Context) bool {
|
||||
return ctx.Value(ctxSetupMode).(bool)
|
||||
}
|
||||
|
||||
func GetP11Context(ctx context.Context, entry *config.CaCertificateEntry) (*crypto11.Context, error) {
|
||||
contexts := ctx.Value(ctxP11Contexts).(map[string]*crypto11.Context)
|
||||
|
||||
if p11Context, ok := contexts[entry.Storage]; ok {
|
||||
return p11Context, nil
|
||||
}
|
||||
|
||||
p11Context, err := prepareCrypto11Context(ctx, entry.Storage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contexts[entry.Storage] = p11Context
|
||||
|
||||
return p11Context, nil
|
||||
}
|
177
pkg/hsm/hsm.go
177
pkg/hsm/hsm.go
|
@ -1,6 +1,7 @@
|
|||
package hsm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
|
@ -14,39 +15,67 @@ import (
|
|||
"math/big"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/ThalesIgnite/crypto11"
|
||||
|
||||
"git.cacert.org/cacert-gosigner/pkg/config"
|
||||
"github.com/ThalesIgnite/crypto11"
|
||||
)
|
||||
|
||||
var (
|
||||
// 1.3.6.1.4.1.18506.2.3.1 Class3 Policy Version 1
|
||||
oidCAcertClass3PolicyV1 = []int{1, 3, 6, 1, 4, 1, 18506, 2, 3, 1}
|
||||
|
||||
errKeyGenerationRefused = errors.New("not in setup mode, refusing key generation")
|
||||
errCertificateGenerationRefused = errors.New("not in setup mode, refusing certificate generation")
|
||||
)
|
||||
|
||||
func GetRootCACertificate(p11Context *crypto11.Context, settings *config.Settings, caCert *config.CaCertificateEntry) (*x509.Certificate, crypto.Signer, error) {
|
||||
keyPair, err := getKeyPair(p11Context, caCert.Label, caCert.KeyInfo)
|
||||
func GetRootCACertificate(ctx context.Context, label string) (*x509.Certificate, error) {
|
||||
var (
|
||||
certificate *x509.Certificate
|
||||
keyPair crypto.Signer
|
||||
)
|
||||
|
||||
sc := GetSignerConfig(ctx)
|
||||
|
||||
caCert, err := sc.GetCADefinition(label)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
certFile := caCert.CertificateFileName()
|
||||
if !caCert.IsRoot() {
|
||||
return nil, fmt.Errorf("CA definition %s is not a root CA definition", label)
|
||||
}
|
||||
|
||||
certificate, err := loadCertificate(certFile)
|
||||
certFile := sc.CertificateFileName(label)
|
||||
|
||||
certificate, err = loadCertificate(certFile)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if certificate != nil && !IsSetupMode(ctx) {
|
||||
caCert.Certificate = certificate
|
||||
|
||||
return certificate, nil
|
||||
}
|
||||
|
||||
keyPair, err = getKeyPair(ctx, label, caCert.KeyInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if certificate != nil && certificateMatches(certificate, keyPair) {
|
||||
return certificate, keyPair, nil
|
||||
caCert.Certificate, caCert.KeyPair = certificate, keyPair
|
||||
|
||||
return certificate, nil
|
||||
}
|
||||
|
||||
notBefore := time.Now()
|
||||
notAfter := notBefore.AddDate(settings.RootYears, 0, 0)
|
||||
if !IsSetupMode(ctx) {
|
||||
return nil, errCertificateGenerationRefused
|
||||
}
|
||||
|
||||
subject := settings.Organization
|
||||
subject.CommonName = caCert.CommonName
|
||||
notBefore, notAfter := sc.CalculateValidity(caCert)
|
||||
subject := sc.CalculateSubject(caCert)
|
||||
|
||||
certificate, err = generateRootCACertificate(
|
||||
certFile,
|
||||
|
@ -64,43 +93,72 @@ func GetRootCACertificate(p11Context *crypto11.Context, settings *config.Setting
|
|||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = addCertificate(p11Context, caCert.Label, certificate)
|
||||
p11Context, err := GetP11Context(ctx, caCert)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return certificate, keyPair, nil
|
||||
err = addCertificate(p11Context, label, certificate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
caCert.Certificate, caCert.KeyPair = certificate, keyPair
|
||||
|
||||
return certificate, nil
|
||||
}
|
||||
|
||||
func GetIntermediaryCACertificate(
|
||||
p11Context *crypto11.Context,
|
||||
settings *config.Settings,
|
||||
caCert *config.CaCertificateEntry,
|
||||
) (*x509.Certificate, crypto.Signer, error) {
|
||||
keyPair, err := getKeyPair(p11Context, caCert.Label, caCert.KeyInfo)
|
||||
func GetIntermediaryCACertificate(ctx context.Context, certLabel string) (*x509.Certificate, error) {
|
||||
var (
|
||||
certificate *x509.Certificate
|
||||
keyPair crypto.Signer
|
||||
)
|
||||
|
||||
sc := GetSignerConfig(ctx)
|
||||
|
||||
caCert, err := sc.GetCADefinition(certLabel)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
certFile := caCert.CertificateFileName()
|
||||
if caCert.IsRoot() {
|
||||
return nil, fmt.Errorf(
|
||||
"CA definition %s is a root CA definition, intermediary expected",
|
||||
certLabel,
|
||||
)
|
||||
}
|
||||
|
||||
certificate, err := loadCertificate(certFile)
|
||||
keyPair, err = getKeyPair(ctx, certLabel, caCert.KeyInfo)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
certFile := sc.CertificateFileName(certLabel)
|
||||
|
||||
certificate, err = loadCertificate(certFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if certificate != nil && certificateMatches(certificate, keyPair) {
|
||||
return certificate, keyPair, nil
|
||||
caCert.Certificate, caCert.KeyPair = certificate, keyPair
|
||||
|
||||
return certificate, nil
|
||||
}
|
||||
|
||||
notBefore, notAfter := settings.CalculateValidity(caCert)
|
||||
subject := settings.CalculateSubject(caCert)
|
||||
if !IsSetupMode(ctx) {
|
||||
return nil, errCertificateGenerationRefused
|
||||
}
|
||||
|
||||
notBefore, notAfter := sc.CalculateValidity(caCert)
|
||||
subject := sc.CalculateSubject(caCert)
|
||||
|
||||
certificate, err = generateIntermediaryCACertificate(
|
||||
caCert,
|
||||
sc,
|
||||
certLabel,
|
||||
keyPair.Public(),
|
||||
&x509.Certificate{
|
||||
Subject: subject,
|
||||
|
@ -112,9 +170,9 @@ func GetIntermediaryCACertificate(
|
|||
IsCA: true,
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
||||
ExtKeyUsage: caCert.ExtKeyUsage,
|
||||
IssuingCertificateURL: []string{settings.BuildIssuerURL(caCert.Parent)},
|
||||
OCSPServer: []string{settings.BuildOCSPURL(caCert.Parent)},
|
||||
CRLDistributionPoints: []string{settings.BuildCRLUrl(caCert.Parent)},
|
||||
IssuingCertificateURL: []string{sc.BuildIssuerURL(caCert)},
|
||||
OCSPServer: []string{sc.BuildOCSPURL(caCert)},
|
||||
CRLDistributionPoints: []string{sc.BuildCRLUrl(caCert)},
|
||||
PolicyIdentifiers: []asn1.ObjectIdentifier{
|
||||
// use policy identifiers from http://wiki.cacert.org/OidAllocation
|
||||
oidCAcertClass3PolicyV1,
|
||||
|
@ -123,25 +181,37 @@ func GetIntermediaryCACertificate(
|
|||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = addCertificate(p11Context, caCert.Label, certificate)
|
||||
p11Context, err := GetP11Context(ctx, caCert)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return certificate, keyPair, nil
|
||||
err = addCertificate(p11Context, certLabel, certificate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
caCert.Certificate, caCert.KeyPair = certificate, keyPair
|
||||
|
||||
return certificate, nil
|
||||
}
|
||||
|
||||
func generateIntermediaryCACertificate(caCert *config.CaCertificateEntry, publicKey crypto.PublicKey, template *x509.Certificate) (*x509.Certificate, error) {
|
||||
func generateIntermediaryCACertificate(config *config.SignerConfig, certLabel string, publicKey crypto.PublicKey, template *x509.Certificate) (*x509.Certificate, error) {
|
||||
parent, err := config.GetParentCA(certLabel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serial, err := randomSerialNumber()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
template.SerialNumber = serial
|
||||
template.SignatureAlgorithm, err = determineSignatureAlgorithm(caCert.Parent.KeyPair)
|
||||
template.SignatureAlgorithm, err = determineSignatureAlgorithm(parent.KeyPair)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -150,9 +220,9 @@ func generateIntermediaryCACertificate(caCert *config.CaCertificateEntry, public
|
|||
certBytes, err := x509.CreateCertificate(
|
||||
rand.Reader,
|
||||
template,
|
||||
caCert.Parent.Certificate,
|
||||
parent.Certificate,
|
||||
publicKey,
|
||||
caCert.Parent.KeyPair,
|
||||
parent.KeyPair,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create intermediary CA certificate: %w", err)
|
||||
|
@ -163,7 +233,7 @@ func generateIntermediaryCACertificate(caCert *config.CaCertificateEntry, public
|
|||
Bytes: certBytes,
|
||||
}
|
||||
|
||||
certFile := caCert.CertificateFileName()
|
||||
certFile := config.CertificateFileName(certLabel)
|
||||
|
||||
err = os.WriteFile(certFile, pem.EncodeToMemory(certBlock), 0o600)
|
||||
if err != nil {
|
||||
|
@ -192,7 +262,22 @@ func addCertificate(p11Context *crypto11.Context, label string, certificate *x50
|
|||
return nil
|
||||
}
|
||||
|
||||
func getKeyPair(p11Context *crypto11.Context, label string, keyInfo *config.PrivateKeyInfo) (crypto.Signer, error) {
|
||||
func getKeyPair(ctx context.Context, label string, keyInfo *config.PrivateKeyInfo) (crypto.Signer, error) {
|
||||
sc := GetSignerConfig(ctx)
|
||||
cert, err := sc.GetCADefinition(label)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if cert.KeyPair != nil {
|
||||
return cert.KeyPair, nil
|
||||
}
|
||||
|
||||
p11Context, err := GetP11Context(ctx, cert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keyPair, err := p11Context.FindKeyPair(nil, []byte(label))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not find requested key pair: %w", err)
|
||||
|
@ -202,6 +287,10 @@ func getKeyPair(p11Context *crypto11.Context, label string, keyInfo *config.Priv
|
|||
return keyPair, nil
|
||||
}
|
||||
|
||||
if !IsSetupMode(ctx) {
|
||||
return nil, errKeyGenerationRefused
|
||||
}
|
||||
|
||||
switch keyInfo.Algorithm {
|
||||
case x509.RSA:
|
||||
keyPair, err = generateRSAKeyPair(p11Context, label, keyInfo)
|
||||
|
|
|
@ -1,61 +1,41 @@
|
|||
package hsm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"git.cacert.org/cacert-gosigner/pkg/config"
|
||||
"github.com/ThalesIgnite/crypto11"
|
||||
)
|
||||
|
||||
func EnsureCAKeysAndCertificates(p11Context *crypto11.Context, conf *config.SignerConfig) error {
|
||||
var err error
|
||||
func EnsureCAKeysAndCertificates(ctx context.Context) error {
|
||||
var label string
|
||||
|
||||
for _, root := range conf.CAs {
|
||||
root.Certificate, root.KeyPair, err = GetRootCACertificate(p11Context, conf.Global, root)
|
||||
conf := GetSignerConfig(ctx)
|
||||
|
||||
for _, label := range conf.RootCAs() {
|
||||
crt, err := GetRootCACertificate(ctx, label)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("got root CA certificate:\n Subject %s\n Issuer %s\n Valid from %s until %s\n Serial %s",
|
||||
root.Certificate.Subject,
|
||||
root.Certificate.Issuer,
|
||||
root.Certificate.NotBefore,
|
||||
root.Certificate.NotAfter,
|
||||
root.Certificate.SerialNumber)
|
||||
|
||||
for _, intermediary := range root.SubCAs {
|
||||
err := setupIntermediaries(p11Context, conf.Global, intermediary, root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
crt.Subject,
|
||||
crt.Issuer,
|
||||
crt.NotBefore,
|
||||
crt.NotAfter,
|
||||
crt.SerialNumber)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupIntermediaries(p11Context *crypto11.Context, settings *config.Settings, intermediary, parent *config.CaCertificateEntry) error {
|
||||
var err error
|
||||
|
||||
intermediary.Parent = parent
|
||||
|
||||
intermediary.Certificate, intermediary.KeyPair, err = GetIntermediaryCACertificate(p11Context, settings, intermediary)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("got intermediary CA certificate:\n Subject %s\n Issuer %s\n Valid from %s until %s\n Serial %s",
|
||||
intermediary.Certificate.Subject,
|
||||
intermediary.Certificate.Issuer,
|
||||
intermediary.Certificate.NotBefore,
|
||||
intermediary.Certificate.NotAfter,
|
||||
intermediary.Certificate.SerialNumber)
|
||||
|
||||
for _, sub := range intermediary.SubCAs {
|
||||
err := setupIntermediaries(p11Context, settings, sub, intermediary)
|
||||
for _, label = range conf.IntermediaryCAs() {
|
||||
crt, err := GetIntermediaryCACertificate(ctx, label)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("got intermediary CA certificate:\n Subject %s\n Issuer %s\n Valid from %s until %s\n Serial %s",
|
||||
crt.Subject,
|
||||
crt.Issuer,
|
||||
crt.NotBefore,
|
||||
crt.NotAfter,
|
||||
crt.SerialNumber)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
71
pkg/hsm/storage.go
Normal file
71
pkg/hsm/storage.go
Normal file
|
@ -0,0 +1,71 @@
|
|||
package hsm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/ThalesIgnite/crypto11"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
func prepareCrypto11Context(ctx context.Context, label string) (*crypto11.Context, error) {
|
||||
storage, ok := GetSignerConfig(ctx).KeyStorage[label]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("could not find storage definition with label %s", label)
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
p11Context *crypto11.Context
|
||||
)
|
||||
|
||||
p11Config := &crypto11.Config{
|
||||
Path: storage.Module,
|
||||
TokenLabel: storage.Label,
|
||||
}
|
||||
|
||||
log.Printf("using PKCS#11 module %s", p11Config.Path)
|
||||
log.Printf("looking for token with label %s", p11Config.TokenLabel)
|
||||
|
||||
p11Config.Pin, err = getPin(p11Config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p11Context, err = crypto11.Configure(p11Config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not configure PKCS#11 library: %v", err)
|
||||
}
|
||||
|
||||
return p11Context, nil
|
||||
}
|
||||
|
||||
func getPin(p11Config *crypto11.Config) (string, error) {
|
||||
tokenPinEnv := fmt.Sprintf("TOKEN_PIN_%s", strings.ToUpper(p11Config.TokenLabel))
|
||||
pin, found := os.LookupEnv(tokenPinEnv)
|
||||
if !found {
|
||||
log.Printf("environment variable %s has not been set", tokenPinEnv)
|
||||
|
||||
if !term.IsTerminal(syscall.Stdin) {
|
||||
return "", errors.New("stdin is not a terminal")
|
||||
}
|
||||
|
||||
fmt.Printf("Enter PIN for token %s: ", p11Config.TokenLabel)
|
||||
|
||||
bytePin, err := term.ReadPassword(syscall.Stdin)
|
||||
if err != nil {
|
||||
return "", errors.New("could not read PIN")
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
|
||||
pin = string(bytePin)
|
||||
}
|
||||
|
||||
return strings.TrimSpace(pin), nil
|
||||
}
|
Loading…
Reference in a new issue