Skip to content

Commit 24a6900

Browse files
authored
Merge pull request smallstep#613 from gdbelvin/extractable
Extractable private keys and certs
2 parents d68090e + 91fb57e commit 24a6900

File tree

7 files changed

+121
-17
lines changed

7 files changed

+121
-17
lines changed

Diff for: CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
66

77
## [Unreleased - 0.17.7] - DATE
88
### Added
9+
- Support for generate extractable keys and certificates on a pkcs#11 module.
910
### Changed
1011
### Deprecated
1112
### Removed

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

+6
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ type Config struct {
5050
NoCerts bool
5151
EnableSSH bool
5252
Force bool
53+
Extractable bool
5354
}
5455

5556
// Validate checks the flags in the config.
@@ -117,6 +118,7 @@ func main() {
117118
flag.BoolVar(&c.EnableSSH, "ssh", false, "Enable the creation of ssh keys.")
118119
flag.BoolVar(&c.NoCerts, "no-certs", false, "Do not store certificates in the module.")
119120
flag.BoolVar(&c.Force, "force", false, "Force the delete of previous keys.")
121+
flag.BoolVar(&c.Extractable, "extractable", false, "Allow export of private keys under wrap.")
120122
flag.Usage = usage
121123
flag.Parse()
122124

@@ -293,6 +295,7 @@ func createPKI(k kms.KeyManager, c Config) error {
293295
resp, err := k.CreateKey(&apiv1.CreateKeyRequest{
294296
Name: c.RootKeyObject,
295297
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
298+
Extractable: c.Extractable,
296299
})
297300
if err != nil {
298301
return err
@@ -332,6 +335,7 @@ func createPKI(k kms.KeyManager, c Config) error {
332335
if err := cm.StoreCertificate(&apiv1.StoreCertificateRequest{
333336
Name: c.RootObject,
334337
Certificate: root,
338+
Extractable: c.Extractable,
335339
}); err != nil {
336340
return err
337341
}
@@ -373,6 +377,7 @@ func createPKI(k kms.KeyManager, c Config) error {
373377
resp, err := k.CreateKey(&apiv1.CreateKeyRequest{
374378
Name: c.CrtKeyObject,
375379
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
380+
Extractable: c.Extractable,
376381
})
377382
if err != nil {
378383
return err
@@ -409,6 +414,7 @@ func createPKI(k kms.KeyManager, c Config) error {
409414
if err := cm.StoreCertificate(&apiv1.StoreCertificateRequest{
410415
Name: c.CrtObject,
411416
Certificate: intermediate,
417+
Extractable: c.Extractable,
412418
}); err != nil {
413419
return err
414420
}

Diff for: kms/apiv1/requests.go

+14-2
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ type GetPublicKeyRequest struct {
100100
type CreateKeyRequest struct {
101101
// Name represents the key name or label used to identify a key.
102102
//
103-
// Used by: awskms, cloudkms, pkcs11, yubikey.
103+
// Used by: awskms, cloudkms, azurekms, pkcs11, yubikey.
104104
Name string
105105

106106
// SignatureAlgorithm represents the type of key to create.
@@ -110,8 +110,14 @@ type CreateKeyRequest struct {
110110
Bits int
111111

112112
// ProtectionLevel specifies how cryptographic operations are performed.
113-
// Used by: cloudkms
113+
// Used by: cloudkms, azurekms.
114114
ProtectionLevel ProtectionLevel
115+
116+
// Extractable defines if the new key may be exported from the HSM under a
117+
// wrap key. On pkcs11 sets the CKA_EXTRACTABLE bit.
118+
//
119+
// Used by: pkcs11
120+
Extractable bool
115121
}
116122

117123
// CreateKeyResponse is the response value of the kms.CreateKey method.
@@ -152,4 +158,10 @@ type LoadCertificateRequest struct {
152158
type StoreCertificateRequest struct {
153159
Name string
154160
Certificate *x509.Certificate
161+
162+
// Extractable defines if the new certificate may be exported from the HSM
163+
// under a wrap key. On pkcs11 sets the CKA_EXTRACTABLE bit.
164+
//
165+
// Used by: pkcs11
166+
Extractable bool
155167
}

Diff for: kms/pkcs11/other_test.go

+37-2
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,23 @@ func (s *stubPKCS11) FindCertificate(id, label []byte, serial *big.Int) (*x509.C
8080

8181
}
8282

83+
func (s *stubPKCS11) ImportCertificateWithAttributes(template crypto11.AttributeSet, cert *x509.Certificate) error {
84+
var id, label []byte
85+
if v := template[crypto11.CkaId]; v != nil {
86+
id = v.Value
87+
}
88+
if v := template[crypto11.CkaLabel]; v != nil {
89+
label = v.Value
90+
}
91+
return s.ImportCertificateWithLabel(id, label, cert)
92+
}
93+
8394
func (s *stubPKCS11) ImportCertificateWithLabel(id, label []byte, cert *x509.Certificate) error {
8495
switch {
85-
case id == nil && label == nil:
86-
return errors.New("id and label cannot both be nil")
96+
case id == nil:
97+
return errors.New("id cannot both be nil")
98+
case label == nil:
99+
return errors.New("label cannot both be nil")
87100
case cert == nil:
88101
return errors.New("certificate cannot be nil")
89102
}
@@ -111,6 +124,17 @@ func (s *stubPKCS11) DeleteCertificate(id, label []byte, serial *big.Int) error
111124
return nil
112125
}
113126

127+
func (s *stubPKCS11) GenerateRSAKeyPairWithAttributes(public, private crypto11.AttributeSet, bits int) (crypto11.SignerDecrypter, error) {
128+
var id, label []byte
129+
if v := public[crypto11.CkaId]; v != nil {
130+
id = v.Value
131+
}
132+
if v := public[crypto11.CkaLabel]; v != nil {
133+
label = v.Value
134+
}
135+
return s.GenerateRSAKeyPairWithLabel(id, label, bits)
136+
}
137+
114138
func (s *stubPKCS11) GenerateRSAKeyPairWithLabel(id, label []byte, bits int) (crypto11.SignerDecrypter, error) {
115139
if id == nil && label == nil {
116140
return nil, errors.New("id and label cannot both be nil")
@@ -131,6 +155,17 @@ func (s *stubPKCS11) GenerateRSAKeyPairWithLabel(id, label []byte, bits int) (cr
131155
return k, nil
132156
}
133157

158+
func (s *stubPKCS11) GenerateECDSAKeyPairWithAttributes(public, private crypto11.AttributeSet, curve elliptic.Curve) (crypto11.Signer, error) {
159+
var id, label []byte
160+
if v := public[crypto11.CkaId]; v != nil {
161+
id = v.Value
162+
}
163+
if v := public[crypto11.CkaLabel]; v != nil {
164+
label = v.Value
165+
}
166+
return s.GenerateECDSAKeyPairWithLabel(id, label, curve)
167+
}
168+
134169
func (s *stubPKCS11) GenerateECDSAKeyPairWithLabel(id, label []byte, curve elliptic.Curve) (crypto11.Signer, error) {
135170
if id == nil && label == nil {
136171
return nil, errors.New("id and label cannot both be nil")

Diff for: kms/pkcs11/pkcs11.go

+34-10
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ const DefaultRSASize = 3072
3232
type P11 interface {
3333
FindKeyPair(id, label []byte) (crypto11.Signer, error)
3434
FindCertificate(id, label []byte, serial *big.Int) (*x509.Certificate, error)
35-
ImportCertificateWithLabel(id, label []byte, cert *x509.Certificate) error
35+
ImportCertificateWithAttributes(template crypto11.AttributeSet, certificate *x509.Certificate) error
3636
DeleteCertificate(id, label []byte, serial *big.Int) error
37-
GenerateRSAKeyPairWithLabel(id, label []byte, bits int) (crypto11.SignerDecrypter, error)
38-
GenerateECDSAKeyPairWithLabel(id, label []byte, curve elliptic.Curve) (crypto11.Signer, error)
37+
GenerateRSAKeyPairWithAttributes(public, private crypto11.AttributeSet, bits int) (crypto11.SignerDecrypter, error)
38+
GenerateECDSAKeyPairWithAttributes(public, private crypto11.AttributeSet, curve elliptic.Curve) (crypto11.Signer, error)
3939
Close() error
4040
}
4141

@@ -185,6 +185,12 @@ func (k *PKCS11) StoreCertificate(req *apiv1.StoreCertificateRequest) error {
185185
return errors.Wrap(err, "storeCertificate failed")
186186
}
187187

188+
// Enforce the use of both id and labels. This is not strictly necessary in
189+
// PKCS #11, but it's a good practice.
190+
if len(id) == 0 || len(object) == 0 {
191+
return errors.Errorf("key with uri %s is not valid, id and object are required", req.Name)
192+
}
193+
188194
cert, err := k.p11.FindCertificate(id, object, nil)
189195
if err != nil {
190196
return errors.Wrap(err, "storeCertificate failed")
@@ -195,7 +201,15 @@ func (k *PKCS11) StoreCertificate(req *apiv1.StoreCertificateRequest) error {
195201
}, "storeCertificate failed")
196202
}
197203

198-
if err := k.p11.ImportCertificateWithLabel(id, object, req.Certificate); err != nil {
204+
// Import certificate with the necessary attributes.
205+
template, err := crypto11.NewAttributeSetWithIDAndLabel(id, object)
206+
if err != nil {
207+
return errors.Wrap(err, "storeCertificate failed")
208+
}
209+
if req.Extractable {
210+
template.Set(crypto11.CkaExtractable, true)
211+
}
212+
if err := k.p11.ImportCertificateWithAttributes(template, req.Certificate); err != nil {
199213
return errors.Wrap(err, "storeCertificate failed")
200214
}
201215

@@ -284,24 +298,34 @@ func generateKey(ctx P11, req *apiv1.CreateKeyRequest) (crypto11.Signer, error)
284298
return nil, errors.Errorf("key with uri %s is not valid, id and object are required", req.Name)
285299
}
286300

301+
// Create template for public and private keys
302+
public, err := crypto11.NewAttributeSetWithIDAndLabel(id, object)
303+
if err != nil {
304+
return nil, err
305+
}
306+
private := public.Copy()
307+
if req.Extractable {
308+
private.Set(crypto11.CkaExtractable, true)
309+
}
310+
287311
bits := req.Bits
288312
if bits == 0 {
289313
bits = DefaultRSASize
290314
}
291315

292316
switch req.SignatureAlgorithm {
293317
case apiv1.UnspecifiedSignAlgorithm:
294-
return ctx.GenerateECDSAKeyPairWithLabel(id, object, elliptic.P256())
318+
return ctx.GenerateECDSAKeyPairWithAttributes(public, private, elliptic.P256())
295319
case apiv1.SHA256WithRSA, apiv1.SHA384WithRSA, apiv1.SHA512WithRSA:
296-
return ctx.GenerateRSAKeyPairWithLabel(id, object, bits)
320+
return ctx.GenerateRSAKeyPairWithAttributes(public, private, bits)
297321
case apiv1.SHA256WithRSAPSS, apiv1.SHA384WithRSAPSS, apiv1.SHA512WithRSAPSS:
298-
return ctx.GenerateRSAKeyPairWithLabel(id, object, bits)
322+
return ctx.GenerateRSAKeyPairWithAttributes(public, private, bits)
299323
case apiv1.ECDSAWithSHA256:
300-
return ctx.GenerateECDSAKeyPairWithLabel(id, object, elliptic.P256())
324+
return ctx.GenerateECDSAKeyPairWithAttributes(public, private, elliptic.P256())
301325
case apiv1.ECDSAWithSHA384:
302-
return ctx.GenerateECDSAKeyPairWithLabel(id, object, elliptic.P384())
326+
return ctx.GenerateECDSAKeyPairWithAttributes(public, private, elliptic.P384())
303327
case apiv1.ECDSAWithSHA512:
304-
return ctx.GenerateECDSAKeyPairWithLabel(id, object, elliptic.P521())
328+
return ctx.GenerateECDSAKeyPairWithAttributes(public, private, elliptic.P521())
305329
case apiv1.PureEd25519:
306330
return nil, fmt.Errorf("signature algorithm %s is not supported", req.SignatureAlgorithm)
307331
default:

Diff for: kms/pkcs11/pkcs11_test.go

+27-2
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,16 @@ func TestPKCS11_CreateKey(t *testing.T) {
208208
SigningKey: testObject,
209209
},
210210
}, false},
211+
{"default extractable", args{&apiv1.CreateKeyRequest{
212+
Name: testObject,
213+
Extractable: true,
214+
}}, &apiv1.CreateKeyResponse{
215+
Name: testObject,
216+
PublicKey: &ecdsa.PublicKey{},
217+
CreateSignerRequest: apiv1.CreateSignerRequest{
218+
SigningKey: testObject,
219+
},
220+
}, false},
211221
{"RSA SHA256WithRSA", args{&apiv1.CreateKeyRequest{
212222
Name: testObject,
213223
SignatureAlgorithm: apiv1.SHA256WithRSA,
@@ -563,6 +573,7 @@ func TestPKCS11_StoreCertificate(t *testing.T) {
563573
// Make sure to delete the created certificate
564574
t.Cleanup(func() {
565575
k.DeleteCertificate(testObject)
576+
k.DeleteCertificate(testObjectAlt)
566577
})
567578

568579
type args struct {
@@ -577,6 +588,11 @@ func TestPKCS11_StoreCertificate(t *testing.T) {
577588
Name: testObject,
578589
Certificate: cert,
579590
}}, false},
591+
{"ok extractable", args{&apiv1.StoreCertificateRequest{
592+
Name: testObjectAlt,
593+
Certificate: cert,
594+
Extractable: true,
595+
}}, false},
580596
{"fail already exists", args{&apiv1.StoreCertificateRequest{
581597
Name: testObject,
582598
Certificate: cert,
@@ -593,13 +609,22 @@ func TestPKCS11_StoreCertificate(t *testing.T) {
593609
Name: "http:id=7770;object=create-cert",
594610
Certificate: cert,
595611
}}, true},
596-
{"fail ImportCertificateWithLabel", args{&apiv1.StoreCertificateRequest{
597-
Name: "pkcs11:foo=bar",
612+
{"fail missing id", args{&apiv1.StoreCertificateRequest{
613+
Name: "pkcs11:object=create-cert",
614+
Certificate: cert,
615+
}}, true},
616+
{"fail missing object", args{&apiv1.StoreCertificateRequest{
617+
Name: "pkcs11:id=7770;object=",
598618
Certificate: cert,
599619
}}, true},
600620
}
601621
for _, tt := range tests {
602622
t.Run(tt.name, func(t *testing.T) {
623+
if tt.args.req.Extractable {
624+
if testModule == "SoftHSM2" {
625+
t.Skip("Extractable certificates are not supported on SoftHSM2")
626+
}
627+
}
603628
if err := k.StoreCertificate(tt.args.req); (err != nil) != tt.wantErr {
604629
t.Errorf("PKCS11.StoreCertificate() error = %v, wantErr %v", err, tt.wantErr)
605630
}

Diff for: kms/pkcs11/setup_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
var (
1919
testModule = ""
2020
testObject = "pkcs11:id=7370;object=test-name"
21+
testObjectAlt = "pkcs11:id=7377;object=alt-test-name"
2122
testObjectByID = "pkcs11:id=7370"
2223
testObjectByLabel = "pkcs11:object=test-name"
2324
testKeys = []struct {
@@ -105,7 +106,7 @@ func setup(t TBTesting, k *PKCS11) {
105106
}); err != nil && !errors.Is(errors.Cause(err), apiv1.ErrAlreadyExists{
106107
Message: c.Name + " already exists",
107108
}) {
108-
t.Errorf("PKCS1.StoreCertificate() error = %+v", err)
109+
t.Errorf("PKCS1.StoreCertificate() error = %v", err)
109110
continue
110111
}
111112
testCerts[i].Certificates = append(testCerts[i].Certificates, cert)

0 commit comments

Comments
 (0)