Skip to content

Commit 9187805

Browse files
authored
Merge pull request smallstep#741 from gdbelvin/ssh
Support CSR Requests from PKCS11
2 parents fca7de6 + febb619 commit 9187805

File tree

1 file changed

+128
-74
lines changed

1 file changed

+128
-74
lines changed

Diff for: cmd/step-pkcs11-init/main.go

+128-74
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import (
3232
// Config is a mapping of the cli flags.
3333
type Config struct {
3434
KMS string
35-
RootOnly bool
35+
GenerateRoot bool
3636
RootObject string
3737
RootKeyObject string
3838
RootSubject string
@@ -58,22 +58,29 @@ func (c *Config) Validate() error {
5858
switch {
5959
case c.KMS == "":
6060
return errors.New("flag `--kms` is required")
61+
case c.CrtPath == "":
62+
return errors.New("flag `--crt-cert-path` is required")
6163
case c.RootFile != "" && c.KeyFile == "":
62-
return errors.New("flag `--root` requires flag `--key`")
64+
return errors.New("flag `--root-cert-file` requires flag `--root-key-file`")
6365
case c.KeyFile != "" && c.RootFile == "":
64-
return errors.New("flag `--key` requires flag `--root`")
65-
case c.RootOnly && c.RootFile != "":
66-
return errors.New("flag `--root-only` is incompatible with flag `--root`")
66+
return errors.New("flag `--root-key-file` requires flag `--root-cert-file`")
6767
case c.RootFile == "" && c.RootObject == "":
68-
return errors.New("one of flag `--root` or `--root-cert` is required")
69-
case c.RootFile == "" && c.RootKeyObject == "":
70-
return errors.New("one of flag `--root` or `--root-key` is required")
68+
return errors.New("one of flag `--root-cert-file` or `--root-cert-obj` is required")
69+
case c.KeyFile == "" && c.RootKeyObject == "":
70+
return errors.New("one of flag `--root-key-file` or `--root-key-obj` is required")
71+
case c.CrtKeyPath == "" && c.CrtKeyObject == "":
72+
return errors.New("one of flag `--crt-key-path` or `--crt-key-obj` is required")
73+
case c.RootFile == "" && c.GenerateRoot && c.RootKeyObject == "":
74+
return errors.New("flag `--root-gen` requires flag `--root-key-obj`")
75+
case c.RootFile == "" && c.GenerateRoot && c.RootPath == "":
76+
return errors.New("flag `--root-gen` requires `--root-cert-path`")
7177
default:
7278
if c.RootFile != "" {
79+
c.GenerateRoot = false
7380
c.RootObject = ""
7481
c.RootKeyObject = ""
7582
}
76-
if c.RootOnly {
83+
if c.CrtKeyPath != "" {
7784
c.CrtObject = ""
7885
c.CrtKeyObject = ""
7986
}
@@ -101,21 +108,27 @@ func main() {
101108
var c Config
102109
flag.StringVar(&c.KMS, "kms", kmsuri, "PKCS #11 URI with the module-path and token to connect to the module.")
103110
flag.StringVar(&c.Pin, "pin", "", "PKCS #11 PIN")
104-
flag.StringVar(&c.RootObject, "root-cert", "pkcs11:id=7330;object=root-cert", "PKCS #11 URI with object id and label to store the root certificate.")
105-
flag.StringVar(&c.RootPath, "root-cert-path", "root_ca.crt", "Location to write the root certificate.")
106-
flag.StringVar(&c.RootKeyObject, "root-key", "pkcs11:id=7330;object=root-key", "PKCS #11 URI with object id and label to store the root key.")
111+
// Option 1: Generate new root
112+
flag.BoolVar(&c.GenerateRoot, "root-gen", true, "Enable the generation of a root key.")
107113
flag.StringVar(&c.RootSubject, "root-name", "PKCS #11 Smallstep Root", "Subject and Issuer of the root certificate.")
108-
flag.StringVar(&c.CrtObject, "crt-cert", "pkcs11:id=7331;object=intermediate-cert", "PKCS #11 URI with object id and label to store the intermediate certificate.")
109-
flag.StringVar(&c.CrtPath, "crt-cert-path", "intermediate_ca.crt", "Location to write the intermediate certificate.")
110-
flag.StringVar(&c.CrtKeyObject, "crt-key", "pkcs11:id=7331;object=intermediate-key", "PKCS #11 URI with object id and label to store the intermediate certificate.")
114+
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.")
115+
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.")
116+
// Option 2: Read root from disk and sign intermediate
117+
flag.StringVar(&c.RootFile, "root-cert-file", "", "Path to the root certificate to use.")
118+
flag.StringVar(&c.KeyFile, "root-key-file", "", "Path to the root key to use.")
119+
// Option 3: Generate certificate signing request
111120
flag.StringVar(&c.CrtSubject, "crt-name", "PKCS #11 Smallstep Intermediate", "Subject of the intermediate certificate.")
112-
flag.StringVar(&c.CrtKeyPath, "crt-key-path", "intermediate_ca_key", "Location to write the intermediate private key.")
121+
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.")
122+
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.")
123+
// SSH certificates
124+
flag.BoolVar(&c.EnableSSH, "ssh", false, "Enable the creation of ssh keys.")
113125
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.")
114126
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.")
115-
flag.BoolVar(&c.RootOnly, "root-only", false, "Store only only the root certificate and sign and intermediate.")
116-
flag.StringVar(&c.RootFile, "root", "", "Path to the root certificate to use.")
117-
flag.StringVar(&c.KeyFile, "key", "", "Path to the root key to use.")
118-
flag.BoolVar(&c.EnableSSH, "ssh", false, "Enable the creation of ssh keys.")
127+
// Output files
128+
flag.StringVar(&c.RootPath, "root-cert-path", "root_ca.crt", "Location to write the root certificate.")
129+
flag.StringVar(&c.CrtPath, "crt-cert-path", "intermediate_ca.crt", "Location to write the intermediate certificate.")
130+
flag.StringVar(&c.CrtKeyPath, "crt-key-path", "", "Location to write the intermediate private key.")
131+
// Others
119132
flag.BoolVar(&c.NoCerts, "no-certs", false, "Do not store certificates in the module.")
120133
flag.BoolVar(&c.Force, "force", false, "Force the delete of previous keys.")
121134
flag.BoolVar(&c.Extractable, "extractable", false, "Allow export of private keys under wrap.")
@@ -276,22 +289,8 @@ func createPKI(k kms.KeyManager, c Config) error {
276289
// Root Certificate
277290
var signer crypto.Signer
278291
var root *x509.Certificate
279-
if c.RootFile != "" && c.KeyFile != "" {
280-
root, err = pemutil.ReadCertificate(c.RootFile)
281-
if err != nil {
282-
return err
283-
}
284-
285-
key, err := pemutil.Read(c.KeyFile)
286-
if err != nil {
287-
return err
288-
}
289-
290-
var ok bool
291-
if signer, ok = key.(crypto.Signer); !ok {
292-
return errors.Errorf("key type '%T' does not implement a signer", key)
293-
}
294-
} else {
292+
switch {
293+
case c.GenerateRoot:
295294
resp, err := k.CreateKey(&apiv1.CreateKeyRequest{
296295
Name: c.RootKeyObject,
297296
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
@@ -331,14 +330,16 @@ func createPKI(k kms.KeyManager, c Config) error {
331330
return errors.Wrap(err, "error parsing root certificate")
332331
}
333332

334-
if cm, ok := k.(kms.CertificateManager); ok && !c.NoCerts {
333+
if cm, ok := k.(kms.CertificateManager); ok && c.RootObject != "" && !c.NoCerts {
335334
if err := cm.StoreCertificate(&apiv1.StoreCertificateRequest{
336335
Name: c.RootObject,
337336
Certificate: root,
338337
Extractable: c.Extractable,
339338
}); err != nil {
340339
return err
341340
}
341+
} else {
342+
c.RootObject = ""
342343
}
343344

344345
if err := fileutil.WriteFile(c.RootPath, pem.EncodeToMemory(&pem.Block{
@@ -350,12 +351,31 @@ func createPKI(k kms.KeyManager, c Config) error {
350351

351352
ui.PrintSelected("Root Key", resp.Name)
352353
ui.PrintSelected("Root Certificate", c.RootPath)
354+
if c.RootObject != "" {
355+
ui.PrintSelected("Root Certificate Object", c.RootObject)
356+
}
357+
case c.RootFile != "" && c.KeyFile != "": // Read Root From File
358+
root, err = pemutil.ReadCertificate(c.RootFile)
359+
if err != nil {
360+
return err
361+
}
362+
363+
key, err := pemutil.Read(c.KeyFile)
364+
if err != nil {
365+
return err
366+
}
367+
368+
var ok bool
369+
if signer, ok = key.(crypto.Signer); !ok {
370+
return errors.Errorf("key type '%T' does not implement a signer", key)
371+
}
353372
}
354373

355374
// Intermediate Certificate
356375
var keyName string
357376
var publicKey crypto.PublicKey
358-
if c.RootOnly {
377+
var intSigner crypto.Signer
378+
if c.CrtKeyPath != "" {
359379
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
360380
if err != nil {
361381
return errors.Wrap(err, "error creating intermediate key")
@@ -373,6 +393,7 @@ func createPKI(k kms.KeyManager, c Config) error {
373393
}
374394

375395
publicKey = priv.Public()
396+
intSigner = priv
376397
} else {
377398
resp, err := k.CreateKey(&apiv1.CreateKeyRequest{
378399
Name: c.CrtKeyObject,
@@ -384,56 +405,89 @@ func createPKI(k kms.KeyManager, c Config) error {
384405
}
385406
publicKey = resp.PublicKey
386407
keyName = resp.Name
387-
}
388408

389-
template := &x509.Certificate{
390-
IsCA: true,
391-
NotBefore: now,
392-
NotAfter: now.Add(time.Hour * 24 * 365 * 10),
393-
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
394-
BasicConstraintsValid: true,
395-
MaxPathLen: 0,
396-
MaxPathLenZero: true,
397-
Issuer: root.Subject,
398-
Subject: pkix.Name{CommonName: c.CrtSubject},
399-
SerialNumber: mustSerialNumber(),
400-
SubjectKeyId: mustSubjectKeyID(publicKey),
401-
}
402-
403-
b, err := x509.CreateCertificate(rand.Reader, template, root, publicKey, signer)
404-
if err != nil {
405-
return err
409+
intSigner, err = k.CreateSigner(&resp.CreateSignerRequest)
410+
if err != nil {
411+
return err
412+
}
406413
}
407414

408-
intermediate, err := x509.ParseCertificate(b)
409-
if err != nil {
410-
return errors.Wrap(err, "error parsing intermediate certificate")
411-
}
415+
if root != nil {
416+
template := &x509.Certificate{
417+
IsCA: true,
418+
NotBefore: now,
419+
NotAfter: now.Add(time.Hour * 24 * 365 * 10),
420+
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
421+
BasicConstraintsValid: true,
422+
MaxPathLen: 0,
423+
MaxPathLenZero: true,
424+
Issuer: root.Subject,
425+
Subject: pkix.Name{CommonName: c.CrtSubject},
426+
SerialNumber: mustSerialNumber(),
427+
SubjectKeyId: mustSubjectKeyID(publicKey),
428+
}
412429

413-
if cm, ok := k.(kms.CertificateManager); ok && !c.NoCerts {
414-
if err := cm.StoreCertificate(&apiv1.StoreCertificateRequest{
415-
Name: c.CrtObject,
416-
Certificate: intermediate,
417-
Extractable: c.Extractable,
418-
}); err != nil {
430+
b, err := x509.CreateCertificate(rand.Reader, template, root, publicKey, signer)
431+
if err != nil {
419432
return err
420433
}
421-
}
422434

423-
if err := fileutil.WriteFile(c.CrtPath, pem.EncodeToMemory(&pem.Block{
424-
Type: "CERTIFICATE",
425-
Bytes: b,
426-
}), 0600); err != nil {
427-
return err
435+
intermediate, err := x509.ParseCertificate(b)
436+
if err != nil {
437+
return errors.Wrap(err, "error parsing intermediate certificate")
438+
}
439+
440+
if cm, ok := k.(kms.CertificateManager); ok && c.CrtObject != "" && !c.NoCerts {
441+
if err := cm.StoreCertificate(&apiv1.StoreCertificateRequest{
442+
Name: c.CrtObject,
443+
Certificate: intermediate,
444+
Extractable: c.Extractable,
445+
}); err != nil {
446+
return err
447+
}
448+
} else {
449+
c.CrtObject = ""
450+
}
451+
452+
if err := fileutil.WriteFile(c.CrtPath, pem.EncodeToMemory(&pem.Block{
453+
Type: "CERTIFICATE",
454+
Bytes: b,
455+
}), 0600); err != nil {
456+
return err
457+
}
458+
} else {
459+
// No root available, generate CSR for external root.
460+
csrTemplate := x509.CertificateRequest{
461+
Subject: pkix.Name{CommonName: c.CrtSubject},
462+
SignatureAlgorithm: x509.ECDSAWithSHA256,
463+
}
464+
// step: generate the csr request
465+
csrCertificate, err := x509.CreateCertificateRequest(rand.Reader, &csrTemplate, intSigner)
466+
if err != nil {
467+
return err
468+
}
469+
if err := fileutil.WriteFile(c.CrtPath, pem.EncodeToMemory(&pem.Block{
470+
Type: "CERTIFICATE REQUEST",
471+
Bytes: csrCertificate,
472+
}), 0600); err != nil {
473+
return err
474+
}
428475
}
429476

430-
if c.RootOnly {
477+
if c.CrtKeyPath != "" {
431478
ui.PrintSelected("Intermediate Key", c.CrtKeyPath)
432479
} else {
433480
ui.PrintSelected("Intermediate Key", keyName)
434481
}
435482

436-
ui.PrintSelected("Intermediate Certificate", c.CrtPath)
483+
if root != nil {
484+
ui.PrintSelected("Intermediate Certificate", c.CrtPath)
485+
if c.CrtObject != "" {
486+
ui.PrintSelected("Intermediate Certificate Object", c.CrtObject)
487+
}
488+
} else {
489+
ui.PrintSelected("Intermediate Certificate Request", c.CrtPath)
490+
}
437491

438492
if c.SSHHostKeyObject != "" {
439493
resp, err := k.CreateKey(&apiv1.CreateKeyRequest{

0 commit comments

Comments
 (0)