forked from TrueCloudLab/certificates
Make the YubiKey management key configurable.
With this change the default management key is not required as the user is able to set its own. Fixes #323
This commit is contained in:
parent
87bbcee239
commit
f100b2d0e3
3 changed files with 61 additions and 18 deletions
|
@ -9,6 +9,7 @@ import (
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
|
"encoding/hex"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -27,18 +28,23 @@ import (
|
||||||
_ "github.com/smallstep/certificates/kms/yubikey"
|
_ "github.com/smallstep/certificates/kms/yubikey"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Config is a mapping of the cli flags.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
RootOnly bool
|
RootOnly bool
|
||||||
RootSlot string
|
RootSlot string
|
||||||
CrtSlot string
|
CrtSlot string
|
||||||
RootFile string
|
RootFile string
|
||||||
KeyFile string
|
KeyFile string
|
||||||
Pin string
|
Pin string
|
||||||
Force bool
|
ManagementKey string
|
||||||
|
Force bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate checks the flags in the config.
|
||||||
func (c *Config) Validate() error {
|
func (c *Config) Validate() error {
|
||||||
switch {
|
switch {
|
||||||
|
case c.ManagementKey != "" && len(c.ManagementKey) != 48:
|
||||||
|
return errors.New("flag `--management-key` must be 48 hexadecimal characters (24 bytes)")
|
||||||
case c.RootFile != "" && c.KeyFile == "":
|
case c.RootFile != "" && c.KeyFile == "":
|
||||||
return errors.New("flag `--root` requires flag `--key`")
|
return errors.New("flag `--root` requires flag `--key`")
|
||||||
case c.KeyFile != "" && c.RootFile == "":
|
case c.KeyFile != "" && c.RootFile == "":
|
||||||
|
@ -56,12 +62,18 @@ func (c *Config) Validate() error {
|
||||||
if c.RootOnly {
|
if c.RootOnly {
|
||||||
c.CrtSlot = ""
|
c.CrtSlot = ""
|
||||||
}
|
}
|
||||||
|
if c.ManagementKey != "" {
|
||||||
|
if _, err := hex.DecodeString(c.ManagementKey); err != nil {
|
||||||
|
return errors.Wrap(err, "flag `--management-key` is not valid")
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var c Config
|
var c Config
|
||||||
|
flag.StringVar(&c.ManagementKey, "management-key", "", `Management key to use in hexadecimal format. (default "010203040506070801020304050607080102030405060708")`)
|
||||||
flag.BoolVar(&c.RootOnly, "root-only", false, "Slot only the root certificate and sign and intermediate.")
|
flag.BoolVar(&c.RootOnly, "root-only", false, "Slot only the root certificate and sign and intermediate.")
|
||||||
flag.StringVar(&c.RootSlot, "root-slot", "9a", "Slot to store the root certificate.")
|
flag.StringVar(&c.RootSlot, "root-slot", "9a", "Slot to store the root certificate.")
|
||||||
flag.StringVar(&c.CrtSlot, "crt-slot", "9c", "Slot to store the intermediate certificate.")
|
flag.StringVar(&c.CrtSlot, "crt-slot", "9c", "Slot to store the intermediate certificate.")
|
||||||
|
@ -82,8 +94,9 @@ func main() {
|
||||||
c.Pin = string(pin)
|
c.Pin = string(pin)
|
||||||
|
|
||||||
k, err := kms.New(context.Background(), apiv1.Options{
|
k, err := kms.New(context.Background(), apiv1.Options{
|
||||||
Type: string(apiv1.YubiKey),
|
Type: string(apiv1.YubiKey),
|
||||||
Pin: c.Pin,
|
Pin: c.Pin,
|
||||||
|
ManagementKey: c.ManagementKey,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatal(err)
|
fatal(err)
|
||||||
|
@ -109,7 +122,11 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func fatal(err error) {
|
func fatal(err error) {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
if os.Getenv("STEPDEBUG") == "1" {
|
||||||
|
fmt.Fprintf(os.Stderr, "%+v\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
}
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,11 +19,12 @@ type KeyManager interface {
|
||||||
// CertificateManager is the interface implemented by the KMS that can load and
|
// CertificateManager is the interface implemented by the KMS that can load and
|
||||||
// store x509.Certificates.
|
// store x509.Certificates.
|
||||||
type CertificateManager interface {
|
type CertificateManager interface {
|
||||||
LoadCerticate(req *LoadCertificateRequest) (*x509.Certificate, error)
|
LoadCertificate(req *LoadCertificateRequest) (*x509.Certificate, error)
|
||||||
StoreCertificate(req *StoreCertificateRequest) error
|
StoreCertificate(req *StoreCertificateRequest) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrNotImplemented
|
// ErrNotImplemented is the type of error returned if an operation is not
|
||||||
|
// implemented.
|
||||||
type ErrNotImplemented struct {
|
type ErrNotImplemented struct {
|
||||||
msg string
|
msg string
|
||||||
}
|
}
|
||||||
|
@ -53,6 +54,7 @@ const (
|
||||||
YubiKey Type = "yubikey"
|
YubiKey Type = "yubikey"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Options are the KMS options. They represent the kms object in the ca.json.
|
||||||
type Options struct {
|
type Options struct {
|
||||||
// The type of the KMS to use.
|
// The type of the KMS to use.
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
|
@ -66,6 +68,15 @@ type Options struct {
|
||||||
// Pin used to access the PKCS11 module.
|
// Pin used to access the PKCS11 module.
|
||||||
Pin string `json:"pin"`
|
Pin string `json:"pin"`
|
||||||
|
|
||||||
|
// ManagementKey used in YubiKeys. Default management key is the hexadecimal
|
||||||
|
// string 010203040506070801020304050607080102030405060708:
|
||||||
|
// []byte{
|
||||||
|
// 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||||
|
// 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||||
|
// 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||||
|
// }
|
||||||
|
ManagementKey string `json:"managementKey"`
|
||||||
|
|
||||||
// Region to use in AmazonKMS.
|
// Region to use in AmazonKMS.
|
||||||
Region string `json:"region"`
|
Region string `json:"region"`
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"encoding/hex"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -16,13 +17,26 @@ import (
|
||||||
|
|
||||||
// YubiKey implements the KMS interface on a YubiKey.
|
// YubiKey implements the KMS interface on a YubiKey.
|
||||||
type YubiKey struct {
|
type YubiKey struct {
|
||||||
yk *piv.YubiKey
|
yk *piv.YubiKey
|
||||||
pin string
|
pin string
|
||||||
|
managementKey [24]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// New initializes a new YubiKey.
|
// New initializes a new YubiKey.
|
||||||
// TODO(mariano): only one card is currently supported.
|
// TODO(mariano): only one card is currently supported.
|
||||||
func New(ctx context.Context, opts apiv1.Options) (*YubiKey, error) {
|
func New(ctx context.Context, opts apiv1.Options) (*YubiKey, error) {
|
||||||
|
managementKey := piv.DefaultManagementKey
|
||||||
|
if opts.ManagementKey != "" {
|
||||||
|
b, err := hex.DecodeString(opts.ManagementKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "error decoding managementKey")
|
||||||
|
}
|
||||||
|
if len(b) != 24 {
|
||||||
|
return nil, errors.New("invalid managementKey: length is not 24 bytes")
|
||||||
|
}
|
||||||
|
copy(managementKey[:], b[:24])
|
||||||
|
}
|
||||||
|
|
||||||
cards, err := piv.Cards()
|
cards, err := piv.Cards()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -37,8 +51,9 @@ func New(ctx context.Context, opts apiv1.Options) (*YubiKey, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return &YubiKey{
|
return &YubiKey{
|
||||||
yk: yk,
|
yk: yk,
|
||||||
pin: opts.Pin,
|
pin: opts.Pin,
|
||||||
|
managementKey: managementKey,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +91,7 @@ func (k *YubiKey) StoreCertificate(req *apiv1.StoreCertificateRequest) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = k.yk.SetCertificate(piv.DefaultManagementKey, slot, req.Certificate)
|
err = k.yk.SetCertificate(k.managementKey, slot, req.Certificate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "error storing certificate")
|
return errors.Wrap(err, "error storing certificate")
|
||||||
}
|
}
|
||||||
|
@ -110,7 +125,7 @@ func (k *YubiKey) CreateKey(req *apiv1.CreateKeyRequest) (*apiv1.CreateKeyRespon
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pub, err := k.yk.GenerateKey(piv.DefaultManagementKey, slot, piv.Key{
|
pub, err := k.yk.GenerateKey(k.managementKey, slot, piv.Key{
|
||||||
Algorithm: alg,
|
Algorithm: alg,
|
||||||
PINPolicy: piv.PINPolicyAlways,
|
PINPolicy: piv.PINPolicyAlways,
|
||||||
TouchPolicy: piv.TouchPolicyNever,
|
TouchPolicy: piv.TouchPolicyNever,
|
||||||
|
|
Loading…
Reference in a new issue