Refactor initialization of SCEP authority
This commit is contained in:
parent
99cd3b74fe
commit
2a249d20de
17 changed files with 479 additions and 68 deletions
|
@ -12,6 +12,7 @@ import (
|
||||||
|
|
||||||
"github.com/smallstep/certificates/cas"
|
"github.com/smallstep/certificates/cas"
|
||||||
"github.com/smallstep/certificates/linkedca"
|
"github.com/smallstep/certificates/linkedca"
|
||||||
|
"github.com/smallstep/certificates/scep"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/certificates/authority/admin"
|
"github.com/smallstep/certificates/authority/admin"
|
||||||
|
@ -46,6 +47,9 @@ type Authority struct {
|
||||||
federatedX509Certs []*x509.Certificate
|
federatedX509Certs []*x509.Certificate
|
||||||
certificates *sync.Map
|
certificates *sync.Map
|
||||||
|
|
||||||
|
// SCEP CA
|
||||||
|
scepService *scep.Service
|
||||||
|
|
||||||
// SSH CA
|
// SSH CA
|
||||||
sshCAUserCertSignKey ssh.Signer
|
sshCAUserCertSignKey ssh.Signer
|
||||||
sshCAHostCertSignKey ssh.Signer
|
sshCAHostCertSignKey ssh.Signer
|
||||||
|
@ -309,6 +313,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.
|
// Read root certificates and store them in the certificates map.
|
||||||
if len(a.rootX509Certs) == 0 {
|
if len(a.rootX509Certs) == 0 {
|
||||||
a.rootX509Certs = make([]*x509.Certificate, len(a.config.Root))
|
a.rootX509Certs = make([]*x509.Certificate, len(a.config.Root))
|
||||||
|
@ -529,3 +565,12 @@ func (a *Authority) CloseForReload() {
|
||||||
log.Printf("error closing the key manager: %v", err)
|
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()
|
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) {
|
func (s *SCEP) Init(config Config) (err error) {
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
@ -70,6 +70,8 @@ func (s *SCEP) Init(config Config) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: add other, SCEP specific, options?
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -110,6 +110,10 @@ func (m *mockKeyManager) CreateSigner(req *kmsapi.CreateSignerRequest) (crypto.S
|
||||||
return signer, m.errCreatesigner
|
return signer, m.errCreatesigner
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *mockKeyManager) CreateDecrypter(req *kmsapi.CreateDecrypterRequest) (crypto.Decrypter, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *mockKeyManager) Close() error {
|
func (m *mockKeyManager) Close() error {
|
||||||
return m.errClose
|
return m.errClose
|
||||||
}
|
}
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -13,7 +13,7 @@ require (
|
||||||
github.com/google/uuid v1.1.2
|
github.com/google/uuid v1.1.2
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5
|
github.com/googleapis/gax-go/v2 v2.0.5
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
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/newrelic/go-agent v2.15.0+incompatible
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/rs/xid v1.2.1
|
github.com/rs/xid v1.2.1
|
||||||
|
|
26
go.sum
26
go.sum
|
@ -62,6 +62,7 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5
|
||||||
github.com/aws/aws-sdk-go v1.30.29 h1:NXNqBS9hjOCpDL8SyCyl38gZX3LLLunKOJc5E7vJ8P0=
|
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/aws/aws-sdk-go v1.30.29/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
|
github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
|
||||||
|
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/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 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
|
@ -116,10 +117,16 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9
|
||||||
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-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/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 h1:rfjdFdASfGV5KLJhSjgpGJ5lzVZVtRWn8ovy/H9HQ/U=
|
||||||
github.com/go-piv/piv-go v1.7.0/go.mod h1:ON2WvQncm7dIkCQ7kYJs+nc3V4jHGfrrJnSF8HKy7Gk=
|
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 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
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 h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
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=
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
@ -185,6 +192,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.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 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
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.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
|
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
|
||||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
@ -211,6 +221,12 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
|
||||||
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.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 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/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
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=
|
||||||
|
>>>>>>> 7ad90d1 (Refactor initialization of SCEP authority)
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
@ -232,6 +248,11 @@ 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.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 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
|
||||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
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=
|
||||||
|
>>>>>>> 7ad90d1 (Refactor initialization of SCEP authority)
|
||||||
github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f h1:eVB9ELsoq5ouItQBr5Tj334bhPJG/MX+m7rTchmzVUQ=
|
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/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||||
github.com/micromdm/scep v1.0.0 h1:ai//kcZnxZPq1YE/MatiE2bIRD94KOAwZRpN1fhVQXY=
|
github.com/micromdm/scep v1.0.0 h1:ai//kcZnxZPq1YE/MatiE2bIRD94KOAwZRpN1fhVQXY=
|
||||||
|
@ -248,6 +269,7 @@ github.com/newrelic/go-agent v2.15.0+incompatible h1:IB0Fy+dClpBq9aEoIrLyQXzU34J
|
||||||
github.com/newrelic/go-agent v2.15.0+incompatible/go.mod h1:a8Fv1b/fYhFSReoTU6HDkTYIMZeSVNffmoS726Y0LzQ=
|
github.com/newrelic/go-agent v2.15.0+incompatible/go.mod h1:a8Fv1b/fYhFSReoTU6HDkTYIMZeSVNffmoS726Y0LzQ=
|
||||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
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.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
@ -314,6 +336,8 @@ github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
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 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
|
||||||
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
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.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
@ -371,6 +395,7 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||||
golang.org/x/mod v0.3.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-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-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
@ -417,6 +442,7 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
|
||||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/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 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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-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-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/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)
|
GetPublicKey(req *GetPublicKeyRequest) (crypto.PublicKey, error)
|
||||||
CreateKey(req *CreateKeyRequest) (*CreateKeyResponse, error)
|
CreateKey(req *CreateKeyRequest) (*CreateKeyResponse, error)
|
||||||
CreateSigner(req *CreateSignerRequest) (crypto.Signer, error)
|
CreateSigner(req *CreateSignerRequest) (crypto.Signer, error)
|
||||||
|
CreateDecrypter(req *CreateDecrypterRequest) (crypto.Decrypter, error) // TODO: split into separate interface?
|
||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -133,6 +133,17 @@ type CreateSignerRequest struct {
|
||||||
Password []byte
|
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
|
// LoadCertificateRequest is the parameter used in the LoadCertificate method of
|
||||||
// a CertificateManager.
|
// a CertificateManager.
|
||||||
type LoadCertificateRequest struct {
|
type LoadCertificateRequest struct {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package awskms
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto"
|
"crypto"
|
||||||
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -221,6 +222,11 @@ func (k *KMS) CreateSigner(req *apiv1.CreateSignerRequest) (crypto.Signer, error
|
||||||
return NewSigner(k.service, req.SigningKey)
|
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.
|
// Close closes the connection of the KMS client.
|
||||||
func (k *KMS) Close() error {
|
func (k *KMS) Close() error {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -3,6 +3,7 @@ package cloudkms
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto"
|
"crypto"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -284,6 +285,11 @@ func (k *CloudKMS) GetPublicKey(req *apiv1.GetPublicKeyRequest) (crypto.PublicKe
|
||||||
return pk, nil
|
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
|
// getPublicKeyWithRetries retries the request if the error is
|
||||||
// FailedPrecondition, caused because the key is in the PENDING_GENERATION
|
// FailedPrecondition, caused because the key is in the PENDING_GENERATION
|
||||||
// status.
|
// status.
|
||||||
|
|
|
@ -352,3 +352,8 @@ func findCertificate(ctx P11, rawuri string) (*x509.Certificate, error) {
|
||||||
}
|
}
|
||||||
return cert, nil
|
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)
|
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/ed25519"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
@ -204,3 +205,8 @@ func (k *SSHAgentKMS) GetPublicKey(req *apiv1.GetPublicKeyRequest) (crypto.Publi
|
||||||
return nil, errors.Errorf("unsupported public key type %T", v)
|
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"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -189,6 +190,11 @@ func (k *YubiKey) CreateSigner(req *apiv1.CreateSignerRequest) (crypto.Signer, e
|
||||||
return signer, nil
|
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.
|
// Close releases the connection to the YubiKey.
|
||||||
func (k *YubiKey) Close() error {
|
func (k *YubiKey) Close() error {
|
||||||
return errors.Wrap(k.yk.Close(), "error closing yubikey")
|
return errors.Wrap(k.yk.Close(), "error closing yubikey")
|
||||||
|
|
|
@ -28,6 +28,8 @@ const (
|
||||||
opnGetCACert = "GetCACert"
|
opnGetCACert = "GetCACert"
|
||||||
opnGetCACaps = "GetCACaps"
|
opnGetCACaps = "GetCACaps"
|
||||||
opnPKIOperation = "PKIOperation"
|
opnPKIOperation = "PKIOperation"
|
||||||
|
|
||||||
|
// TODO: add other (more optional) operations and handling
|
||||||
)
|
)
|
||||||
|
|
||||||
// SCEPRequest is a SCEP server request.
|
// 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) {
|
func (h *Handler) Post(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
scepRequest, err := decodeSCEPRequest(r)
|
scepRequest, err := decodeSCEPRequest(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
|
@ -252,27 +255,22 @@ func (h *Handler) PKIOperation(w http.ResponseWriter, r *http.Request, scepReque
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
certs, err := h.Auth.GetCACertificates()
|
pkimsg := &scep.PKIMessage{
|
||||||
if err != nil {
|
TransactionID: msg.TransactionID,
|
||||||
|
MessageType: msg.MessageType,
|
||||||
|
SenderNonce: msg.SenderNonce,
|
||||||
|
Raw: msg.Raw,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := h.Auth.DecryptPKIEnvelope(pkimsg); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: instead of getting the key to decrypt, add a decrypt function to the auth; less leaky
|
if pkimsg.MessageType == microscep.PKCSReq {
|
||||||
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 {
|
|
||||||
// TODO: CSR validation, like challenge password
|
// TODO: CSR validation, like challenge password
|
||||||
}
|
}
|
||||||
|
|
||||||
csr := msg.CSRReqMessage.CSR
|
csr := pkimsg.CSRReqMessage.CSR
|
||||||
id, err := createKeyIdentifier(csr.PublicKey)
|
id, err := createKeyIdentifier(csr.PublicKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -282,6 +280,7 @@ func (h *Handler) PKIOperation(w http.ResponseWriter, r *http.Request, scepReque
|
||||||
|
|
||||||
days := 40
|
days := 40
|
||||||
|
|
||||||
|
// TODO: use information from provisioner, like claims
|
||||||
template := &x509.Certificate{
|
template := &x509.Certificate{
|
||||||
SerialNumber: serial,
|
SerialNumber: serial,
|
||||||
Subject: csr.Subject,
|
Subject: csr.Subject,
|
||||||
|
@ -296,16 +295,16 @@ func (h *Handler) PKIOperation(w http.ResponseWriter, r *http.Request, scepReque
|
||||||
EmailAddresses: csr.EmailAddresses,
|
EmailAddresses: csr.EmailAddresses,
|
||||||
}
|
}
|
||||||
|
|
||||||
certRep, err := msg.SignCSR(ca, key, template)
|
certRep, err := h.Auth.SignCSR(pkimsg, template)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
//cert := certRep.CertRepMessage.Certificate
|
// //cert := certRep.CertRepMessage.Certificate
|
||||||
//name := certName(cert)
|
// //name := certName(cert)
|
||||||
|
|
||||||
// TODO: check if CN already exists, if renewal is allowed and if existing should be revoked; fail if not
|
// // 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: store the new cert for CN locally; should go into the DB
|
||||||
|
|
||||||
scepResponse.Data = certRep.Raw
|
scepResponse.Data = certRep.Raw
|
||||||
|
|
||||||
|
@ -321,7 +320,7 @@ func certName(cert *x509.Certificate) string {
|
||||||
return string(cert.Signature)
|
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.
|
// according to the first method in RFC5280 section 4.2.1.2.
|
||||||
func createKeyIdentifier(pub crypto.PublicKey) ([]byte, error) {
|
func createKeyIdentifier(pub crypto.PublicKey) ([]byte, error) {
|
||||||
|
|
||||||
|
@ -390,9 +389,9 @@ func ProvisionerFromContext(ctx context.Context) (scep.Provisioner, error) {
|
||||||
if val == nil {
|
if val == nil {
|
||||||
return nil, errors.New("provisioner expected in request context")
|
return nil, errors.New("provisioner expected in request context")
|
||||||
}
|
}
|
||||||
pval, ok := val.(scep.Provisioner)
|
p, ok := val.(scep.Provisioner)
|
||||||
if !ok || pval == nil {
|
if !ok || p == nil {
|
||||||
return nil, errors.New("provisioner in context is not a SCEP provisioner")
|
return nil, errors.New("provisioner in context is not a SCEP provisioner")
|
||||||
}
|
}
|
||||||
return pval, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,26 @@
|
||||||
package scep
|
package scep
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rsa"
|
"bytes"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/pem"
|
|
||||||
"errors"
|
"errors"
|
||||||
"io/ioutil"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
"github.com/smallstep/certificates/authority/provisioner"
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
database "github.com/smallstep/certificates/db"
|
database "github.com/smallstep/certificates/db"
|
||||||
|
|
||||||
"go.step.sm/crypto/pemutil"
|
"go.step.sm/crypto/pemutil"
|
||||||
|
|
||||||
"github.com/smallstep/nosql"
|
"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.
|
// 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
|
// GetLinkExplicit(linkType Link, provName string, absoluteLink bool, baseURL *url.URL, inputs ...string) string
|
||||||
|
|
||||||
GetCACertificates() ([]*x509.Certificate, error)
|
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.
|
// Authority is the layer that handles all SCEP interactions.
|
||||||
|
@ -54,17 +66,16 @@ type Authority struct {
|
||||||
// dir *directory
|
// dir *directory
|
||||||
|
|
||||||
intermediateCertificate *x509.Certificate
|
intermediateCertificate *x509.Certificate
|
||||||
intermediateKey *rsa.PrivateKey
|
|
||||||
|
|
||||||
//signer crypto.Signer
|
|
||||||
|
|
||||||
|
service Service
|
||||||
signAuth SignAuthority
|
signAuth SignAuthority
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthorityOptions required to create a new SCEP Authority.
|
// AuthorityOptions required to create a new SCEP Authority.
|
||||||
type AuthorityOptions struct {
|
type AuthorityOptions struct {
|
||||||
IntermediateCertificatePath string
|
IntermediateCertificatePath string
|
||||||
IntermediateKeyPath string
|
|
||||||
|
Service Service
|
||||||
|
|
||||||
// Backdate
|
// Backdate
|
||||||
Backdate provisioner.Duration
|
Backdate provisioner.Duration
|
||||||
|
@ -79,7 +90,7 @@ type AuthorityOptions struct {
|
||||||
Prefix string
|
Prefix string
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignAuthority is the interface implemented by a CA authority.
|
// SignAuthority is the interface for a signing authority
|
||||||
type SignAuthority interface {
|
type SignAuthority interface {
|
||||||
Sign(cr *x509.CertificateRequest, opts provisioner.SignOptions, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error)
|
Sign(cr *x509.CertificateRequest, opts provisioner.SignOptions, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error)
|
||||||
LoadProvisionerByID(string) (provisioner.Interface, error)
|
LoadProvisionerByID(string) (provisioner.Interface, error)
|
||||||
|
@ -87,6 +98,7 @@ type SignAuthority interface {
|
||||||
|
|
||||||
// New returns a new Authority that implements the SCEP interface.
|
// New returns a new Authority that implements the SCEP interface.
|
||||||
func New(signAuth SignAuthority, ops AuthorityOptions) (*Authority, error) {
|
func New(signAuth SignAuthority, ops AuthorityOptions) (*Authority, error) {
|
||||||
|
|
||||||
if _, ok := ops.DB.(*database.SimpleDB); !ok {
|
if _, ok := ops.DB.(*database.SimpleDB); !ok {
|
||||||
// TODO: see ACME implementation
|
// TODO: see ACME implementation
|
||||||
}
|
}
|
||||||
|
@ -100,42 +112,17 @@ func New(signAuth SignAuthority, ops AuthorityOptions) (*Authority, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
intermediateKey, err := readPrivateKey(ops.IntermediateKeyPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Authority{
|
return &Authority{
|
||||||
backdate: ops.Backdate,
|
backdate: ops.Backdate,
|
||||||
db: ops.DB,
|
db: ops.DB,
|
||||||
prefix: ops.Prefix,
|
prefix: ops.Prefix,
|
||||||
dns: ops.DNS,
|
dns: ops.DNS,
|
||||||
intermediateCertificate: certificateChain[0],
|
intermediateCertificate: certificateChain[0],
|
||||||
intermediateKey: intermediateKey,
|
service: ops.Service,
|
||||||
signAuth: signAuth,
|
signAuth: signAuth,
|
||||||
}, nil
|
}, 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
|
// LoadProvisionerByID calls out to the SignAuthority interface to load a
|
||||||
// provisioner by ID.
|
// provisioner by ID.
|
||||||
func (a *Authority) LoadProvisionerByID(id string) (provisioner.Interface, error) {
|
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
|
// GetCACertificates returns the certificate (chain) for the CA
|
||||||
func (a *Authority) GetCACertificates() ([]*x509.Certificate, error) {
|
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 {
|
if a.intermediateCertificate == nil {
|
||||||
return nil, errors.New("no intermediate certificate available in SCEP authority")
|
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
|
return []*x509.Certificate{a.intermediateCertificate}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSigningKey returns the RSA private key for the CA
|
// DecryptPKIEnvelope decrypts an enveloped message
|
||||||
// TODO: we likely should provide utility functions for decrypting and
|
func (a *Authority) DecryptPKIEnvelope(msg *PKIMessage) error {
|
||||||
// signing instead of providing the signing key directly
|
|
||||||
func (a *Authority) GetSigningKey() (*rsa.PrivateKey, error) {
|
|
||||||
|
|
||||||
if a.intermediateKey == nil {
|
data := msg.Raw
|
||||||
return nil, errors.New("no intermediate key available in SCEP authority")
|
|
||||||
|
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
|
// 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