Add AuthorizeSign method to SCEP authority

This commit is contained in:
Herman Slatman 2021-02-26 14:00:47 +01:00 committed by max furman
parent 812e1c7218
commit da65f46d0f
3 changed files with 42 additions and 27 deletions

View file

@ -1,6 +1,7 @@
package provisioner package provisioner
import ( import (
"context"
"time" "time"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -13,7 +14,7 @@ type SCEP struct {
Type string `json:"type"` Type string `json:"type"`
Name string `json:"name"` Name string `json:"name"`
// ForceCN bool `json:"forceCN,omitempty"` ForceCN bool `json:"forceCN,omitempty"`
Options *Options `json:"options,omitempty"` Options *Options `json:"options,omitempty"`
Claims *Claims `json:"claims,omitempty"` Claims *Claims `json:"claims,omitempty"`
claimer *Claimer claimer *Claimer
@ -75,6 +76,21 @@ func (s *SCEP) Init(config Config) (err error) {
return err return err
} }
// AuthorizeSign does not do any validation, because all validation is handled
// in the SCEP protocol. This method returns a list of modifiers / constraints
// on the resulting certificate.
func (s *SCEP) AuthorizeSign(ctx context.Context, token string) ([]SignOption, error) {
return []SignOption{
// modifiers / withOptions
newProvisionerExtensionOption(TypeSCEP, s.Name, ""),
newForceCNOption(s.ForceCN),
profileDefaultDuration(s.claimer.DefaultTLSCertDuration()),
// validators
defaultPublicKeyValidator{},
newValidityValidator(s.claimer.MinTLSCertDuration(), s.claimer.MaxTLSCertDuration()),
}, nil
}
// Interface guards // Interface guards
var ( var (
_ Interface = (*SCEP)(nil) _ Interface = (*SCEP)(nil)

View file

@ -6,8 +6,6 @@ import (
"crypto/x509" "crypto/x509"
"errors" "errors"
"fmt" "fmt"
"math/big"
"math/rand"
"github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/authority/provisioner"
database "github.com/smallstep/certificates/db" database "github.com/smallstep/certificates/db"
@ -53,8 +51,6 @@ type Interface interface {
// GetLinkExplicit(linkType Link, provName string, absoluteLink bool, baseURL *url.URL, inputs ...string) string // GetLinkExplicit(linkType Link, provName string, absoluteLink bool, baseURL *url.URL, inputs ...string) string
GetCACertificates() ([]*x509.Certificate, error) GetCACertificates() ([]*x509.Certificate, error)
//GetSigningKey() (*rsa.PrivateKey, error)
DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) error DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) error
SignCSR(ctx context.Context, msg *PKIMessage, template *x509.Certificate) (*PKIMessage, error) SignCSR(ctx context.Context, msg *PKIMessage, template *x509.Certificate) (*PKIMessage, error)
} }
@ -201,12 +197,12 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err
case microscep.PKCSReq, microscep.UpdateReq, microscep.RenewalReq: case microscep.PKCSReq, microscep.UpdateReq, microscep.RenewalReq:
csr, err := x509.ParseCertificateRequest(msg.pkiEnvelope) csr, err := x509.ParseCertificateRequest(msg.pkiEnvelope)
if err != nil { if err != nil {
return fmt.Errorf("parse CSR from pkiEnvelope") return fmt.Errorf("parse CSR from pkiEnvelope: %w", err)
} }
// check for challengePassword // check for challengePassword
cp, err := microx509util.ParseChallengePassword(msg.pkiEnvelope) cp, err := microx509util.ParseChallengePassword(msg.pkiEnvelope)
if err != nil { if err != nil {
return fmt.Errorf("scep: parse challenge password in pkiEnvelope") return fmt.Errorf("scep: parse challenge password in pkiEnvelope: %w", err)
} }
msg.CSRReqMessage = &microscep.CSRReqMessage{ msg.CSRReqMessage = &microscep.CSRReqMessage{
RawDecrypted: msg.pkiEnvelope, RawDecrypted: msg.pkiEnvelope,
@ -227,6 +223,11 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err
//func (msg *PKIMessage) SignCSR(crtAuth *x509.Certificate, keyAuth *rsa.PrivateKey, template *x509.Certificate) (*PKIMessage, error) { //func (msg *PKIMessage) SignCSR(crtAuth *x509.Certificate, keyAuth *rsa.PrivateKey, template *x509.Certificate) (*PKIMessage, error) {
func (a *Authority) SignCSR(ctx context.Context, msg *PKIMessage, template *x509.Certificate) (*PKIMessage, error) { func (a *Authority) SignCSR(ctx context.Context, msg *PKIMessage, template *x509.Certificate) (*PKIMessage, error) {
// TODO: intermediate storage of the request? In SCEP it's possible to request a csr/certificate
// to be signed, which can be performed asynchronously / out-of-band. In that case a client can
// poll for the status. It seems to be similar as what can happen in ACME, so might want to model
// the implementation after the one in the ACME authority. Requires storage, etc.
p, err := ProvisionerFromContext(ctx) p, err := ProvisionerFromContext(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
@ -245,32 +246,32 @@ func (a *Authority) SignCSR(ctx context.Context, msg *PKIMessage, template *x509
data := x509util.NewTemplateData() data := x509util.NewTemplateData()
data.SetCommonName(csr.Subject.CommonName) data.SetCommonName(csr.Subject.CommonName)
data.SetSANs(csr.DNSNames) data.SetSANs(csr.DNSNames)
data.SetCertificateRequest(csr)
// TODO: proper options // Get authorizations from the SCEP provisioner.
opts := provisioner.SignOptions{} ctx = provisioner.NewContextWithMethod(ctx, provisioner.SignMethod)
signOps := []provisioner.SignOption{} signOps, err := p.AuthorizeSign(ctx, "")
if err != nil {
return nil, fmt.Errorf("error retrieving authorization options from SCEP provisioner: %w", err)
}
opts := provisioner.SignOptions{
// NotBefore: provisioner.NewTimeDuration(o.NotBefore),
// NotAfter: provisioner.NewTimeDuration(o.NotAfter),
}
templateOptions, err := provisioner.TemplateOptions(p.GetOptions(), data) templateOptions, err := provisioner.TemplateOptions(p.GetOptions(), data)
if err != nil { if err != nil {
return nil, fmt.Errorf("error creating template options from SCEP provisioner") return nil, fmt.Errorf("error creating template options from SCEP provisioner: %w", err)
} }
signOps = append(signOps, templateOptions) signOps = append(signOps, templateOptions)
// // Create and store a new certificate. certChain, err := a.signAuth.Sign(csr, opts, signOps...)
// certChain, err := auth.Sign(csr, provisioner.SignOptions{
// NotBefore: provisioner.NewTimeDuration(o.NotBefore),
// NotAfter: provisioner.NewTimeDuration(o.NotAfter),
// }, signOps...)
// if err != nil {
// return nil, ServerInternalErr(errors.Wrapf(err, "error generating certificate for order %s", o.ID))
// }
certs, err := a.signAuth.Sign(csr, opts, signOps...)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("error generating certificate for order %w", err)
} }
cert := certs[0] cert := certChain[0]
// fmt.Println("CERT") // fmt.Println("CERT")
// fmt.Println(cert) // fmt.Println(cert)
@ -278,9 +279,6 @@ func (a *Authority) SignCSR(ctx context.Context, msg *PKIMessage, template *x509
// fmt.Println(cert.Issuer) // fmt.Println(cert.Issuer)
// fmt.Println(cert.Subject) // fmt.Println(cert.Subject)
serial := big.NewInt(int64(rand.Int63())) // TODO: serial logic?
cert.SerialNumber = serial
// create a degenerate cert structure // create a degenerate cert structure
deg, err := DegenerateCertificates([]*x509.Certificate{cert}) deg, err := DegenerateCertificates([]*x509.Certificate{cert})
if err != nil { if err != nil {

View file

@ -1,6 +1,7 @@
package scep package scep
import ( import (
"context"
"time" "time"
"github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/authority/provisioner"
@ -9,7 +10,7 @@ import (
// Provisioner is an interface that implements a subset of the provisioner.Interface -- // Provisioner is an interface that implements a subset of the provisioner.Interface --
// only those methods required by the SCEP api/authority. // only those methods required by the SCEP api/authority.
type Provisioner interface { type Provisioner interface {
// AuthorizeSign(ctx context.Context, token string) ([]provisioner.SignOption, error) AuthorizeSign(ctx context.Context, token string) ([]provisioner.SignOption, error)
GetName() string GetName() string
DefaultTLSCertDuration() time.Duration DefaultTLSCertDuration() time.Duration
GetOptions() *provisioner.Options GetOptions() *provisioner.Options