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"
2022-02-03 13:21:23 +00:00
"net"
2021-10-30 07:52:50 +00:00
"math/big"
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"
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"
2020-08-24 21:44:11 +00:00
"go.step.sm/crypto/jose"
2020-08-14 22:33:50 +00:00
"go.step.sm/crypto/keyutil"
"go.step.sm/crypto/pemutil"
2020-08-05 23:02:46 +00:00
"go.step.sm/crypto/x509util"
2021-08-06 01:45:50 +00:00
"golang.org/x/crypto/ssh"
2018-10-05 21:48:36 +00:00
)
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
}
2019-03-12 01:47:57 +00:00
var oidAuthorityKeyIdentifier = asn1 . ObjectIdentifier { 2 , 5 , 29 , 35 }
2020-07-05 16:45:01 +00:00
var oidSubjectKeyIdentifier = asn1 . ObjectIdentifier { 2 , 5 , 29 , 14 }
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
2018-10-19 05:26:39 +00:00
for _ , op := range extraOpts {
switch k := op . ( type ) {
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 )
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
}
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 {
2020-07-21 00:25:53 +00:00
if _ , ok := err . ( * x509util . TemplateError ) ; ok {
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 ... ,
)
}
}
// 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 {
Template : leaf ,
2021-03-18 02:33:35 +00:00
CSR : csr ,
2020-09-09 02:26:32 +00:00
Lifetime : lifetime ,
2020-09-11 02:09:46 +00:00
Backdate : signOpts . Backdate ,
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 ... )
if err = a . storeCertificate ( fullchain ) ; err != nil {
2019-03-05 08:07:13 +00:00
if 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
}
// 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 ) {
2020-07-08 07:22:53 +00:00
return a . Rekey ( oldCert , nil )
2020-07-01 13:40:13 +00:00
}
2020-07-09 19:11:40 +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.
// 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 ) {
2020-07-09 19:11:40 +00:00
isRekey := ( pk != nil )
2020-01-24 06:04:34 +00:00
opts := [ ] interface { } { 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
2019-10-28 18:50:43 +00:00
if err := a . authorizeRenew ( oldCert ) ; err != nil {
2020-07-08 06:17:59 +00:00
return nil , errs . Wrap ( http . StatusInternalServerError , err , "authority.Rekey" , 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 {
2018-10-05 21:48:36 +00:00
Subject : oldCert . Subject ,
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
}
2020-09-11 02:09:46 +00:00
resp , err := a . x509CAService . RenewCertificate ( & casapi . RenewCertificateRequest {
Template : newCert ,
Lifetime : lifetime ,
Backdate : backdate ,
} )
2018-10-05 21:48:36 +00:00
if err != nil {
2020-07-08 06:17:59 +00:00
return nil , errs . Wrap ( http . StatusInternalServerError , err , "authority.Rekey" , 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 {
2019-12-10 21:10:45 +00:00
if err != db . ErrNotImplemented {
2020-07-08 06:17:59 +00:00
return nil , errs . Wrap ( http . StatusInternalServerError , err , "authority.Rekey; error storing certificate in db" , 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`.
func ( a * Authority ) storeCertificate ( fullchain [ ] * x509 . Certificate ) error {
2021-07-21 01:16:24 +00:00
type certificateChainStorer interface {
2021-04-26 19:28:51 +00:00
StoreCertificateChain ( ... * x509 . Certificate ) error
2021-07-21 01:16:24 +00:00
}
// Store certificate in linkedca
if s , ok := a . adminDB . ( certificateChainStorer ) ; ok {
return s . StoreCertificateChain ( fullchain ... )
}
// Store certificate in local db
if s , ok := a . db . ( certificateChainStorer ) ; ok {
2021-04-26 19:28:51 +00:00
return s . StoreCertificateChain ( fullchain ... )
}
return a . db . StoreCertificate ( fullchain [ 0 ] )
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
}
// Store certificate in linkedca
if s , ok := a . adminDB . ( renewedCertificateChainStorer ) ; ok {
return s . StoreRenewedCertificate ( oldCert , fullchain ... )
}
// Store certificate in local db
if s , ok := a . db . ( renewedCertificateChainStorer ) ; ok {
2021-04-29 22:55:22 +00:00
return s . StoreRenewedCertificate ( oldCert , fullchain ... )
}
return a . db . StoreCertificate ( fullchain [ 0 ] )
}
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 ( ) ,
}
2019-10-28 18:50:43 +00:00
var (
p provisioner . Interface
err error
)
2021-11-04 06:05:07 +00:00
2022-04-05 03:19:13 +00:00
if revokeOpts . Crt == nil {
// Attempt to get the certificate expiry using the serial number.
cert , err := a . db . GetCertificate ( revokeOpts . Serial )
// Revocation of a certificate not in the database may be requested, so fill in the expiry only
// if we can
if err == nil {
rci . ExpiresAt = cert . NotAfter
}
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 {
2019-12-20 21:30:05 +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.
2021-05-03 19:48:20 +00:00
p , err = a . LoadProvisionerByToken ( token , & claims . Claims )
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 ) {
2019-12-20 21:30:05 +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 ) ,
)
} 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
}
2019-12-20 21:30:05 +00:00
if provisioner . MethodFromContext ( ctx ) == provisioner . SSHRevokeMethod {
2021-08-06 01:45:50 +00:00
err = a . revokeSSH ( nil , rci )
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.
_ , 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.
2021-08-06 01:45:50 +00:00
err = a . revoke ( revokedCert , rci )
2021-10-30 07:52:50 +00:00
// Generate a new CRL so CRL requesters will always get an up-to-date CRL whenever they request it
2021-11-04 06:05:07 +00:00
err = a . GenerateCertificateRevocationList ( )
if err != nil {
return errs . Wrap ( http . StatusInternalServerError , err , "authority.Revoke" , opts ... )
}
2019-10-28 18:50:43 +00:00
}
2019-03-05 08:07:13 +00:00
switch err {
case nil :
return nil
case db . ErrNotImplemented :
2020-01-24 06:04:34 +00:00
return errs . NotImplemented ( "authority.Revoke; no persistence layer configured" , opts ... )
2019-03-05 08:07:13 +00:00
case db . ErrAlreadyExists :
2021-11-18 23:12:44 +00:00
return errs . ApplyOptions (
errs . BadRequest ( "certificate with serial number '%s' is already revoked" , rci . Serial ) ,
opts ... ,
)
2019-03-05 08:07:13 +00:00
default :
2019-12-20 21:30:05 +00:00
return errs . Wrap ( http . StatusInternalServerError , err , "authority.Revoke" , opts ... )
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 )
}
return a . db . Revoke ( rci )
}
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 ) {
if a . config . CRL == nil {
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" )
2021-10-30 07:52:50 +00:00
}
2021-11-04 06:05:07 +00:00
if crlInfo == nil {
return nil , nil
}
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 {
if a . config . CRL == nil {
// CRL is disabled
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" )
}
crlInfo , err := crlDB . GetCRL ( )
if err != nil {
return errors . Wrap ( err , "could not retrieve CRL from database" )
2021-10-30 07:52:50 +00:00
}
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
// 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-11-04 06:05:07 +00:00
var n int64
2021-10-30 07:52:50 +00:00
var bn big . Int
if crlInfo != nil {
n = crlInfo . Number + 1
}
bn . SetInt64 ( n )
// Convert our database db.RevokedCertificateInfo types into the pkix representation ready for the
// CAS to sign it
var revokedCertificates [ ] pkix . RevokedCertificate
for _ , revokedCert := range * revokedList {
var sn big . Int
sn . SetString ( revokedCert . Serial , 10 )
revokedCertificates = append ( revokedCertificates , pkix . RevokedCertificate {
SerialNumber : & sn ,
RevocationTime : revokedCert . RevokedAt ,
Extensions : nil ,
} )
}
// 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 ,
ThisUpdate : time . Now ( ) . UTC ( ) ,
2021-11-04 06:05:07 +00:00
NextUpdate : time . Now ( ) . UTC ( ) . Add ( a . config . CRL . CacheDuration . Duration ) ,
2021-10-30 07:52:50 +00:00
ExtraExtensions : nil ,
}
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
2021-11-04 06:05:07 +00:00
// expiry time, and the DER-encoded CRL
2021-10-30 07:52:50 +00:00
newCRLInfo := db . CertificateRevocationListInfo {
Number : n ,
ExpiresAt : revocationList . NextUpdate ,
2021-11-04 06:05:07 +00:00
DER : certificateRevocationList . CRL ,
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-02-03 13:21:23 +00:00
cr , err := x509util . CreateCertificateRequest ( "Step Online CA" , 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
2020-09-11 02:09:46 +00:00
resp , err := a . x509CAService . CreateCertificate ( & casapi . CreateCertificateRequest {
Template : certTpl ,
2021-03-18 02:33:35 +00:00
CSR : cr ,
2020-09-11 02:09:46 +00:00
Lifetime : 24 * time . Hour ,
Backdate : 1 * time . Minute ,
} )
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
// 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
cause = fmt . Errorf ( "%s at offset %d" , cause . Error ( ) , 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" )
}