Skip to content

Commit

Permalink
Merge pull request #150 from jessepeterson/depot-signer-struct
Browse files Browse the repository at this point in the history
Create Depot signer struct with Options
  • Loading branch information
jessepeterson authored Mar 10, 2021
2 parents 65e41ff + 7a3f3b8 commit 31fff25
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 64 deletions.
7 changes: 6 additions & 1 deletion cmd/scepserver/scepserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,12 @@ func main() {
lginfo.Log("err", "missing CA certificate")
os.Exit(1)
}
signer := scepdepot.CSRSigner(depot, allowRenewal, clientValidity, *flCAPass)
var signer scepserver.CSRSigner = scepdepot.NewSigner(
depot,
scepdepot.WithAllowRenewalDays(allowRenewal),
scepdepot.WithValidityDays(clientValidity),
scepdepot.WithCAPass(*flCAPass),
)
if *flChallengePassword != "" {
signer = scepserver.ChallengeMiddleware(*flChallengePassword, signer)
}
Expand Down
166 changes: 104 additions & 62 deletions depot/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,72 +6,114 @@ import (
"time"

"github.com/micromdm/scep/cryptoutil"
scepserver "github.com/micromdm/scep/server"

"github.com/micromdm/scep/scep"
)

// CSRSigner returns a CSRSignerFunc for use in new scepserver service
func CSRSigner(depot Depot, allowRenewal, clientValidity int, caPass string) scepserver.CSRSignerFunc {
return func(m *scep.CSRReqMessage) (*x509.Certificate, error) {
csr := m.CSR
id, err := cryptoutil.GenerateSubjectKeyID(csr.PublicKey)
if err != nil {
return nil, err
}

serial, err := depot.Serial()
if err != nil {
return nil, err
}

// create cert template
tmpl := &x509.Certificate{
SerialNumber: serial,
Subject: csr.Subject,
NotBefore: time.Now().Add(-600).UTC(),
NotAfter: time.Now().AddDate(0, 0, clientValidity).UTC(),
SubjectKeyId: id,
KeyUsage: x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{
x509.ExtKeyUsageClientAuth,
},
SignatureAlgorithm: csr.SignatureAlgorithm,
DNSNames: csr.DNSNames,
EmailAddresses: csr.EmailAddresses,
IPAddresses: csr.IPAddresses,
URIs: csr.URIs,
}

crts, key, err := depot.CA([]byte(caPass))
ca := crts[0]
// sign the CSR creating a DER encoded cert
crtBytes, err := x509.CreateCertificate(rand.Reader, tmpl, ca, m.CSR.PublicKey, key)
if err != nil {
return nil, err
}
// parse the certificate
crt, err := x509.ParseCertificate(crtBytes)
if err != nil {
return nil, err
}

name := certName(crt)

// Test if this certificate is already in the CADB, revoke if needed
// revocation is done if the validity of the existing certificate is
// less than allowRenewal (14 days by default)
_, err = depot.HasCN(name, allowRenewal, crt, false)
if err != nil {
return nil, err
}

if err := depot.Put(name, crt); err != nil {
return nil, err
}

return crt, nil
// Signer signs x509 certificates and stores them in a Depot
type Signer struct {
depot Depot
caPass string
allowRenewalDays int
validityDays int
}

// Option customizes Signer
type Option func(*Signer)

// NewSigner creates a new Signer
func NewSigner(depot Depot, opts ...Option) *Signer {
s := &Signer{
depot: depot,
allowRenewalDays: 14,
validityDays: 365,
}
for _, opt := range opts {
opt(s)
}
return s
}

// WithCAPass specifies the password to use with an encrypted CA key
func WithCAPass(pass string) Option {
return func(s *Signer) {
s.caPass = pass
}
}

// WithAllowRenewalDays sets the allowable renewal time for existing certs
func WithAllowRenewalDays(r int) Option {
return func(s *Signer) {
s.allowRenewalDays = r
}
}

// WithValidityDays sets the validity period new certs will use
func WithValidityDays(v int) Option {
return func(s *Signer) {
s.validityDays = v
}
}

// SignCSR signs a certificate using Signer's Depot CA
func (s *Signer) SignCSR(m *scep.CSRReqMessage) (*x509.Certificate, error) {
id, err := cryptoutil.GenerateSubjectKeyID(m.CSR.PublicKey)
if err != nil {
return nil, err
}

serial, err := s.depot.Serial()
if err != nil {
return nil, err
}

// create cert template
tmpl := &x509.Certificate{
SerialNumber: serial,
Subject: m.CSR.Subject,
NotBefore: time.Now().Add(-600).UTC(),
NotAfter: time.Now().AddDate(0, 0, s.validityDays).UTC(),
SubjectKeyId: id,
KeyUsage: x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{
x509.ExtKeyUsageClientAuth,
},
SignatureAlgorithm: m.CSR.SignatureAlgorithm,
DNSNames: m.CSR.DNSNames,
EmailAddresses: m.CSR.EmailAddresses,
IPAddresses: m.CSR.IPAddresses,
URIs: m.CSR.URIs,
}

caCerts, caKey, err := s.depot.CA([]byte(s.caPass))
if err != nil {
return nil, err
}

crtBytes, err := x509.CreateCertificate(rand.Reader, tmpl, caCerts[0], m.CSR.PublicKey, caKey)
if err != nil {
return nil, err
}

crt, err := x509.ParseCertificate(crtBytes)
if err != nil {
return nil, err
}

name := certName(crt)

// Test if this certificate is already in the CADB, revoke if needed
// revocation is done if the validity of the existing certificate is
// less than allowRenewalDays
_, err = s.depot.HasCN(name, s.allowRenewalDays, crt, false)
if err != nil {
return nil, err
}

if err := s.depot.Put(name, crt); err != nil {
return nil, err
}

return crt, nil
}

func certName(crt *x509.Certificate) string {
Expand Down
2 changes: 1 addition & 1 deletion server/service_bolt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func TestCaCert(t *testing.T) {
caCert := certs[0]

// SCEP service
svc, err := scepserver.NewService(caCert, key, scepdepot.CSRSigner(depot, 14, 365, ""))
svc, err := scepserver.NewService(caCert, key, scepdepot.NewSigner(depot))
if err != nil {
t.Fatal(err)
}
Expand Down

0 comments on commit 31fff25

Please sign in to comment.