Refactor SCEP provisioner and decrypter

pull/1523/head
Herman Slatman 12 months ago
parent 0377fe559b
commit 180162bd6a
No known key found for this signature in database
GPG Key ID: F4D8A44EA0A75A4F

@ -244,13 +244,16 @@ func (p ProvisionersResponse) MarshalJSON() ([]byte, error) {
continue
}
fmt.Println(scepProv.KMS)
fmt.Println(fmt.Sprintf("%#+v", scepProv))
type old struct {
challengePassword string
decrypterCertificate string
decrypterCertificate []byte
decrypterKey string
decrypterKeyPassword string
}
o := old{scepProv.ChallengePassword, scepProv.DecrypterCert, scepProv.DecrypterKey, scepProv.DecrypterKeyPassword}
o := old{scepProv.ChallengePassword, scepProv.DecrypterCertificate, scepProv.DecrypterKey, scepProv.DecrypterKeyPassword}
scepProv.ChallengePassword = "*** REDACTED ***"
// TODO: remove the details in the API response
// scepProv.DecrypterCert = ""
@ -259,7 +262,7 @@ func (p ProvisionersResponse) MarshalJSON() ([]byte, error) {
defer func(o old) { //nolint:gocritic // defer in loop required to restore initial state of provisioners
scepProv.ChallengePassword = o.challengePassword
scepProv.DecrypterCert = o.decrypterCertificate
scepProv.DecrypterCertificate = o.decrypterCertificate
scepProv.DecrypterKey = o.decrypterKey
scepProv.DecrypterKeyPassword = o.decrypterKeyPassword
}(o)

@ -641,56 +641,45 @@ func (a *Authority) init() error {
return err
}
// Check if a KMS with decryption capability is required and available
if a.requiresDecrypter() {
if _, ok := a.keyManager.(kmsapi.Decrypter); !ok {
return errors.New("keymanager doesn't provide crypto.Decrypter")
}
}
// TODO: decide if this is a good approach for providing the SCEP functionality
// It currently mirrors the logic for the x509CAService
if a.requiresSCEPService() && a.scepService == nil {
// The SCEP functionality is provided through an instance of
// scep.Service. It is initialized once when the CA is started.
// TODO: should the SCEP service support reloading? For example,
// when the admin resources are reloaded, specifically the provisioners,
// it can happen that the SCEP service is no longer required and can
// be destroyed, or that it needs to be instantiated. It may also need
// to be revalidated, because not all SCEP provisioner may have a
// valid decrypter available.
if a.requiresSCEP() && a.GetSCEP() == nil {
var options scep.Options
// Read intermediate and create X509 signer and decrypter for default CAS.
options.CertificateChain, err = pemutil.ReadCertificateBundle(a.config.IntermediateCert)
options.Roots = a.rootX509Certs
options.Intermediates, err = pemutil.ReadCertificateBundle(a.config.IntermediateCert)
if err != nil {
return err
}
options.CertificateChain = append(options.CertificateChain, a.rootX509Certs...)
options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{
options.SignerCert = options.Intermediates[0]
if options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{
SigningKey: a.config.IntermediateKey,
Password: a.password,
})
if err != nil {
}); err != nil {
return err
}
options.SignerCert = options.CertificateChain[0]
options.DecrypterCert = options.CertificateChain[0]
// TODO: instead of creating the decrypter here, pass the
// intermediate key + chain down to the SCEP service / authority,
// and only instantiate it when required there.
// and only instantiate it when required there. Is that possible?
// Also with entering passwords?
// TODO: if moving the logic, try improving the logic for the
// decrypter password too?
if km, ok := a.keyManager.(kmsapi.Decrypter); ok {
options.Decrypter, err = km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{
// decrypter password too? Right now it needs to be entered multiple
// times; I've observed it to be three times maximum, every time
// the intermediate key is read.
_, isRSA := options.Signer.Public().(*rsa.PublicKey)
if km, ok := a.keyManager.(kmsapi.Decrypter); ok && isRSA {
if decrypter, err := km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{
DecryptionKey: a.config.IntermediateKey,
Password: a.password,
})
if err == nil {
// when creating the decrypter fails, ignore the error
// TODO(hs): decide if this is OK. It could fail at startup, but it
// could be up later. Right now decryption would always fail.
key, ok := options.Decrypter.Public().(*rsa.PublicKey)
if !ok {
return errors.New("only RSA keys are currently supported as decrypters")
}
if !key.Equal(options.DecrypterCert.PublicKey) {
return errors.New("mismatch between decryption certificate and decrypter public keys")
}
}); err == nil {
// only pass the decrypter down when it was successfully created
options.Decrypter = decrypter
}
}
@ -698,8 +687,6 @@ func (a *Authority) init() error {
if err != nil {
return err
}
// TODO: mimick the x509CAService GetCertificateAuthority here too?
}
// Load X509 constraints engine.
@ -851,17 +838,9 @@ func (a *Authority) IsRevoked(sn string) (bool, error) {
return a.db.IsRevoked(sn)
}
// requiresDecrypter returns whether the Authority
// requires a KMS that provides a crypto.Decrypter
// Currently this is only required when SCEP is
// enabled.
func (a *Authority) requiresDecrypter() bool {
return a.requiresSCEPService()
}
// requiresSCEPService iterates over the configured provisioners
// and determines if one of them is a SCEP provisioner.
func (a *Authority) requiresSCEPService() bool {
func (a *Authority) requiresSCEP() bool {
for _, p := range a.config.AuthorityConfig.Provisioners {
if p.GetType() == provisioner.TypeSCEP {
return true
@ -870,12 +849,8 @@ func (a *Authority) requiresSCEPService() bool {
return false
}
// GetSCEPService returns the configured SCEP Service.
//
// TODO: this function is intended to exist temporarily in order to make SCEP
// work more easily. It can be made more correct by using the right
// interfaces/abstractions after it works as expected.
func (a *Authority) GetSCEPService() *scep.Service {
// GetSCEP returns the configured SCEP Service.
func (a *Authority) GetSCEP() *scep.Service {
return a.scepService
}

@ -478,7 +478,7 @@ func testScepAuthority(t *testing.T, opts ...Option) *Authority {
return a
}
func TestAuthority_GetSCEPService(t *testing.T) {
func TestAuthority_GetSCEP(t *testing.T) {
_ = testScepAuthority(t)
p := provisioner.List{
&provisioner.SCEP{
@ -542,7 +542,7 @@ func TestAuthority_GetSCEPService(t *testing.T) {
return
}
if tt.wantService {
if got := a.GetSCEPService(); (got != nil) != tt.wantService {
if got := a.GetSCEP(); (got != nil) != tt.wantService {
t.Errorf("Authority.GetSCEPService() = %v, wantService %v", got, tt.wantService)
}
}

@ -6,6 +6,7 @@ import (
"crypto/rsa"
"crypto/subtle"
"crypto/x509"
"encoding/pem"
"fmt"
"net/http"
"time"
@ -14,7 +15,6 @@ import (
"go.step.sm/crypto/kms"
kmsapi "go.step.sm/crypto/kms/apiv1"
"go.step.sm/crypto/pemutil"
"go.step.sm/linkedca"
"github.com/smallstep/certificates/webhook"
@ -40,7 +40,7 @@ type SCEP struct {
// TODO
KMS *kms.Options `json:"kms,omitempty"`
DecrypterCert string `json:"decrypterCert"`
DecrypterCertificate []byte `json:"decrypterCertificate"`
DecrypterKey string `json:"decrypterKey"`
DecrypterKeyPassword string `json:"decrypterKeyPassword"`
@ -198,18 +198,32 @@ func (s *SCEP) Init(config Config) (err error) {
}
km, ok := s.keyManager.(kmsapi.Decrypter)
if !ok {
return fmt.Errorf(`%q is not a kmsapi.Decrypter`, s.KMS.Type)
return fmt.Errorf("%q is not a kmsapi.Decrypter", s.KMS.Type)
}
if s.DecrypterKey != "" || s.DecrypterCert != "" {
if s.DecrypterKey != "" || len(s.DecrypterCertificate) > 0 {
if s.decrypter, err = km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{
DecryptionKey: s.DecrypterKey,
Password: []byte(s.DecrypterKeyPassword),
}); err != nil {
return fmt.Errorf("failed creating decrypter: %w", err)
}
if s.decrypterCertificate, err = pemutil.ReadCertificate(s.DecrypterCert); err != nil {
return fmt.Errorf("failed reading certificate: %w", err)
// Parse decrypter certificate
block, rest := pem.Decode(s.DecrypterCertificate)
if len(rest) > 0 {
fmt.Println(string(rest))
return errors.New("failed parsing decrypter certificate: trailing data")
}
if block == nil {
return errors.New("failed parsing decrypter certificate: no PEM block found")
}
if s.decrypterCertificate, err = x509.ParseCertificate(block.Bytes); err != nil {
return fmt.Errorf("failed parsing decrypter certificate: %w", err)
}
// if s.decrypterCertificate, err = pemutil.ReadCertificate(s.DecrypterCertFile); err != nil {
// return fmt.Errorf("failed reading certificate: %w", err)
// }
decrypterPublicKey, ok := s.decrypter.Public().(*rsa.PublicKey)
if !ok {
return fmt.Errorf("only RSA keys are supported")

@ -8,6 +8,7 @@ import (
"encoding/pem"
"fmt"
"os"
"strings"
"github.com/pkg/errors"
"gopkg.in/square/go-jose.v2/jwt"
@ -15,6 +16,7 @@ import (
"go.step.sm/cli-utils/step"
"go.step.sm/cli-utils/ui"
"go.step.sm/crypto/jose"
"go.step.sm/crypto/kms"
"go.step.sm/linkedca"
"github.com/smallstep/certificates/authority/admin"
@ -235,7 +237,7 @@ func (a *Authority) StoreProvisioner(ctx context.Context, prov *linkedca.Provisi
}
if err := certProv.Init(provisionerConfig); err != nil {
return admin.WrapError(admin.ErrorBadRequestType, err, "error validating configuration for provisioner %s", prov.Name)
return admin.WrapError(admin.ErrorBadRequestType, err, "error validating configuration for provisioner %q", prov.Name)
}
// Store to database -- this will set the ID.
@ -960,7 +962,7 @@ func ProvisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface,
}, nil
case *linkedca.ProvisionerDetails_SCEP:
cfg := d.SCEP
return &provisioner.SCEP{
s := &provisioner.SCEP{
ID: p.Id,
Type: p.Type.String(),
Name: p.Name,
@ -972,7 +974,19 @@ func ProvisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface,
EncryptionAlgorithmIdentifier: int(cfg.EncryptionAlgorithmIdentifier),
Claims: claims,
Options: options,
}, nil
}
if decrypter := cfg.GetDecrypter(); decrypter != nil {
if dkms := decrypter.GetKms(); dkms != nil {
s.KMS = &kms.Options{
Type: kms.Type(strings.ToLower(linkedca.KMS_Type_name[int32(dkms.Type)])),
CredentialsFile: dkms.CredentialsFile,
}
}
s.DecrypterCertificate = decrypter.DecrypterCertificate
s.DecrypterKey = decrypter.DecrypterKey
s.DecrypterKeyPassword = decrypter.DecrypterKeyPassword
}
return s, nil
case *linkedca.ProvisionerDetails_Nebula:
var roots []byte
for i, root := range d.Nebula.GetRoots() {

@ -250,19 +250,24 @@ func (ca *CA) Init(cfg *config.Config) (*CA, error) {
var scepAuthority *scep.Authority
if ca.shouldServeSCEPEndpoints() {
scepPrefix := "scep"
scepAuthority, err = scep.New(auth, scep.AuthorityOptions{
Service: auth.GetSCEPService(),
DNS: dns,
Prefix: scepPrefix,
})
if err != nil {
return nil, errors.Wrap(err, "error creating SCEP authority")
if scepAuthority, err = scep.New(auth, scep.AuthorityOptions{
Service: auth.GetSCEP(),
}); err != nil {
return nil, errors.Wrap(err, "failed creating SCEP authority")
}
// TODO: validate that the scepAuthority is fully valid? E.g. initialization
// may have configured the default decrypter, but if that's not set or if it's
// somehow not usable, all SCEP provisioners should have a valid decrypter
// configured by now.
if err := scepAuthority.Validate(); err != nil {
return nil, errors.Wrap(err, "failed validating SCEP authority")
}
// According to the RFC (https://tools.ietf.org/html/rfc8894#section-7.10),
// SCEP operations are performed using HTTP, so that's why the API is mounted
// to the insecure mux.
scepPrefix := "scep"
insecureMux.Route("/"+scepPrefix, func(r chi.Router) {
scepAPI.Route(r)
})
@ -584,10 +589,10 @@ func (ca *CA) getTLSConfig(auth *authority.Authority) (*tls.Config, *tls.Config,
// shouldServeSCEPEndpoints returns if the CA should be
// configured with endpoints for SCEP. This is assumed to be
// true if a SCEPService exists, which is true in case a
// SCEP provisioner was configured.
// true if a SCEPService exists, which is true in case at
// least one SCEP provisioner was configured.
func (ca *CA) shouldServeSCEPEndpoints() bool {
return ca.auth.GetSCEPService() != nil
return ca.auth.GetSCEP() != nil
}
//nolint:unused // useful for debugging

@ -12,7 +12,7 @@ require (
github.com/google/go-cmp v0.5.9
github.com/google/go-tpm v0.3.3
github.com/google/uuid v1.3.0
github.com/googleapis/gax-go/v2 v2.8.0
github.com/googleapis/gax-go/v2 v2.9.0
github.com/hashicorp/vault/api v1.9.1
github.com/hashicorp/vault/api/auth/approle v0.4.0
github.com/hashicorp/vault/api/auth/kubernetes v0.4.0
@ -25,16 +25,16 @@ require (
github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262
github.com/smallstep/go-attestation v0.4.4-0.20230509120429-e17291421738
github.com/smallstep/nosql v0.6.0
github.com/stretchr/testify v1.8.2
github.com/stretchr/testify v1.8.3
github.com/urfave/cli v1.22.13
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352
go.step.sm/cli-utils v0.7.6
go.step.sm/crypto v0.30.0
go.step.sm/crypto v0.31.0
go.step.sm/linkedca v0.19.1
golang.org/x/crypto v0.8.0
golang.org/x/crypto v0.9.0
golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0
golang.org/x/net v0.9.0
google.golang.org/api v0.121.0
golang.org/x/net v0.10.0
google.golang.org/api v0.123.0
google.golang.org/grpc v1.55.0
google.golang.org/protobuf v1.30.0
gopkg.in/square/go-jose.v2 v2.6.0
@ -57,7 +57,7 @@ require (
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.0 // indirect
github.com/ThalesIgnite/crypto11 v1.2.5 // indirect
github.com/aws/aws-sdk-go v1.44.259 // indirect
github.com/aws/aws-sdk-go v1.44.267 // indirect
github.com/cenkalti/backoff/v3 v3.0.0 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
@ -80,7 +80,7 @@ require (
github.com/golang/snappy v0.0.4 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/certificate-transparency-go v1.1.4 // indirect
github.com/google/go-tpm-tools v0.3.11 // indirect
github.com/google/go-tpm-tools v0.3.12 // indirect
github.com/google/go-tspi v0.3.0 // indirect
github.com/google/s2a-go v0.1.3 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
@ -129,7 +129,7 @@ require (
go.etcd.io/bbolt v1.3.7 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/oauth2 v0.7.0 // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/time v0.1.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
@ -145,3 +145,5 @@ require (
// use github.com/smallstep/pkcs7 fork with patches applied
replace go.mozilla.org/pkcs7 => github.com/smallstep/pkcs7 v0.0.0-20230302202335-4c094085c948
replace go.step.sm/linkedca => ./../linkedca

@ -165,8 +165,8 @@ github.com/aws/aws-sdk-go v1.25.11/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi
github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go v1.44.259 h1:7yDn1dcv4DZFMKpu+2exIH5O6ipNj9qXrKfdMUaIJwY=
github.com/aws/aws-sdk-go v1.44.259/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go v1.44.267 h1:Asrp6EMqqRxZvjK0NjzkWcrOk15RnWtupuUrUuZMabk=
github.com/aws/aws-sdk-go v1.44.267/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
@ -430,7 +430,7 @@ github.com/google/go-licenses v0.0.0-20210329231322-ce1d9163b77d/go.mod h1:+TYOm
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE=
github.com/google/go-replayers/httpreplay v0.1.0/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no=
github.com/google/go-sev-guest v0.5.2 h1:dlCehnxU9aJWEIcTb0j7oZ/yM4qeno7AO6zWokb4mu0=
github.com/google/go-sev-guest v0.6.1 h1:NajHkAaLqN9/aW7bCFSUplUMtDgk2+HcN7jC2btFtk0=
github.com/google/go-tpm v0.1.2-0.20190725015402-ae6dd98980d4/go.mod h1:H9HbmUG2YgV/PHITkO7p6wxEEj/v5nlsVWIwumwH2NI=
github.com/google/go-tpm v0.3.0/go.mod h1:iVLWvrPp/bHeEkxTFi9WG6K9w0iy2yIszHwZGHPbzAw=
github.com/google/go-tpm v0.3.2/go.mod h1:j71sMBTfp3X5jPHz852ZOfQMUOf65Gb/Th8pRmp7fvg=
@ -441,8 +441,8 @@ github.com/google/go-tpm-tools v0.2.0/go.mod h1:npUd03rQ60lxN7tzeBJreG38RvWwme2N
github.com/google/go-tpm-tools v0.2.1/go.mod h1:npUd03rQ60lxN7tzeBJreG38RvWwme2N1reF/eeiBk4=
github.com/google/go-tpm-tools v0.3.1/go.mod h1:PSg+r5hSZI5tP3X7LBQx2sW1VSZUqZHBSrKyDqrB21U=
github.com/google/go-tpm-tools v0.3.9/go.mod h1:22JvWmHcD5w55cs+nMeqDGDxgNS15/2pDq2cLqnc3rc=
github.com/google/go-tpm-tools v0.3.11 h1:imObhmECgDS+ua4aAVPkMfCzE9LTZjS/MmVMCrAG4VY=
github.com/google/go-tpm-tools v0.3.11/go.mod h1:5UcOsOyG5B2hWhKsqNI3TtOjTcZs5sh+3913uMN29Y8=
github.com/google/go-tpm-tools v0.3.12 h1:hpWglH4RaZnGVbgOK3IThI5K++jnFvjQ94EIN34xrUU=
github.com/google/go-tpm-tools v0.3.12/go.mod h1:2OtmyPGPuaWWIOjr+IDhNQb6t5njjbSmZtzc350Q6Ro=
github.com/google/go-tspi v0.2.1-0.20190423175329-115dea689aad/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI=
github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus=
github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI=
@ -491,8 +491,8 @@ github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5
github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.8.0 h1:UBtEZqx1bjXtOQ5BVTkuYghXrr3N4V123VKJK67vJZc=
github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
github.com/googleapis/gax-go/v2 v2.9.0 h1:ie5/2yPjucjZW6fEGjLhS5+PhEg6owWMrFB5d7TFFhw=
github.com/googleapis/gax-go/v2 v2.9.0/go.mod h1:qf/E3rjAvrwLsAnQW+IClIu+z03yUf4KOoO82NfZ+QY=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU=
github.com/goreleaser/goreleaser v0.134.0/go.mod h1:ZT6Y2rSYa6NxQzIsdfWWNWAlYGXGbreo66NmE+3X3WQ=
@ -968,8 +968,9 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg=
github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU=
@ -1062,8 +1063,8 @@ go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16g
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.step.sm/cli-utils v0.7.6 h1:YkpLVrepmy2c5+eaz/wduiGxlgrRx3YdAStE37if25g=
go.step.sm/cli-utils v0.7.6/go.mod h1:j+FxFZ2gbWkAJl0eded/rksuxmNqWpmyxbkXcukGJaY=
go.step.sm/crypto v0.30.0 h1:EzqPTvW1g6kxEnfIf/exDW+MhHGeEhtoNMhQX7P/UwI=
go.step.sm/crypto v0.30.0/go.mod h1:6jFFgUoafyHvb6rNq3NJrBByof4SCzj1n8ThyXuMVAM=
go.step.sm/crypto v0.31.0 h1:8ZG/BxC+0+LzPpk/764h5yubpG3GfxcRVR4E+Aye72g=
go.step.sm/crypto v0.31.0/go.mod h1:Dv4lpkijKiZVkoc6zp+Xaw1xmy+voia1mykvbpQIvuc=
go.step.sm/linkedca v0.19.1 h1:uY0ByT/uB3FCQ8zIo9mU7MWG7HKf5sDXNEBeN94MuP8=
go.step.sm/linkedca v0.19.1/go.mod h1:vPV2ad3LFQJmV7XWt87VlnJSs6UOqgsbVGVWe3veEmI=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
@ -1111,8 +1112,8 @@ golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -1211,8 +1212,8 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -1340,8 +1341,8 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -1349,7 +1350,7 @@ golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -1490,8 +1491,8 @@ google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtuk
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
google.golang.org/api v0.121.0 h1:8Oopoo8Vavxx6gt+sgs8s8/X60WBAtKQq6JqnkF+xow=
google.golang.org/api v0.121.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms=
google.golang.org/api v0.123.0 h1:yHVU//vA+qkOhm4reEC9LtzHVUCN/IqqNRl1iQ9xE20=
google.golang.org/api v0.123.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=

@ -6,7 +6,6 @@ import (
"crypto/x509"
"errors"
"fmt"
"net/url"
microx509util "github.com/micromdm/scep/v2/cryptoutil/x509util"
microscep "github.com/micromdm/scep/v2/scep"
@ -19,9 +18,7 @@ import (
// Authority is the layer that handles all SCEP interactions.
type Authority struct {
prefix string
dns string
service *Service
service *Service // TODO: refactor, so that this is not required
signAuth SignAuthority
}
@ -50,15 +47,9 @@ func MustFromContext(ctx context.Context) *Authority {
// AuthorityOptions required to create a new SCEP Authority.
type AuthorityOptions struct {
// Service provides the certificate chain, the signer and the decrypter to the Authority
// Service provides the roots, intermediates, the signer and the (default)
// decrypter to the SCEP Authority.
Service *Service
// DNS is the host used to generate accurate SCEP links. By default the authority
// will use the Host from the request, so this value will only be used if
// request.Host is empty.
DNS string
// Prefix is a URL path prefix under which the SCEP api is served. This
// prefix is required to generate accurate SCEP links.
Prefix string
}
// SignAuthority is the interface for a signing authority
@ -70,14 +61,35 @@ type SignAuthority interface {
// New returns a new Authority that implements the SCEP interface.
func New(signAuth SignAuthority, ops AuthorityOptions) (*Authority, error) {
authority := &Authority{
prefix: ops.Prefix,
dns: ops.DNS,
signAuth: signAuth,
service: ops.Service,
}
return authority, nil
}
func (a *Authority) Validate() error {
// if a default decrypter is set, the Authority is able
// to decrypt SCEP requests. No need to verify the provisioners.
if a.service.defaultDecrypter != nil {
return nil
}
for _, name := range []string{"scepca"} { // TODO: correct names; provided through options
p, err := a.LoadProvisionerByName(name)
if err != nil {
fmt.Println("prov load fail: %w", err)
}
if scepProv, ok := p.(*provisioner.SCEP); ok {
if cert, decrypter := scepProv.GetDecrypter(); cert == nil || decrypter == nil {
fmt.Println(fmt.Sprintf("SCEP provisioner %q doesn't have valid decrypter", scepProv.GetName()))
// TODO: return error
}
}
}
return nil
}
var (
// TODO: check the default capabilities; https://tools.ietf.org/html/rfc8894#section-3.5.2
defaultCapabilities = []string{
@ -97,79 +109,38 @@ func (a *Authority) LoadProvisionerByName(name string) (provisioner.Interface, e
return a.signAuth.LoadProvisionerByName(name)
}
// GetLinkExplicit returns the requested link from the directory.
func (a *Authority) GetLinkExplicit(provName string, abs bool, baseURL *url.URL, inputs ...string) string {
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, _ ...string) string {
link := "/" + provisionerName
if abs {
// Copy the baseURL value from the pointer. https://github.com/golang/go/issues/38351
u := url.URL{}
if baseURL != nil {
u = *baseURL
}
// If no Scheme is set, then default to http (in case of SCEP)
if u.Scheme == "" {
u.Scheme = "http"
}
// If no Host is set, then use the default (first DNS attr in the ca.json).
if u.Host == "" {
u.Host = a.dns
}
u.Path = a.prefix + link
return u.String()
}
return link
}
// GetCACertificates returns the certificate (chain) for the CA
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
//
// This means we might need to think about if we should use the current intermediate CA
// certificate as the "SCEP Server (RA)" certificate. It might be better to have a distinct
// RA certificate, with a corresponding rsa.PrivateKey, just for SCEP usage, which is signed by
// the intermediate CA. Will need to look how we can provide this nicely within step-ca.
//
// This might also mean that we might want to use a distinct instance of KMS for doing the key operations,
// so that we can use RSA just for SCEP.
//
// 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) provisioner and may
// use a distinct certificate, apart from the intermediate.
// GetCACertificates returns the certificate (chain) for the CA.
//
// This methods returns 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
//
// In case a provisioner specific decrypter is available, this is used as the "SCEP Server (RA)" certificate
// instead of the CA intermediate directly. This uses a distinct instance of a KMS for doing the SCEp key
// operations, so that RSA can be used for just SCEP.
//
// 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.
func (a *Authority) GetCACertificates(ctx context.Context) (certs []*x509.Certificate, err error) {
p, err := provisionerFromContext(ctx)
if err != nil {
return nil, err
}
if len(a.service.certificateChain) == 0 {
return nil, errors.New("no intermediate certificate available in SCEP authority")
return
}
certs := []*x509.Certificate{}
// if a provisioner specific RSA decrypter is available, it is returned as
// the first certificate.
if decrypterCertificate, _ := p.GetDecrypter(); decrypterCertificate != nil {
certs = append(certs, decrypterCertificate)
certs = append(certs, a.service.signerCertificate)
} else {
certs = append(certs, a.service.defaultDecrypterCertificate)
}
// NOTE: we're adding the CA roots here, but they are (highly likely) different than what the RFC means.
// Clients are responsible to select the right cert(s) to use, though.
if p.ShouldIncludeRootInChain() && len(a.service.certificateChain) > 1 {
certs = append(certs, a.service.certificateChain[1])
// TODO: ensure logic, so that signer is first intermediate and that
// there are no doubles certificates.
//certs = append(certs, a.service.signerCertificate)
certs = append(certs, a.service.intermediates...)
// the CA roots are added for completeness. Clients are responsible
// to select the right cert(s) to store and use.
if p.ShouldIncludeRootInChain() {
certs = append(certs, a.service.roots...)
}
return certs, nil
@ -182,7 +153,7 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err
return fmt.Errorf("error parsing pkcs7 content: %w", err)
}
fmt.Println(fmt.Sprintf("%#+v", a.service.defaultDecrypterCertificate))
fmt.Println(fmt.Sprintf("%#+v", a.service.signerCertificate))
fmt.Println(fmt.Sprintf("%#+v", a.service.defaultDecrypter))
cert, pkey, err := a.selectDecrypter(ctx)
@ -246,7 +217,7 @@ func (a *Authority) selectDecrypter(ctx context.Context) (cert *x509.Certificate
}
// fallback to the CA wide decrypter
cert = a.service.defaultDecrypterCertificate
cert = a.service.signerCertificate
pkey = a.service.defaultDecrypter
return

@ -2,77 +2,76 @@ package scep
import (
"crypto"
"crypto/rsa"
"crypto/x509"
"github.com/pkg/errors"
"errors"
)
type Options struct {
// CertificateChain is the issuer certificate, along with any other bundled certificates
// to be returned in the chain for consumers. Configured in the ca.json crt property.
CertificateChain []*x509.Certificate
SignerCert *x509.Certificate
DecrypterCert *x509.Certificate
// Roots contains the (federated) CA roots certificate(s)
Roots []*x509.Certificate `json:"-"`
// Intermediates points issuer certificate, along with any other bundled certificates
// to be returned in the chain for consumers.
Intermediates []*x509.Certificate `json:"-"`
// SignerCert points to the certificate of the CA signer. It usually is the same as the
// first certificate in the CertificateChain.
SignerCert *x509.Certificate `json:"-"`
// Signer signs CSRs in SCEP. Configured in the ca.json key property.
Signer crypto.Signer `json:"-"`
// Decrypter decrypts encrypted SCEP messages. Configured in the ca.json key property.
Decrypter crypto.Decrypter `json:"-"`
}
type comparablePublicKey interface {
Equal(crypto.PublicKey) bool
}
// Validate checks the fields in Options.
func (o *Options) Validate() error {
if o.CertificateChain == nil {
return errors.New("certificate chain not configured correctly")
switch {
case len(o.Intermediates) == 0:
return errors.New("no intermediate certificate available for SCEP authority")
case o.Signer == nil:
return errors.New("no signer available for SCEP authority")
case o.SignerCert == nil:
return errors.New("no signer certificate available for SCEP authority")
}
if len(o.CertificateChain) < 1 {
return errors.New("certificate chain should at least have one certificate")
// check if the signer (intermediate CA) certificate has the same public key as
// the signer. According to the RFC it seems valid to have different keys for
// the intermediate and the CA signing new certificates, so this might change
// in the future.
signerPublicKey := o.Signer.Public().(comparablePublicKey)
if !signerPublicKey.Equal(o.SignerCert.PublicKey) {
return errors.New("mismatch between signer certificate and public key")
}
// According to the RFC: https://tools.ietf.org/html/rfc8894#section-3.1, SCEP
// can be used with something different than RSA, but requires the encryption
// to be performed using the challenge password. An older version of specification
// states that only RSA is supported: https://tools.ietf.org/html/draft-nourse-scep-23#section-2.1.1
// Other algorithms than RSA do not seem to be supported in certnanny/sscep, but it might work
// decrypter can be nil in case a signing only key is used; validation complete.
if o.Decrypter == nil {
return nil
}
// If a decrypter is available, check that it's backed by an RSA key. According to the
// RFC: https://tools.ietf.org/html/rfc8894#section-3.1, SCEP can be used with something
// different than RSA, but requires the encryption to be performed using the challenge
// password in that case. An older version of specification states that only RSA is
// supported: https://tools.ietf.org/html/draft-nourse-scep-23#section-2.1.1. Other
// algorithms do not seem to be supported in certnanny/sscep, but it might work
// in micromdm/scep. Currently only RSA is allowed, but it might be an option
// to try other algorithms in the future.
//intermediate := o.CertificateChain[0]
//intermediate := o.SignerCert
// if intermediate.PublicKeyAlgorithm != x509.RSA {
// return errors.New("only the RSA algorithm is (currently) supported")
// }
// TODO: add checks for key usage?
//signerPublicKey, ok := o.Signer.Public().(*rsa.PublicKey)
// if !ok {
// return errors.New("only RSA public keys are (currently) supported as signers")
// }
// check if the intermediate ca certificate has the same public key as the signer.
// According to the RFC it seems valid to have different keys for the intermediate
// and the CA signing new certificates, so this might change in the future.
// if !signerPublicKey.Equal(intermediate.PublicKey) {
// return errors.New("mismatch between certificate chain and signer public keys")
// }
// TODO: this could be a different decrypter, based on the value
// in the provisioner.
// decrypterPublicKey, ok := o.Decrypter.Public().(*rsa.PublicKey)
// if !ok {
// return errors.New("only RSA public keys are (currently) supported as decrypters")
// }
decrypterPublicKey, ok := o.Decrypter.Public().(*rsa.PublicKey)
if !ok {
return errors.New("only RSA keys are (currently) supported as decrypters")
}
// check if intermediate public key is the same as the decrypter public key.
// In certnanny/sscep it's mentioned that the signing key can be different
// from the decrypting (and encrypting) key. Currently that's not supported.
// if !decrypterPublicKey.Equal(intermediate.PublicKey) {
// return errors.New("mismatch between certificate chain and decrypter public keys")
// }
// if !decrypterPublicKey.Equal(o.DecrypterCert.PublicKey) {
// return errors.New("mismatch between certificate chain and decrypter public keys")
// }
// from the decrypting (and encrypting) key. These options are only used and
// validated when the intermediate CA is also used as the decrypter, though,
// so they should match.
if !decrypterPublicKey.Equal(o.SignerCert.PublicKey) {
return errors.New("mismatch between certificate chain and decrypter public keys")
}
return nil
}

@ -6,13 +6,15 @@ import (
"crypto/x509"
)
// Service is a wrapper for crypto.Signer and crypto.Decrypter
// Service is a wrapper for a crypto.Decrypter and crypto.Signer for
// decrypting SCEP requests and signing certificates in response to
// SCEP certificate requests.
type Service struct {
certificateChain []*x509.Certificate
signerCertificate *x509.Certificate
signer crypto.Signer
defaultDecrypterCertificate *x509.Certificate
defaultDecrypter crypto.Decrypter
roots []*x509.Certificate
intermediates []*x509.Certificate
signerCertificate *x509.Certificate
signer crypto.Signer
defaultDecrypter crypto.Decrypter
}
// NewService returns a new Service type.
@ -20,13 +22,11 @@ func NewService(_ context.Context, opts Options) (*Service, error) {
if err := opts.Validate(); err != nil {
return nil, err
}
// TODO: should this become similar to the New CertificateAuthorityService as in x509CAService?
return &Service{
certificateChain: opts.CertificateChain,
signerCertificate: opts.SignerCert,
signer: opts.Signer,
defaultDecrypterCertificate: opts.DecrypterCert,
defaultDecrypter: opts.Decrypter,
roots: opts.Roots,
intermediates: opts.Intermediates,
signerCertificate: opts.SignerCert,
signer: opts.Signer,
defaultDecrypter: opts.Decrypter,
}, nil
}

Loading…
Cancel
Save