forked from TrueCloudLab/certificates
Merge pull request #1240 from smallstep/remove-deprecated-binaries
Remove deprecated binaries
This commit is contained in:
commit
4bb25d4a52
8 changed files with 11 additions and 1493 deletions
|
@ -27,6 +27,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- The deprecated CLI utils `step-awskms-init`, `step-cloudkms-init`,
|
||||||
|
`step-pkcs11-init`, `step-yubikey-init` have been removed.
|
||||||
|
[`step`](https://github.com/smallstep/cli) and
|
||||||
|
[`step-kms-plugin`](https://github.com/smallstep/step-kms-plugin) should be
|
||||||
|
used instead.
|
||||||
|
|
||||||
## [v0.23.1] - 2022-01-10
|
## [v0.23.1] - 2022-01-10
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
45
Makefile
45
Makefile
|
@ -1,13 +1,5 @@
|
||||||
PKG?=github.com/smallstep/certificates/cmd/step-ca
|
PKG?=github.com/smallstep/certificates/cmd/step-ca
|
||||||
BINNAME?=step-ca
|
BINNAME?=step-ca
|
||||||
CLOUDKMS_BINNAME?=step-cloudkms-init
|
|
||||||
CLOUDKMS_PKG?=github.com/smallstep/certificates/cmd/step-cloudkms-init
|
|
||||||
AWSKMS_BINNAME?=step-awskms-init
|
|
||||||
AWSKMS_PKG?=github.com/smallstep/certificates/cmd/step-awskms-init
|
|
||||||
YUBIKEY_BINNAME?=step-yubikey-init
|
|
||||||
YUBIKEY_PKG?=github.com/smallstep/certificates/cmd/step-yubikey-init
|
|
||||||
PKCS11_BINNAME?=step-pkcs11-init
|
|
||||||
PKCS11_PKG?=github.com/smallstep/certificates/cmd/step-pkcs11-init
|
|
||||||
|
|
||||||
# Set V to 1 for verbose output from the Makefile
|
# Set V to 1 for verbose output from the Makefile
|
||||||
Q=$(if $V,,@)
|
Q=$(if $V,,@)
|
||||||
|
@ -90,29 +82,13 @@ GOFLAGS := CGO_ENABLED=0
|
||||||
download:
|
download:
|
||||||
$Q go mod download
|
$Q go mod download
|
||||||
|
|
||||||
build: $(PREFIX)bin/$(BINNAME) $(PREFIX)bin/$(CLOUDKMS_BINNAME) $(PREFIX)bin/$(AWSKMS_BINNAME) $(PREFIX)bin/$(YUBIKEY_BINNAME) $(PREFIX)bin/$(PKCS11_BINNAME)
|
build: $(PREFIX)bin/$(BINNAME)
|
||||||
@echo "Build Complete!"
|
@echo "Build Complete!"
|
||||||
|
|
||||||
$(PREFIX)bin/$(BINNAME): download $(call rwildcard,*.go)
|
$(PREFIX)bin/$(BINNAME): download $(call rwildcard,*.go)
|
||||||
$Q mkdir -p $(@D)
|
$Q mkdir -p $(@D)
|
||||||
$Q $(GOOS_OVERRIDE) $(GOFLAGS) go build -v -o $(PREFIX)bin/$(BINNAME) $(LDFLAGS) $(PKG)
|
$Q $(GOOS_OVERRIDE) $(GOFLAGS) go build -v -o $(PREFIX)bin/$(BINNAME) $(LDFLAGS) $(PKG)
|
||||||
|
|
||||||
$(PREFIX)bin/$(CLOUDKMS_BINNAME): download $(call rwildcard,*.go)
|
|
||||||
$Q mkdir -p $(@D)
|
|
||||||
$Q $(GOOS_OVERRIDE) $(GOFLAGS) go build -v -o $(PREFIX)bin/$(CLOUDKMS_BINNAME) $(LDFLAGS) $(CLOUDKMS_PKG)
|
|
||||||
|
|
||||||
$(PREFIX)bin/$(AWSKMS_BINNAME): download $(call rwildcard,*.go)
|
|
||||||
$Q mkdir -p $(@D)
|
|
||||||
$Q $(GOOS_OVERRIDE) $(GOFLAGS) go build -v -o $(PREFIX)bin/$(AWSKMS_BINNAME) $(LDFLAGS) $(AWSKMS_PKG)
|
|
||||||
|
|
||||||
$(PREFIX)bin/$(YUBIKEY_BINNAME): download $(call rwildcard,*.go)
|
|
||||||
$Q mkdir -p $(@D)
|
|
||||||
$Q $(GOOS_OVERRIDE) $(GOFLAGS) go build -v -o $(PREFIX)bin/$(YUBIKEY_BINNAME) $(LDFLAGS) $(YUBIKEY_PKG)
|
|
||||||
|
|
||||||
$(PREFIX)bin/$(PKCS11_BINNAME): download $(call rwildcard,*.go)
|
|
||||||
$Q mkdir -p $(@D)
|
|
||||||
$Q $(GOOS_OVERRIDE) $(GOFLAGS) go build -v -o $(PREFIX)bin/$(PKCS11_BINNAME) $(LDFLAGS) $(PKCS11_PKG)
|
|
||||||
|
|
||||||
# Target to force a build of step-ca without running tests
|
# Target to force a build of step-ca without running tests
|
||||||
simple: build
|
simple: build
|
||||||
|
|
||||||
|
@ -133,7 +109,6 @@ generate:
|
||||||
test:
|
test:
|
||||||
$Q $(GOFLAGS) gotestsum -- -coverprofile=coverage.out -short -covermode=atomic ./...
|
$Q $(GOFLAGS) gotestsum -- -coverprofile=coverage.out -short -covermode=atomic ./...
|
||||||
|
|
||||||
|
|
||||||
testcgo:
|
testcgo:
|
||||||
$Q gotestsum -- -coverprofile=coverage.out -short -covermode=atomic ./...
|
$Q gotestsum -- -coverprofile=coverage.out -short -covermode=atomic ./...
|
||||||
|
|
||||||
|
@ -166,15 +141,11 @@ lint:
|
||||||
|
|
||||||
INSTALL_PREFIX?=/usr/
|
INSTALL_PREFIX?=/usr/
|
||||||
|
|
||||||
install: $(PREFIX)bin/$(BINNAME) $(PREFIX)bin/$(CLOUDKMS_BINNAME) $(PREFIX)bin/$(AWSKMS_BINNAME)
|
install: $(PREFIX)bin/$(BINNAME)
|
||||||
$Q install -D $(PREFIX)bin/$(BINNAME) $(DESTDIR)$(INSTALL_PREFIX)bin/$(BINNAME)
|
$Q install -D $(PREFIX)bin/$(BINNAME) $(DESTDIR)$(INSTALL_PREFIX)bin/$(BINNAME)
|
||||||
$Q install -D $(PREFIX)bin/$(CLOUDKMS_BINNAME) $(DESTDIR)$(INSTALL_PREFIX)bin/$(CLOUDKMS_BINNAME)
|
|
||||||
$Q install -D $(PREFIX)bin/$(AWSKMS_BINNAME) $(DESTDIR)$(INSTALL_PREFIX)bin/$(AWSKMS_BINNAME)
|
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
$Q rm -f $(DESTDIR)$(INSTALL_PREFIX)/bin/$(BINNAME)
|
$Q rm -f $(DESTDIR)$(INSTALL_PREFIX)/bin/$(BINNAME)
|
||||||
$Q rm -f $(DESTDIR)$(INSTALL_PREFIX)/bin/$(CLOUDKMS_BINNAME)
|
|
||||||
$Q rm -f $(DESTDIR)$(INSTALL_PREFIX)/bin/$(AWSKMS_BINNAME)
|
|
||||||
|
|
||||||
.PHONY: install uninstall
|
.PHONY: install uninstall
|
||||||
|
|
||||||
|
@ -186,18 +157,6 @@ clean:
|
||||||
ifneq ($(BINNAME),"")
|
ifneq ($(BINNAME),"")
|
||||||
$Q rm -f bin/$(BINNAME)
|
$Q rm -f bin/$(BINNAME)
|
||||||
endif
|
endif
|
||||||
ifneq ($(CLOUDKMS_BINNAME),"")
|
|
||||||
$Q rm -f bin/$(CLOUDKMS_BINNAME)
|
|
||||||
endif
|
|
||||||
ifneq ($(AWSKMS_BINNAME),"")
|
|
||||||
$Q rm -f bin/$(AWSKMS_BINNAME)
|
|
||||||
endif
|
|
||||||
ifneq ($(YUBIKEY_BINNAME),"")
|
|
||||||
$Q rm -f bin/$(YUBIKEY_BINNAME)
|
|
||||||
endif
|
|
||||||
ifneq ($(PKCS11_BINNAME),"")
|
|
||||||
$Q rm -f bin/$(PKCS11_BINNAME)
|
|
||||||
endif
|
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
|
|
||||||
|
|
|
@ -1,248 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/sha1" //nolint:gosec // used to create the Subject Key Identifier by RFC 5280
|
|
||||||
"crypto/x509"
|
|
||||||
"crypto/x509/pkix"
|
|
||||||
"encoding/pem"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.step.sm/cli-utils/fileutil"
|
|
||||||
"go.step.sm/cli-utils/ui"
|
|
||||||
"go.step.sm/crypto/kms/apiv1"
|
|
||||||
"go.step.sm/crypto/kms/awskms"
|
|
||||||
"go.step.sm/crypto/pemutil"
|
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var credentialsFile, region string
|
|
||||||
var enableSSH bool
|
|
||||||
flag.StringVar(&credentialsFile, "credentials-file", "", "Path to the `file` containing the AWS KMS credentials.")
|
|
||||||
flag.StringVar(®ion, "region", "", "AWS KMS region name.")
|
|
||||||
flag.BoolVar(&enableSSH, "ssh", false, "Create SSH keys.")
|
|
||||||
flag.Usage = usage
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
// Initialize windows terminal
|
|
||||||
ui.Init()
|
|
||||||
|
|
||||||
ui.Println("⚠️ This command is deprecated and will be removed in future releases.")
|
|
||||||
ui.Println("⚠️ Please use https://github.com/smallstep/step-kms-plugin instead.")
|
|
||||||
|
|
||||||
c, err := awskms.New(context.Background(), apiv1.Options{
|
|
||||||
Type: apiv1.AmazonKMS,
|
|
||||||
Region: region,
|
|
||||||
CredentialsFile: credentialsFile,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := createX509(c); err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if enableSSH {
|
|
||||||
ui.Println()
|
|
||||||
if err := createSSH(c); err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset windows terminal
|
|
||||||
ui.Reset()
|
|
||||||
}
|
|
||||||
|
|
||||||
func fatal(err error) {
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
ui.Reset()
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func usage() {
|
|
||||||
fmt.Fprintln(os.Stderr, "Usage: step-awskms-init")
|
|
||||||
fmt.Fprintln(os.Stderr, `
|
|
||||||
The step-awskms-init command initializes a public key infrastructure (PKI)
|
|
||||||
to be used by step-ca.
|
|
||||||
|
|
||||||
This tool is experimental and in the future it will be integrated in step cli.
|
|
||||||
|
|
||||||
OPTIONS`)
|
|
||||||
fmt.Fprintln(os.Stderr)
|
|
||||||
flag.PrintDefaults()
|
|
||||||
fmt.Fprintf(os.Stderr, `
|
|
||||||
COPYRIGHT
|
|
||||||
|
|
||||||
(c) 2018-%d Smallstep Labs, Inc.
|
|
||||||
`, time.Now().Year())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func createX509(c *awskms.KMS) error {
|
|
||||||
ui.Println("Creating X.509 PKI ...")
|
|
||||||
|
|
||||||
// Root Certificate
|
|
||||||
resp, err := c.CreateKey(&apiv1.CreateKeyRequest{
|
|
||||||
Name: "root",
|
|
||||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
signer, err := c.CreateSigner(&resp.CreateSignerRequest)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
now := time.Now()
|
|
||||||
root := &x509.Certificate{
|
|
||||||
IsCA: true,
|
|
||||||
NotBefore: now,
|
|
||||||
NotAfter: now.Add(time.Hour * 24 * 365 * 10),
|
|
||||||
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
|
||||||
BasicConstraintsValid: true,
|
|
||||||
MaxPathLen: 1,
|
|
||||||
MaxPathLenZero: false,
|
|
||||||
Issuer: pkix.Name{CommonName: "Smallstep Root"},
|
|
||||||
Subject: pkix.Name{CommonName: "Smallstep Root"},
|
|
||||||
SerialNumber: mustSerialNumber(),
|
|
||||||
SubjectKeyId: mustSubjectKeyID(resp.PublicKey),
|
|
||||||
AuthorityKeyId: mustSubjectKeyID(resp.PublicKey),
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := x509.CreateCertificate(rand.Reader, root, root, resp.PublicKey, signer)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := fileutil.WriteFile("root_ca.crt", pem.EncodeToMemory(&pem.Block{
|
|
||||||
Type: "CERTIFICATE",
|
|
||||||
Bytes: b,
|
|
||||||
}), 0600); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.PrintSelected("Root Key", resp.Name)
|
|
||||||
ui.PrintSelected("Root Certificate", "root_ca.crt")
|
|
||||||
|
|
||||||
root, err = pemutil.ReadCertificate("root_ca.crt")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intermediate Certificate
|
|
||||||
resp, err = c.CreateKey(&apiv1.CreateKeyRequest{
|
|
||||||
Name: "intermediate",
|
|
||||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
intermediate := &x509.Certificate{
|
|
||||||
IsCA: true,
|
|
||||||
NotBefore: now,
|
|
||||||
NotAfter: now.Add(time.Hour * 24 * 365 * 10),
|
|
||||||
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
|
||||||
BasicConstraintsValid: true,
|
|
||||||
MaxPathLen: 0,
|
|
||||||
MaxPathLenZero: true,
|
|
||||||
Issuer: root.Subject,
|
|
||||||
Subject: pkix.Name{CommonName: "Smallstep Intermediate"},
|
|
||||||
SerialNumber: mustSerialNumber(),
|
|
||||||
SubjectKeyId: mustSubjectKeyID(resp.PublicKey),
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err = x509.CreateCertificate(rand.Reader, intermediate, root, resp.PublicKey, signer)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := fileutil.WriteFile("intermediate_ca.crt", pem.EncodeToMemory(&pem.Block{
|
|
||||||
Type: "CERTIFICATE",
|
|
||||||
Bytes: b,
|
|
||||||
}), 0600); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.PrintSelected("Intermediate Key", resp.Name)
|
|
||||||
ui.PrintSelected("Intermediate Certificate", "intermediate_ca.crt")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func createSSH(c *awskms.KMS) error {
|
|
||||||
ui.Println("Creating SSH Keys ...")
|
|
||||||
|
|
||||||
// User Key
|
|
||||||
resp, err := c.CreateKey(&apiv1.CreateKeyRequest{
|
|
||||||
Name: "ssh-user-key",
|
|
||||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
key, err := ssh.NewPublicKey(resp.PublicKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := fileutil.WriteFile("ssh_user_ca_key.pub", ssh.MarshalAuthorizedKey(key), 0600); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.PrintSelected("SSH User Public Key", "ssh_user_ca_key.pub")
|
|
||||||
ui.PrintSelected("SSH User Private Key", resp.Name)
|
|
||||||
|
|
||||||
// Host Key
|
|
||||||
resp, err = c.CreateKey(&apiv1.CreateKeyRequest{
|
|
||||||
Name: "ssh-host-key",
|
|
||||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
key, err = ssh.NewPublicKey(resp.PublicKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := fileutil.WriteFile("ssh_host_ca_key.pub", ssh.MarshalAuthorizedKey(key), 0600); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.PrintSelected("SSH Host Public Key", "ssh_host_ca_key.pub")
|
|
||||||
ui.PrintSelected("SSH Host Private Key", resp.Name)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func mustSerialNumber() *big.Int {
|
|
||||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
|
||||||
sn, err := rand.Int(rand.Reader, serialNumberLimit)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return sn
|
|
||||||
}
|
|
||||||
|
|
||||||
func mustSubjectKeyID(key crypto.PublicKey) []byte {
|
|
||||||
b, err := x509.MarshalPKIXPublicKey(key)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
//nolint:gosec // used to create the Subject Key Identifier by RFC 5280
|
|
||||||
hash := sha1.Sum(b)
|
|
||||||
return hash[:]
|
|
||||||
}
|
|
|
@ -1,286 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/sha1" //nolint:gosec // used to create the Subject Key Identifier by RFC 5280
|
|
||||||
"crypto/x509"
|
|
||||||
"crypto/x509/pkix"
|
|
||||||
"encoding/pem"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.step.sm/cli-utils/fileutil"
|
|
||||||
"go.step.sm/cli-utils/ui"
|
|
||||||
"go.step.sm/crypto/kms/apiv1"
|
|
||||||
"go.step.sm/crypto/kms/cloudkms"
|
|
||||||
"go.step.sm/crypto/pemutil"
|
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var credentialsFile string
|
|
||||||
var project, location, ring string
|
|
||||||
var protectionLevelName string
|
|
||||||
var enableSSH bool
|
|
||||||
flag.StringVar(&credentialsFile, "credentials-file", "", "Path to the `file` containing the Google's Cloud KMS credentials.")
|
|
||||||
flag.StringVar(&project, "project", "", "Google Cloud Project ID.")
|
|
||||||
flag.StringVar(&location, "location", "global", "Cloud KMS location name.")
|
|
||||||
flag.StringVar(&ring, "ring", "pki", "Cloud KMS ring name.")
|
|
||||||
flag.StringVar(&protectionLevelName, "protection-level", "SOFTWARE", "Protection level to use, SOFTWARE or HSM.")
|
|
||||||
flag.BoolVar(&enableSSH, "ssh", false, "Create SSH keys.")
|
|
||||||
flag.Usage = usage
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case project == "":
|
|
||||||
usage()
|
|
||||||
case location == "":
|
|
||||||
fmt.Fprintln(os.Stderr, "flag `--location` is required")
|
|
||||||
os.Exit(1)
|
|
||||||
case ring == "":
|
|
||||||
fmt.Fprintln(os.Stderr, "flag `--ring` is required")
|
|
||||||
os.Exit(1)
|
|
||||||
case protectionLevelName == "":
|
|
||||||
fmt.Fprintln(os.Stderr, "flag `--protection-level` is required")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
var protectionLevel apiv1.ProtectionLevel
|
|
||||||
switch strings.ToUpper(protectionLevelName) {
|
|
||||||
case "SOFTWARE":
|
|
||||||
protectionLevel = apiv1.Software
|
|
||||||
case "HSM":
|
|
||||||
protectionLevel = apiv1.HSM
|
|
||||||
default:
|
|
||||||
fmt.Fprintf(os.Stderr, "invalid value `%s` for flag `--protection-level`; options are `SOFTWARE` or `HSM`\n", protectionLevelName)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize windows terminal
|
|
||||||
ui.Init()
|
|
||||||
|
|
||||||
ui.Println("⚠️ This command is deprecated and will be removed in future releases.")
|
|
||||||
ui.Println("⚠️ Please use https://github.com/smallstep/step-kms-plugin instead.")
|
|
||||||
|
|
||||||
c, err := cloudkms.New(context.Background(), apiv1.Options{
|
|
||||||
Type: apiv1.CloudKMS,
|
|
||||||
CredentialsFile: credentialsFile,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := createPKI(c, project, location, ring, protectionLevel); err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if enableSSH {
|
|
||||||
ui.Println()
|
|
||||||
if err := createSSH(c, project, location, ring, protectionLevel); err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset windows terminal
|
|
||||||
ui.Reset()
|
|
||||||
}
|
|
||||||
|
|
||||||
func fatal(err error) {
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
ui.Reset()
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func usage() {
|
|
||||||
fmt.Fprintln(os.Stderr, "Usage: step-cloudkms-init --project <name>")
|
|
||||||
fmt.Fprintln(os.Stderr, `
|
|
||||||
The step-cloudkms-init command initializes a public key infrastructure (PKI)
|
|
||||||
to be used by step-ca.
|
|
||||||
|
|
||||||
This tool is experimental and in the future it will be integrated in step cli.
|
|
||||||
|
|
||||||
OPTIONS`)
|
|
||||||
fmt.Fprintln(os.Stderr)
|
|
||||||
flag.PrintDefaults()
|
|
||||||
fmt.Fprintf(os.Stderr, `
|
|
||||||
COPYRIGHT
|
|
||||||
|
|
||||||
(c) 2018-%d Smallstep Labs, Inc.
|
|
||||||
`, time.Now().Year())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func createPKI(c *cloudkms.CloudKMS, project, location, keyRing string, protectionLevel apiv1.ProtectionLevel) error {
|
|
||||||
ui.Println("Creating PKI ...")
|
|
||||||
|
|
||||||
parent := "projects/" + project + "/locations/" + location + "/keyRings/" + keyRing + "/cryptoKeys"
|
|
||||||
|
|
||||||
// Root Certificate
|
|
||||||
resp, err := c.CreateKey(&apiv1.CreateKeyRequest{
|
|
||||||
Name: parent + "/root",
|
|
||||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
|
||||||
ProtectionLevel: protectionLevel,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
signer, err := c.CreateSigner(&resp.CreateSignerRequest)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
now := time.Now()
|
|
||||||
root := &x509.Certificate{
|
|
||||||
IsCA: true,
|
|
||||||
NotBefore: now,
|
|
||||||
NotAfter: now.Add(time.Hour * 24 * 365 * 10),
|
|
||||||
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
|
||||||
BasicConstraintsValid: true,
|
|
||||||
MaxPathLen: 1,
|
|
||||||
MaxPathLenZero: false,
|
|
||||||
Issuer: pkix.Name{CommonName: "Smallstep Root"},
|
|
||||||
Subject: pkix.Name{CommonName: "Smallstep Root"},
|
|
||||||
SerialNumber: mustSerialNumber(),
|
|
||||||
SubjectKeyId: mustSubjectKeyID(resp.PublicKey),
|
|
||||||
AuthorityKeyId: mustSubjectKeyID(resp.PublicKey),
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := x509.CreateCertificate(rand.Reader, root, root, resp.PublicKey, signer)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := fileutil.WriteFile("root_ca.crt", pem.EncodeToMemory(&pem.Block{
|
|
||||||
Type: "CERTIFICATE",
|
|
||||||
Bytes: b,
|
|
||||||
}), 0600); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.PrintSelected("Root Key", resp.Name)
|
|
||||||
ui.PrintSelected("Root Certificate", "root_ca.crt")
|
|
||||||
|
|
||||||
root, err = pemutil.ReadCertificate("root_ca.crt")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intermediate Certificate
|
|
||||||
resp, err = c.CreateKey(&apiv1.CreateKeyRequest{
|
|
||||||
Name: parent + "/intermediate",
|
|
||||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
|
||||||
ProtectionLevel: protectionLevel,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
intermediate := &x509.Certificate{
|
|
||||||
IsCA: true,
|
|
||||||
NotBefore: now,
|
|
||||||
NotAfter: now.Add(time.Hour * 24 * 365 * 10),
|
|
||||||
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
|
||||||
BasicConstraintsValid: true,
|
|
||||||
MaxPathLen: 0,
|
|
||||||
MaxPathLenZero: true,
|
|
||||||
Issuer: root.Subject,
|
|
||||||
Subject: pkix.Name{CommonName: "Smallstep Intermediate"},
|
|
||||||
SerialNumber: mustSerialNumber(),
|
|
||||||
SubjectKeyId: mustSubjectKeyID(resp.PublicKey),
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err = x509.CreateCertificate(rand.Reader, intermediate, root, resp.PublicKey, signer)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := fileutil.WriteFile("intermediate_ca.crt", pem.EncodeToMemory(&pem.Block{
|
|
||||||
Type: "CERTIFICATE",
|
|
||||||
Bytes: b,
|
|
||||||
}), 0600); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.PrintSelected("Intermediate Key", resp.Name)
|
|
||||||
ui.PrintSelected("Intermediate Certificate", "intermediate_ca.crt")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func createSSH(c *cloudkms.CloudKMS, project, location, keyRing string, protectionLevel apiv1.ProtectionLevel) error {
|
|
||||||
ui.Println("Creating SSH Keys ...")
|
|
||||||
|
|
||||||
parent := "projects/" + project + "/locations/" + location + "/keyRings/" + keyRing + "/cryptoKeys"
|
|
||||||
|
|
||||||
// User Key
|
|
||||||
resp, err := c.CreateKey(&apiv1.CreateKeyRequest{
|
|
||||||
Name: parent + "/ssh-user-key",
|
|
||||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
|
||||||
ProtectionLevel: protectionLevel,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
key, err := ssh.NewPublicKey(resp.PublicKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := fileutil.WriteFile("ssh_user_ca_key.pub", ssh.MarshalAuthorizedKey(key), 0600); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.PrintSelected("SSH User Public Key", "ssh_user_ca_key.pub")
|
|
||||||
ui.PrintSelected("SSH User Private Key", resp.Name)
|
|
||||||
|
|
||||||
// Host Key
|
|
||||||
resp, err = c.CreateKey(&apiv1.CreateKeyRequest{
|
|
||||||
Name: parent + "/ssh-host-key",
|
|
||||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
|
||||||
ProtectionLevel: protectionLevel,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
key, err = ssh.NewPublicKey(resp.PublicKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := fileutil.WriteFile("ssh_host_ca_key.pub", ssh.MarshalAuthorizedKey(key), 0600); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.PrintSelected("SSH Host Public Key", "ssh_host_ca_key.pub")
|
|
||||||
ui.PrintSelected("SSH Host Private Key", resp.Name)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func mustSerialNumber() *big.Int {
|
|
||||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
|
||||||
sn, err := rand.Int(rand.Reader, serialNumberLimit)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return sn
|
|
||||||
}
|
|
||||||
|
|
||||||
func mustSubjectKeyID(key crypto.PublicKey) []byte {
|
|
||||||
b, err := x509.MarshalPKIXPublicKey(key)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
//nolint:gosec // used to create the Subject Key Identifier by RFC 5280
|
|
||||||
hash := sha1.Sum(b)
|
|
||||||
return hash[:]
|
|
||||||
}
|
|
|
@ -1,553 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto"
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/elliptic"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/sha1" //nolint:gosec // used to create the Subject Key Identifier by RFC 5280
|
|
||||||
"crypto/x509"
|
|
||||||
"crypto/x509/pkix"
|
|
||||||
"encoding/pem"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"go.step.sm/cli-utils/fileutil"
|
|
||||||
"go.step.sm/cli-utils/ui"
|
|
||||||
"go.step.sm/crypto/kms"
|
|
||||||
"go.step.sm/crypto/kms/apiv1"
|
|
||||||
"go.step.sm/crypto/kms/uri"
|
|
||||||
"go.step.sm/crypto/pemutil"
|
|
||||||
|
|
||||||
// Enable pkcs11.
|
|
||||||
_ "go.step.sm/crypto/kms/pkcs11"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Config is a mapping of the cli flags.
|
|
||||||
type Config struct {
|
|
||||||
KMS string
|
|
||||||
GenerateRoot bool
|
|
||||||
RootObject string
|
|
||||||
RootKeyObject string
|
|
||||||
RootSubject string
|
|
||||||
RootPath string
|
|
||||||
CrtObject string
|
|
||||||
CrtPath string
|
|
||||||
CrtKeyObject string
|
|
||||||
CrtSubject string
|
|
||||||
CrtKeyPath string
|
|
||||||
SSHHostKeyObject string
|
|
||||||
SSHUserKeyObject string
|
|
||||||
RootFile string
|
|
||||||
KeyFile string
|
|
||||||
Pin string
|
|
||||||
PinFile string
|
|
||||||
NoCerts bool
|
|
||||||
EnableSSH bool
|
|
||||||
Force bool
|
|
||||||
Extractable bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate checks the flags in the config.
|
|
||||||
func (c *Config) Validate() error {
|
|
||||||
switch {
|
|
||||||
case c.KMS == "":
|
|
||||||
return errors.New("flag `--kms` is required")
|
|
||||||
case c.CrtPath == "":
|
|
||||||
return errors.New("flag `--crt-cert-path` is required")
|
|
||||||
case c.RootFile != "" && c.KeyFile == "":
|
|
||||||
return errors.New("flag `--root-cert-file` requires flag `--root-key-file`")
|
|
||||||
case c.KeyFile != "" && c.RootFile == "":
|
|
||||||
return errors.New("flag `--root-key-file` requires flag `--root-cert-file`")
|
|
||||||
case c.RootFile == "" && c.RootObject == "":
|
|
||||||
return errors.New("one of flag `--root-cert-file` or `--root-cert-obj` is required")
|
|
||||||
case c.KeyFile == "" && c.RootKeyObject == "":
|
|
||||||
return errors.New("one of flag `--root-key-file` or `--root-key-obj` is required")
|
|
||||||
case c.CrtKeyPath == "" && c.CrtKeyObject == "":
|
|
||||||
return errors.New("one of flag `--crt-key-path` or `--crt-key-obj` is required")
|
|
||||||
case c.RootFile == "" && c.GenerateRoot && c.RootKeyObject == "":
|
|
||||||
return errors.New("flag `--root-gen` requires flag `--root-key-obj`")
|
|
||||||
case c.RootFile == "" && c.GenerateRoot && c.RootPath == "":
|
|
||||||
return errors.New("flag `--root-gen` requires `--root-cert-path`")
|
|
||||||
case c.Pin != "" && c.PinFile != "":
|
|
||||||
return errors.New("Only set one of pin and pin-file")
|
|
||||||
default:
|
|
||||||
if c.RootFile != "" {
|
|
||||||
c.GenerateRoot = false
|
|
||||||
c.RootObject = ""
|
|
||||||
c.RootKeyObject = ""
|
|
||||||
}
|
|
||||||
if c.CrtKeyPath != "" {
|
|
||||||
c.CrtObject = ""
|
|
||||||
c.CrtKeyObject = ""
|
|
||||||
}
|
|
||||||
if !c.EnableSSH {
|
|
||||||
c.SSHHostKeyObject = ""
|
|
||||||
c.SSHUserKeyObject = ""
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var kmsuri string
|
|
||||||
switch runtime.GOOS {
|
|
||||||
case "darwin":
|
|
||||||
kmsuri = "pkcs11:module-path=/usr/local/lib/pkcs11/yubihsm_pkcs11.dylib;token=YubiHSM"
|
|
||||||
case "linux":
|
|
||||||
kmsuri = "pkcs11:module-path=/usr/lib/x86_64-linux-gnu/pkcs11/yubihsm_pkcs11.so;token=YubiHSM"
|
|
||||||
case "windows":
|
|
||||||
if home, err := os.UserHomeDir(); err == nil {
|
|
||||||
kmsuri = "pkcs11:module-path=" + home + "\\yubihsm2-sdk\\bin\\yubihsm_pkcs11.dll" + ";token=YubiHSM"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var c Config
|
|
||||||
flag.StringVar(&c.KMS, "kms", kmsuri, "PKCS #11 URI with the module-path and token to connect to the module.")
|
|
||||||
flag.StringVar(&c.Pin, "pin", "", "PKCS #11 PIN")
|
|
||||||
flag.StringVar(&c.PinFile, "pin-file", "", "PKCS #11 PIN File")
|
|
||||||
// Option 1: Generate new root
|
|
||||||
flag.BoolVar(&c.GenerateRoot, "root-gen", true, "Enable the generation of a root key.")
|
|
||||||
flag.StringVar(&c.RootSubject, "root-name", "PKCS #11 Smallstep Root", "Subject and Issuer of the root certificate.")
|
|
||||||
flag.StringVar(&c.RootObject, "root-cert-obj", "pkcs11:id=7330;object=root-cert", "PKCS #11 URI with object id and label to store the root certificate.")
|
|
||||||
flag.StringVar(&c.RootKeyObject, "root-key-obj", "pkcs11:id=7330;object=root-key", "PKCS #11 URI with object id and label to store the root key.")
|
|
||||||
// Option 2: Read root from disk and sign intermediate
|
|
||||||
flag.StringVar(&c.RootFile, "root-cert-file", "", "Path to the root certificate to use.")
|
|
||||||
flag.StringVar(&c.KeyFile, "root-key-file", "", "Path to the root key to use.")
|
|
||||||
// Option 3: Generate certificate signing request
|
|
||||||
flag.StringVar(&c.CrtSubject, "crt-name", "PKCS #11 Smallstep Intermediate", "Subject of the intermediate certificate.")
|
|
||||||
flag.StringVar(&c.CrtObject, "crt-cert-obj", "pkcs11:id=7331;object=intermediate-cert", "PKCS #11 URI with object id and label to store the intermediate certificate.")
|
|
||||||
flag.StringVar(&c.CrtKeyObject, "crt-key-obj", "pkcs11:id=7331;object=intermediate-key", "PKCS #11 URI with object id and label to store the intermediate certificate.")
|
|
||||||
// SSH certificates
|
|
||||||
flag.BoolVar(&c.EnableSSH, "ssh", false, "Enable the creation of ssh keys.")
|
|
||||||
flag.StringVar(&c.SSHHostKeyObject, "ssh-host-key", "pkcs11:id=7332;object=ssh-host-key", "PKCS #11 URI with object id and label to store the key used to sign SSH host certificates.")
|
|
||||||
flag.StringVar(&c.SSHUserKeyObject, "ssh-user-key", "pkcs11:id=7333;object=ssh-user-key", "PKCS #11 URI with object id and label to store the key used to sign SSH user certificates.")
|
|
||||||
// Output files
|
|
||||||
flag.StringVar(&c.RootPath, "root-cert-path", "root_ca.crt", "Location to write the root certificate.")
|
|
||||||
flag.StringVar(&c.CrtPath, "crt-cert-path", "intermediate_ca.crt", "Location to write the intermediate certificate.")
|
|
||||||
flag.StringVar(&c.CrtKeyPath, "crt-key-path", "", "Location to write the intermediate private key.")
|
|
||||||
// Others
|
|
||||||
flag.BoolVar(&c.NoCerts, "no-certs", false, "Do not store certificates in the module.")
|
|
||||||
flag.BoolVar(&c.Force, "force", false, "Force the delete of previous keys.")
|
|
||||||
flag.BoolVar(&c.Extractable, "extractable", false, "Allow export of private keys under wrap.")
|
|
||||||
flag.Usage = usage
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if err := c.Validate(); err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
u, err := uri.ParseWithScheme("pkcs11", c.KMS)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize windows terminal
|
|
||||||
ui.Init()
|
|
||||||
|
|
||||||
ui.Println("⚠️ This command is deprecated and will be removed in future releases.")
|
|
||||||
ui.Println("⚠️ Please use https://github.com/smallstep/step-kms-plugin instead.")
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case u.Get("pin-value") != "":
|
|
||||||
case u.Get("pin-source") != "":
|
|
||||||
case c.Pin != "":
|
|
||||||
case c.PinFile != "":
|
|
||||||
content, err := os.ReadFile(c.PinFile)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
c.Pin = string(content)
|
|
||||||
|
|
||||||
default:
|
|
||||||
pin, err := ui.PromptPassword("What is the PKCS#11 PIN?")
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
c.Pin = string(pin)
|
|
||||||
}
|
|
||||||
|
|
||||||
k, err := kms.New(context.Background(), apiv1.Options{
|
|
||||||
Type: apiv1.PKCS11,
|
|
||||||
URI: c.KMS,
|
|
||||||
Pin: c.Pin,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
_ = k.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Check if the slots are empty, fail if they are not
|
|
||||||
certUris := []string{
|
|
||||||
c.RootObject, c.CrtObject,
|
|
||||||
}
|
|
||||||
keyUris := []string{
|
|
||||||
c.RootKeyObject, c.CrtKeyObject,
|
|
||||||
c.SSHHostKeyObject, c.SSHUserKeyObject,
|
|
||||||
}
|
|
||||||
if !c.Force {
|
|
||||||
for _, u := range certUris {
|
|
||||||
if u != "" && !c.NoCerts {
|
|
||||||
checkObject(k, u)
|
|
||||||
checkCertificate(k, u)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, u := range keyUris {
|
|
||||||
if u != "" {
|
|
||||||
checkObject(k, u)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
deleter, ok := k.(interface {
|
|
||||||
DeleteKey(uri string) error
|
|
||||||
DeleteCertificate(uri string) error
|
|
||||||
})
|
|
||||||
if ok {
|
|
||||||
for _, u := range certUris {
|
|
||||||
if u != "" && !c.NoCerts {
|
|
||||||
// Some HSMs like Nitrokey will overwrite the key with the
|
|
||||||
// certificate label.
|
|
||||||
if err := deleter.DeleteKey(u); err != nil {
|
|
||||||
fatalClose(err, k)
|
|
||||||
}
|
|
||||||
if err := deleter.DeleteCertificate(u); err != nil {
|
|
||||||
fatalClose(err, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, u := range keyUris {
|
|
||||||
if u != "" {
|
|
||||||
if err := deleter.DeleteKey(u); err != nil {
|
|
||||||
fatalClose(err, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := createPKI(k, c); err != nil {
|
|
||||||
fatalClose(err, k)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset windows terminal
|
|
||||||
ui.Reset()
|
|
||||||
}
|
|
||||||
|
|
||||||
func fatal(err error) {
|
|
||||||
if os.Getenv("STEPDEBUG") == "1" {
|
|
||||||
fmt.Fprintf(os.Stderr, "%+v\n", err)
|
|
||||||
} else {
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
}
|
|
||||||
ui.Reset()
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func fatalClose(err error, k kms.KeyManager) {
|
|
||||||
_ = k.Close()
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func usage() {
|
|
||||||
fmt.Fprintln(os.Stderr, "Usage: step-pkcs11-init")
|
|
||||||
fmt.Fprintln(os.Stderr, `
|
|
||||||
The step-pkcs11-init command initializes a public key infrastructure (PKI)
|
|
||||||
to be used by step-ca.
|
|
||||||
|
|
||||||
This tool is experimental and in the future it will be integrated in step cli.
|
|
||||||
|
|
||||||
OPTIONS`)
|
|
||||||
fmt.Fprintln(os.Stderr)
|
|
||||||
flag.PrintDefaults()
|
|
||||||
fmt.Fprintf(os.Stderr, `
|
|
||||||
COPYRIGHT
|
|
||||||
|
|
||||||
(c) 2018-%d Smallstep Labs, Inc.
|
|
||||||
`, time.Now().Year())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkCertificate(k kms.KeyManager, rawuri string) {
|
|
||||||
if cm, ok := k.(kms.CertificateManager); ok {
|
|
||||||
if _, err := cm.LoadCertificate(&apiv1.LoadCertificateRequest{
|
|
||||||
Name: rawuri,
|
|
||||||
}); err == nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "⚠️ Your PKCS #11 module already has a certificate on %s.\n", rawuri)
|
|
||||||
fmt.Fprintln(os.Stderr, " If you want to delete it and start fresh, use `--force`.")
|
|
||||||
_ = k.Close()
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkObject(k kms.KeyManager, rawuri string) {
|
|
||||||
if _, err := k.GetPublicKey(&apiv1.GetPublicKeyRequest{
|
|
||||||
Name: rawuri,
|
|
||||||
}); err == nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "⚠️ Your PKCS #11 module already has a key on %s.\n", rawuri)
|
|
||||||
fmt.Fprintln(os.Stderr, " If you want to delete it and start fresh, use `--force`.")
|
|
||||||
_ = k.Close()
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createPKI(k kms.KeyManager, c Config) error {
|
|
||||||
var err error
|
|
||||||
ui.Println("Creating PKI ...")
|
|
||||||
now := time.Now()
|
|
||||||
|
|
||||||
// Root Certificate
|
|
||||||
var signer crypto.Signer
|
|
||||||
var root *x509.Certificate
|
|
||||||
switch {
|
|
||||||
case c.GenerateRoot:
|
|
||||||
resp, err := k.CreateKey(&apiv1.CreateKeyRequest{
|
|
||||||
Name: c.RootKeyObject,
|
|
||||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
|
||||||
Extractable: c.Extractable,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
signer, err = k.CreateSigner(&resp.CreateSignerRequest)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
template := &x509.Certificate{
|
|
||||||
IsCA: true,
|
|
||||||
NotBefore: now,
|
|
||||||
NotAfter: now.Add(time.Hour * 24 * 365 * 10),
|
|
||||||
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
|
||||||
BasicConstraintsValid: true,
|
|
||||||
MaxPathLen: 1,
|
|
||||||
MaxPathLenZero: false,
|
|
||||||
Issuer: pkix.Name{CommonName: c.RootSubject},
|
|
||||||
Subject: pkix.Name{CommonName: c.RootSubject},
|
|
||||||
SerialNumber: mustSerialNumber(),
|
|
||||||
SubjectKeyId: mustSubjectKeyID(resp.PublicKey),
|
|
||||||
AuthorityKeyId: mustSubjectKeyID(resp.PublicKey),
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := x509.CreateCertificate(rand.Reader, template, template, resp.PublicKey, signer)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
root, err = x509.ParseCertificate(b)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "error parsing root certificate")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cm, ok := k.(kms.CertificateManager); ok && c.RootObject != "" && !c.NoCerts {
|
|
||||||
if err := cm.StoreCertificate(&apiv1.StoreCertificateRequest{
|
|
||||||
Name: c.RootObject,
|
|
||||||
Certificate: root,
|
|
||||||
Extractable: c.Extractable,
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
c.RootObject = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := fileutil.WriteFile(c.RootPath, pem.EncodeToMemory(&pem.Block{
|
|
||||||
Type: "CERTIFICATE",
|
|
||||||
Bytes: b,
|
|
||||||
}), 0600); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.PrintSelected("Root Key", resp.Name)
|
|
||||||
ui.PrintSelected("Root Certificate", c.RootPath)
|
|
||||||
if c.RootObject != "" {
|
|
||||||
ui.PrintSelected("Root Certificate Object", c.RootObject)
|
|
||||||
}
|
|
||||||
case c.RootFile != "" && c.KeyFile != "": // Read Root From File
|
|
||||||
root, err = pemutil.ReadCertificate(c.RootFile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
key, err := pemutil.Read(c.KeyFile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var ok bool
|
|
||||||
if signer, ok = key.(crypto.Signer); !ok {
|
|
||||||
return errors.Errorf("key type '%T' does not implement a signer", key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intermediate Certificate
|
|
||||||
var keyName string
|
|
||||||
var publicKey crypto.PublicKey
|
|
||||||
var intSigner crypto.Signer
|
|
||||||
if c.CrtKeyPath != "" {
|
|
||||||
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "error creating intermediate key")
|
|
||||||
}
|
|
||||||
|
|
||||||
pass, err := ui.PromptPasswordGenerate("What do you want your password to be? [leave empty and we'll generate one]",
|
|
||||||
ui.WithRichPrompt())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = pemutil.Serialize(priv, pemutil.WithPassword(pass), pemutil.ToFile(c.CrtKeyPath, 0600))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
publicKey = priv.Public()
|
|
||||||
intSigner = priv
|
|
||||||
} else {
|
|
||||||
resp, err := k.CreateKey(&apiv1.CreateKeyRequest{
|
|
||||||
Name: c.CrtKeyObject,
|
|
||||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
|
||||||
Extractable: c.Extractable,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
publicKey = resp.PublicKey
|
|
||||||
keyName = resp.Name
|
|
||||||
|
|
||||||
intSigner, err = k.CreateSigner(&resp.CreateSignerRequest)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if root != nil {
|
|
||||||
template := &x509.Certificate{
|
|
||||||
IsCA: true,
|
|
||||||
NotBefore: now,
|
|
||||||
NotAfter: now.Add(time.Hour * 24 * 365 * 10),
|
|
||||||
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
|
||||||
BasicConstraintsValid: true,
|
|
||||||
MaxPathLen: 0,
|
|
||||||
MaxPathLenZero: true,
|
|
||||||
Issuer: root.Subject,
|
|
||||||
Subject: pkix.Name{CommonName: c.CrtSubject},
|
|
||||||
SerialNumber: mustSerialNumber(),
|
|
||||||
SubjectKeyId: mustSubjectKeyID(publicKey),
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := x509.CreateCertificate(rand.Reader, template, root, publicKey, signer)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
intermediate, err := x509.ParseCertificate(b)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "error parsing intermediate certificate")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cm, ok := k.(kms.CertificateManager); ok && c.CrtObject != "" && !c.NoCerts {
|
|
||||||
if err := cm.StoreCertificate(&apiv1.StoreCertificateRequest{
|
|
||||||
Name: c.CrtObject,
|
|
||||||
Certificate: intermediate,
|
|
||||||
Extractable: c.Extractable,
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
c.CrtObject = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := fileutil.WriteFile(c.CrtPath, pem.EncodeToMemory(&pem.Block{
|
|
||||||
Type: "CERTIFICATE",
|
|
||||||
Bytes: b,
|
|
||||||
}), 0600); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// No root available, generate CSR for external root.
|
|
||||||
csrTemplate := x509.CertificateRequest{
|
|
||||||
Subject: pkix.Name{CommonName: c.CrtSubject},
|
|
||||||
SignatureAlgorithm: x509.ECDSAWithSHA256,
|
|
||||||
}
|
|
||||||
// step: generate the csr request
|
|
||||||
csrCertificate, err := x509.CreateCertificateRequest(rand.Reader, &csrTemplate, intSigner)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := fileutil.WriteFile(c.CrtPath, pem.EncodeToMemory(&pem.Block{
|
|
||||||
Type: "CERTIFICATE REQUEST",
|
|
||||||
Bytes: csrCertificate,
|
|
||||||
}), 0600); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.CrtKeyPath != "" {
|
|
||||||
ui.PrintSelected("Intermediate Key", c.CrtKeyPath)
|
|
||||||
} else {
|
|
||||||
ui.PrintSelected("Intermediate Key", keyName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if root != nil {
|
|
||||||
ui.PrintSelected("Intermediate Certificate", c.CrtPath)
|
|
||||||
if c.CrtObject != "" {
|
|
||||||
ui.PrintSelected("Intermediate Certificate Object", c.CrtObject)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ui.PrintSelected("Intermediate Certificate Request", c.CrtPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.SSHHostKeyObject != "" {
|
|
||||||
resp, err := k.CreateKey(&apiv1.CreateKeyRequest{
|
|
||||||
Name: c.SSHHostKeyObject,
|
|
||||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ui.PrintSelected("SSH Host Key", resp.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.SSHUserKeyObject != "" {
|
|
||||||
resp, err := k.CreateKey(&apiv1.CreateKeyRequest{
|
|
||||||
Name: c.SSHUserKeyObject,
|
|
||||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ui.PrintSelected("SSH User Key", resp.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func mustSerialNumber() *big.Int {
|
|
||||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
|
||||||
sn, err := rand.Int(rand.Reader, serialNumberLimit)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return sn
|
|
||||||
}
|
|
||||||
|
|
||||||
func mustSubjectKeyID(key crypto.PublicKey) []byte {
|
|
||||||
b, err := x509.MarshalPKIXPublicKey(key)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
//nolint:gosec // used to create the Subject Key Identifier by RFC 5280
|
|
||||||
hash := sha1.Sum(b)
|
|
||||||
return hash[:]
|
|
||||||
}
|
|
|
@ -1,355 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto"
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/elliptic"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/sha1" //nolint:gosec // used to create the Subject Key Identifier by RFC 5280
|
|
||||||
"crypto/x509"
|
|
||||||
"crypto/x509/pkix"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/pem"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"go.step.sm/cli-utils/fileutil"
|
|
||||||
"go.step.sm/cli-utils/ui"
|
|
||||||
"go.step.sm/crypto/kms"
|
|
||||||
"go.step.sm/crypto/kms/apiv1"
|
|
||||||
"go.step.sm/crypto/pemutil"
|
|
||||||
|
|
||||||
// Enable yubikey.
|
|
||||||
_ "go.step.sm/crypto/kms/yubikey"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Config is a mapping of the cli flags.
|
|
||||||
type Config struct {
|
|
||||||
RootOnly bool
|
|
||||||
RootSlot string
|
|
||||||
CrtSlot string
|
|
||||||
RootFile string
|
|
||||||
KeyFile string
|
|
||||||
Pin string
|
|
||||||
ManagementKey string
|
|
||||||
Force bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate checks the flags in the config.
|
|
||||||
func (c *Config) Validate() error {
|
|
||||||
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 == "":
|
|
||||||
return errors.New("flag `--root` requires flag `--key`")
|
|
||||||
case c.KeyFile != "" && c.RootFile == "":
|
|
||||||
return errors.New("flag `--key` requires flag `--root`")
|
|
||||||
case c.RootOnly && c.RootFile != "":
|
|
||||||
return errors.New("flag `--root-only` is incompatible with flag `--root`")
|
|
||||||
case c.RootSlot == c.CrtSlot:
|
|
||||||
return errors.New("flag `--root-slot` and flag `--crt-slot` cannot be the same")
|
|
||||||
case c.RootFile == "" && c.RootSlot == "":
|
|
||||||
return errors.New("one of flag `--root` or `--root-slot` is required")
|
|
||||||
default:
|
|
||||||
if c.RootFile != "" {
|
|
||||||
c.RootSlot = ""
|
|
||||||
}
|
|
||||||
if c.RootOnly {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
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.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.RootFile, "root", "", "Path to the root certificate to use.")
|
|
||||||
flag.StringVar(&c.KeyFile, "key", "", "Path to the root key to use.")
|
|
||||||
flag.BoolVar(&c.Force, "force", false, "Force the delete of previous keys.")
|
|
||||||
flag.Usage = usage
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if err := c.Validate(); err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize windows terminal
|
|
||||||
ui.Init()
|
|
||||||
|
|
||||||
ui.Println("⚠️ This command is deprecated and will be removed in future releases.")
|
|
||||||
ui.Println("⚠️ Please use https://github.com/smallstep/step-kms-plugin instead.")
|
|
||||||
|
|
||||||
pin, err := ui.PromptPassword("What is the YubiKey PIN?")
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
c.Pin = string(pin)
|
|
||||||
|
|
||||||
k, err := kms.New(context.Background(), apiv1.Options{
|
|
||||||
Type: apiv1.YubiKey,
|
|
||||||
Pin: c.Pin,
|
|
||||||
ManagementKey: c.ManagementKey,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the slots are empty, fail if they are not
|
|
||||||
if !c.Force {
|
|
||||||
switch {
|
|
||||||
case c.RootSlot != "":
|
|
||||||
checkSlot(k, c.RootSlot)
|
|
||||||
case c.CrtSlot != "":
|
|
||||||
checkSlot(k, c.CrtSlot)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := createPKI(k, c); err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
_ = k.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Reset windows terminal
|
|
||||||
ui.Reset()
|
|
||||||
}
|
|
||||||
|
|
||||||
func fatal(err error) {
|
|
||||||
if os.Getenv("STEPDEBUG") == "1" {
|
|
||||||
fmt.Fprintf(os.Stderr, "%+v\n", err)
|
|
||||||
} else {
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
}
|
|
||||||
ui.Reset()
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func usage() {
|
|
||||||
fmt.Fprintln(os.Stderr, "Usage: step-yubikey-init")
|
|
||||||
fmt.Fprintln(os.Stderr, `
|
|
||||||
The step-yubikey-init command initializes a public key infrastructure (PKI)
|
|
||||||
to be used by step-ca.
|
|
||||||
|
|
||||||
This tool is experimental and in the future it will be integrated in step cli.
|
|
||||||
|
|
||||||
OPTIONS`)
|
|
||||||
fmt.Fprintln(os.Stderr)
|
|
||||||
flag.PrintDefaults()
|
|
||||||
fmt.Fprintf(os.Stderr, `
|
|
||||||
COPYRIGHT
|
|
||||||
|
|
||||||
(c) 2018-%d Smallstep Labs, Inc.
|
|
||||||
`, time.Now().Year())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkSlot(k kms.KeyManager, slot string) {
|
|
||||||
if _, err := k.GetPublicKey(&apiv1.GetPublicKeyRequest{
|
|
||||||
Name: slot,
|
|
||||||
}); err == nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "⚠️ Your YubiKey already has a key in the slot %s.\n", slot)
|
|
||||||
fmt.Fprintln(os.Stderr, " If you want to delete it and start fresh, use `--force`.")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createPKI(k kms.KeyManager, c Config) error {
|
|
||||||
var err error
|
|
||||||
ui.Println("Creating PKI ...")
|
|
||||||
now := time.Now()
|
|
||||||
|
|
||||||
// Root Certificate
|
|
||||||
var signer crypto.Signer
|
|
||||||
var root *x509.Certificate
|
|
||||||
if c.RootFile != "" && c.KeyFile != "" {
|
|
||||||
root, err = pemutil.ReadCertificate(c.RootFile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
key, err := pemutil.Read(c.KeyFile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var ok bool
|
|
||||||
if signer, ok = key.(crypto.Signer); !ok {
|
|
||||||
return errors.Errorf("key type '%T' does not implement a signer", key)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
resp, err := k.CreateKey(&apiv1.CreateKeyRequest{
|
|
||||||
Name: c.RootSlot,
|
|
||||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
signer, err = k.CreateSigner(&resp.CreateSignerRequest)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
template := &x509.Certificate{
|
|
||||||
IsCA: true,
|
|
||||||
NotBefore: now,
|
|
||||||
NotAfter: now.Add(time.Hour * 24 * 365 * 10),
|
|
||||||
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
|
||||||
BasicConstraintsValid: true,
|
|
||||||
MaxPathLen: 1,
|
|
||||||
MaxPathLenZero: false,
|
|
||||||
Issuer: pkix.Name{CommonName: "YubiKey Smallstep Root"},
|
|
||||||
Subject: pkix.Name{CommonName: "YubiKey Smallstep Root"},
|
|
||||||
SerialNumber: mustSerialNumber(),
|
|
||||||
SubjectKeyId: mustSubjectKeyID(resp.PublicKey),
|
|
||||||
AuthorityKeyId: mustSubjectKeyID(resp.PublicKey),
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := x509.CreateCertificate(rand.Reader, template, template, resp.PublicKey, signer)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
root, err = x509.ParseCertificate(b)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "error parsing root certificate")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cm, ok := k.(kms.CertificateManager); ok {
|
|
||||||
if err := cm.StoreCertificate(&apiv1.StoreCertificateRequest{
|
|
||||||
Name: c.RootSlot,
|
|
||||||
Certificate: root,
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := fileutil.WriteFile("root_ca.crt", pem.EncodeToMemory(&pem.Block{
|
|
||||||
Type: "CERTIFICATE",
|
|
||||||
Bytes: b,
|
|
||||||
}), 0600); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.PrintSelected("Root Key", resp.Name)
|
|
||||||
ui.PrintSelected("Root Certificate", "root_ca.crt")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intermediate Certificate
|
|
||||||
var keyName string
|
|
||||||
var publicKey crypto.PublicKey
|
|
||||||
if c.RootOnly {
|
|
||||||
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "error creating intermediate key")
|
|
||||||
}
|
|
||||||
|
|
||||||
pass, err := ui.PromptPasswordGenerate("What do you want your password to be? [leave empty and we'll generate one]",
|
|
||||||
ui.WithRichPrompt())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = pemutil.Serialize(priv, pemutil.WithPassword(pass), pemutil.ToFile("intermediate_ca_key", 0600))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
publicKey = priv.Public()
|
|
||||||
} else {
|
|
||||||
resp, err := k.CreateKey(&apiv1.CreateKeyRequest{
|
|
||||||
Name: c.CrtSlot,
|
|
||||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
publicKey = resp.PublicKey
|
|
||||||
keyName = resp.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
template := &x509.Certificate{
|
|
||||||
IsCA: true,
|
|
||||||
NotBefore: now,
|
|
||||||
NotAfter: now.Add(time.Hour * 24 * 365 * 10),
|
|
||||||
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
|
||||||
BasicConstraintsValid: true,
|
|
||||||
MaxPathLen: 0,
|
|
||||||
MaxPathLenZero: true,
|
|
||||||
Issuer: root.Subject,
|
|
||||||
Subject: pkix.Name{CommonName: "YubiKey Smallstep Intermediate"},
|
|
||||||
SerialNumber: mustSerialNumber(),
|
|
||||||
SubjectKeyId: mustSubjectKeyID(publicKey),
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := x509.CreateCertificate(rand.Reader, template, root, publicKey, signer)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
intermediate, err := x509.ParseCertificate(b)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "error parsing intermediate certificate")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cm, ok := k.(kms.CertificateManager); ok {
|
|
||||||
if err := cm.StoreCertificate(&apiv1.StoreCertificateRequest{
|
|
||||||
Name: c.CrtSlot,
|
|
||||||
Certificate: intermediate,
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := fileutil.WriteFile("intermediate_ca.crt", pem.EncodeToMemory(&pem.Block{
|
|
||||||
Type: "CERTIFICATE",
|
|
||||||
Bytes: b,
|
|
||||||
}), 0600); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.RootOnly {
|
|
||||||
ui.PrintSelected("Intermediate Key", "intermediate_ca_key")
|
|
||||||
} else {
|
|
||||||
ui.PrintSelected("Intermediate Key", keyName)
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.PrintSelected("Intermediate Certificate", "intermediate_ca.crt")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func mustSerialNumber() *big.Int {
|
|
||||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
|
||||||
sn, err := rand.Int(rand.Reader, serialNumberLimit)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return sn
|
|
||||||
}
|
|
||||||
|
|
||||||
func mustSubjectKeyID(key crypto.PublicKey) []byte {
|
|
||||||
b, err := x509.MarshalPKIXPublicKey(key)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
//nolint:gosec // used to create the Subject Key Identifier by RFC 5280
|
|
||||||
hash := sha1.Sum(b)
|
|
||||||
return hash[:]
|
|
||||||
}
|
|
|
@ -5,13 +5,11 @@ COPY . .
|
||||||
|
|
||||||
RUN apk add --no-cache curl git make
|
RUN apk add --no-cache curl git make
|
||||||
RUN make V=1 download
|
RUN make V=1 download
|
||||||
RUN make V=1 bin/step-ca bin/step-awskms-init bin/step-cloudkms-init
|
RUN make V=1 bin/step-ca
|
||||||
|
|
||||||
FROM smallstep/step-cli:latest
|
FROM smallstep/step-cli:latest
|
||||||
|
|
||||||
COPY --from=builder /src/bin/step-ca /usr/local/bin/step-ca
|
COPY --from=builder /src/bin/step-ca /usr/local/bin/step-ca
|
||||||
COPY --from=builder /src/bin/step-awskms-init /usr/local/bin/step-awskms-init
|
|
||||||
COPY --from=builder /src/bin/step-cloudkms-init /usr/local/bin/step-cloudkms-init
|
|
||||||
|
|
||||||
USER root
|
USER root
|
||||||
RUN apk add --no-cache libcap && setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/step-ca
|
RUN apk add --no-cache libcap && setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/step-ca
|
||||||
|
|
|
@ -8,14 +8,9 @@ RUN apk add --no-cache gcc musl-dev pkgconf pcsc-lite-dev
|
||||||
RUN make V=1 download
|
RUN make V=1 download
|
||||||
RUN make V=1 GOFLAGS="" build
|
RUN make V=1 GOFLAGS="" build
|
||||||
|
|
||||||
|
|
||||||
FROM smallstep/step-cli:latest
|
FROM smallstep/step-cli:latest
|
||||||
|
|
||||||
COPY --from=builder /src/bin/step-ca /usr/local/bin/step-ca
|
COPY --from=builder /src/bin/step-ca /usr/local/bin/step-ca
|
||||||
COPY --from=builder /src/bin/step-awskms-init /usr/local/bin/step-awskms-init
|
|
||||||
COPY --from=builder /src/bin/step-cloudkms-init /usr/local/bin/step-cloudkms-init
|
|
||||||
COPY --from=builder /src/bin/step-pkcs11-init /usr/local/bin/step-pkcs11-init
|
|
||||||
COPY --from=builder /src/bin/step-yubikey-init /usr/local/bin/step-yubikey-init
|
|
||||||
|
|
||||||
USER root
|
USER root
|
||||||
RUN apk add --no-cache libcap && setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/step-ca
|
RUN apk add --no-cache libcap && setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/step-ca
|
||||||
|
|
Loading…
Reference in a new issue