forked from TrueCloudLab/certificates
Refactor initialization of SCEP authority
This commit is contained in:
parent
f871f8135c
commit
7ad90d10b3
19 changed files with 476 additions and 72 deletions
|
@ -11,6 +11,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/smallstep/certificates/cas"
|
||||
"github.com/smallstep/certificates/scep"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/smallstep/certificates/authority/provisioner"
|
||||
|
@ -42,6 +43,9 @@ type Authority struct {
|
|||
federatedX509Certs []*x509.Certificate
|
||||
certificates *sync.Map
|
||||
|
||||
// SCEP CA
|
||||
scepService *scep.Service
|
||||
|
||||
// SSH CA
|
||||
sshCAUserCertSignKey ssh.Signer
|
||||
sshCAHostCertSignKey ssh.Signer
|
||||
|
@ -196,6 +200,38 @@ func (a *Authority) init() error {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: decide if this is a good approach for providing the SCEP functionality
|
||||
if a.scepService == nil {
|
||||
var options casapi.Options
|
||||
if a.config.AuthorityConfig.Options != nil {
|
||||
options = *a.config.AuthorityConfig.Options
|
||||
}
|
||||
|
||||
// Read intermediate and create X509 signer for default CAS.
|
||||
if options.Is(casapi.SoftCAS) {
|
||||
options.CertificateChain, err = pemutil.ReadCertificateBundle(a.config.IntermediateCert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{
|
||||
SigningKey: a.config.IntermediateKey,
|
||||
Password: []byte(a.config.Password),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options.Decrypter, err = a.keyManager.CreateDecrypter(&kmsapi.CreateDecrypterRequest{
|
||||
DecryptionKey: a.config.IntermediateKey,
|
||||
Password: []byte(a.config.Password),
|
||||
})
|
||||
}
|
||||
|
||||
a.scepService = &scep.Service{
|
||||
Signer: options.Signer,
|
||||
Decrypter: options.Decrypter,
|
||||
}
|
||||
}
|
||||
|
||||
// Read root certificates and store them in the certificates map.
|
||||
if len(a.rootX509Certs) == 0 {
|
||||
a.rootX509Certs = make([]*x509.Certificate, len(a.config.Root))
|
||||
|
@ -394,3 +430,12 @@ func (a *Authority) CloseForReload() {
|
|||
log.Printf("error closing the key manager: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return *a.scepService
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ func (s *SCEP) DefaultTLSCertDuration() time.Duration {
|
|||
return s.claimer.DefaultTLSCertDuration()
|
||||
}
|
||||
|
||||
// Init initializes and validates the fields of a JWK type.
|
||||
// Init initializes and validates the fields of a SCEP type.
|
||||
func (s *SCEP) Init(config Config) (err error) {
|
||||
|
||||
switch {
|
||||
|
@ -70,6 +70,8 @@ func (s *SCEP) Init(config Config) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
// TODO: add other, SCEP specific, options?
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
3
ca/ca.go
3
ca/ca.go
|
@ -156,10 +156,11 @@ func (ca *CA) Init(config *authority.Config) (*CA, error) {
|
|||
// well as certificates via SCEP.
|
||||
tlsConfig = nil
|
||||
|
||||
// TODO: get the SCEP service
|
||||
scepPrefix := "scep"
|
||||
scepAuthority, err := scep.New(auth, scep.AuthorityOptions{
|
||||
IntermediateCertificatePath: config.IntermediateCert,
|
||||
IntermediateKeyPath: config.IntermediateKey,
|
||||
Service: auth.GetSCEPService(),
|
||||
Backdate: *config.AuthorityConfig.Backdate,
|
||||
DB: auth.GetDatabase().(nosql.DB),
|
||||
DNS: dns,
|
||||
|
|
|
@ -25,6 +25,7 @@ type Options struct {
|
|||
// They are configured in ca.json crt and key properties.
|
||||
CertificateChain []*x509.Certificate
|
||||
Signer crypto.Signer `json:"-"`
|
||||
Decrypter crypto.Decrypter `json:"-"`
|
||||
|
||||
// IsCreator is set to true when we're creating a certificate authority. Is
|
||||
// used to skip some validations when initializing a CertificateAuthority.
|
||||
|
|
|
@ -110,6 +110,10 @@ func (m *mockKeyManager) CreateSigner(req *kmsapi.CreateSignerRequest) (crypto.S
|
|||
return signer, m.errCreatesigner
|
||||
}
|
||||
|
||||
func (m *mockKeyManager) CreateDecrypter(req *kmsapi.CreateDecrypterRequest) (crypto.Decrypter, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *mockKeyManager) Close() error {
|
||||
return m.errClose
|
||||
}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -13,7 +13,7 @@ require (
|
|||
github.com/google/uuid v1.1.2
|
||||
github.com/googleapis/gax-go/v2 v2.0.5
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
||||
github.com/micromdm/scep v1.0.0
|
||||
github.com/micromdm/scep v1.0.1-0.20210219005251-6027e7654b23
|
||||
github.com/newrelic/go-agent v2.15.0+incompatible
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/rs/xid v1.2.1
|
||||
|
|
21
go.sum
21
go.sum
|
@ -56,6 +56,7 @@ github.com/ThalesIgnite/crypto11 v1.2.4/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR
|
|||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/aws/aws-sdk-go v1.30.29 h1:NXNqBS9hjOCpDL8SyCyl38gZX3LLLunKOJc5E7vJ8P0=
|
||||
github.com/aws/aws-sdk-go v1.30.29/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
|
@ -103,10 +104,16 @@ github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxm
|
|||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-kit/kit v0.4.0 h1:KeVK+Emj3c3S4eRztFuzbFYb2BAgf2jmwDwyXEri7Lo=
|
||||
github.com/go-kit/kit v0.4.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-piv/piv-go v1.7.0 h1:rfjdFdASfGV5KLJhSjgpGJ5lzVZVtRWn8ovy/H9HQ/U=
|
||||
github.com/go-piv/piv-go v1.7.0/go.mod h1:ON2WvQncm7dIkCQ7kYJs+nc3V4jHGfrrJnSF8HKy7Gk=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.6.0 h1:MmJCxYVKTJ0SplGKqFVX3SBnmaUhODHZrrFF6jMbpZk=
|
||||
github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
|
@ -177,6 +184,9 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
|||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.4.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda/go.mod h1:MyndkAZd5rUMdNogn35MWXBX1UiBigrU8eTj8DoAC2c=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
|
@ -202,6 +212,8 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGi
|
|||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||
|
@ -223,8 +235,8 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx
|
|||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/micromdm/scep v1.0.0 h1:ai//kcZnxZPq1YE/MatiE2bIRD94KOAwZRpN1fhVQXY=
|
||||
github.com/micromdm/scep v1.0.0/go.mod h1:CID2SixSr5FvoauZdAFUSpQkn5MAuSy9oyURMGOJbag=
|
||||
github.com/micromdm/scep v1.0.1-0.20210219005251-6027e7654b23 h1:QACkVsQ7Qx4PuPDFL2OFD5u4OnYT0TkReWk9IVqaGHA=
|
||||
github.com/micromdm/scep v1.0.1-0.20210219005251-6027e7654b23/go.mod h1:a82oNZyGV8jj9Do7dh8EkA90+esBls0CZHR6B85Qda8=
|
||||
github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f h1:eVB9ELsoq5ouItQBr5Tj334bhPJG/MX+m7rTchmzVUQ=
|
||||
github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
|
||||
|
@ -236,6 +248,7 @@ github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx
|
|||
github.com/newrelic/go-agent v2.15.0+incompatible h1:IB0Fy+dClpBq9aEoIrLyQXzU34JyI1xVTanPLB/+jvU=
|
||||
github.com/newrelic/go-agent v2.15.0+incompatible/go.mod h1:a8Fv1b/fYhFSReoTU6HDkTYIMZeSVNffmoS726Y0LzQ=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
@ -291,6 +304,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
|||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
|
||||
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||
go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 h1:A/5uWzF44DlIgdm/PQFwfMkW0JX+cIcQi/SwLAmZP5M=
|
||||
go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
|
@ -343,6 +358,7 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
|||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20170726083632-f5079bd7f6f7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -387,6 +403,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20170728174421-0f826bdd13b5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
|
|
@ -13,6 +13,7 @@ type KeyManager interface {
|
|||
GetPublicKey(req *GetPublicKeyRequest) (crypto.PublicKey, error)
|
||||
CreateKey(req *CreateKeyRequest) (*CreateKeyResponse, error)
|
||||
CreateSigner(req *CreateSignerRequest) (crypto.Signer, error)
|
||||
CreateDecrypter(req *CreateDecrypterRequest) (crypto.Decrypter, error) // TODO: split into separate interface?
|
||||
Close() error
|
||||
}
|
||||
|
||||
|
|
|
@ -133,6 +133,17 @@ type CreateSignerRequest struct {
|
|||
Password []byte
|
||||
}
|
||||
|
||||
// CreateDecrypterRequest is the parameter used in the kms.Decrypt method.
|
||||
type CreateDecrypterRequest struct {
|
||||
Decrypter crypto.Decrypter
|
||||
DecryptionKey string
|
||||
DecryptionKeyPEM []byte
|
||||
TokenLabel string
|
||||
PublicKey string
|
||||
PublicKeyPEM []byte
|
||||
Password []byte
|
||||
}
|
||||
|
||||
// LoadCertificateRequest is the parameter used in the LoadCertificate method of
|
||||
// a CertificateManager.
|
||||
type LoadCertificateRequest struct {
|
||||
|
|
|
@ -3,6 +3,7 @@ package awskms
|
|||
import (
|
||||
"context"
|
||||
"crypto"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -221,6 +222,11 @@ func (k *KMS) CreateSigner(req *apiv1.CreateSignerRequest) (crypto.Signer, error
|
|||
return NewSigner(k.service, req.SigningKey)
|
||||
}
|
||||
|
||||
// CreateDecrypter creates a new crypto.decrypter backed by AWS KMS
|
||||
func (k *KMS) CreateDecrypter(req *apiv1.CreateDecrypterRequest) (crypto.Decrypter, error) {
|
||||
return nil, fmt.Errorf("not implemented yet")
|
||||
}
|
||||
|
||||
// Close closes the connection of the KMS client.
|
||||
func (k *KMS) Close() error {
|
||||
return nil
|
||||
|
|
|
@ -3,6 +3,7 @@ package cloudkms
|
|||
import (
|
||||
"context"
|
||||
"crypto"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -285,6 +286,11 @@ func (k *CloudKMS) GetPublicKey(req *apiv1.GetPublicKeyRequest) (crypto.PublicKe
|
|||
return pk, nil
|
||||
}
|
||||
|
||||
// CreateDecrypter creates a new crypto.Decrypter backed by Google Cloud KMS
|
||||
func (k *CloudKMS) CreateDecrypter(req *apiv1.CreateDecrypterRequest) (crypto.Decrypter, error) {
|
||||
return nil, fmt.Errorf("not implemented yet")
|
||||
}
|
||||
|
||||
// getPublicKeyWithRetries retries the request if the error is
|
||||
// FailedPrecondition, caused because the key is in the PENDING_GENERATION
|
||||
// status.
|
||||
|
|
|
@ -352,3 +352,8 @@ func findCertificate(ctx P11, rawuri string) (*x509.Certificate, error) {
|
|||
}
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// CreateDecrypter creates a new crypto.Decrypter backed by PKCS11
|
||||
func (k *PKCS11) CreateDecrypter(req *apiv1.CreateDecrypterRequest) (crypto.Decrypter, error) {
|
||||
return nil, fmt.Errorf("not implemented yet")
|
||||
}
|
||||
|
|
|
@ -145,3 +145,39 @@ func (k *SoftKMS) GetPublicKey(req *apiv1.GetPublicKeyRequest) (crypto.PublicKey
|
|||
return nil, errors.Errorf("unsupported public key type %T", v)
|
||||
}
|
||||
}
|
||||
|
||||
// CreateDecrypter creates a new crypto.Decrypter backed by disk/software
|
||||
func (k *SoftKMS) CreateDecrypter(req *apiv1.CreateDecrypterRequest) (crypto.Decrypter, error) {
|
||||
|
||||
var opts []pemutil.Options
|
||||
if req.Password != nil {
|
||||
opts = append(opts, pemutil.WithPassword(req.Password))
|
||||
}
|
||||
|
||||
switch {
|
||||
case req.Decrypter != nil:
|
||||
return req.Decrypter, nil
|
||||
case len(req.DecryptionKeyPEM) != 0:
|
||||
v, err := pemutil.ParseKey(req.DecryptionKeyPEM, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decrypter, ok := v.(crypto.Decrypter)
|
||||
if !ok {
|
||||
return nil, errors.New("decryptorKeyPEM is not a crypto.Decrypter")
|
||||
}
|
||||
return decrypter, nil
|
||||
case req.DecryptionKey != "":
|
||||
v, err := pemutil.Read(req.DecryptionKey, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decrypter, ok := v.(crypto.Decrypter)
|
||||
if !ok {
|
||||
return nil, errors.New("decryptionKey is not a crypto.Decrypter")
|
||||
}
|
||||
return decrypter, nil
|
||||
default:
|
||||
return nil, errors.New("failed to load softKMS: please define decryptionKeyPEM or decryptionKey")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"crypto/ed25519"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
|
@ -204,3 +205,8 @@ func (k *SSHAgentKMS) GetPublicKey(req *apiv1.GetPublicKeyRequest) (crypto.Publi
|
|||
return nil, errors.Errorf("unsupported public key type %T", v)
|
||||
}
|
||||
}
|
||||
|
||||
// CreateDecrypter creates a crypto.Decrypter backed by ssh-agent
|
||||
func (k *SSHAgentKMS) CreateDecrypter(req *apiv1.CreateDecrypterRequest) (crypto.Decrypter, error) {
|
||||
return nil, fmt.Errorf("not implemented yet")
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"crypto"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
|
@ -189,6 +190,11 @@ func (k *YubiKey) CreateSigner(req *apiv1.CreateSignerRequest) (crypto.Signer, e
|
|||
return signer, nil
|
||||
}
|
||||
|
||||
// CreateDecrypter creates a new crypto.Decrypter backed by a YubiKey
|
||||
func (k *YubiKey) CreateDecrypter(req *apiv1.CreateDecrypterRequest) (crypto.Decrypter, error) {
|
||||
return nil, fmt.Errorf("not implemented yet")
|
||||
}
|
||||
|
||||
// Close releases the connection to the YubiKey.
|
||||
func (k *YubiKey) Close() error {
|
||||
return errors.Wrap(k.yk.Close(), "error closing yubikey")
|
||||
|
|
|
@ -28,6 +28,8 @@ const (
|
|||
opnGetCACert = "GetCACert"
|
||||
opnGetCACaps = "GetCACaps"
|
||||
opnPKIOperation = "PKIOperation"
|
||||
|
||||
// TODO: add other (more optional) operations and handling
|
||||
)
|
||||
|
||||
// SCEPRequest is a SCEP server request.
|
||||
|
@ -98,6 +100,7 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func (h *Handler) Post(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
scepRequest, err := decodeSCEPRequest(r)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
|
@ -252,27 +255,22 @@ func (h *Handler) PKIOperation(w http.ResponseWriter, r *http.Request, scepReque
|
|||
return err
|
||||
}
|
||||
|
||||
certs, err := h.Auth.GetCACertificates()
|
||||
if err != nil {
|
||||
pkimsg := &scep.PKIMessage{
|
||||
TransactionID: msg.TransactionID,
|
||||
MessageType: msg.MessageType,
|
||||
SenderNonce: msg.SenderNonce,
|
||||
Raw: msg.Raw,
|
||||
}
|
||||
|
||||
if err := h.Auth.DecryptPKIEnvelope(pkimsg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: instead of getting the key to decrypt, add a decrypt function to the auth; less leaky
|
||||
key, err := h.Auth.GetSigningKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ca := certs[0]
|
||||
if err := msg.DecryptPKIEnvelope(ca, key); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if msg.MessageType == microscep.PKCSReq {
|
||||
if pkimsg.MessageType == microscep.PKCSReq {
|
||||
// TODO: CSR validation, like challenge password
|
||||
}
|
||||
|
||||
csr := msg.CSRReqMessage.CSR
|
||||
csr := pkimsg.CSRReqMessage.CSR
|
||||
id, err := createKeyIdentifier(csr.PublicKey)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -282,6 +280,7 @@ func (h *Handler) PKIOperation(w http.ResponseWriter, r *http.Request, scepReque
|
|||
|
||||
days := 40
|
||||
|
||||
// TODO: use information from provisioner, like claims
|
||||
template := &x509.Certificate{
|
||||
SerialNumber: serial,
|
||||
Subject: csr.Subject,
|
||||
|
@ -296,16 +295,16 @@ func (h *Handler) PKIOperation(w http.ResponseWriter, r *http.Request, scepReque
|
|||
EmailAddresses: csr.EmailAddresses,
|
||||
}
|
||||
|
||||
certRep, err := msg.SignCSR(ca, key, template)
|
||||
certRep, err := h.Auth.SignCSR(pkimsg, template)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//cert := certRep.CertRepMessage.Certificate
|
||||
//name := certName(cert)
|
||||
// //cert := certRep.CertRepMessage.Certificate
|
||||
// //name := certName(cert)
|
||||
|
||||
// TODO: check if CN already exists, if renewal is allowed and if existing should be revoked; fail if not
|
||||
// TODO: store the new cert for CN locally; should go into the DB
|
||||
// // TODO: check if CN already exists, if renewal is allowed and if existing should be revoked; fail if not
|
||||
// // TODO: store the new cert for CN locally; should go into the DB
|
||||
|
||||
scepResponse.Data = certRep.Raw
|
||||
|
||||
|
@ -321,7 +320,7 @@ func certName(cert *x509.Certificate) string {
|
|||
return string(cert.Signature)
|
||||
}
|
||||
|
||||
// createKeyIdentifier create an identifier for public keys
|
||||
// createKeyIdentifier creates an identifier for public keys
|
||||
// according to the first method in RFC5280 section 4.2.1.2.
|
||||
func createKeyIdentifier(pub crypto.PublicKey) ([]byte, error) {
|
||||
|
||||
|
@ -390,9 +389,9 @@ func ProvisionerFromContext(ctx context.Context) (scep.Provisioner, error) {
|
|||
if val == nil {
|
||||
return nil, errors.New("provisioner expected in request context")
|
||||
}
|
||||
pval, ok := val.(scep.Provisioner)
|
||||
if !ok || pval == nil {
|
||||
p, ok := val.(scep.Provisioner)
|
||||
if !ok || p == nil {
|
||||
return nil, errors.New("provisioner in context is not a SCEP provisioner")
|
||||
}
|
||||
return pval, nil
|
||||
return p, nil
|
||||
}
|
||||
|
|
|
@ -1,17 +1,26 @@
|
|||
package scep
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"bytes"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
|
||||
"github.com/smallstep/certificates/authority/provisioner"
|
||||
database "github.com/smallstep/certificates/db"
|
||||
|
||||
"go.step.sm/crypto/pemutil"
|
||||
|
||||
"github.com/smallstep/nosql"
|
||||
|
||||
microx509util "github.com/micromdm/scep/crypto/x509util"
|
||||
microscep "github.com/micromdm/scep/scep"
|
||||
|
||||
"github.com/smallstep/certificates/scep/pkcs7"
|
||||
|
||||
"go.step.sm/crypto/x509util"
|
||||
)
|
||||
|
||||
// Interface is the SCEP authority interface.
|
||||
|
@ -41,7 +50,10 @@ type Interface interface {
|
|||
// GetLinkExplicit(linkType Link, provName string, absoluteLink bool, baseURL *url.URL, inputs ...string) string
|
||||
|
||||
GetCACertificates() ([]*x509.Certificate, error)
|
||||
GetSigningKey() (*rsa.PrivateKey, error)
|
||||
//GetSigningKey() (*rsa.PrivateKey, error)
|
||||
|
||||
DecryptPKIEnvelope(*PKIMessage) error
|
||||
SignCSR(msg *PKIMessage, template *x509.Certificate) (*PKIMessage, error)
|
||||
}
|
||||
|
||||
// Authority is the layer that handles all SCEP interactions.
|
||||
|
@ -54,17 +66,16 @@ type Authority struct {
|
|||
// dir *directory
|
||||
|
||||
intermediateCertificate *x509.Certificate
|
||||
intermediateKey *rsa.PrivateKey
|
||||
|
||||
//signer crypto.Signer
|
||||
|
||||
service Service
|
||||
signAuth SignAuthority
|
||||
}
|
||||
|
||||
// AuthorityOptions required to create a new SCEP Authority.
|
||||
type AuthorityOptions struct {
|
||||
IntermediateCertificatePath string
|
||||
IntermediateKeyPath string
|
||||
|
||||
Service Service
|
||||
|
||||
// Backdate
|
||||
Backdate provisioner.Duration
|
||||
|
@ -79,7 +90,7 @@ type AuthorityOptions struct {
|
|||
Prefix string
|
||||
}
|
||||
|
||||
// SignAuthority is the interface implemented by a CA authority.
|
||||
// SignAuthority is the interface for a signing authority
|
||||
type SignAuthority interface {
|
||||
Sign(cr *x509.CertificateRequest, opts provisioner.SignOptions, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error)
|
||||
LoadProvisionerByID(string) (provisioner.Interface, error)
|
||||
|
@ -87,6 +98,7 @@ type SignAuthority interface {
|
|||
|
||||
// New returns a new Authority that implements the SCEP interface.
|
||||
func New(signAuth SignAuthority, ops AuthorityOptions) (*Authority, error) {
|
||||
|
||||
if _, ok := ops.DB.(*database.SimpleDB); !ok {
|
||||
// TODO: see ACME implementation
|
||||
}
|
||||
|
@ -100,42 +112,17 @@ func New(signAuth SignAuthority, ops AuthorityOptions) (*Authority, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
intermediateKey, err := readPrivateKey(ops.IntermediateKeyPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Authority{
|
||||
backdate: ops.Backdate,
|
||||
db: ops.DB,
|
||||
prefix: ops.Prefix,
|
||||
dns: ops.DNS,
|
||||
intermediateCertificate: certificateChain[0],
|
||||
intermediateKey: intermediateKey,
|
||||
service: ops.Service,
|
||||
signAuth: signAuth,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func readPrivateKey(path string) (*rsa.PrivateKey, error) {
|
||||
|
||||
keyBytes, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
block, _ := pem.Decode([]byte(keyBytes))
|
||||
if block == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// LoadProvisionerByID calls out to the SignAuthority interface to load a
|
||||
// provisioner by ID.
|
||||
func (a *Authority) LoadProvisionerByID(id string) (provisioner.Interface, error) {
|
||||
|
@ -145,6 +132,20 @@ func (a *Authority) LoadProvisionerByID(id string) (provisioner.Interface, error
|
|||
// GetCACertificates returns the certificate (chain) for the CA
|
||||
func (a *Authority) GetCACertificates() ([]*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.
|
||||
|
||||
if a.intermediateCertificate == nil {
|
||||
return nil, errors.New("no intermediate certificate available in SCEP authority")
|
||||
}
|
||||
|
@ -152,16 +153,210 @@ func (a *Authority) GetCACertificates() ([]*x509.Certificate, error) {
|
|||
return []*x509.Certificate{a.intermediateCertificate}, nil
|
||||
}
|
||||
|
||||
// GetSigningKey returns the RSA private key for the CA
|
||||
// TODO: we likely should provide utility functions for decrypting and
|
||||
// signing instead of providing the signing key directly
|
||||
func (a *Authority) GetSigningKey() (*rsa.PrivateKey, error) {
|
||||
// DecryptPKIEnvelope decrypts an enveloped message
|
||||
func (a *Authority) DecryptPKIEnvelope(msg *PKIMessage) error {
|
||||
|
||||
if a.intermediateKey == nil {
|
||||
return nil, errors.New("no intermediate key available in SCEP authority")
|
||||
data := msg.Raw
|
||||
|
||||
p7, err := pkcs7.Parse(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return a.intermediateKey, nil
|
||||
var tID microscep.TransactionID
|
||||
if err := p7.UnmarshalSignedAttribute(oidSCEPtransactionID, &tID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var msgType microscep.MessageType
|
||||
if err := p7.UnmarshalSignedAttribute(oidSCEPmessageType, &msgType); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg.p7 = p7
|
||||
|
||||
p7c, err := pkcs7.Parse(p7.Content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
envelope, err := p7c.Decrypt(a.intermediateCertificate, a.service.Decrypter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg.pkiEnvelope = envelope
|
||||
|
||||
switch msg.MessageType {
|
||||
case microscep.CertRep:
|
||||
certs, err := microscep.CACerts(msg.pkiEnvelope)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
msg.CertRepMessage.Certificate = certs[0] // TODO: check correctness of this
|
||||
return nil
|
||||
case microscep.PKCSReq, microscep.UpdateReq, microscep.RenewalReq:
|
||||
csr, err := x509.ParseCertificateRequest(msg.pkiEnvelope)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parse CSR from pkiEnvelope")
|
||||
}
|
||||
// check for challengePassword
|
||||
cp, err := microx509util.ParseChallengePassword(msg.pkiEnvelope)
|
||||
if err != nil {
|
||||
return fmt.Errorf("scep: parse challenge password in pkiEnvelope")
|
||||
}
|
||||
msg.CSRReqMessage = µscep.CSRReqMessage{
|
||||
RawDecrypted: msg.pkiEnvelope,
|
||||
CSR: csr,
|
||||
ChallengePassword: cp,
|
||||
}
|
||||
//msg.Certificate = p7.Certificates[0] // TODO: check if this is necessary to add (again)
|
||||
return nil
|
||||
case microscep.GetCRL, microscep.GetCert, microscep.CertPoll:
|
||||
return fmt.Errorf("not implemented") //errNotImplemented
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SignCSR creates an x509.Certificate based on a 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(msg *PKIMessage, template *x509.Certificate) (*PKIMessage, error) {
|
||||
|
||||
// check if CSRReqMessage has already been decrypted
|
||||
if msg.CSRReqMessage.CSR == nil {
|
||||
if err := a.DecryptPKIEnvelope(msg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
csr := msg.CSRReqMessage.CSR
|
||||
|
||||
// Template data
|
||||
data := x509util.NewTemplateData()
|
||||
data.SetCommonName(csr.Subject.CommonName)
|
||||
//data.Set(x509util.SANsKey, sans)
|
||||
|
||||
// templateOptions, err := provisioner.TemplateOptions(p.GetOptions(), data)
|
||||
// if err != nil {
|
||||
// return nil, ServerInternalErr(errors.Wrapf(err, "error creating template options from ACME provisioner"))
|
||||
// }
|
||||
// signOps = append(signOps, templateOptions)
|
||||
|
||||
// // Create and store a new certificate.
|
||||
// certChain, err := auth.Sign(csr, provisioner.SignOptions{
|
||||
// NotBefore: provisioner.NewTimeDuration(o.NotBefore),
|
||||
// NotAfter: provisioner.NewTimeDuration(o.NotAfter),
|
||||
// }, signOps...)
|
||||
// if err != nil {
|
||||
// return nil, ServerInternalErr(errors.Wrapf(err, "error generating certificate for order %s", o.ID))
|
||||
// }
|
||||
|
||||
// TODO: proper options
|
||||
signOps := provisioner.SignOptions{}
|
||||
signOps2 := []provisioner.SignOption{}
|
||||
|
||||
certs, err := a.signAuth.Sign(csr, signOps, signOps2...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cert := certs[0]
|
||||
|
||||
// fmt.Println("CERT")
|
||||
// fmt.Println(cert)
|
||||
// fmt.Println(fmt.Sprintf("%T", cert))
|
||||
// fmt.Println(cert.Issuer)
|
||||
// fmt.Println(cert.Subject)
|
||||
|
||||
serial := big.NewInt(int64(rand.Int63())) // TODO: serial logic?
|
||||
cert.SerialNumber = serial
|
||||
|
||||
// create a degenerate cert structure
|
||||
deg, err := DegenerateCertificates([]*x509.Certificate{cert})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
e7, err := pkcs7.Encrypt(deg, msg.p7.Certificates)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// PKIMessageAttributes to be signed
|
||||
config := pkcs7.SignerInfoConfig{
|
||||
ExtraSignedAttributes: []pkcs7.Attribute{
|
||||
{
|
||||
Type: oidSCEPtransactionID,
|
||||
Value: msg.TransactionID,
|
||||
},
|
||||
{
|
||||
Type: oidSCEPpkiStatus,
|
||||
Value: microscep.SUCCESS,
|
||||
},
|
||||
{
|
||||
Type: oidSCEPmessageType,
|
||||
Value: microscep.CertRep,
|
||||
},
|
||||
{
|
||||
Type: oidSCEPrecipientNonce,
|
||||
Value: msg.SenderNonce,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
signedData, err := pkcs7.NewSignedData(e7)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// add the certificate into the signed data type
|
||||
// this cert must be added before the signedData because the recipient will expect it
|
||||
// as the first certificate in the array
|
||||
signedData.AddCertificate(cert)
|
||||
|
||||
authCert := a.intermediateCertificate
|
||||
|
||||
// sign the attributes
|
||||
if err := signedData.AddSigner(authCert, a.service.Signer, config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
certRepBytes, err := signedData.Finish()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cr := &CertRepMessage{
|
||||
PKIStatus: microscep.SUCCESS,
|
||||
RecipientNonce: microscep.RecipientNonce(msg.SenderNonce),
|
||||
Certificate: cert,
|
||||
degenerate: deg,
|
||||
}
|
||||
|
||||
// create a CertRep message from the original
|
||||
crepMsg := &PKIMessage{
|
||||
Raw: certRepBytes,
|
||||
TransactionID: msg.TransactionID,
|
||||
MessageType: microscep.CertRep,
|
||||
CertRepMessage: cr,
|
||||
}
|
||||
|
||||
return crepMsg, nil
|
||||
}
|
||||
|
||||
// DegenerateCertificates creates degenerate certificates pkcs#7 type
|
||||
func DegenerateCertificates(certs []*x509.Certificate) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
for _, cert := range certs {
|
||||
buf.Write(cert.Raw)
|
||||
}
|
||||
degenerate, err := pkcs7.DegenerateCertificate(buf.Bytes())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return degenerate, nil
|
||||
}
|
||||
|
||||
// Interface guards
|
||||
|
|
54
scep/scep.go
Normal file
54
scep/scep.go
Normal file
|
@ -0,0 +1,54 @@
|
|||
package scep
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
|
||||
microscep "github.com/micromdm/scep/scep"
|
||||
|
||||
"github.com/smallstep/certificates/scep/pkcs7"
|
||||
)
|
||||
|
||||
// SCEP OIDs
|
||||
var (
|
||||
oidSCEPmessageType = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 2}
|
||||
oidSCEPpkiStatus = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 3}
|
||||
oidSCEPfailInfo = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 4}
|
||||
oidSCEPsenderNonce = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 5}
|
||||
oidSCEPrecipientNonce = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 6}
|
||||
oidSCEPtransactionID = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 7}
|
||||
oidChallengePassword = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 7}
|
||||
)
|
||||
|
||||
// PKIMessage defines the possible SCEP message types
|
||||
type PKIMessage struct {
|
||||
microscep.TransactionID
|
||||
microscep.MessageType
|
||||
microscep.SenderNonce
|
||||
*microscep.CSRReqMessage
|
||||
|
||||
*CertRepMessage
|
||||
|
||||
// DER Encoded PKIMessage
|
||||
Raw []byte
|
||||
|
||||
// parsed
|
||||
p7 *pkcs7.PKCS7
|
||||
|
||||
// decrypted enveloped content
|
||||
pkiEnvelope []byte
|
||||
|
||||
// Used to sign message
|
||||
Recipients []*x509.Certificate
|
||||
}
|
||||
|
||||
// CertRepMessage is a type of PKIMessage
|
||||
type CertRepMessage struct {
|
||||
microscep.PKIStatus
|
||||
microscep.RecipientNonce
|
||||
microscep.FailInfo
|
||||
|
||||
Certificate *x509.Certificate
|
||||
|
||||
degenerate []byte
|
||||
}
|
9
scep/service.go
Normal file
9
scep/service.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package scep
|
||||
|
||||
import "crypto"
|
||||
|
||||
// Service is a (temporary?) wrapper for signer/decrypters
|
||||
type Service struct {
|
||||
Signer crypto.Signer
|
||||
Decrypter crypto.Decrypter
|
||||
}
|
Loading…
Reference in a new issue