Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions command/ca/certificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,13 @@ Request a new certificate with an X5C provisioner:
$ step ca certificate foo.internal foo.crt foo.key --x5c-cert x5c.cert --x5c-key x5c.key
'''

Request a new certificate with an X5C using an certificate from a Yubikey:
'''
$ step ca certificate joe@example.com joe.crt joe.key \
--x5c-cert yubikey:slot-id=9a \
--x5c-key 'yubikey:slot-id=9a?pin=value=123456'
'''

**Certificate Templates** - With a provisioner configured with a custom
template we can use the **--set** flag to pass user variables:
'''
Expand Down
18 changes: 14 additions & 4 deletions command/ca/rekey.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,22 @@ Rekey a certificate forcing the overwrite of the previous certificate and key
$ step ca rekey --force internal.crt internal.key
'''

Rekey a certificate which key is in a KMS, with another from the same KMS:
Rekey a certificate using a KMS, with another from the same KMS:
'''
$ step ca rekey --private-key 'yubikey:slot-id=9a?pin-value=123456' \
yubikey.crt 'yubikey:slot-id=82?pin-value=123456'
'''

Rekey a certificate using a KMS with the <--kms> flag:
'''
$ step ca rekey \
--kms 'pkcs11:module-path=/usr/local/lib/softhsm/libsofthsm2.so;token=smallstep?pin-value=password' \
--private-key 'pkcs11:id=4002'
pkcs11.crt 'pkcs11:id=4001'
--private-key 'pkcs11:id=4002' pkcs11.crt 'pkcs11:id=4001'
'''

'''
$ step ca rekey --key yubikey:pin-value=123456 --private-key yubikey:slot-id=9a \
yubikey.crt 'yubikey:slot-id=82
'''

Rekey a certificate providing the <--ca-url> and <--root> flags:
Expand Down Expand Up @@ -239,7 +249,7 @@ func rekeyCertificateAction(ctx *cli.Context) error {
// For now, if the --kms flag is given, do not allow to generate a new key
// and write it on disk. We can't use the daemon mode because we
// cannot generate new keys.
if kmsURI != "" {
if kmsURI != "" || cryptoutil.IsKMS(keyFile) {
switch {
case givenPrivate == "":
return errs.RequiredWithFlag(ctx, "kms", "private-key")
Expand Down
5 changes: 5 additions & 0 deletions command/ca/renew.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ $ step ca renew --mtls=false --force internal.crt internal.key

Renew a certificate which key is in a KMS:
'''
$ step ca renew yubikey.crt 'yubikey:slot-id=9a?pin-value=123456'
'''

Renew a certificate which key is in a KMS, using the <--kms> flag:
'''
$ step ca renew \
--kms 'pkcs11:module-path=/usr/local/lib/softhsm/libsofthsm2.so;token=smallstep?pin-value=password' \
pkcs11.crt 'pkcs11:id=4001'
Expand Down
5 changes: 3 additions & 2 deletions command/ca/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,9 @@ Generate an X5C provisioner token using a certificate in a YubiKey. Note that a
YubiKey does not support storing a certificate bundle. To make it work, you must
add the intermediate and the root in the provisioner configuration:
'''
$ step ca token --kms yubikey:pin-value=123456 \
--x5c-cert yubikey:slot-id=82 --x5c-key yubikey:slot-id=82 \
$ step ca token \
--x5c-cert yubikey:slot-id=82 \
--x5c-key 'yubikey:slot-id=82?pin=value=123456' \
internal.example.com
'''

Expand Down
10 changes: 10 additions & 0 deletions command/certificate/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -330,8 +330,18 @@ $ step certificate create --csr --template csr.tpl --san coyote@acme.corp \
"Wile E. Coyote" coyote.csr coyote.key
'''

Create a CSR using <step-kms-plugin>:
'''
$ step certificate create --csr --key 'yubikey:slot-id=9a?pin=value=123456' coyote@acme.corp coyote.csr
'''

Create a root certificate using <step-kms-plugin>:
'''
$ step certificate create --profile root-ca --key 'yubikey:slot-id=9a?pin=value=123456' 'KMS Root' root_ca.crt
'''

Create a root certificate using <step-kms-plugin> and the <--kms> flag:
'''
$ step kms create \
--kms 'pkcs11:module-path=/usr/local/lib/softhsm/libsofthsm2.so;token=smallstep?pin-value=password' \
'pkcs11:id=4000;object=root-key'
Expand Down
11 changes: 8 additions & 3 deletions command/certificate/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,11 @@ $ step certificate sign \
--kms 'pkcs11:module-path=/usr/local/lib/softhsm/libsofthsm2.so;token=smallstep?pin-value=password' \
leaf.csr issuer.crt 'pkcs11:id=4001'
'''
`,

Sign a CSR using a certificate and a key stored in a KMS:
'''
$ step certificate sign leaf.csr yubikey-slot-id=9a 'yubikey-slot-id=9a?pin-value=123456'
'''`,
Flags: []cli.Flag{
flags.KMSUri,
cli.StringFlag{
Expand Down Expand Up @@ -238,6 +242,7 @@ func signAction(ctx *cli.Context) error {
csrFile := ctx.Args().Get(0)
crtFile := ctx.Args().Get(1)
keyFile := ctx.Args().Get(2)
kms := ctx.String("kms")

// Parse certificate request
csr, err := pemutil.ReadCertificateRequest(csrFile)
Expand All @@ -249,7 +254,7 @@ func signAction(ctx *cli.Context) error {
}

// Parse issuer and issuer key (at least one should be present)
issuers, err := pemutil.ReadCertificateBundle(crtFile)
issuers, err := cryptoutil.LoadCertificate(kms, crtFile)
if err != nil {
return err
}
Expand All @@ -265,7 +270,7 @@ func signAction(ctx *cli.Context) error {
opts = append(opts, pemutil.WithPasswordFile(passFile))
}

signer, err := cryptoutil.CreateSigner(ctx.String("kms"), keyFile, opts...)
signer, err := cryptoutil.CreateSigner(kms, keyFile, opts...)
if err != nil {
return err
}
Expand Down
24 changes: 15 additions & 9 deletions internal/cryptoutil/cryptoutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"errors"
"fmt"
"io"
"os"
"os/exec"
"strconv"
"strings"
Expand All @@ -22,15 +23,24 @@ import (
"go.step.sm/crypto/pemutil"
)

// IsKMS returns true if the given uri is a KMS URI.
// IsKMS returns true if the given uri is a KMS URI. It will return false if a
// file exists with the same name, even if the path matches a KMS uri pattern.
func IsKMS(rawuri string) bool {
if _, err := os.Stat(rawuri); err == nil {
return false
}

typ, err := kms.TypeOf(rawuri)
if err != nil || typ == apiv1.DefaultKMS {
return false
}
return true
}

func isFilename(kmsURI, name string) bool {
return kmsURI == "" && !IsKMS(name)
}

// Attestor is the interface implemented by step-kms-plugin using the key, sign,
// and attest commands.
type Attestor interface {
Expand All @@ -39,7 +49,7 @@ type Attestor interface {
}

func PublicKey(kmsURI, name string, opts ...pemutil.Options) (crypto.PublicKey, error) {
if kmsURI == "" {
if isFilename(kmsURI, name) {
s, err := pemutil.Read(name, opts...)
if err != nil {
return nil, err
Expand All @@ -61,7 +71,7 @@ func PublicKey(kmsURI, name string, opts ...pemutil.Options) (crypto.PublicKey,
// CreateSigner reads a key from a file with a given name or creates a signer
// with the given kms and name uri.
func CreateSigner(kmsURI, name string, opts ...pemutil.Options) (crypto.Signer, error) {
if kmsURI == "" || isSoftKMS(kmsURI) {
if isFilename(kmsURI, name) {
s, err := pemutil.Read(name, opts...)
if err != nil {
return nil, err
Expand All @@ -75,13 +85,9 @@ func CreateSigner(kmsURI, name string, opts ...pemutil.Options) (crypto.Signer,
return newKMSSigner(kmsURI, name)
}

func isSoftKMS(kmsURI string) bool {
return strings.HasPrefix(strings.ToLower(strings.TrimSpace(kmsURI)), "softkms")
}

// LoadCertificate returns a x509.Certificate from a kms or file
func LoadCertificate(kmsURI, certPath string) ([]*x509.Certificate, error) {
if kmsURI == "" {
if isFilename(kmsURI, certPath) {
s, err := pemutil.ReadCertificateBundle(certPath)
if err != nil {
return nil, fmt.Errorf("file %s does not contain a valid certificate: %w", certPath, err)
Expand Down Expand Up @@ -117,7 +123,7 @@ func LoadCertificate(kmsURI, certPath string) ([]*x509.Certificate, error) {

// LoadJSONWebKey returns a jose.JSONWebKey from a KMS or a file.
func LoadJSONWebKey(kmsURI, name string, opts ...jose.Option) (*jose.JSONWebKey, error) {
if kmsURI == "" {
if isFilename(kmsURI, name) {
return jose.ReadKey(name, opts...)
}

Expand Down