forked from TrueCloudLab/certificates
Store new certificates in database
This commit is contained in:
parent
f0050e5ca9
commit
aa2ce0a2a5
3 changed files with 113 additions and 71 deletions
|
@ -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,
|
||||
|
|
|
@ -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
80
scep/certificate.go
Normal 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
|
||||
}
|
Loading…
Reference in a new issue