@ -23,7 +23,7 @@ type Interface interface {
LoadProvisionerByID ( string ) ( provisioner . Interface , error )
GetLinkExplicit ( provName string , absoluteLink bool , baseURL * url . URL , inputs ... string ) string
GetCACertificates ( ) ( [ ] * x509 . Certificate , error )
GetCACertificates ( ctx context . Context ) ( [ ] * x509 . Certificate , error )
DecryptPKIEnvelope ( ctx context . Context , msg * PKIMessage ) error
SignCSR ( ctx context . Context , csr * x509 . CertificateRequest , msg * PKIMessage ) ( * PKIMessage , error )
CreateFailureResponse ( ctx context . Context , csr * x509 . CertificateRequest , msg * PKIMessage , info FailInfoName , infoText string ) ( * PKIMessage , error )
@ -36,6 +36,7 @@ type Authority struct {
prefix string
dns string
intermediateCertificate * x509 . Certificate
caCerts [ ] * x509 . Certificate // TODO(hs): change to use these instead of root and intermediate
service * Service
signAuth SignAuthority
}
@ -72,6 +73,8 @@ func New(signAuth SignAuthority, ops AuthorityOptions) (*Authority, error) {
// in its entirety to make this more interoperable with the rest of
// step-ca, I think.
if ops . Service != nil {
authority . caCerts = ops . Service . certificateChain
// TODO(hs): look into refactoring SCEP into using just caCerts everywhere, if it makes sense for more elaborate SCEP configuration. Keeping it like this for clarity (for now).
authority . intermediateCertificate = ops . Service . certificateChain [ 0 ]
authority . service = ops . Service
}
@ -82,7 +85,7 @@ func New(signAuth SignAuthority, ops AuthorityOptions) (*Authority, error) {
var (
// TODO: check the default capabilities; https://tools.ietf.org/html/rfc8894#section-3.5.2
defaultCapabilities = [ ] string {
"Renewal" ,
"Renewal" , // NOTE: removing this will result in macOS SCEP client stating the server doesn't support renewal, but it uses PKCSreq to do so.
"SHA-1" ,
"SHA-256" ,
"AES" ,
@ -100,18 +103,13 @@ func (a *Authority) LoadProvisionerByID(id string) (provisioner.Interface, error
// GetLinkExplicit returns the requested link from the directory.
func ( a * Authority ) GetLinkExplicit ( provName string , abs bool , baseURL * url . URL , inputs ... string ) string {
// TODO: taken from ACME; move it to directory (if we need a directory in SCEP)?
return a . getLinkExplicit ( provName , abs , baseURL , inputs ... )
}
// getLinkExplicit returns an absolute or partial path to the given resource and a base
// URL dynamically obtained from the request for which the link is being calculated.
func ( a * Authority ) getLinkExplicit ( provisionerName string , abs bool , baseURL * url . URL , inputs ... string ) string {
// TODO: do we need to provide a way to provide a different suffix?
// Like "/cgi-bin/pkiclient.exe"? Or would it be enough to have that as the name?
link := "/" + provisionerName
if abs {
// Copy the baseURL value from the pointer. https://github.com/golang/go/issues/38351
u := url . URL { }
@ -137,7 +135,7 @@ func (a *Authority) getLinkExplicit(provisionerName string, abs bool, baseURL *u
}
// GetCACertificates returns the certificate (chain) for the CA
func ( a * Authority ) GetCACertificates ( ) ( [ ] * x509 . Certificate , error ) {
func ( a * Authority ) GetCACertificates ( ctx context . Context ) ( [ ] * x509 . Certificate , error ) {
// TODO: this should return: the "SCEP Server (RA)" certificate, the issuing CA up to and excl. the root
// Some clients do need the root certificate however; also see: https://github.com/openxpki/openxpki/issues/73
@ -153,14 +151,27 @@ func (a *Authority) GetCACertificates() ([]*x509.Certificate, error) {
// Using an RA does not seem to exist in https://tools.ietf.org/html/rfc8894, but is mentioned in
// https://tools.ietf.org/id/draft-nourse-scep-21.html. Will continue using the CA directly for now.
//
// The certificate to use should probably depend on the (configured) P rovisioner and may
// The certificate to use should probably depend on the (configured) p rovisioner and may
// use a distinct certificate, apart from the intermediate.
if a . intermediateCertificate == nil {
p , err := provisionerFromContext ( ctx )
if err != nil {
return nil , err
}
if len ( a . caCerts ) == 0 {
return nil , errors . New ( "no intermediate certificate available in SCEP authority" )
}
return [ ] * x509 . Certificate { a . intermediateCertificate } , nil
certs := [ ] * x509 . Certificate { }
certs = append ( certs , a . caCerts [ 0 ] )
// TODO(hs): we're adding the roots here, but they may be something different than what the RFC means. Clients are responsible to select the right cert(s) to use, though.
if p . ShouldIncludeRootsInChain ( ) && len ( a . caCerts ) >= 2 {
certs = append ( certs , a . caCerts [ 1 : ] ... )
}
return certs , nil
}
// DecryptPKIEnvelope decrypts an enveloped message
@ -211,8 +222,6 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err
// SignCSR creates an x509.Certificate based on a CSR template and Cert Authority credentials
// returns a new PKIMessage with CertRep data
//func (msg *PKIMessage) SignCSR(crtAuth *x509.Certificate, keyAuth *rsa.PrivateKey, template *x509.Certificate) (*PKIMessage, error) {
//func (a *Authority) SignCSR(ctx context.Context, msg *PKIMessage, template *x509.Certificate) (*PKIMessage, error) {
func ( a * Authority ) SignCSR ( ctx context . Context , csr * x509 . CertificateRequest , msg * PKIMessage ) ( * PKIMessage , error ) {
// TODO: intermediate storage of the request? In SCEP it's possible to request a csr/certificate
@ -220,7 +229,7 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m
// poll for the status. It seems to be similar as what can happen in ACME, so might want to model
// the implementation after the one in the ACME authority. Requires storage, etc.
p , err := P rovisionerFromContext( ctx )
p , err := p rovisionerFromContext( ctx )
if err != nil {
return nil , err
}
@ -292,10 +301,17 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m
return nil , err
}
// apparently the pkcs7 library uses a global default setting for the content encryption
// algorithm to use when en- or decrypting data. We need to restore the current setting after
// the cryptographic operation, so that other usages of the library are not influenced by
// this call to Encrypt().
encryptionAlgorithmToRestore := pkcs7 . ContentEncryptionAlgorithm
pkcs7 . ContentEncryptionAlgorithm = p . GetContentEncryptionAlgorithm ( )
e7 , err := pkcs7 . Encrypt ( deg , msg . P7 . Certificates )
if err != nil {
return nil , err
}
pkcs7 . ContentEncryptionAlgorithm = encryptionAlgorithmToRestore
// PKIMessageAttributes to be signed
config := pkcs7 . SignerInfoConfig {
@ -434,7 +450,7 @@ func (a *Authority) CreateFailureResponse(ctx context.Context, csr *x509.Certifi
// MatchChallengePassword verifies a SCEP challenge password
func ( a * Authority ) MatchChallengePassword ( ctx context . Context , password string ) ( bool , error ) {
p , err := P rovisionerFromContext( ctx )
p , err := p rovisionerFromContext( ctx )
if err != nil {
return false , err
}
@ -453,7 +469,7 @@ func (a *Authority) MatchChallengePassword(ctx context.Context, password string)
// GetCACaps returns the CA capabilities
func ( a * Authority ) GetCACaps ( ctx context . Context ) [ ] string {
p , err := P rovisionerFromContext( ctx )
p , err := p rovisionerFromContext( ctx )
if err != nil {
return defaultCapabilities
}