2018-10-05 21:48:36 +00:00
package authority
import (
2019-10-28 18:50:43 +00:00
"context"
2020-07-03 10:28:15 +00:00
"crypto"
2018-10-05 21:48:36 +00:00
"crypto/tls"
"crypto/x509"
2021-10-30 07:52:50 +00:00
"crypto/x509/pkix"
2018-10-19 05:26:39 +00:00
"encoding/asn1"
2019-03-05 08:07:13 +00:00
"encoding/base64"
2022-01-12 09:41:36 +00:00
"encoding/json"
2018-10-05 21:48:36 +00:00
"encoding/pem"
2022-01-12 09:41:36 +00:00
"fmt"
2021-10-30 07:52:50 +00:00
"math/big"
2022-04-06 00:31:40 +00:00
"net"
2018-10-05 21:48:36 +00:00
"net/http"
2022-01-10 14:49:37 +00:00
"strings"
2018-10-05 21:48:36 +00:00
"time"
"github.com/pkg/errors"
2022-04-24 11:11:32 +00:00
"golang.org/x/crypto/ssh"
"go.step.sm/crypto/jose"
"go.step.sm/crypto/keyutil"
"go.step.sm/crypto/pemutil"
"go.step.sm/crypto/x509util"
2021-05-03 19:48:20 +00:00
"github.com/smallstep/certificates/authority/config"
2019-03-07 01:37:49 +00:00
"github.com/smallstep/certificates/authority/provisioner"
2020-09-09 02:26:32 +00:00
casapi "github.com/smallstep/certificates/cas/apiv1"
2019-03-05 08:07:13 +00:00
"github.com/smallstep/certificates/db"
2019-12-20 21:30:05 +00:00
"github.com/smallstep/certificates/errs"
2022-09-30 00:16:26 +00:00
"github.com/smallstep/certificates/webhook"
2022-10-27 18:57:48 +00:00
"github.com/smallstep/nosql/database"
2018-10-05 21:48:36 +00:00
)
2022-11-04 23:42:07 +00:00
type tokenKey struct { }
// NewTokenContext adds the given token to the context.
func NewTokenContext ( ctx context . Context , token string ) context . Context {
return context . WithValue ( ctx , tokenKey { } , token )
}
// TokenFromContext returns the token from the given context.
func TokenFromContext ( ctx context . Context ) ( token string , ok bool ) {
token , ok = ctx . Value ( tokenKey { } ) . ( string )
return
}
2018-10-19 05:26:39 +00:00
// GetTLSOptions returns the tls options configured.
2021-05-03 19:48:20 +00:00
func ( a * Authority ) GetTLSOptions ( ) * config . TLSOptions {
2018-10-19 05:26:39 +00:00
return a . config . TLS
2018-10-05 21:48:36 +00:00
}
2022-10-27 01:55:24 +00:00
var (
oidAuthorityKeyIdentifier = asn1 . ObjectIdentifier { 2 , 5 , 29 , 35 }
oidSubjectKeyIdentifier = asn1 . ObjectIdentifier { 2 , 5 , 29 , 14 }
oidExtensionIssuingDistributionPoint = asn1 . ObjectIdentifier { 2 , 5 , 29 , 28 }
)
2018-10-05 21:48:36 +00:00
2021-05-03 19:48:20 +00:00
func withDefaultASN1DN ( def * config . ASN1DN ) provisioner . CertificateModifierFunc {
2020-07-23 01:24:45 +00:00
return func ( crt * x509 . Certificate , opts provisioner . SignOptions ) error {
2018-10-05 21:48:36 +00:00
if def == nil {
return errors . New ( "default ASN1DN template cannot be nil" )
}
if len ( crt . Subject . Country ) == 0 && def . Country != "" {
crt . Subject . Country = append ( crt . Subject . Country , def . Country )
}
if len ( crt . Subject . Organization ) == 0 && def . Organization != "" {
crt . Subject . Organization = append ( crt . Subject . Organization , def . Organization )
}
if len ( crt . Subject . OrganizationalUnit ) == 0 && def . OrganizationalUnit != "" {
crt . Subject . OrganizationalUnit = append ( crt . Subject . OrganizationalUnit , def . OrganizationalUnit )
}
if len ( crt . Subject . Locality ) == 0 && def . Locality != "" {
crt . Subject . Locality = append ( crt . Subject . Locality , def . Locality )
}
if len ( crt . Subject . Province ) == 0 && def . Province != "" {
crt . Subject . Province = append ( crt . Subject . Province , def . Province )
}
if len ( crt . Subject . StreetAddress ) == 0 && def . StreetAddress != "" {
crt . Subject . StreetAddress = append ( crt . Subject . StreetAddress , def . StreetAddress )
}
2021-10-08 18:59:57 +00:00
if crt . Subject . SerialNumber == "" && def . SerialNumber != "" {
2021-07-28 02:19:58 +00:00
crt . Subject . SerialNumber = def . SerialNumber
}
2021-10-08 18:59:57 +00:00
if crt . Subject . CommonName == "" && def . CommonName != "" {
2021-07-28 02:19:58 +00:00
crt . Subject . CommonName = def . CommonName
}
2018-10-05 21:48:36 +00:00
return nil
}
}
// Sign creates a signed certificate from a certificate signing request.
2020-07-23 01:24:45 +00:00
func ( a * Authority ) Sign ( csr * x509 . CertificateRequest , signOpts provisioner . SignOptions , extraOpts ... provisioner . SignOption ) ( [ ] * x509 . Certificate , error ) {
2018-10-19 05:26:39 +00:00
var (
2020-07-21 00:25:53 +00:00
certOptions [ ] x509util . Option
2020-07-08 02:01:47 +00:00
certValidators [ ] provisioner . CertificateValidator
certModifiers [ ] provisioner . CertificateModifier
certEnforcers [ ] provisioner . CertificateEnforcer
2018-10-19 05:26:39 +00:00
)
2020-01-03 01:48:28 +00:00
2020-07-08 02:01:47 +00:00
opts := [ ] interface { } { errs . WithKeyVal ( "csr" , csr ) , errs . WithKeyVal ( "signOptions" , signOpts ) }
if err := csr . CheckSignature ( ) ; err != nil {
2021-11-19 02:44:58 +00:00
return nil , errs . ApplyOptions (
errs . BadRequestErr ( err , "invalid certificate request" ) ,
opts ... ,
)
2020-07-08 02:01:47 +00:00
}
2020-01-03 01:48:28 +00:00
// Set backdate with the configured value
signOpts . Backdate = a . config . AuthorityConfig . Backdate . Duration
2022-03-22 02:24:05 +00:00
var prov provisioner . Interface
2022-08-03 02:28:49 +00:00
var pInfo * casapi . ProvisionerInfo
2022-09-30 00:16:26 +00:00
var attData * provisioner . AttestationData
var webhookCtl webhookController
2018-10-19 05:26:39 +00:00
for _ , op := range extraOpts {
switch k := op . ( type ) {
2022-03-22 02:24:05 +00:00
// Capture current provisioner
case provisioner . Interface :
prov = k
2022-08-03 02:28:49 +00:00
pInfo = & casapi . ProvisionerInfo {
2022-08-11 00:44:14 +00:00
ID : prov . GetID ( ) ,
Type : prov . GetType ( ) . String ( ) ,
Name : prov . GetName ( ) ,
2022-08-03 02:28:49 +00:00
}
2020-07-08 02:01:47 +00:00
// Adds new options to NewCertificate
case provisioner . CertificateOptions :
certOptions = append ( certOptions , k . Options ( signOpts ) ... )
2020-08-20 22:13:22 +00:00
// Validate the given certificate request.
2019-03-07 01:37:49 +00:00
case provisioner . CertificateRequestValidator :
if err := k . Valid ( csr ) ; err != nil {
2021-11-24 02:58:16 +00:00
return nil , errs . ApplyOptions (
errs . ForbiddenErr ( err , "error validating certificate" ) ,
opts ... ,
)
2018-10-19 05:26:39 +00:00
}
2020-07-08 02:01:47 +00:00
// Validates the unsigned certificate template.
case provisioner . CertificateValidator :
certValidators = append ( certValidators , k )
// Modifies a certificate before validating it.
case provisioner . CertificateModifier :
certModifiers = append ( certModifiers , k )
// Modifies a certificate after validating it.
2020-03-31 18:41:36 +00:00
case provisioner . CertificateEnforcer :
2020-07-08 02:01:47 +00:00
certEnforcers = append ( certEnforcers , k )
2022-09-16 19:37:41 +00:00
// Extra information from ACME attestations.
case provisioner . AttestationData :
2022-09-30 00:16:26 +00:00
attData = & k
// Capture the provisioner's webhook controller
case webhookController :
webhookCtl = k
2018-10-19 05:26:39 +00:00
default :
2020-01-24 06:04:34 +00:00
return nil , errs . InternalServer ( "authority.Sign; invalid extra option type %T" , append ( [ ] interface { } { k } , opts ... ) ... )
2018-10-19 05:26:39 +00:00
}
2018-10-05 21:48:36 +00:00
}
2022-09-30 00:16:26 +00:00
if err := callEnrichingWebhooksX509 ( webhookCtl , attData , csr ) ; err != nil {
return nil , errs . ApplyOptions (
errs . ForbiddenErr ( err , err . Error ( ) ) ,
errs . WithKeyVal ( "csr" , csr ) ,
errs . WithKeyVal ( "signOptions" , signOpts ) ,
)
}
2020-07-21 00:25:53 +00:00
cert , err := x509util . NewCertificate ( csr , certOptions ... )
2018-10-05 21:48:36 +00:00
if err != nil {
2022-08-23 19:43:48 +00:00
var te * x509util . TemplateError
if errors . As ( err , & te ) {
2021-11-19 02:44:58 +00:00
return nil , errs . ApplyOptions (
errs . BadRequestErr ( err , err . Error ( ) ) ,
2020-07-23 02:18:45 +00:00
errs . WithKeyVal ( "csr" , csr ) ,
errs . WithKeyVal ( "signOptions" , signOpts ) ,
)
2020-07-16 19:25:40 +00:00
}
2022-01-12 09:41:36 +00:00
// explicitly check for unmarshaling errors, which are most probably caused by JSON template (syntax) errors
2022-01-10 14:49:37 +00:00
if strings . HasPrefix ( err . Error ( ) , "error unmarshaling certificate" ) {
2022-01-12 09:41:36 +00:00
return nil , errs . InternalServerErr ( templatingError ( err ) ,
2022-01-10 14:49:37 +00:00
errs . WithKeyVal ( "csr" , csr ) ,
errs . WithKeyVal ( "signOptions" , signOpts ) ,
2022-01-12 09:41:36 +00:00
errs . WithMessage ( "error applying certificate template" ) ,
2022-01-10 14:49:37 +00:00
)
}
2019-12-20 21:30:05 +00:00
return nil , errs . Wrap ( http . StatusInternalServerError , err , "authority.Sign" , opts ... )
2018-10-19 05:26:39 +00:00
}
2020-07-08 02:01:47 +00:00
// Certificate modifiers before validation
leaf := cert . GetCertificate ( )
2020-07-13 18:39:28 +00:00
// Set default subject
if err := withDefaultASN1DN ( a . config . AuthorityConfig . Template ) . Modify ( leaf , signOpts ) ; err != nil {
2021-11-24 02:58:16 +00:00
return nil , errs . ApplyOptions (
errs . ForbiddenErr ( err , "error creating certificate" ) ,
opts ... ,
)
2020-07-13 18:39:28 +00:00
}
2020-07-08 02:01:47 +00:00
for _ , m := range certModifiers {
if err := m . Modify ( leaf , signOpts ) ; err != nil {
2021-11-24 02:58:16 +00:00
return nil , errs . ApplyOptions (
errs . ForbiddenErr ( err , "error creating certificate" ) ,
opts ... ,
)
2019-03-21 00:36:45 +00:00
}
2020-03-31 00:33:04 +00:00
}
2020-07-08 02:01:47 +00:00
// Certificate validation.
for _ , v := range certValidators {
if err := v . Valid ( leaf , signOpts ) ; err != nil {
2021-11-24 02:58:16 +00:00
return nil , errs . ApplyOptions (
errs . ForbiddenErr ( err , "error validating certificate" ) ,
opts ... ,
)
2020-03-31 00:33:04 +00:00
}
2019-03-21 00:36:45 +00:00
}
2020-07-08 02:01:47 +00:00
// Certificate modifiers after validation
for _ , m := range certEnforcers {
if err := m . Enforce ( leaf ) ; err != nil {
2021-11-24 02:58:16 +00:00
return nil , errs . ApplyOptions (
errs . ForbiddenErr ( err , "error creating certificate" ) ,
opts ... ,
)
2020-07-08 02:01:47 +00:00
}
2018-10-05 21:48:36 +00:00
}
2022-02-02 22:36:58 +00:00
// Process injected modifiers after validation
for _ , m := range a . x509Enforcers {
if err := m . Enforce ( leaf ) ; err != nil {
return nil , errs . ApplyOptions (
errs . ForbiddenErr ( err , "error creating certificate" ) ,
opts ... ,
)
}
}
2022-03-21 14:53:59 +00:00
// Check if authority is allowed to sign the certificate
2022-04-26 11:12:16 +00:00
if err := a . isAllowedToSignX509Certificate ( leaf ) ; err != nil {
2022-09-22 01:35:18 +00:00
var ee * errs . Error
if errors . As ( err , & ee ) {
2022-09-22 01:46:34 +00:00
return nil , errs . ApplyOptions ( ee , opts ... )
2022-04-24 11:11:32 +00:00
}
2022-03-21 14:53:59 +00:00
return nil , errs . InternalServerErr ( err ,
errs . WithKeyVal ( "csr" , csr ) ,
errs . WithKeyVal ( "signOptions" , signOpts ) ,
errs . WithMessage ( "error creating certificate" ) ,
)
}
2022-03-08 12:26:07 +00:00
2022-09-30 00:16:26 +00:00
// Send certificate to webhooks for authorization
if err := callAuthorizingWebhooksX509 ( webhookCtl , cert , leaf , attData ) ; err != nil {
return nil , errs . ApplyOptions (
errs . ForbiddenErr ( err , "error creating certificate" ) ,
opts ... ,
)
}
2022-02-02 22:36:58 +00:00
// Sign certificate
2020-09-11 02:09:46 +00:00
lifetime := leaf . NotAfter . Sub ( leaf . NotBefore . Add ( signOpts . Backdate ) )
2020-09-09 02:26:32 +00:00
resp , err := a . x509CAService . CreateCertificate ( & casapi . CreateCertificateRequest {
2022-08-03 02:28:49 +00:00
Template : leaf ,
CSR : csr ,
Lifetime : lifetime ,
Backdate : signOpts . Backdate ,
Provisioner : pInfo ,
2020-09-09 02:26:32 +00:00
} )
2018-10-05 21:48:36 +00:00
if err != nil {
2020-09-09 02:26:32 +00:00
return nil , errs . Wrap ( http . StatusInternalServerError , err , "authority.Sign; error creating certificate" , opts ... )
2018-10-05 21:48:36 +00:00
}
2020-09-09 02:26:32 +00:00
2021-04-26 19:28:51 +00:00
fullchain := append ( [ ] * x509 . Certificate { resp . Certificate } , resp . CertificateChain ... )
2022-10-06 19:22:19 +00:00
// Wrap provisioner with extra information.
prov = wrapProvisioner ( prov , attData )
// Store certificate in the db.
2022-03-22 02:24:05 +00:00
if err = a . storeCertificate ( prov , fullchain ) ; err != nil {
2022-08-23 19:43:48 +00:00
if ! errors . Is ( err , db . ErrNotImplemented ) {
2019-12-20 21:30:05 +00:00
return nil , errs . Wrap ( http . StatusInternalServerError , err ,
"authority.Sign; error storing certificate in db" , opts ... )
2019-03-05 08:07:13 +00:00
}
}
2021-04-26 19:28:51 +00:00
return fullchain , nil
2018-10-05 21:48:36 +00:00
}
2022-04-26 11:12:16 +00:00
// isAllowedToSignX509Certificate checks if the Authority is allowed
// to sign the X.509 certificate.
func ( a * Authority ) isAllowedToSignX509Certificate ( cert * x509 . Certificate ) error {
2022-09-21 01:52:47 +00:00
if err := a . constraintsEngine . ValidateCertificate ( cert ) ; err != nil {
return err
}
2022-04-26 11:12:16 +00:00
return a . policyEngine . IsX509CertificateAllowed ( cert )
2022-03-21 14:53:59 +00:00
}
2022-03-24 13:55:40 +00:00
// AreSANsAllowed evaluates the provided sans against the
// authority X.509 policy.
func ( a * Authority ) AreSANsAllowed ( ctx context . Context , sans [ ] string ) error {
2022-04-26 11:12:16 +00:00
return a . policyEngine . AreSANsAllowed ( sans )
2022-03-24 13:55:40 +00:00
}
2022-11-04 23:42:07 +00:00
// Renew creates a new Certificate identical to the old certificate, except with
// a validity window that begins 'now'.
2019-10-09 19:57:12 +00:00
func ( a * Authority ) Renew ( oldCert * x509 . Certificate ) ( [ ] * x509 . Certificate , error ) {
2022-11-04 23:42:07 +00:00
return a . RenewContext ( context . Background ( ) , oldCert , nil )
2020-07-01 13:40:13 +00:00
}
2022-11-04 23:42:07 +00:00
// Rekey is used for rekeying and renewing based on the public key. If the
// public key is 'nil' then it's assumed that the cert should be renewed using
// the existing public key. If the public key is not 'nil' then it's assumed
// that the cert should be rekeyed.
//
2020-07-09 19:11:40 +00:00
// For both Rekey and Renew all other attributes of the new certificate should
// match the old certificate. The exceptions are 'AuthorityKeyId' (which may
// have changed), 'SubjectKeyId' (different in case of rekey), and
// 'NotBefore/NotAfter' (the validity duration of the new certificate should be
// equal to the old one, but starting 'now').
2020-07-08 06:17:59 +00:00
func ( a * Authority ) Rekey ( oldCert * x509 . Certificate , pk crypto . PublicKey ) ( [ ] * x509 . Certificate , error ) {
2022-11-04 23:42:07 +00:00
return a . RenewContext ( context . Background ( ) , oldCert , pk )
}
// RenewContext creates a new certificate identical to the old one, but it can
// optionally replace the public key with the given one. When running on RA
// mode, it can only renew a certificate using a renew token instead.
//
// For both rekey and renew operations, all other attributes of the new
// certificate should match the old certificate. The exceptions are
// 'AuthorityKeyId' (which may have changed), 'SubjectKeyId' (different in case
// of rekey), and 'NotBefore/NotAfter' (the validity duration of the new
// certificate should be equal to the old one, but starting 'now').
func ( a * Authority ) RenewContext ( ctx context . Context , oldCert * x509 . Certificate , pk crypto . PublicKey ) ( [ ] * x509 . Certificate , error ) {
2020-07-09 19:11:40 +00:00
isRekey := ( pk != nil )
2022-11-04 23:42:07 +00:00
opts := [ ] errs . Option {
errs . WithKeyVal ( "serialNumber" , oldCert . SerialNumber . String ( ) ) ,
}
2019-12-20 21:30:05 +00:00
2018-11-01 22:43:24 +00:00
// Check step provisioner extensions
2022-11-04 23:42:07 +00:00
if err := a . authorizeRenew ( ctx , oldCert ) ; err != nil {
return nil , errs . StatusCodeError ( http . StatusInternalServerError , err , opts ... )
2018-11-01 22:43:24 +00:00
}
2020-01-03 01:48:28 +00:00
// Durations
backdate := a . config . AuthorityConfig . Backdate . Duration
2018-10-05 21:48:36 +00:00
duration := oldCert . NotAfter . Sub ( oldCert . NotBefore )
2020-09-11 02:09:46 +00:00
lifetime := duration - backdate
2020-01-03 01:48:28 +00:00
2020-09-11 02:09:46 +00:00
// Create new certificate from previous values.
// Issuer, NotBefore, NotAfter and SubjectKeyId will be set by the CAS.
2019-03-21 00:12:52 +00:00
newCert := & x509 . Certificate {
2022-10-20 02:10:50 +00:00
RawSubject : oldCert . RawSubject ,
2018-10-05 21:48:36 +00:00
KeyUsage : oldCert . KeyUsage ,
UnhandledCriticalExtensions : oldCert . UnhandledCriticalExtensions ,
ExtKeyUsage : oldCert . ExtKeyUsage ,
UnknownExtKeyUsage : oldCert . UnknownExtKeyUsage ,
BasicConstraintsValid : oldCert . BasicConstraintsValid ,
2019-11-13 19:18:05 +00:00
IsCA : oldCert . IsCA ,
MaxPathLen : oldCert . MaxPathLen ,
MaxPathLenZero : oldCert . MaxPathLenZero ,
OCSPServer : oldCert . OCSPServer ,
IssuingCertificateURL : oldCert . IssuingCertificateURL ,
2019-12-20 21:30:05 +00:00
PermittedDNSDomainsCritical : oldCert . PermittedDNSDomainsCritical ,
PermittedEmailAddresses : oldCert . PermittedEmailAddresses ,
2019-11-13 19:18:05 +00:00
DNSNames : oldCert . DNSNames ,
EmailAddresses : oldCert . EmailAddresses ,
IPAddresses : oldCert . IPAddresses ,
URIs : oldCert . URIs ,
2018-10-05 21:48:36 +00:00
PermittedDNSDomains : oldCert . PermittedDNSDomains ,
ExcludedDNSDomains : oldCert . ExcludedDNSDomains ,
PermittedIPRanges : oldCert . PermittedIPRanges ,
ExcludedIPRanges : oldCert . ExcludedIPRanges ,
ExcludedEmailAddresses : oldCert . ExcludedEmailAddresses ,
PermittedURIDomains : oldCert . PermittedURIDomains ,
ExcludedURIDomains : oldCert . ExcludedURIDomains ,
CRLDistributionPoints : oldCert . CRLDistributionPoints ,
PolicyIdentifiers : oldCert . PolicyIdentifiers ,
}
2020-07-09 19:11:40 +00:00
if isRekey {
2020-07-08 07:22:53 +00:00
newCert . PublicKey = pk
2020-07-09 19:11:40 +00:00
} else {
newCert . PublicKey = oldCert . PublicKey
2020-07-08 07:22:53 +00:00
}
2020-07-05 16:45:01 +00:00
// Copy all extensions except:
2020-09-11 02:09:46 +00:00
//
// 1. Authority Key Identifier - This one might be different if we rotate
// the intermediate certificate and it will cause a TLS bad certificate
// error.
//
// 2. Subject Key Identifier, if rekey - For rekey, SubjectKeyIdentifier
// extension will be calculated for the new public key by
// x509util.CreateCertificate()
2019-02-15 00:44:36 +00:00
for _ , ext := range oldCert . Extensions {
2020-07-08 07:22:53 +00:00
if ext . Id . Equal ( oidAuthorityKeyIdentifier ) {
continue
2020-07-01 13:40:13 +00:00
}
2020-07-09 19:11:40 +00:00
if ext . Id . Equal ( oidSubjectKeyIdentifier ) && isRekey {
2020-07-08 07:22:53 +00:00
newCert . SubjectKeyId = nil
continue
2019-02-15 00:44:36 +00:00
}
2020-07-08 07:22:53 +00:00
newCert . ExtraExtensions = append ( newCert . ExtraExtensions , ext )
2019-02-15 00:44:36 +00:00
}
2022-09-22 19:17:16 +00:00
// Check if the certificate is allowed to be renewed, name constraints might
// change over time.
//
// TODO(hslatman,maraino): consider adding policies too and consider if
// RenewSSH should check policies.
if err := a . constraintsEngine . ValidateCertificate ( newCert ) ; err != nil {
2022-09-22 01:35:18 +00:00
var ee * errs . Error
if errors . As ( err , & ee ) {
2022-11-04 23:42:07 +00:00
return nil , errs . StatusCodeError ( ee . StatusCode ( ) , err , opts ... )
2022-09-22 01:35:18 +00:00
}
return nil , errs . InternalServerErr ( err ,
errs . WithKeyVal ( "serialNumber" , oldCert . SerialNumber . String ( ) ) ,
errs . WithMessage ( "error renewing certificate" ) ,
)
2022-09-21 01:52:47 +00:00
}
2022-11-04 23:42:07 +00:00
// The token can optionally be in the context. If the CA is running in RA
// mode, this can be used to renew a certificate.
token , _ := TokenFromContext ( ctx )
2020-09-11 02:09:46 +00:00
resp , err := a . x509CAService . RenewCertificate ( & casapi . RenewCertificateRequest {
Template : newCert ,
Lifetime : lifetime ,
Backdate : backdate ,
2022-11-04 23:42:07 +00:00
Token : token ,
2020-09-11 02:09:46 +00:00
} )
2018-10-05 21:48:36 +00:00
if err != nil {
2022-11-04 23:42:07 +00:00
return nil , errs . StatusCodeError ( http . StatusInternalServerError , err , opts ... )
2018-10-05 21:48:36 +00:00
}
2021-04-26 19:28:51 +00:00
fullchain := append ( [ ] * x509 . Certificate { resp . Certificate } , resp . CertificateChain ... )
2021-04-29 22:55:22 +00:00
if err = a . storeRenewedCertificate ( oldCert , fullchain ) ; err != nil {
2022-08-23 19:43:48 +00:00
if ! errors . Is ( err , db . ErrNotImplemented ) {
2022-11-04 23:42:07 +00:00
return nil , errs . StatusCodeError ( http . StatusInternalServerError , err , opts ... )
2019-12-10 21:10:45 +00:00
}
}
2021-04-26 19:28:51 +00:00
return fullchain , nil
}
// storeCertificate allows to use an extension of the db.AuthDB interface that
// can log the full chain of certificates.
//
// TODO: at some point we should replace the db.AuthDB interface to implement
// `StoreCertificate(...*x509.Certificate) error` instead of just
// `StoreCertificate(*x509.Certificate) error`.
2022-03-22 02:24:05 +00:00
func ( a * Authority ) storeCertificate ( prov provisioner . Interface , fullchain [ ] * x509 . Certificate ) error {
2021-07-21 01:16:24 +00:00
type certificateChainStorer interface {
2022-03-22 02:24:05 +00:00
StoreCertificateChain ( provisioner . Interface , ... * x509 . Certificate ) error
}
2022-05-19 01:33:22 +00:00
type certificateChainSimpleStorer interface {
2021-04-26 19:28:51 +00:00
StoreCertificateChain ( ... * x509 . Certificate ) error
2021-07-21 01:16:24 +00:00
}
2022-05-19 01:33:22 +00:00
2021-07-21 01:16:24 +00:00
// Store certificate in linkedca
2022-03-22 02:24:05 +00:00
switch s := a . adminDB . ( type ) {
case certificateChainStorer :
2022-05-19 01:33:22 +00:00
return s . StoreCertificateChain ( prov , fullchain ... )
case certificateChainSimpleStorer :
2021-07-21 01:16:24 +00:00
return s . StoreCertificateChain ( fullchain ... )
}
2022-03-22 02:24:05 +00:00
2021-07-21 01:16:24 +00:00
// Store certificate in local db
2022-03-22 02:24:05 +00:00
switch s := a . db . ( type ) {
case certificateChainStorer :
2022-05-19 01:33:22 +00:00
return s . StoreCertificateChain ( prov , fullchain ... )
case certificateChainSimpleStorer :
2021-04-26 19:28:51 +00:00
return s . StoreCertificateChain ( fullchain ... )
2022-05-19 01:33:22 +00:00
case db . CertificateStorer :
return s . StoreCertificate ( fullchain [ 0 ] )
2022-03-22 02:24:05 +00:00
default :
2022-05-19 01:33:22 +00:00
return nil
2021-04-26 19:28:51 +00:00
}
2018-10-05 21:48:36 +00:00
}
2021-04-29 22:55:22 +00:00
// storeRenewedCertificate allows to use an extension of the db.AuthDB interface
// that can log if a certificate has been renewed or rekeyed.
//
// TODO: at some point we should implement this in the standard implementation.
func ( a * Authority ) storeRenewedCertificate ( oldCert * x509 . Certificate , fullchain [ ] * x509 . Certificate ) error {
2021-07-21 01:16:24 +00:00
type renewedCertificateChainStorer interface {
2021-04-29 22:55:22 +00:00
StoreRenewedCertificate ( * x509 . Certificate , ... * x509 . Certificate ) error
2021-07-21 01:16:24 +00:00
}
2022-05-19 01:33:22 +00:00
2021-07-21 01:16:24 +00:00
// Store certificate in linkedca
if s , ok := a . adminDB . ( renewedCertificateChainStorer ) ; ok {
return s . StoreRenewedCertificate ( oldCert , fullchain ... )
}
2022-05-19 01:33:22 +00:00
2021-07-21 01:16:24 +00:00
// Store certificate in local db
2022-05-19 01:33:22 +00:00
switch s := a . db . ( type ) {
case renewedCertificateChainStorer :
2021-04-29 22:55:22 +00:00
return s . StoreRenewedCertificate ( oldCert , fullchain ... )
2022-05-19 01:33:22 +00:00
case db . CertificateStorer :
return s . StoreCertificate ( fullchain [ 0 ] )
default :
return nil
2021-04-29 22:55:22 +00:00
}
}
2019-03-05 08:07:13 +00:00
// RevokeOptions are the options for the Revoke API.
type RevokeOptions struct {
Serial string
Reason string
ReasonCode int
PassiveOnly bool
MTLS bool
2021-07-02 22:21:17 +00:00
ACME bool
2019-03-05 08:07:13 +00:00
Crt * x509 . Certificate
OTT string
}
// Revoke revokes a certificate.
//
// NOTE: Only supports passive revocation - prevent existing certificates from
// being renewed.
//
// TODO: Add OCSP and CRL support.
2019-12-20 21:30:05 +00:00
func ( a * Authority ) Revoke ( ctx context . Context , revokeOpts * RevokeOptions ) error {
2020-01-24 06:04:34 +00:00
opts := [ ] interface { } {
2019-12-20 21:30:05 +00:00
errs . WithKeyVal ( "serialNumber" , revokeOpts . Serial ) ,
errs . WithKeyVal ( "reasonCode" , revokeOpts . ReasonCode ) ,
errs . WithKeyVal ( "reason" , revokeOpts . Reason ) ,
errs . WithKeyVal ( "passiveOnly" , revokeOpts . PassiveOnly ) ,
errs . WithKeyVal ( "MTLS" , revokeOpts . MTLS ) ,
2021-07-02 22:21:17 +00:00
errs . WithKeyVal ( "ACME" , revokeOpts . ACME ) ,
2020-08-24 21:44:11 +00:00
errs . WithKeyVal ( "context" , provisioner . MethodFromContext ( ctx ) . String ( ) ) ,
2019-03-05 08:07:13 +00:00
}
2021-07-02 22:21:17 +00:00
if revokeOpts . MTLS || revokeOpts . ACME {
2019-12-20 21:30:05 +00:00
opts = append ( opts , errs . WithKeyVal ( "certificate" , base64 . StdEncoding . EncodeToString ( revokeOpts . Crt . Raw ) ) )
2019-03-05 08:07:13 +00:00
} else {
2019-12-20 21:30:05 +00:00
opts = append ( opts , errs . WithKeyVal ( "token" , revokeOpts . OTT ) )
2019-03-05 08:07:13 +00:00
}
rci := & db . RevokedCertificateInfo {
2019-12-20 21:30:05 +00:00
Serial : revokeOpts . Serial ,
ReasonCode : revokeOpts . ReasonCode ,
Reason : revokeOpts . Reason ,
MTLS : revokeOpts . MTLS ,
2021-12-02 16:11:36 +00:00
ACME : revokeOpts . ACME ,
2019-03-05 08:07:13 +00:00
RevokedAt : time . Now ( ) . UTC ( ) ,
}
2022-10-27 01:55:24 +00:00
// For X509 CRLs attempt to get the expiration date of the certificate.
if provisioner . MethodFromContext ( ctx ) == provisioner . RevokeMethod {
if revokeOpts . Crt == nil {
cert , err := a . db . GetCertificate ( revokeOpts . Serial )
if err == nil {
rci . ExpiresAt = cert . NotAfter
}
} else {
rci . ExpiresAt = revokeOpts . Crt . NotAfter
2022-04-05 03:19:13 +00:00
}
2021-11-04 06:05:07 +00:00
}
2021-07-02 22:21:17 +00:00
// If not mTLS nor ACME, then get the TokenID of the token.
if ! ( revokeOpts . MTLS || revokeOpts . ACME ) {
2019-12-20 21:30:05 +00:00
token , err := jose . ParseSigned ( revokeOpts . OTT )
2019-10-28 18:50:43 +00:00
if err != nil {
2022-10-27 01:55:24 +00:00
return errs . Wrap ( http . StatusUnauthorized , err , "authority.Revoke; error parsing token" , opts ... )
2019-10-28 18:50:43 +00:00
}
2019-12-20 21:30:05 +00:00
// Get claims w/out verification.
2019-10-28 18:50:43 +00:00
var claims Claims
if err = token . UnsafeClaimsWithoutVerification ( & claims ) ; err != nil {
2019-12-20 21:30:05 +00:00
return errs . Wrap ( http . StatusUnauthorized , err , "authority.Revoke" , opts ... )
2019-10-28 18:50:43 +00:00
}
// This method will also validate the audiences for JWK provisioners.
2022-10-27 01:55:24 +00:00
p , err := a . LoadProvisionerByToken ( token , & claims . Claims )
2021-05-03 19:48:20 +00:00
if err != nil {
return err
2019-10-28 18:50:43 +00:00
}
2021-03-22 20:37:31 +00:00
rci . ProvisionerID = p . GetID ( )
2019-12-20 21:30:05 +00:00
rci . TokenID , err = p . GetTokenID ( revokeOpts . OTT )
2021-08-11 18:50:54 +00:00
if err != nil && ! errors . Is ( err , provisioner . ErrAllowTokenReuse ) {
2022-10-27 01:55:24 +00:00
return errs . Wrap ( http . StatusInternalServerError , err , "authority.Revoke; could not get ID for token" )
2019-03-05 08:07:13 +00:00
}
2021-10-08 18:59:57 +00:00
opts = append ( opts ,
errs . WithKeyVal ( "provisionerID" , rci . ProvisionerID ) ,
errs . WithKeyVal ( "tokenID" , rci . TokenID ) ,
)
2022-10-27 01:55:24 +00:00
} else if p , err := a . LoadProvisionerByCertificate ( revokeOpts . Crt ) ; err == nil {
2019-10-28 18:50:43 +00:00
// Load the Certificate provisioner if one exists.
2021-10-08 18:59:57 +00:00
rci . ProvisionerID = p . GetID ( )
opts = append ( opts , errs . WithKeyVal ( "provisionerID" , rci . ProvisionerID ) )
2019-03-05 08:07:13 +00:00
}
2022-10-27 01:55:24 +00:00
failRevoke := func ( err error ) error {
switch {
case errors . Is ( err , db . ErrNotImplemented ) :
return errs . NotImplemented ( "authority.Revoke; no persistence layer configured" , opts ... )
case errors . Is ( err , db . ErrAlreadyExists ) :
return errs . ApplyOptions (
errs . BadRequest ( "certificate with serial number '%s' is already revoked" , rci . Serial ) ,
opts ... ,
)
default :
return errs . Wrap ( http . StatusInternalServerError , err , "authority.Revoke" , opts ... )
}
}
2019-12-20 21:30:05 +00:00
if provisioner . MethodFromContext ( ctx ) == provisioner . SSHRevokeMethod {
2022-10-27 01:55:24 +00:00
if err := a . revokeSSH ( nil , rci ) ; err != nil {
return failRevoke ( err )
}
2020-09-16 01:14:03 +00:00
} else {
// Revoke an X.509 certificate using CAS. If the certificate is not
2020-10-06 00:39:44 +00:00
// provided we will try to read it from the db. If the read fails we
2020-10-06 01:00:50 +00:00
// won't throw an error as it will be responsibility of the CAS
2020-10-06 00:39:44 +00:00
// implementation to require a certificate.
2020-09-16 01:14:03 +00:00
var revokedCert * x509 . Certificate
if revokeOpts . Crt != nil {
revokedCert = revokeOpts . Crt
} else if rci . Serial != "" {
revokedCert , _ = a . db . GetCertificate ( rci . Serial )
}
// CAS operation, note that SoftCAS (default) is a noop.
// The revoke happens when this is stored in the db.
2022-10-27 01:55:24 +00:00
_ , err := a . x509CAService . RevokeCertificate ( & casapi . RevokeCertificateRequest {
2021-03-18 02:33:35 +00:00
Certificate : revokedCert ,
SerialNumber : rci . Serial ,
Reason : rci . Reason ,
ReasonCode : rci . ReasonCode ,
2021-03-18 02:47:36 +00:00
PassiveOnly : revokeOpts . PassiveOnly ,
2020-09-16 01:14:03 +00:00
} )
if err != nil {
return errs . Wrap ( http . StatusInternalServerError , err , "authority.Revoke" , opts ... )
}
// Save as revoked in the Db.
2022-10-27 01:55:24 +00:00
if err := a . revoke ( revokedCert , rci ) ; err != nil {
return failRevoke ( err )
2022-09-08 02:09:37 +00:00
}
2021-10-30 07:52:50 +00:00
2022-10-27 01:55:24 +00:00
// Generate a new CRL so CRL requesters will always get an up-to-date
// CRL whenever they request it.
if a . config . CRL . IsEnabled ( ) && a . config . CRL . GenerateOnRevoke {
if err := a . GenerateCertificateRevocationList ( ) ; err != nil {
2022-10-07 02:30:00 +00:00
return errs . Wrap ( http . StatusInternalServerError , err , "authority.Revoke" , opts ... )
}
2021-11-04 06:05:07 +00:00
}
2019-10-28 18:50:43 +00:00
}
2022-10-27 01:55:24 +00:00
return nil
2019-03-05 08:07:13 +00:00
}
2021-08-06 01:45:50 +00:00
func ( a * Authority ) revoke ( crt * x509 . Certificate , rci * db . RevokedCertificateInfo ) error {
if lca , ok := a . adminDB . ( interface {
Revoke ( * x509 . Certificate , * db . RevokedCertificateInfo ) error
} ) ; ok {
return lca . Revoke ( crt , rci )
}
return a . db . Revoke ( rci )
}
func ( a * Authority ) revokeSSH ( crt * ssh . Certificate , rci * db . RevokedCertificateInfo ) error {
if lca , ok := a . adminDB . ( interface {
RevokeSSH ( * ssh . Certificate , * db . RevokedCertificateInfo ) error
} ) ; ok {
return lca . RevokeSSH ( crt , rci )
}
2022-05-25 23:55:22 +00:00
return a . db . RevokeSSH ( rci )
2021-08-06 01:45:50 +00:00
}
2021-11-04 06:05:07 +00:00
// GetCertificateRevocationList will return the currently generated CRL from the DB, or a not implemented
// error if the underlying AuthDB does not support CRLs
func ( a * Authority ) GetCertificateRevocationList ( ) ( [ ] byte , error ) {
2022-10-27 01:55:24 +00:00
if ! a . config . CRL . IsEnabled ( ) {
2022-03-29 00:51:39 +00:00
return nil , errs . Wrap ( http . StatusNotFound , errors . Errorf ( "Certificate Revocation Lists are not enabled" ) , "authority.GetCertificateRevocationList" )
2021-11-04 06:05:07 +00:00
}
2021-10-30 07:52:50 +00:00
2021-11-04 06:05:07 +00:00
crlDB , ok := a . db . ( db . CertificateRevocationListDB )
if ! ok {
2022-03-29 00:51:39 +00:00
return nil , errs . Wrap ( http . StatusNotImplemented , errors . Errorf ( "Database does not support Certificate Revocation Lists" ) , "authority.GetCertificateRevocationList" )
2021-11-04 06:05:07 +00:00
}
2021-10-30 07:52:50 +00:00
2021-11-04 06:05:07 +00:00
crlInfo , err := crlDB . GetCRL ( )
2021-10-30 07:52:50 +00:00
if err != nil {
2021-11-04 06:05:07 +00:00
return nil , errs . Wrap ( http . StatusInternalServerError , err , "authority.GetCertificateRevocationList" )
}
return crlInfo . DER , nil
}
// GenerateCertificateRevocationList generates a DER representation of a signed CRL and stores it in the
// database. Returns nil if CRL generation has been disabled in the config
func ( a * Authority ) GenerateCertificateRevocationList ( ) error {
2022-10-27 01:55:24 +00:00
if ! a . config . CRL . IsEnabled ( ) {
2021-11-04 06:05:07 +00:00
return nil
}
crlDB , ok := a . db . ( db . CertificateRevocationListDB )
if ! ok {
return errors . Errorf ( "Database does not support CRL generation" )
2021-10-30 07:52:50 +00:00
}
// some CAS may not implement the CRLGenerator interface, so check before we proceed
caCRLGenerator , ok := a . x509CAService . ( casapi . CertificateAuthorityCRLGenerator )
if ! ok {
2021-11-04 06:05:07 +00:00
return errors . Errorf ( "CA does not support CRL Generation" )
}
2022-10-27 01:55:24 +00:00
// use a mutex to ensure only one CRL is generated at a time to avoid
// concurrency issues
a . crlMutex . Lock ( )
2022-09-15 07:03:42 +00:00
defer a . crlMutex . Unlock ( )
2021-11-04 06:05:07 +00:00
crlInfo , err := crlDB . GetCRL ( )
2022-10-27 01:55:24 +00:00
if err != nil && ! database . IsErrNotFound ( err ) {
2021-11-04 06:05:07 +00:00
return errors . Wrap ( err , "could not retrieve CRL from database" )
2021-10-30 07:52:50 +00:00
}
2022-10-27 18:57:48 +00:00
now := time . Now ( ) . Truncate ( time . Second ) . UTC ( )
2021-11-04 06:05:07 +00:00
revokedList , err := crlDB . GetRevokedCertificates ( )
if err != nil {
return errors . Wrap ( err , "could not retrieve revoked certificates list from database" )
}
2021-10-30 07:52:50 +00:00
2022-10-27 01:55:24 +00:00
// Number is a monotonically increasing integer (essentially the CRL version
// number) that we need to keep track of and increase every time we generate
// a new CRL
2021-10-30 07:52:50 +00:00
var bn big . Int
if crlInfo != nil {
2022-10-27 01:55:24 +00:00
bn . SetInt64 ( crlInfo . Number + 1 )
2021-10-30 07:52:50 +00:00
}
2022-10-27 01:55:24 +00:00
// Convert our database db.RevokedCertificateInfo types into the pkix
// representation ready for the CAS to sign it
2021-10-30 07:52:50 +00:00
var revokedCertificates [ ] pkix . RevokedCertificate
2022-10-27 19:20:13 +00:00
skipExpiredTime := now . Add ( - config . DefaultCRLExpiredDuration )
2021-10-30 07:52:50 +00:00
for _ , revokedCert := range * revokedList {
2022-10-27 01:55:24 +00:00
// skip expired certificates
if ! revokedCert . ExpiresAt . IsZero ( ) && revokedCert . ExpiresAt . Before ( skipExpiredTime ) {
continue
}
2021-10-30 07:52:50 +00:00
var sn big . Int
sn . SetString ( revokedCert . Serial , 10 )
revokedCertificates = append ( revokedCertificates , pkix . RevokedCertificate {
SerialNumber : & sn ,
RevocationTime : revokedCert . RevokedAt ,
Extensions : nil ,
} )
}
2022-10-07 02:30:00 +00:00
var updateDuration time . Duration
if a . config . CRL . CacheDuration != nil {
updateDuration = a . config . CRL . CacheDuration . Duration
} else if crlInfo != nil {
updateDuration = crlInfo . Duration
}
2021-10-30 07:52:50 +00:00
// Create a RevocationList representation ready for the CAS to sign
// TODO: allow SignatureAlgorithm to be specified?
revocationList := x509 . RevocationList {
SignatureAlgorithm : 0 ,
RevokedCertificates : revokedCertificates ,
Number : & bn ,
2022-10-27 01:55:24 +00:00
ThisUpdate : now ,
NextUpdate : now . Add ( updateDuration ) ,
}
2022-11-11 16:50:20 +00:00
// Set CRL IDP to config item, otherwise, leave as default
var fullName string
if a . config . CRL . IDPurl != "" {
fullName = a . config . CRL . IDPurl
} else {
fullName = a . config . Audience ( "/1.0/crl" ) [ 0 ]
}
2022-10-27 01:55:24 +00:00
// Add distribution point.
//
// Note that this is currently using the port 443 by default.
if b , err := marshalDistributionPoint ( fullName , false ) ; err == nil {
revocationList . ExtraExtensions = [ ] pkix . Extension {
{ Id : oidExtensionIssuingDistributionPoint , Value : b } ,
}
2021-10-30 07:52:50 +00:00
}
2021-11-04 06:05:07 +00:00
certificateRevocationList , err := caCRLGenerator . CreateCRL ( & casapi . CreateCRLRequest { RevocationList : & revocationList } )
2021-10-30 07:52:50 +00:00
if err != nil {
2021-11-04 06:05:07 +00:00
return errors . Wrap ( err , "could not create CRL" )
2021-10-30 07:52:50 +00:00
}
// Create a new db.CertificateRevocationListInfo, which stores the new Number we just generated, the
2022-10-07 02:30:00 +00:00
// expiry time, duration, and the DER-encoded CRL
2021-10-30 07:52:50 +00:00
newCRLInfo := db . CertificateRevocationListInfo {
2022-10-27 01:55:24 +00:00
Number : bn . Int64 ( ) ,
2021-10-30 07:52:50 +00:00
ExpiresAt : revocationList . NextUpdate ,
2021-11-04 06:05:07 +00:00
DER : certificateRevocationList . CRL ,
2022-10-07 02:30:00 +00:00
Duration : updateDuration ,
2021-10-30 07:52:50 +00:00
}
2021-11-04 06:05:07 +00:00
// Store the CRL in the database ready for retrieval by api endpoints
err = crlDB . StoreCRL ( & newCRLInfo )
2021-10-30 07:52:50 +00:00
if err != nil {
2021-11-04 06:05:07 +00:00
return errors . Wrap ( err , "could not store CRL in database" )
2021-10-30 07:52:50 +00:00
}
2021-11-04 06:05:07 +00:00
return nil
2021-10-30 07:52:50 +00:00
}
2018-10-05 21:48:36 +00:00
// GetTLSCertificate creates a new leaf certificate to be used by the CA HTTPS server.
func ( a * Authority ) GetTLSCertificate ( ) ( * tls . Certificate , error ) {
2020-08-10 23:09:22 +00:00
fatal := func ( err error ) ( * tls . Certificate , error ) {
2019-12-20 21:30:05 +00:00
return nil , errs . Wrap ( http . StatusInternalServerError , err , "authority.GetTLSCertificate" )
2018-10-05 21:48:36 +00:00
}
2020-08-10 23:09:22 +00:00
// Generate default key.
2020-08-14 22:33:50 +00:00
priv , err := keyutil . GenerateDefaultKey ( )
2018-10-05 21:48:36 +00:00
if err != nil {
2020-08-10 23:09:22 +00:00
return fatal ( err )
}
signer , ok := priv . ( crypto . Signer )
if ! ok {
return fatal ( errors . New ( "private key is not a crypto.Signer" ) )
2018-10-05 21:48:36 +00:00
}
2022-02-03 13:21:23 +00:00
// prepare the sans: IPv6 DNS hostname representations are converted to their IP representation
sans := make ( [ ] string , len ( a . config . DNSNames ) )
for i , san := range a . config . DNSNames {
if strings . HasPrefix ( san , "[" ) && strings . HasSuffix ( san , "]" ) {
if ip := net . ParseIP ( san [ 1 : len ( san ) - 1 ] ) ; ip != nil {
san = ip . String ( )
}
}
sans [ i ] = san
}
2020-08-10 23:09:22 +00:00
// Create initial certificate request.
2022-04-12 20:57:55 +00:00
cr , err := x509util . CreateCertificateRequest ( a . config . CommonName , sans , signer )
2018-10-05 21:48:36 +00:00
if err != nil {
2020-08-10 23:09:22 +00:00
return fatal ( err )
}
2020-08-28 21:33:26 +00:00
// Generate certificate template directly from the certificate request.
template , err := x509util . NewCertificate ( cr )
2020-08-10 23:09:22 +00:00
if err != nil {
return fatal ( err )
}
2020-08-28 21:33:26 +00:00
// Get x509 certificate template, set validity and sign it.
2020-08-10 23:09:22 +00:00
now := time . Now ( )
2020-08-28 21:33:26 +00:00
certTpl := template . GetCertificate ( )
certTpl . NotBefore = now . Add ( - 1 * time . Minute )
certTpl . NotAfter = now . Add ( 24 * time . Hour )
2020-08-10 23:09:22 +00:00
2022-09-21 22:54:28 +00:00
// Policy and constraints require this fields to be set. At this moment they
// are only present in the extra extension.
certTpl . DNSNames = cr . DNSNames
certTpl . IPAddresses = cr . IPAddresses
certTpl . EmailAddresses = cr . EmailAddresses
certTpl . URIs = cr . URIs
2022-09-23 18:04:27 +00:00
// Fail if name constraints do not allow the server names.
if err := a . constraintsEngine . ValidateCertificate ( certTpl ) ; err != nil {
2022-09-21 22:54:28 +00:00
return fatal ( err )
}
2020-09-11 02:09:46 +00:00
resp , err := a . x509CAService . CreateCertificate ( & casapi . CreateCertificateRequest {
2022-08-11 22:14:26 +00:00
Template : certTpl ,
CSR : cr ,
Lifetime : 24 * time . Hour ,
Backdate : 1 * time . Minute ,
IsCAServerCert : true ,
2020-09-11 02:09:46 +00:00
} )
2020-08-10 23:09:22 +00:00
if err != nil {
return fatal ( err )
2018-10-05 21:48:36 +00:00
}
2020-08-10 23:09:22 +00:00
// Generate PEM blocks to create tls.Certificate
2020-09-11 02:09:46 +00:00
pemBlocks := pem . EncodeToMemory ( & pem . Block {
2018-10-05 21:48:36 +00:00
Type : "CERTIFICATE" ,
2020-09-11 02:09:46 +00:00
Bytes : resp . Certificate . Raw ,
2018-10-05 21:48:36 +00:00
} )
2020-09-11 02:09:46 +00:00
for _ , crt := range resp . CertificateChain {
pemBlocks = append ( pemBlocks , pem . EncodeToMemory ( & pem . Block {
Type : "CERTIFICATE" ,
Bytes : crt . Raw ,
} ) ... )
2018-10-05 21:48:36 +00:00
}
2020-08-10 23:09:22 +00:00
keyPEM , err := pemutil . Serialize ( priv )
2018-10-05 21:48:36 +00:00
if err != nil {
2020-08-10 23:09:22 +00:00
return fatal ( err )
2018-10-05 21:48:36 +00:00
}
2020-09-11 02:09:46 +00:00
tlsCrt , err := tls . X509KeyPair ( pemBlocks , pem . EncodeToMemory ( keyPEM ) )
2018-10-05 21:48:36 +00:00
if err != nil {
2020-08-10 23:09:22 +00:00
return fatal ( err )
2018-10-05 21:48:36 +00:00
}
2020-08-10 23:09:22 +00:00
// Set leaf certificate
2020-09-11 02:09:46 +00:00
tlsCrt . Leaf = resp . Certificate
2018-10-05 21:48:36 +00:00
return & tlsCrt , nil
}
2022-01-12 09:41:36 +00:00
2022-10-27 01:55:24 +00:00
// RFC 5280, 5.2.5
type distributionPoint struct {
DistributionPoint distributionPointName ` asn1:"optional,tag:0" `
OnlyContainsUserCerts bool ` asn1:"optional,tag:1" `
OnlyContainsCACerts bool ` asn1:"optional,tag:2" `
OnlySomeReasons asn1 . BitString ` asn1:"optional,tag:3" `
IndirectCRL bool ` asn1:"optional,tag:4" `
OnlyContainsAttributeCerts bool ` asn1:"optional,tag:5" `
}
type distributionPointName struct {
FullName [ ] asn1 . RawValue ` asn1:"optional,tag:0" `
RelativeName pkix . RDNSequence ` asn1:"optional,tag:1" `
}
func marshalDistributionPoint ( fullName string , isCA bool ) ( [ ] byte , error ) {
return asn1 . Marshal ( distributionPoint {
DistributionPoint : distributionPointName {
FullName : [ ] asn1 . RawValue {
{ Class : 2 , Tag : 6 , Bytes : [ ] byte ( fullName ) } ,
} ,
} ,
OnlyContainsUserCerts : ! isCA ,
OnlyContainsCACerts : isCA ,
} )
}
2022-01-12 09:41:36 +00:00
// templatingError tries to extract more information about the cause of
// an error related to (most probably) malformed template data and adds
// this to the error message.
func templatingError ( err error ) error {
cause := errors . Cause ( err )
2022-01-12 10:15:39 +00:00
var (
syntaxError * json . SyntaxError
typeError * json . UnmarshalTypeError
)
2022-01-12 09:41:36 +00:00
if errors . As ( err , & syntaxError ) {
// offset is arguably not super clear to the user, but it's the best we can do here
2022-08-23 19:43:48 +00:00
cause = fmt . Errorf ( "%w at offset %d" , cause , syntaxError . Offset )
2022-01-12 20:34:38 +00:00
} else if errors . As ( err , & typeError ) {
2022-01-12 10:15:39 +00:00
// slightly rewriting the default error message to include the offset
cause = fmt . Errorf ( "cannot unmarshal %s at offset %d into Go value of type %s" , typeError . Value , typeError . Offset , typeError . Type )
}
2022-01-12 09:41:36 +00:00
return errors . Wrap ( cause , "error applying certificate template" )
}
2022-09-30 00:16:26 +00:00
func callEnrichingWebhooksX509 ( webhookCtl webhookController , attData * provisioner . AttestationData , csr * x509 . CertificateRequest ) error {
if webhookCtl == nil {
return nil
}
var attested * webhook . AttestationData
if attData != nil {
attested = & webhook . AttestationData {
PermanentIdentifier : attData . PermanentIdentifier ,
}
}
whEnrichReq , err := webhook . NewRequestBody (
webhook . WithX509CertificateRequest ( csr ) ,
webhook . WithAttestationData ( attested ) ,
)
if err != nil {
return err
}
if err := webhookCtl . Enrich ( whEnrichReq ) ; err != nil {
return err
}
return nil
}
func callAuthorizingWebhooksX509 ( webhookCtl webhookController , cert * x509util . Certificate , leaf * x509 . Certificate , attData * provisioner . AttestationData ) error {
if webhookCtl == nil {
return nil
}
var attested * webhook . AttestationData
if attData != nil {
attested = & webhook . AttestationData {
PermanentIdentifier : attData . PermanentIdentifier ,
}
}
whAuthBody , err := webhook . NewRequestBody (
webhook . WithX509Certificate ( cert , leaf ) ,
webhook . WithAttestationData ( attested ) ,
)
if err != nil {
return err
}
if err := webhookCtl . Authorize ( whAuthBody ) ; err != nil {
return err
}
return nil
}