2018-10-05 21:48:36 +00:00
|
|
|
package authority
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/sha256"
|
2019-01-05 01:51:32 +00:00
|
|
|
"crypto/x509"
|
2018-10-05 21:48:36 +00:00
|
|
|
"encoding/hex"
|
2018-10-20 01:25:59 +00:00
|
|
|
"fmt"
|
2018-10-05 21:48:36 +00:00
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/smallstep/cli/crypto/pemutil"
|
|
|
|
"github.com/smallstep/cli/crypto/x509util"
|
|
|
|
)
|
|
|
|
|
2018-10-20 01:25:59 +00:00
|
|
|
const legacyAuthority = "step-certificate-authority"
|
|
|
|
|
2018-10-05 21:48:36 +00:00
|
|
|
// Authority implements the Certificate Authority internal interface.
|
|
|
|
type Authority struct {
|
|
|
|
config *Config
|
2019-01-07 23:30:28 +00:00
|
|
|
rootX509Certs []*x509.Certificate
|
2018-10-05 21:48:36 +00:00
|
|
|
intermediateIdentity *x509util.Identity
|
|
|
|
validateOnce bool
|
|
|
|
certificates *sync.Map
|
|
|
|
ottMap *sync.Map
|
|
|
|
startTime time.Time
|
|
|
|
provisionerIDIndex *sync.Map
|
|
|
|
encryptedKeyIndex *sync.Map
|
|
|
|
provisionerKeySetIndex *sync.Map
|
2018-10-25 22:40:12 +00:00
|
|
|
sortedProvisioners provisionerSlice
|
2018-10-20 01:25:59 +00:00
|
|
|
audiences []string
|
2018-10-05 21:48:36 +00:00
|
|
|
// Do not re-initialize
|
|
|
|
initOnce bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// New creates and initiates a new Authority type.
|
|
|
|
func New(config *Config) (*Authority, error) {
|
2018-10-25 22:40:12 +00:00
|
|
|
err := config.Validate()
|
|
|
|
if err != nil {
|
2018-10-05 21:48:36 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2018-10-25 22:40:12 +00:00
|
|
|
|
|
|
|
// Get sorted provisioners
|
|
|
|
var sorted provisionerSlice
|
|
|
|
if config.AuthorityConfig != nil {
|
|
|
|
sorted, err = newSortedProvisioners(config.AuthorityConfig.Provisioners)
|
2018-10-26 22:05:37 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-10-25 22:40:12 +00:00
|
|
|
}
|
|
|
|
|
2018-12-21 23:27:22 +00:00
|
|
|
// Define audiences: legacy + possible urls without the ports.
|
|
|
|
// The CA might have proxies in front so we cannot rely on the port.
|
2018-10-25 22:40:12 +00:00
|
|
|
audiences := []string{legacyAuthority}
|
|
|
|
for _, name := range config.DNSNames {
|
2018-12-21 22:02:06 +00:00
|
|
|
audiences = append(audiences, fmt.Sprintf("https://%s/sign", name), fmt.Sprintf("https://%s/1.0/sign", name))
|
2018-10-25 22:40:12 +00:00
|
|
|
}
|
|
|
|
|
2018-10-05 21:48:36 +00:00
|
|
|
var a = &Authority{
|
|
|
|
config: config,
|
|
|
|
certificates: new(sync.Map),
|
|
|
|
ottMap: new(sync.Map),
|
|
|
|
provisionerIDIndex: new(sync.Map),
|
|
|
|
encryptedKeyIndex: new(sync.Map),
|
|
|
|
provisionerKeySetIndex: new(sync.Map),
|
2018-10-25 22:40:12 +00:00
|
|
|
sortedProvisioners: sorted,
|
|
|
|
audiences: audiences,
|
2018-10-05 21:48:36 +00:00
|
|
|
}
|
|
|
|
if err := a.init(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return a, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// init performs validation and initializes the fields of an Authority struct.
|
|
|
|
func (a *Authority) init() error {
|
|
|
|
// Check if handler has already been validated/initialized.
|
|
|
|
if a.initOnce {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var err error
|
|
|
|
|
2019-01-07 23:30:28 +00:00
|
|
|
// Load the root certificates and add them to the certificate store
|
|
|
|
a.rootX509Certs = make([]*x509.Certificate, len(a.config.Root))
|
|
|
|
for i, path := range a.config.Root {
|
|
|
|
crt, err := pemutil.ReadCertificate(path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// Add root certificate to the certificate map
|
|
|
|
sum := sha256.Sum256(crt.Raw)
|
|
|
|
a.certificates.Store(hex.EncodeToString(sum[:]), crt)
|
|
|
|
a.rootX509Certs[i] = crt
|
|
|
|
}
|
2018-10-05 21:48:36 +00:00
|
|
|
|
2019-01-05 01:51:32 +00:00
|
|
|
// Add federated roots
|
|
|
|
for _, path := range a.config.FederatedRoots {
|
|
|
|
crt, err := pemutil.ReadCertificate(path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
sum := sha256.Sum256(crt.Raw)
|
|
|
|
a.certificates.Store(hex.EncodeToString(sum[:]), crt)
|
|
|
|
}
|
|
|
|
|
2018-10-05 21:48:36 +00:00
|
|
|
// Decrypt and load intermediate public / private key pair.
|
|
|
|
if len(a.config.Password) > 0 {
|
|
|
|
a.intermediateIdentity, err = x509util.LoadIdentityFromDisk(
|
|
|
|
a.config.IntermediateCert,
|
|
|
|
a.config.IntermediateKey,
|
|
|
|
pemutil.WithPassword([]byte(a.config.Password)),
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
a.intermediateIdentity, err = x509util.LoadIdentityFromDisk(a.config.IntermediateCert, a.config.IntermediateKey)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, p := range a.config.AuthorityConfig.Provisioners {
|
2018-10-30 01:00:30 +00:00
|
|
|
a.provisionerIDIndex.Store(p.ID(), p)
|
2018-10-05 21:48:36 +00:00
|
|
|
if len(p.EncryptedKey) != 0 {
|
|
|
|
a.encryptedKeyIndex.Store(p.Key.KeyID, p.EncryptedKey)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
a.startTime = time.Now()
|
|
|
|
// Set flag indicating that initialization has been completed, and should
|
|
|
|
// not be repeated.
|
|
|
|
a.initOnce = true
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|