Store new certificates in database

This commit is contained in:
Herman Slatman 2021-03-10 22:20:02 +01:00 committed by max furman
parent f0050e5ca9
commit aa2ce0a2a5
3 changed files with 113 additions and 71 deletions

View file

@ -299,17 +299,13 @@ func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPRe
}
}
// TODO: check if CN already exists, if renewal is allowed and if existing should be revoked; fail if not
certRep, err := h.Auth.SignCSR(ctx, csr, msg)
if err != nil {
return h.createFailureResponse(ctx, csr, msg, microscep.BadRequest, "error when signing new certificate")
}
// //cert := certRep.CertRepMessage.Certificate
// //name := certName(cert)
// // TODO: check if CN already exists, if renewal is allowed and if existing should be revoked; fail if not
// // TODO: store the new cert for CN locally; should go into the DB
response := SCEPResponse{
Operation: opnPKIOperation,
Data: certRep.Raw,

View file

@ -27,31 +27,14 @@ import (
"go.step.sm/crypto/x509util"
)
var (
certTable = []byte("scep_certs")
)
// Interface is the SCEP authority interface.
type Interface interface {
// GetDirectory(ctx context.Context) (*Directory, error)
// NewNonce() (string, error)
// UseNonce(string) error
// DeactivateAccount(ctx context.Context, accID string) (*Account, error)
// GetAccount(ctx context.Context, accID string) (*Account, error)
// GetAccountByKey(ctx context.Context, key *jose.JSONWebKey) (*Account, error)
// NewAccount(ctx context.Context, ao AccountOptions) (*Account, error)
// UpdateAccount(context.Context, string, []string) (*Account, error)
// GetAuthz(ctx context.Context, accID string, authzID string) (*Authz, error)
// ValidateChallenge(ctx context.Context, accID string, chID string, key *jose.JSONWebKey) (*Challenge, error)
// FinalizeOrder(ctx context.Context, accID string, orderID string, csr *x509.CertificateRequest) (*Order, error)
// GetOrder(ctx context.Context, accID string, orderID string) (*Order, error)
// GetOrdersByAccount(ctx context.Context, accID string) ([]string, error)
// NewOrder(ctx context.Context, oo OrderOptions) (*Order, error)
// GetCertificate(string, string) ([]byte, error)
LoadProvisionerByID(string) (provisioner.Interface, error)
// GetLink(ctx context.Context, linkType Link, absoluteLink bool, inputs ...string) string
// GetLinkExplicit(linkType Link, provName string, absoluteLink bool, baseURL *url.URL, inputs ...string) string
GetLinkExplicit(provName string, absoluteLink bool, baseURL *url.URL, inputs ...string) string
GetCACertificates() ([]*x509.Certificate, error)
DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) error
@ -59,8 +42,6 @@ type Interface interface {
CreateFailureResponse(ctx context.Context, csr *x509.CertificateRequest, msg *PKIMessage, info FailInfoName, infoText string) (*PKIMessage, error)
MatchChallengePassword(ctx context.Context, password string) (bool, error)
GetCACaps(ctx context.Context) []string
GetLinkExplicit(provName string, absoluteLink bool, baseURL *url.URL, inputs ...string) string
}
// Authority is the layer that handles all SCEP interactions.
@ -107,7 +88,14 @@ type SignAuthority interface {
func New(signAuth SignAuthority, ops AuthorityOptions) (*Authority, error) {
if _, ok := ops.DB.(*database.SimpleDB); !ok {
// TODO: see ACME implementation
// If it's not a SimpleDB then go ahead and bootstrap the DB with the
// necessary SCEP tables. SimpleDB should ONLY be used for testing.
tables := [][]byte{certTable}
for _, b := range tables {
if err := ops.DB.CreateTable(b); err != nil {
return nil, fmt.Errorf("%w: error creating table %s", err, string(b))
}
}
}
// TODO: the below is a bit similar as what happens in the core Authority class, which
@ -284,30 +272,6 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m
csr = msg.CSRReqMessage.CSR
}
// subjectKeyID, err := createKeyIdentifier(csr.PublicKey)
// if err != nil {
// return nil, err
// }
// serial := big.NewInt(int64(rand.Int63())) // TODO: serial logic?
// days := 40 // TODO: days
// // TODO: use information from provisioner, like claims
// template := &x509.Certificate{
// SerialNumber: serial,
// Subject: csr.Subject,
// NotBefore: time.Now().Add(-600).UTC(),
// NotAfter: time.Now().AddDate(0, 0, days).UTC(),
// SubjectKeyId: subjectKeyID,
// KeyUsage: x509.KeyUsageDigitalSignature,
// ExtKeyUsage: []x509.ExtKeyUsage{
// x509.ExtKeyUsageClientAuth,
// },
// SignatureAlgorithm: csr.SignatureAlgorithm,
// EmailAddresses: csr.EmailAddresses,
// DNSNames: csr.DNSNames,
// }
// Template data
data := x509util.NewTemplateData()
data.SetCommonName(csr.Subject.CommonName)
@ -339,14 +303,6 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m
cert := certChain[0]
// fmt.Println("CERT")
// fmt.Println(cert)
// fmt.Println(fmt.Sprintf("%T", cert))
// fmt.Println(cert.Issuer)
// fmt.Println(cert.Subject)
// fmt.Println(cert.SerialNumber)
// fmt.Println(string(cert.SubjectKeyId))
// create a degenerate cert structure
deg, err := degenerateCertificates([]*x509.Certificate{cert})
if err != nil {
@ -377,7 +333,7 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m
Type: oidSCEPrecipientNonce,
Value: msg.SenderNonce,
},
pkcs7.Attribute{
{
Type: oidSCEPsenderNonce,
Value: msg.SenderNonce,
},
@ -421,6 +377,16 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m
CertRepMessage: cr,
}
// TODO: save more data?
_, err = newCert(a.db, CertOptions{
Leaf: certChain[0],
Intermediates: certChain[1:],
})
if err != nil {
fmt.Println(err)
return nil, err
}
return crepMsg, nil
}
@ -429,31 +395,31 @@ func (a *Authority) CreateFailureResponse(ctx context.Context, csr *x509.Certifi
config := pkcs7.SignerInfoConfig{
ExtraSignedAttributes: []pkcs7.Attribute{
pkcs7.Attribute{
{
Type: oidSCEPtransactionID,
Value: msg.TransactionID,
},
pkcs7.Attribute{
{
Type: oidSCEPpkiStatus,
Value: microscep.FAILURE,
},
pkcs7.Attribute{
{
Type: oidSCEPfailInfo,
Value: info,
},
pkcs7.Attribute{
{
Type: oidSCEPfailInfoText,
Value: infoText,
},
pkcs7.Attribute{
{
Type: oidSCEPmessageType,
Value: microscep.CertRep,
},
pkcs7.Attribute{
{
Type: oidSCEPsenderNonce,
Value: msg.SenderNonce,
},
pkcs7.Attribute{
{
Type: oidSCEPrecipientNonce,
Value: msg.SenderNonce,
},

80
scep/certificate.go Normal file
View file

@ -0,0 +1,80 @@
package scep
import (
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"time"
"github.com/smallstep/nosql"
)
type certificate struct {
ID string `json:"id"`
Created time.Time `json:"created"`
Leaf []byte `json:"leaf"`
Intermediates []byte `json:"intermediates"`
}
// CertOptions options with which to create and store a cert object.
type CertOptions struct {
Leaf *x509.Certificate
Intermediates []*x509.Certificate
}
func newCert(db nosql.DB, ops CertOptions) (*certificate, error) {
// TODO: according to the RFC this should be IssuerAndSerialNumber,
// but sscep seems to use just the serial number for getcert
id := ops.Leaf.SerialNumber.String()
leaf := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: ops.Leaf.Raw,
})
var intermediates []byte
for _, cert := range ops.Intermediates {
intermediates = append(intermediates, pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: cert.Raw,
})...)
}
cert := &certificate{
ID: id,
Leaf: leaf,
Intermediates: intermediates,
Created: time.Now().UTC(),
}
certB, err := json.Marshal(cert)
if err != nil {
return nil, fmt.Errorf("%w: error marshaling certificate", err)
}
_, swapped, err := db.CmpAndSwap(certTable, []byte(id), nil, certB)
switch {
case err != nil:
return nil, fmt.Errorf("%w: error storing certificate", err)
case !swapped:
return nil, fmt.Errorf("error storing certificate; " +
"value has changed since last read")
default:
return cert, nil
}
}
func getCert(db nosql.DB, id string) (*certificate, error) {
b, err := db.Get(certTable, []byte(id))
if nosql.IsErrNotFound(err) {
return nil, fmt.Errorf("certificate %s not found", id)
} else if err != nil {
return nil, fmt.Errorf("error loading certificate")
}
var cert certificate
if err := json.Unmarshal(b, &cert); err != nil {
return nil, fmt.Errorf("%w: error unmarshaling certificate", err)
}
return &cert, nil
}