forked from TrueCloudLab/certificates
Merge branch 'master' into hs/acme-eab
This commit is contained in:
commit
60a1c34f05
16 changed files with 386 additions and 103 deletions
|
@ -32,25 +32,16 @@ builds:
|
||||||
id: step-cloudkms-init
|
id: step-cloudkms-init
|
||||||
env:
|
env:
|
||||||
- CGO_ENABLED=0
|
- CGO_ENABLED=0
|
||||||
goos:
|
targets:
|
||||||
- linux
|
- darwin_amd64
|
||||||
- darwin
|
- darwin_arm64
|
||||||
- windows
|
- freebsd_amd64
|
||||||
goarch:
|
- linux_386
|
||||||
- amd64
|
- linux_amd64
|
||||||
- arm
|
- linux_arm64
|
||||||
- arm64
|
- linux_arm_6
|
||||||
- 386
|
- linux_arm_7
|
||||||
goarm:
|
- windows_amd64
|
||||||
- 6
|
|
||||||
- 7
|
|
||||||
ignore:
|
|
||||||
- goos: windows
|
|
||||||
goarch: 386
|
|
||||||
- goos: windows
|
|
||||||
goarm: 6
|
|
||||||
- goos: windows
|
|
||||||
goarm: 7
|
|
||||||
flags:
|
flags:
|
||||||
- -trimpath
|
- -trimpath
|
||||||
main: ./cmd/step-cloudkms-init/main.go
|
main: ./cmd/step-cloudkms-init/main.go
|
||||||
|
@ -61,25 +52,16 @@ builds:
|
||||||
id: step-awskms-init
|
id: step-awskms-init
|
||||||
env:
|
env:
|
||||||
- CGO_ENABLED=0
|
- CGO_ENABLED=0
|
||||||
goos:
|
targets:
|
||||||
- linux
|
- darwin_amd64
|
||||||
- darwin
|
- darwin_arm64
|
||||||
- windows
|
- freebsd_amd64
|
||||||
goarch:
|
- linux_386
|
||||||
- amd64
|
- linux_amd64
|
||||||
- arm
|
- linux_arm64
|
||||||
- arm64
|
- linux_arm_6
|
||||||
- 386
|
- linux_arm_7
|
||||||
goarm:
|
- windows_amd64
|
||||||
- 6
|
|
||||||
- 7
|
|
||||||
ignore:
|
|
||||||
- goos: windows
|
|
||||||
goarch: 386
|
|
||||||
- goos: windows
|
|
||||||
goarm: 6
|
|
||||||
- goos: windows
|
|
||||||
goarm: 7
|
|
||||||
flags:
|
flags:
|
||||||
- -trimpath
|
- -trimpath
|
||||||
main: ./cmd/step-awskms-init/main.go
|
main: ./cmd/step-awskms-init/main.go
|
||||||
|
|
19
CHANGELOG.md
19
CHANGELOG.md
|
@ -4,18 +4,27 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [Unreleased - 0.17.5] - DATE
|
## [Unreleased - 0.17.7] - DATE
|
||||||
### Added
|
### Added
|
||||||
- Support for Azure Key Vault as a KMS.
|
- Support for generate extractable keys and certificates on a pkcs#11 module.
|
||||||
- Adapt `pki` package to support key managers.
|
|
||||||
- gocritic linter
|
|
||||||
### Changed
|
### Changed
|
||||||
### Deprecated
|
### Deprecated
|
||||||
### Removed
|
### Removed
|
||||||
### Fixed
|
### Fixed
|
||||||
- gocritic warnings
|
|
||||||
### Security
|
### Security
|
||||||
|
|
||||||
|
## [0.17.6] - 2021-10-20
|
||||||
|
### Notes
|
||||||
|
- 0.17.5 failed in CI/CD
|
||||||
|
|
||||||
|
## [0.17.5] - 2021-10-20
|
||||||
|
### Added
|
||||||
|
- Support for Azure Key Vault as a KMS.
|
||||||
|
- Adapt `pki` package to support key managers.
|
||||||
|
- gocritic linter
|
||||||
|
### Fixed
|
||||||
|
- gocritic warnings
|
||||||
|
|
||||||
## [0.17.4] - 2021-09-28
|
## [0.17.4] - 2021-09-28
|
||||||
### Fixed
|
### Fixed
|
||||||
- Support host-only or user-only SSH CA.
|
- Support host-only or user-only SSH CA.
|
||||||
|
|
24
README.md
24
README.md
|
@ -25,7 +25,7 @@ To get up and running quickly, or as an alternative to running your own `step-ca
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Questions? Find us in [Discussions](https://github.com/smallstep/certificates/discussions) or [Join our Discord](https://bit.ly/step-discord).**
|
**Questions? Find us in [Discussions](https://github.com/smallstep/certificates/discussions) or [Join our Discord](https://u.step.sm/discord).**
|
||||||
|
|
||||||
[Website](https://smallstep.com/certificates) |
|
[Website](https://smallstep.com/certificates) |
|
||||||
[Documentation](https://smallstep.com/docs) |
|
[Documentation](https://smallstep.com/docs) |
|
||||||
|
@ -66,7 +66,8 @@ You can issue certificates in exchange for:
|
||||||
- [Cloud instance identity documents](https://smallstep.com/blog/embarrassingly-easy-certificates-on-aws-azure-gcp/), for VMs on AWS, GCP, and Azure
|
- [Cloud instance identity documents](https://smallstep.com/blog/embarrassingly-easy-certificates-on-aws-azure-gcp/), for VMs on AWS, GCP, and Azure
|
||||||
- [Single-use, short-lived JWK tokens](https://smallstep.com/docs/step-ca/provisioners#jwk) issued by your CD tool — Puppet, Chef, Ansible, Terraform, etc.
|
- [Single-use, short-lived JWK tokens](https://smallstep.com/docs/step-ca/provisioners#jwk) issued by your CD tool — Puppet, Chef, Ansible, Terraform, etc.
|
||||||
- A trusted X.509 certificate (X5C provisioner)
|
- A trusted X.509 certificate (X5C provisioner)
|
||||||
- Expiring SSH host certificates needing rotation (the SSHPOP provisioner)
|
- A SCEP challenge (SCEP provisioner)
|
||||||
|
- An SSH host certificates needing renewal (the SSHPOP provisioner)
|
||||||
- Learn more in our [provisioner documentation](https://smallstep.com/docs/step-ca/provisioners)
|
- Learn more in our [provisioner documentation](https://smallstep.com/docs/step-ca/provisioners)
|
||||||
|
|
||||||
### 🏔 Your own private ACME server
|
### 🏔 Your own private ACME server
|
||||||
|
@ -80,16 +81,17 @@ ACME is the protocol used by Let's Encrypt to automate the issuance of HTTPS cer
|
||||||
- For `tls-alpn-01`, respond to the challenge at the TLS layer ([as Caddy does](https://caddy.community/t/caddy-supports-the-acme-tls-alpn-challenge/4860)) to prove that you control the web server
|
- For `tls-alpn-01`, respond to the challenge at the TLS layer ([as Caddy does](https://caddy.community/t/caddy-supports-the-acme-tls-alpn-challenge/4860)) to prove that you control the web server
|
||||||
|
|
||||||
- Works with any ACME client. We've written examples for:
|
- Works with any ACME client. We've written examples for:
|
||||||
- [certbot](https://smallstep.com/blog/private-acme-server/#certbotuploadsacme-certbotpng-certbot-example)
|
- [certbot](https://smallstep.com/docs/tutorials/acme-protocol-acme-clients#certbot)
|
||||||
- [acme.sh](https://smallstep.com/blog/private-acme-server/#acmeshuploadsacme-acme-shpng-acmesh-example)
|
- [acme.sh](https://smallstep.com/docs/tutorials/acme-protocol-acme-clients#acmesh)
|
||||||
- [Caddy](https://smallstep.com/blog/private-acme-server/#caddyuploadsacme-caddypng-caddy-example)
|
- [win-acme](https://smallstep.com/docs/tutorials/acme-protocol-acme-clients#win-acme)
|
||||||
- [Traefik](https://smallstep.com/blog/private-acme-server/#traefikuploadsacme-traefikpng-traefik-example)
|
- [Caddy](https://smallstep.com/docs/tutorials/acme-protocol-acme-clients#caddy-v2)
|
||||||
- [Apache](https://smallstep.com/blog/private-acme-server/#apacheuploadsacme-apachepng-apache-example)
|
- [Traefik](https://smallstep.com/docs/tutorials/acme-protocol-acme-clients#traefik)
|
||||||
- [nginx](https://smallstep.com/blog/private-acme-server/#nginxuploadsacme-nginxpng-nginx-example)
|
- [Apache](https://smallstep.com/docs/tutorials/acme-protocol-acme-clients#apache)
|
||||||
|
- [nginx](https://smallstep.com/docs/tutorials/acme-protocol-acme-clients#nginx)
|
||||||
- Get certificates programmatically using ACME, using these libraries:
|
- Get certificates programmatically using ACME, using these libraries:
|
||||||
- [`lego`](https://github.com/go-acme/lego) for Golang ([example usage](https://smallstep.com/blog/private-acme-server/#golanguploadsacme-golangpng-go-example))
|
- [`lego`](https://github.com/go-acme/lego) for Golang ([example usage](https://smallstep.com/docs/tutorials/acme-protocol-acme-clients#golang))
|
||||||
- certbot's [`acme` module](https://github.com/certbot/certbot/tree/master/acme) for Python ([example usage](https://smallstep.com/blog/private-acme-server/#pythonuploadsacme-pythonpng-python-example))
|
- certbot's [`acme` module](https://github.com/certbot/certbot/tree/master/acme) for Python ([example usage](https://smallstep.com/docs/tutorials/acme-protocol-acme-clients#python))
|
||||||
- [`acme-client`](https://github.com/publishlab/node-acme-client) for Node.js ([example usage](https://smallstep.com/blog/private-acme-server/#nodejsuploadsacme-node-jspng-nodejs-example))
|
- [`acme-client`](https://github.com/publishlab/node-acme-client) for Node.js ([example usage](https://smallstep.com/docs/tutorials/acme-protocol-acme-clients#node))
|
||||||
- Our own [`step` CLI tool](https://github.com/smallstep/cli) is also an ACME client!
|
- Our own [`step` CLI tool](https://github.com/smallstep/cli) is also an ACME client!
|
||||||
- See our [ACME tutorial](https://smallstep.com/docs/tutorials/acme-challenge) for more
|
- See our [ACME tutorial](https://smallstep.com/docs/tutorials/acme-challenge) for more
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,7 @@ type Config struct {
|
||||||
NoCerts bool
|
NoCerts bool
|
||||||
EnableSSH bool
|
EnableSSH bool
|
||||||
Force bool
|
Force bool
|
||||||
|
Extractable bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate checks the flags in the config.
|
// Validate checks the flags in the config.
|
||||||
|
@ -117,6 +118,7 @@ func main() {
|
||||||
flag.BoolVar(&c.EnableSSH, "ssh", false, "Enable the creation of ssh keys.")
|
flag.BoolVar(&c.EnableSSH, "ssh", false, "Enable the creation of ssh keys.")
|
||||||
flag.BoolVar(&c.NoCerts, "no-certs", false, "Do not store certificates in the module.")
|
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.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.Usage = usage
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
@ -293,6 +295,7 @@ func createPKI(k kms.KeyManager, c Config) error {
|
||||||
resp, err := k.CreateKey(&apiv1.CreateKeyRequest{
|
resp, err := k.CreateKey(&apiv1.CreateKeyRequest{
|
||||||
Name: c.RootKeyObject,
|
Name: c.RootKeyObject,
|
||||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
||||||
|
Extractable: c.Extractable,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -332,6 +335,7 @@ func createPKI(k kms.KeyManager, c Config) error {
|
||||||
if err := cm.StoreCertificate(&apiv1.StoreCertificateRequest{
|
if err := cm.StoreCertificate(&apiv1.StoreCertificateRequest{
|
||||||
Name: c.RootObject,
|
Name: c.RootObject,
|
||||||
Certificate: root,
|
Certificate: root,
|
||||||
|
Extractable: c.Extractable,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -373,6 +377,7 @@ func createPKI(k kms.KeyManager, c Config) error {
|
||||||
resp, err := k.CreateKey(&apiv1.CreateKeyRequest{
|
resp, err := k.CreateKey(&apiv1.CreateKeyRequest{
|
||||||
Name: c.CrtKeyObject,
|
Name: c.CrtKeyObject,
|
||||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
||||||
|
Extractable: c.Extractable,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -409,6 +414,7 @@ func createPKI(k kms.KeyManager, c Config) error {
|
||||||
if err := cm.StoreCertificate(&apiv1.StoreCertificateRequest{
|
if err := cm.StoreCertificate(&apiv1.StoreCertificateRequest{
|
||||||
Name: c.CrtObject,
|
Name: c.CrtObject,
|
||||||
Certificate: intermediate,
|
Certificate: intermediate,
|
||||||
|
Extractable: c.Extractable,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
8
go.mod
8
go.mod
|
@ -32,8 +32,8 @@ require (
|
||||||
github.com/smallstep/nosql v0.3.8
|
github.com/smallstep/nosql v0.3.8
|
||||||
github.com/urfave/cli v1.22.4
|
github.com/urfave/cli v1.22.4
|
||||||
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352
|
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352
|
||||||
go.step.sm/cli-utils v0.6.0
|
go.step.sm/cli-utils v0.6.1
|
||||||
go.step.sm/crypto v0.11.0
|
go.step.sm/crypto v0.13.0
|
||||||
go.step.sm/linkedca v0.8.0
|
go.step.sm/linkedca v0.8.0
|
||||||
golang.org/x/crypto v0.0.0-20210915214749-c084706c2272
|
golang.org/x/crypto v0.0.0-20210915214749-c084706c2272
|
||||||
golang.org/x/net v0.0.0-20210913180222-943fd674d43e
|
golang.org/x/net v0.0.0-20210913180222-943fd674d43e
|
||||||
|
@ -44,6 +44,10 @@ require (
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0
|
gopkg.in/square/go-jose.v2 v2.6.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// avoid license conflict from juju/ansiterm until https://github.com/manifoldco/promptui/pull/181
|
||||||
|
// is merged or other dependency in path currently in violation fixes compliance
|
||||||
|
replace github.com/manifoldco/promptui => github.com/nguyer/promptui v0.8.1-0.20210517132806-70ccd4709797
|
||||||
|
|
||||||
// replace github.com/smallstep/nosql => ../nosql
|
// replace github.com/smallstep/nosql => ../nosql
|
||||||
// replace go.step.sm/crypto => ../crypto
|
// replace go.step.sm/crypto => ../crypto
|
||||||
// replace go.step.sm/cli-utils => ../cli-utils
|
// replace go.step.sm/cli-utils => ../cli-utils
|
||||||
|
|
16
go.sum
16
go.sum
|
@ -357,8 +357,6 @@ github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU=
|
|
||||||
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU=
|
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
@ -377,12 +375,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
||||||
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
||||||
github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||||
github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8=
|
|
||||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
|
||||||
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
|
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
|
||||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
github.com/manifoldco/promptui v0.8.0 h1:R95mMF+McvXZQ7j1g8ucVZE1gLP3Sv6j9vlF9kyRqQo=
|
|
||||||
github.com/manifoldco/promptui v0.8.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ=
|
|
||||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||||
|
@ -432,6 +426,8 @@ github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi
|
||||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||||
github.com/newrelic/go-agent v2.15.0+incompatible h1:IB0Fy+dClpBq9aEoIrLyQXzU34JyI1xVTanPLB/+jvU=
|
github.com/newrelic/go-agent v2.15.0+incompatible h1:IB0Fy+dClpBq9aEoIrLyQXzU34JyI1xVTanPLB/+jvU=
|
||||||
github.com/newrelic/go-agent v2.15.0+incompatible/go.mod h1:a8Fv1b/fYhFSReoTU6HDkTYIMZeSVNffmoS726Y0LzQ=
|
github.com/newrelic/go-agent v2.15.0+incompatible/go.mod h1:a8Fv1b/fYhFSReoTU6HDkTYIMZeSVNffmoS726Y0LzQ=
|
||||||
|
github.com/nguyer/promptui v0.8.1-0.20210517132806-70ccd4709797 h1:unCiBzwNjcuVbP3bgM76z0ORyIuI4sspop1qhkQJ044=
|
||||||
|
github.com/nguyer/promptui v0.8.1-0.20210517132806-70ccd4709797/go.mod h1:CBMXL3a2sC3Q8TjpLcQt8w/3aQ23VSy6r7UFeCG6phA=
|
||||||
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
|
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
|
||||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||||
|
@ -566,11 +562,11 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||||
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
||||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||||
go.step.sm/cli-utils v0.6.0 h1:sH4FxBcjmbxyilKXheSyJuKF/QjpojpiW90ERwUWOgQ=
|
go.step.sm/cli-utils v0.6.1 h1:v31ctEh/BFPGU067fF9Y8u2EIg6LRldUbN2dc/+u/V8=
|
||||||
go.step.sm/cli-utils v0.6.0/go.mod h1:jklBMavFl2PbmGlyxgax08ZnB0uWpadjuOlSKKXz+0U=
|
go.step.sm/cli-utils v0.6.1/go.mod h1:stgyXHHHi9KwcR86sgzDdFC6e/tAmpF4NbqwSK7q/GM=
|
||||||
go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0=
|
go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0=
|
||||||
go.step.sm/crypto v0.11.0 h1:VDpeVgEmqme/FK2w5QINxkOQ1FWOm/Wi2TwQXiacKr8=
|
go.step.sm/crypto v0.13.0 h1:mQuP9Uu2FNmqCJNO0OTbvolnYXzONy4wdUBtUVcP1s8=
|
||||||
go.step.sm/crypto v0.11.0/go.mod h1:5YzQ85BujYBu6NH18jw7nFjwuRnDch35nLzH0ES5sKg=
|
go.step.sm/crypto v0.13.0/go.mod h1:5YzQ85BujYBu6NH18jw7nFjwuRnDch35nLzH0ES5sKg=
|
||||||
go.step.sm/linkedca v0.8.0 h1:86DAufqUtUvFTJgYpgG0McKkpqnjXxg53FTXYyhs0HI=
|
go.step.sm/linkedca v0.8.0 h1:86DAufqUtUvFTJgYpgG0McKkpqnjXxg53FTXYyhs0HI=
|
||||||
go.step.sm/linkedca v0.8.0/go.mod h1:5uTRjozEGSPAZal9xJqlaD38cvJcLe3o1VAFVjqcORo=
|
go.step.sm/linkedca v0.8.0/go.mod h1:5uTRjozEGSPAZal9xJqlaD38cvJcLe3o1VAFVjqcORo=
|
||||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
|
|
@ -100,7 +100,7 @@ type GetPublicKeyRequest struct {
|
||||||
type CreateKeyRequest struct {
|
type CreateKeyRequest struct {
|
||||||
// Name represents the key name or label used to identify a key.
|
// Name represents the key name or label used to identify a key.
|
||||||
//
|
//
|
||||||
// Used by: awskms, cloudkms, pkcs11, yubikey.
|
// Used by: awskms, cloudkms, azurekms, pkcs11, yubikey.
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
// SignatureAlgorithm represents the type of key to create.
|
// SignatureAlgorithm represents the type of key to create.
|
||||||
|
@ -110,8 +110,14 @@ type CreateKeyRequest struct {
|
||||||
Bits int
|
Bits int
|
||||||
|
|
||||||
// ProtectionLevel specifies how cryptographic operations are performed.
|
// ProtectionLevel specifies how cryptographic operations are performed.
|
||||||
// Used by: cloudkms
|
// Used by: cloudkms, azurekms.
|
||||||
ProtectionLevel ProtectionLevel
|
ProtectionLevel ProtectionLevel
|
||||||
|
|
||||||
|
// Extractable defines if the new key may be exported from the HSM under a
|
||||||
|
// wrap key. On pkcs11 sets the CKA_EXTRACTABLE bit.
|
||||||
|
//
|
||||||
|
// Used by: pkcs11
|
||||||
|
Extractable bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateKeyResponse is the response value of the kms.CreateKey method.
|
// CreateKeyResponse is the response value of the kms.CreateKey method.
|
||||||
|
@ -152,4 +158,10 @@ type LoadCertificateRequest struct {
|
||||||
type StoreCertificateRequest struct {
|
type StoreCertificateRequest struct {
|
||||||
Name string
|
Name string
|
||||||
Certificate *x509.Certificate
|
Certificate *x509.Certificate
|
||||||
|
|
||||||
|
// Extractable defines if the new certificate may be exported from the HSM
|
||||||
|
// under a wrap key. On pkcs11 sets the CKA_EXTRACTABLE bit.
|
||||||
|
//
|
||||||
|
// Used by: pkcs11
|
||||||
|
Extractable bool
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,10 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault"
|
"github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault"
|
||||||
|
"github.com/Azure/go-autorest/autorest/azure"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/crypto/cryptobyte"
|
"golang.org/x/crypto/cryptobyte"
|
||||||
"golang.org/x/crypto/cryptobyte/asn1"
|
"golang.org/x/crypto/cryptobyte/asn1"
|
||||||
|
@ -69,15 +71,10 @@ func (s *Signer) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := defaultContext()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
b64 := base64.RawURLEncoding.EncodeToString(digest)
|
b64 := base64.RawURLEncoding.EncodeToString(digest)
|
||||||
|
|
||||||
resp, err := s.client.Sign(ctx, s.vaultBaseURL, s.name, s.version, keyvault.KeySignParameters{
|
// Sign with retry if the key is not ready
|
||||||
Algorithm: alg,
|
resp, err := s.signWithRetry(alg, b64, 3)
|
||||||
Value: &b64,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "keyVault Sign failed")
|
return nil, errors.Wrap(err, "keyVault Sign failed")
|
||||||
}
|
}
|
||||||
|
@ -111,6 +108,31 @@ func (s *Signer) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]
|
||||||
return b.Bytes()
|
return b.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Signer) signWithRetry(alg keyvault.JSONWebKeySignatureAlgorithm, b64 string, retryAttempts int) (keyvault.KeyOperationResult, error) {
|
||||||
|
retry:
|
||||||
|
ctx, cancel := defaultContext()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
resp, err := s.client.Sign(ctx, s.vaultBaseURL, s.name, s.version, keyvault.KeySignParameters{
|
||||||
|
Algorithm: alg,
|
||||||
|
Value: &b64,
|
||||||
|
})
|
||||||
|
if err != nil && retryAttempts > 0 {
|
||||||
|
var requestError *azure.RequestError
|
||||||
|
if errors.As(err, &requestError) {
|
||||||
|
if se := requestError.ServiceError; se != nil && se.InnerError != nil {
|
||||||
|
code, ok := se.InnerError["code"].(string)
|
||||||
|
if ok && code == "KeyNotYetValid" {
|
||||||
|
time.Sleep(time.Second / time.Duration(retryAttempts))
|
||||||
|
retryAttempts--
|
||||||
|
goto retry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
func getSigningAlgorithm(key crypto.PublicKey, opts crypto.SignerOpts) (keyvault.JSONWebKeySignatureAlgorithm, error) {
|
func getSigningAlgorithm(key crypto.PublicKey, opts crypto.SignerOpts) (keyvault.JSONWebKeySignatureAlgorithm, error) {
|
||||||
switch key.(type) {
|
switch key.(type) {
|
||||||
case *rsa.PublicKey:
|
case *rsa.PublicKey:
|
||||||
|
|
|
@ -11,6 +11,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault"
|
"github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault"
|
||||||
|
"github.com/Azure/go-autorest/autorest"
|
||||||
|
"github.com/Azure/go-autorest/autorest/azure"
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
"github.com/smallstep/certificates/kms/apiv1"
|
"github.com/smallstep/certificates/kms/apiv1"
|
||||||
"go.step.sm/crypto/keyutil"
|
"go.step.sm/crypto/keyutil"
|
||||||
|
@ -350,3 +352,142 @@ func TestSigner_Sign(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSigner_Sign_signWithRetry(t *testing.T) {
|
||||||
|
sign := func(kty, crv string, bits int, opts crypto.SignerOpts) (crypto.PublicKey, []byte, string, []byte) {
|
||||||
|
key, err := keyutil.GenerateSigner(kty, crv, bits)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
h := opts.HashFunc().New()
|
||||||
|
h.Write([]byte("random-data"))
|
||||||
|
sum := h.Sum(nil)
|
||||||
|
|
||||||
|
var sig, resultSig []byte
|
||||||
|
if priv, ok := key.(*ecdsa.PrivateKey); ok {
|
||||||
|
r, s, err := ecdsa.Sign(rand.Reader, priv, sum)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
curveBits := priv.Params().BitSize
|
||||||
|
keyBytes := curveBits / 8
|
||||||
|
if curveBits%8 > 0 {
|
||||||
|
keyBytes++
|
||||||
|
}
|
||||||
|
rBytes := r.Bytes()
|
||||||
|
rBytesPadded := make([]byte, keyBytes)
|
||||||
|
copy(rBytesPadded[keyBytes-len(rBytes):], rBytes)
|
||||||
|
|
||||||
|
sBytes := s.Bytes()
|
||||||
|
sBytesPadded := make([]byte, keyBytes)
|
||||||
|
copy(sBytesPadded[keyBytes-len(sBytes):], sBytes)
|
||||||
|
// nolint:gocritic
|
||||||
|
resultSig = append(rBytesPadded, sBytesPadded...)
|
||||||
|
|
||||||
|
var b cryptobyte.Builder
|
||||||
|
b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) {
|
||||||
|
b.AddASN1BigInt(r)
|
||||||
|
b.AddASN1BigInt(s)
|
||||||
|
})
|
||||||
|
sig, err = b.Bytes()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sig, err = key.Sign(rand.Reader, sum, opts)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
resultSig = sig
|
||||||
|
}
|
||||||
|
|
||||||
|
return key.Public(), h.Sum(nil), base64.RawURLEncoding.EncodeToString(resultSig), sig
|
||||||
|
}
|
||||||
|
|
||||||
|
p256, p256Digest, p256ResultSig, p256Sig := sign("EC", "P-256", 0, crypto.SHA256)
|
||||||
|
okResult := keyvault.KeyOperationResult{
|
||||||
|
Result: &p256ResultSig,
|
||||||
|
}
|
||||||
|
failResult := keyvault.KeyOperationResult{}
|
||||||
|
retryError := autorest.DetailedError{
|
||||||
|
Original: &azure.RequestError{
|
||||||
|
ServiceError: &azure.ServiceError{
|
||||||
|
InnerError: map[string]interface{}{
|
||||||
|
"code": "KeyNotYetValid",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
client := mockClient(t)
|
||||||
|
expects := []struct {
|
||||||
|
name string
|
||||||
|
keyVersion string
|
||||||
|
alg keyvault.JSONWebKeySignatureAlgorithm
|
||||||
|
digest []byte
|
||||||
|
result keyvault.KeyOperationResult
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{"ok 1", "", keyvault.ES256, p256Digest, failResult, retryError},
|
||||||
|
{"ok 2", "", keyvault.ES256, p256Digest, failResult, retryError},
|
||||||
|
{"ok 3", "", keyvault.ES256, p256Digest, failResult, retryError},
|
||||||
|
{"ok 4", "", keyvault.ES256, p256Digest, okResult, nil},
|
||||||
|
{"fail", "fail-version", keyvault.ES256, p256Digest, failResult, retryError},
|
||||||
|
{"fail", "fail-version", keyvault.ES256, p256Digest, failResult, retryError},
|
||||||
|
{"fail", "fail-version", keyvault.ES256, p256Digest, failResult, retryError},
|
||||||
|
{"fail", "fail-version", keyvault.ES256, p256Digest, failResult, retryError},
|
||||||
|
}
|
||||||
|
for _, e := range expects {
|
||||||
|
value := base64.RawURLEncoding.EncodeToString(e.digest)
|
||||||
|
client.EXPECT().Sign(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", e.keyVersion, keyvault.KeySignParameters{
|
||||||
|
Algorithm: e.alg,
|
||||||
|
Value: &value,
|
||||||
|
}).Return(e.result, e.err)
|
||||||
|
}
|
||||||
|
|
||||||
|
type fields struct {
|
||||||
|
client KeyVaultClient
|
||||||
|
vaultBaseURL string
|
||||||
|
name string
|
||||||
|
version string
|
||||||
|
publicKey crypto.PublicKey
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
rand io.Reader
|
||||||
|
digest []byte
|
||||||
|
opts crypto.SignerOpts
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
want []byte
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{"ok", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", p256}, args{
|
||||||
|
rand.Reader, p256Digest, crypto.SHA256,
|
||||||
|
}, p256Sig, false},
|
||||||
|
{"fail", fields{client, "https://my-vault.vault.azure.net/", "my-key", "fail-version", p256}, args{
|
||||||
|
rand.Reader, p256Digest, crypto.SHA256,
|
||||||
|
}, nil, true},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
s := &Signer{
|
||||||
|
client: tt.fields.client,
|
||||||
|
vaultBaseURL: tt.fields.vaultBaseURL,
|
||||||
|
name: tt.fields.name,
|
||||||
|
version: tt.fields.version,
|
||||||
|
publicKey: tt.fields.publicKey,
|
||||||
|
}
|
||||||
|
got, err := s.Sign(tt.args.rand, tt.args.digest, tt.args.opts)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("Signer.Sign() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("Signer.Sign() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -80,10 +80,23 @@ func (s *stubPKCS11) FindCertificate(id, label []byte, serial *big.Int) (*x509.C
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *stubPKCS11) ImportCertificateWithAttributes(template crypto11.AttributeSet, cert *x509.Certificate) error {
|
||||||
|
var id, label []byte
|
||||||
|
if v := template[crypto11.CkaId]; v != nil {
|
||||||
|
id = v.Value
|
||||||
|
}
|
||||||
|
if v := template[crypto11.CkaLabel]; v != nil {
|
||||||
|
label = v.Value
|
||||||
|
}
|
||||||
|
return s.ImportCertificateWithLabel(id, label, cert)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *stubPKCS11) ImportCertificateWithLabel(id, label []byte, cert *x509.Certificate) error {
|
func (s *stubPKCS11) ImportCertificateWithLabel(id, label []byte, cert *x509.Certificate) error {
|
||||||
switch {
|
switch {
|
||||||
case id == nil && label == nil:
|
case id == nil:
|
||||||
return errors.New("id and label cannot both be nil")
|
return errors.New("id cannot both be nil")
|
||||||
|
case label == nil:
|
||||||
|
return errors.New("label cannot both be nil")
|
||||||
case cert == nil:
|
case cert == nil:
|
||||||
return errors.New("certificate cannot be nil")
|
return errors.New("certificate cannot be nil")
|
||||||
}
|
}
|
||||||
|
@ -111,6 +124,17 @@ func (s *stubPKCS11) DeleteCertificate(id, label []byte, serial *big.Int) error
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *stubPKCS11) GenerateRSAKeyPairWithAttributes(public, private crypto11.AttributeSet, bits int) (crypto11.SignerDecrypter, error) {
|
||||||
|
var id, label []byte
|
||||||
|
if v := public[crypto11.CkaId]; v != nil {
|
||||||
|
id = v.Value
|
||||||
|
}
|
||||||
|
if v := public[crypto11.CkaLabel]; v != nil {
|
||||||
|
label = v.Value
|
||||||
|
}
|
||||||
|
return s.GenerateRSAKeyPairWithLabel(id, label, bits)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *stubPKCS11) GenerateRSAKeyPairWithLabel(id, label []byte, bits int) (crypto11.SignerDecrypter, error) {
|
func (s *stubPKCS11) GenerateRSAKeyPairWithLabel(id, label []byte, bits int) (crypto11.SignerDecrypter, error) {
|
||||||
if id == nil && label == nil {
|
if id == nil && label == nil {
|
||||||
return nil, errors.New("id and label cannot both be nil")
|
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
|
||||||
return k, nil
|
return k, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *stubPKCS11) GenerateECDSAKeyPairWithAttributes(public, private crypto11.AttributeSet, curve elliptic.Curve) (crypto11.Signer, error) {
|
||||||
|
var id, label []byte
|
||||||
|
if v := public[crypto11.CkaId]; v != nil {
|
||||||
|
id = v.Value
|
||||||
|
}
|
||||||
|
if v := public[crypto11.CkaLabel]; v != nil {
|
||||||
|
label = v.Value
|
||||||
|
}
|
||||||
|
return s.GenerateECDSAKeyPairWithLabel(id, label, curve)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *stubPKCS11) GenerateECDSAKeyPairWithLabel(id, label []byte, curve elliptic.Curve) (crypto11.Signer, error) {
|
func (s *stubPKCS11) GenerateECDSAKeyPairWithLabel(id, label []byte, curve elliptic.Curve) (crypto11.Signer, error) {
|
||||||
if id == nil && label == nil {
|
if id == nil && label == nil {
|
||||||
return nil, errors.New("id and label cannot both be nil")
|
return nil, errors.New("id and label cannot both be nil")
|
||||||
|
|
|
@ -32,10 +32,10 @@ const DefaultRSASize = 3072
|
||||||
type P11 interface {
|
type P11 interface {
|
||||||
FindKeyPair(id, label []byte) (crypto11.Signer, error)
|
FindKeyPair(id, label []byte) (crypto11.Signer, error)
|
||||||
FindCertificate(id, label []byte, serial *big.Int) (*x509.Certificate, error)
|
FindCertificate(id, label []byte, serial *big.Int) (*x509.Certificate, error)
|
||||||
ImportCertificateWithLabel(id, label []byte, cert *x509.Certificate) error
|
ImportCertificateWithAttributes(template crypto11.AttributeSet, certificate *x509.Certificate) error
|
||||||
DeleteCertificate(id, label []byte, serial *big.Int) error
|
DeleteCertificate(id, label []byte, serial *big.Int) error
|
||||||
GenerateRSAKeyPairWithLabel(id, label []byte, bits int) (crypto11.SignerDecrypter, error)
|
GenerateRSAKeyPairWithAttributes(public, private crypto11.AttributeSet, bits int) (crypto11.SignerDecrypter, error)
|
||||||
GenerateECDSAKeyPairWithLabel(id, label []byte, curve elliptic.Curve) (crypto11.Signer, error)
|
GenerateECDSAKeyPairWithAttributes(public, private crypto11.AttributeSet, curve elliptic.Curve) (crypto11.Signer, error)
|
||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,6 +185,12 @@ func (k *PKCS11) StoreCertificate(req *apiv1.StoreCertificateRequest) error {
|
||||||
return errors.Wrap(err, "storeCertificate failed")
|
return errors.Wrap(err, "storeCertificate failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enforce the use of both id and labels. This is not strictly necessary in
|
||||||
|
// PKCS #11, but it's a good practice.
|
||||||
|
if len(id) == 0 || len(object) == 0 {
|
||||||
|
return errors.Errorf("key with uri %s is not valid, id and object are required", req.Name)
|
||||||
|
}
|
||||||
|
|
||||||
cert, err := k.p11.FindCertificate(id, object, nil)
|
cert, err := k.p11.FindCertificate(id, object, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "storeCertificate failed")
|
return errors.Wrap(err, "storeCertificate failed")
|
||||||
|
@ -195,7 +201,15 @@ func (k *PKCS11) StoreCertificate(req *apiv1.StoreCertificateRequest) error {
|
||||||
}, "storeCertificate failed")
|
}, "storeCertificate failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := k.p11.ImportCertificateWithLabel(id, object, req.Certificate); err != nil {
|
// Import certificate with the necessary attributes.
|
||||||
|
template, err := crypto11.NewAttributeSetWithIDAndLabel(id, object)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "storeCertificate failed")
|
||||||
|
}
|
||||||
|
if req.Extractable {
|
||||||
|
template.Set(crypto11.CkaExtractable, true)
|
||||||
|
}
|
||||||
|
if err := k.p11.ImportCertificateWithAttributes(template, req.Certificate); err != nil {
|
||||||
return errors.Wrap(err, "storeCertificate failed")
|
return errors.Wrap(err, "storeCertificate failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,6 +298,16 @@ func generateKey(ctx P11, req *apiv1.CreateKeyRequest) (crypto11.Signer, error)
|
||||||
return nil, errors.Errorf("key with uri %s is not valid, id and object are required", req.Name)
|
return nil, errors.Errorf("key with uri %s is not valid, id and object are required", req.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create template for public and private keys
|
||||||
|
public, err := crypto11.NewAttributeSetWithIDAndLabel(id, object)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
private := public.Copy()
|
||||||
|
if req.Extractable {
|
||||||
|
private.Set(crypto11.CkaExtractable, true)
|
||||||
|
}
|
||||||
|
|
||||||
bits := req.Bits
|
bits := req.Bits
|
||||||
if bits == 0 {
|
if bits == 0 {
|
||||||
bits = DefaultRSASize
|
bits = DefaultRSASize
|
||||||
|
@ -291,17 +315,17 @@ func generateKey(ctx P11, req *apiv1.CreateKeyRequest) (crypto11.Signer, error)
|
||||||
|
|
||||||
switch req.SignatureAlgorithm {
|
switch req.SignatureAlgorithm {
|
||||||
case apiv1.UnspecifiedSignAlgorithm:
|
case apiv1.UnspecifiedSignAlgorithm:
|
||||||
return ctx.GenerateECDSAKeyPairWithLabel(id, object, elliptic.P256())
|
return ctx.GenerateECDSAKeyPairWithAttributes(public, private, elliptic.P256())
|
||||||
case apiv1.SHA256WithRSA, apiv1.SHA384WithRSA, apiv1.SHA512WithRSA:
|
case apiv1.SHA256WithRSA, apiv1.SHA384WithRSA, apiv1.SHA512WithRSA:
|
||||||
return ctx.GenerateRSAKeyPairWithLabel(id, object, bits)
|
return ctx.GenerateRSAKeyPairWithAttributes(public, private, bits)
|
||||||
case apiv1.SHA256WithRSAPSS, apiv1.SHA384WithRSAPSS, apiv1.SHA512WithRSAPSS:
|
case apiv1.SHA256WithRSAPSS, apiv1.SHA384WithRSAPSS, apiv1.SHA512WithRSAPSS:
|
||||||
return ctx.GenerateRSAKeyPairWithLabel(id, object, bits)
|
return ctx.GenerateRSAKeyPairWithAttributes(public, private, bits)
|
||||||
case apiv1.ECDSAWithSHA256:
|
case apiv1.ECDSAWithSHA256:
|
||||||
return ctx.GenerateECDSAKeyPairWithLabel(id, object, elliptic.P256())
|
return ctx.GenerateECDSAKeyPairWithAttributes(public, private, elliptic.P256())
|
||||||
case apiv1.ECDSAWithSHA384:
|
case apiv1.ECDSAWithSHA384:
|
||||||
return ctx.GenerateECDSAKeyPairWithLabel(id, object, elliptic.P384())
|
return ctx.GenerateECDSAKeyPairWithAttributes(public, private, elliptic.P384())
|
||||||
case apiv1.ECDSAWithSHA512:
|
case apiv1.ECDSAWithSHA512:
|
||||||
return ctx.GenerateECDSAKeyPairWithLabel(id, object, elliptic.P521())
|
return ctx.GenerateECDSAKeyPairWithAttributes(public, private, elliptic.P521())
|
||||||
case apiv1.PureEd25519:
|
case apiv1.PureEd25519:
|
||||||
return nil, fmt.Errorf("signature algorithm %s is not supported", req.SignatureAlgorithm)
|
return nil, fmt.Errorf("signature algorithm %s is not supported", req.SignatureAlgorithm)
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -208,6 +208,16 @@ func TestPKCS11_CreateKey(t *testing.T) {
|
||||||
SigningKey: testObject,
|
SigningKey: testObject,
|
||||||
},
|
},
|
||||||
}, false},
|
}, false},
|
||||||
|
{"default extractable", args{&apiv1.CreateKeyRequest{
|
||||||
|
Name: testObject,
|
||||||
|
Extractable: true,
|
||||||
|
}}, &apiv1.CreateKeyResponse{
|
||||||
|
Name: testObject,
|
||||||
|
PublicKey: &ecdsa.PublicKey{},
|
||||||
|
CreateSignerRequest: apiv1.CreateSignerRequest{
|
||||||
|
SigningKey: testObject,
|
||||||
|
},
|
||||||
|
}, false},
|
||||||
{"RSA SHA256WithRSA", args{&apiv1.CreateKeyRequest{
|
{"RSA SHA256WithRSA", args{&apiv1.CreateKeyRequest{
|
||||||
Name: testObject,
|
Name: testObject,
|
||||||
SignatureAlgorithm: apiv1.SHA256WithRSA,
|
SignatureAlgorithm: apiv1.SHA256WithRSA,
|
||||||
|
@ -563,6 +573,7 @@ func TestPKCS11_StoreCertificate(t *testing.T) {
|
||||||
// Make sure to delete the created certificate
|
// Make sure to delete the created certificate
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
k.DeleteCertificate(testObject)
|
k.DeleteCertificate(testObject)
|
||||||
|
k.DeleteCertificate(testObjectAlt)
|
||||||
})
|
})
|
||||||
|
|
||||||
type args struct {
|
type args struct {
|
||||||
|
@ -577,6 +588,11 @@ func TestPKCS11_StoreCertificate(t *testing.T) {
|
||||||
Name: testObject,
|
Name: testObject,
|
||||||
Certificate: cert,
|
Certificate: cert,
|
||||||
}}, false},
|
}}, false},
|
||||||
|
{"ok extractable", args{&apiv1.StoreCertificateRequest{
|
||||||
|
Name: testObjectAlt,
|
||||||
|
Certificate: cert,
|
||||||
|
Extractable: true,
|
||||||
|
}}, false},
|
||||||
{"fail already exists", args{&apiv1.StoreCertificateRequest{
|
{"fail already exists", args{&apiv1.StoreCertificateRequest{
|
||||||
Name: testObject,
|
Name: testObject,
|
||||||
Certificate: cert,
|
Certificate: cert,
|
||||||
|
@ -593,13 +609,22 @@ func TestPKCS11_StoreCertificate(t *testing.T) {
|
||||||
Name: "http:id=7770;object=create-cert",
|
Name: "http:id=7770;object=create-cert",
|
||||||
Certificate: cert,
|
Certificate: cert,
|
||||||
}}, true},
|
}}, true},
|
||||||
{"fail ImportCertificateWithLabel", args{&apiv1.StoreCertificateRequest{
|
{"fail missing id", args{&apiv1.StoreCertificateRequest{
|
||||||
Name: "pkcs11:foo=bar",
|
Name: "pkcs11:object=create-cert",
|
||||||
|
Certificate: cert,
|
||||||
|
}}, true},
|
||||||
|
{"fail missing object", args{&apiv1.StoreCertificateRequest{
|
||||||
|
Name: "pkcs11:id=7770;object=",
|
||||||
Certificate: cert,
|
Certificate: cert,
|
||||||
}}, true},
|
}}, true},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if tt.args.req.Extractable {
|
||||||
|
if testModule == "SoftHSM2" {
|
||||||
|
t.Skip("Extractable certificates are not supported on SoftHSM2")
|
||||||
|
}
|
||||||
|
}
|
||||||
if err := k.StoreCertificate(tt.args.req); (err != nil) != tt.wantErr {
|
if err := k.StoreCertificate(tt.args.req); (err != nil) != tt.wantErr {
|
||||||
t.Errorf("PKCS11.StoreCertificate() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("PKCS11.StoreCertificate() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
var (
|
var (
|
||||||
testModule = ""
|
testModule = ""
|
||||||
testObject = "pkcs11:id=7370;object=test-name"
|
testObject = "pkcs11:id=7370;object=test-name"
|
||||||
|
testObjectAlt = "pkcs11:id=7377;object=alt-test-name"
|
||||||
testObjectByID = "pkcs11:id=7370"
|
testObjectByID = "pkcs11:id=7370"
|
||||||
testObjectByLabel = "pkcs11:object=test-name"
|
testObjectByLabel = "pkcs11:object=test-name"
|
||||||
testKeys = []struct {
|
testKeys = []struct {
|
||||||
|
@ -105,7 +106,7 @@ func setup(t TBTesting, k *PKCS11) {
|
||||||
}); err != nil && !errors.Is(errors.Cause(err), apiv1.ErrAlreadyExists{
|
}); err != nil && !errors.Is(errors.Cause(err), apiv1.ErrAlreadyExists{
|
||||||
Message: c.Name + " already exists",
|
Message: c.Name + " already exists",
|
||||||
}) {
|
}) {
|
||||||
t.Errorf("PKCS1.StoreCertificate() error = %+v", err)
|
t.Errorf("PKCS1.StoreCertificate() error = %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
testCerts[i].Certificates = append(testCerts[i].Certificates, cert)
|
testCerts[i].Certificates = append(testCerts[i].Certificates, cert)
|
||||||
|
|
|
@ -4,11 +4,11 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
"github.com/Masterminds/sprig/v3"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/certificates/authority"
|
"github.com/smallstep/certificates/authority"
|
||||||
authconfig "github.com/smallstep/certificates/authority/config"
|
authconfig "github.com/smallstep/certificates/authority/config"
|
||||||
"github.com/smallstep/certificates/authority/provisioner"
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
|
"github.com/smallstep/certificates/templates"
|
||||||
"go.step.sm/linkedca"
|
"go.step.sm/linkedca"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -21,8 +21,10 @@ type helmVariables struct {
|
||||||
Provisioners []provisioner.Interface
|
Provisioners []provisioner.Interface
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteHelmTemplate a helm template to configure the
|
||||||
|
// smallstep/step-certificates helm chart.
|
||||||
func (p *PKI) WriteHelmTemplate(w io.Writer) error {
|
func (p *PKI) WriteHelmTemplate(w io.Writer) error {
|
||||||
tmpl, err := template.New("helm").Funcs(sprig.TxtFuncMap()).Parse(helmTemplate)
|
tmpl, err := template.New("helm").Funcs(templates.StepFuncMap()).Parse(helmTemplate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "error writing helm template")
|
return errors.Wrap(err, "error writing helm template")
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,25 +126,17 @@ fi
|
||||||
|
|
||||||
echo "Bootstrapping with the CA..."
|
echo "Bootstrapping with the CA..."
|
||||||
export STEPPATH=$(mktemp -d)
|
export STEPPATH=$(mktemp -d)
|
||||||
export STEP_CONSOLE=true
|
|
||||||
|
|
||||||
step ca bootstrap --ca-url $CA_URL --fingerprint $CA_FINGERPRINT
|
step ca bootstrap --ca-url $CA_URL --fingerprint $CA_FINGERPRINT
|
||||||
|
|
||||||
if [ -z "$CA_PROVISIONER_NAME" ]; then
|
if [ -z "$CA_PROVISIONER_NAME" ]; then
|
||||||
declare -a provisioners
|
declare -a provisioners
|
||||||
readarray -t provisioners < <(step ca provisioner list | jq -r '.[] | select(.type == "JWK") | .name')
|
readarray -t provisioners < <(step ca provisioner list | jq -r '.[] | select(.type == "JWK") | .name')
|
||||||
provisioners+=("Create provisioner")
|
|
||||||
printf '%s\n' "${provisioners[@]}"
|
printf '%s\n' "${provisioners[@]}"
|
||||||
|
|
||||||
printf "%b" "\nSelect a JWK provisioner:\n" >&2
|
printf "%b" "\nSelect a JWK provisioner:\n" >&2
|
||||||
select provisioner in "${provisioners[@]}"; do
|
select provisioner in "${provisioners[@]}"; do
|
||||||
if [ "$provisioner" == "Create provisioner" ]; then
|
if [ -n "$provisioner" ]; then
|
||||||
echo "Creating a JWK provisioner on the upstream CA..."
|
|
||||||
echo ""
|
|
||||||
read -p "Label your provisioner (e.g. example-ra): " CA_PROVISIONER_NAME < /dev/tty
|
|
||||||
step beta ca provisioner add $CA_PROVISIONER_NAME --type JWK --create
|
|
||||||
break
|
|
||||||
elif [ -n "$provisioner" ]; then
|
|
||||||
echo "Using existing provisioner $provisioner."
|
echo "Using existing provisioner $provisioner."
|
||||||
CA_PROVISIONER_NAME=$provisioner
|
CA_PROVISIONER_NAME=$provisioner
|
||||||
break
|
break
|
||||||
|
@ -162,6 +154,27 @@ if [ -z "$RA_DNS_NAMES" ]; then
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
count=0
|
||||||
|
ra_dns_names_quoted=""
|
||||||
|
|
||||||
|
for i in ${RA_DNS_NAMES//,/ }
|
||||||
|
do
|
||||||
|
if [ "$count" = "0" ]; then
|
||||||
|
ra_dns_names_quoted="\"$i\""
|
||||||
|
else
|
||||||
|
ra_dns_names_quoted="${ra_dns_names_quoted}, \"$i\""
|
||||||
|
fi
|
||||||
|
count=$((count+1))
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$count" = "0" ]; then
|
||||||
|
echo "You must supply at least one RA DNS name"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Got here"
|
||||||
|
|
||||||
if [ -z "$RA_ADDRESS" ]; then
|
if [ -z "$RA_ADDRESS" ]; then
|
||||||
RA_ADDRESS=""
|
RA_ADDRESS=""
|
||||||
while [[ $RA_ADDRESS = "" ]] ; do
|
while [[ $RA_ADDRESS = "" ]] ; do
|
||||||
|
@ -197,7 +210,7 @@ mkdir -p $(step path)/config
|
||||||
cat <<EOF > $(step path)/config/ca.json
|
cat <<EOF > $(step path)/config/ca.json
|
||||||
{
|
{
|
||||||
"address": "$RA_ADDRESS",
|
"address": "$RA_ADDRESS",
|
||||||
"dnsNames": ["$RA_DNS_NAMES"],
|
"dnsNames": [$ra_dns_names_quoted],
|
||||||
"db": {
|
"db": {
|
||||||
"type": "badgerV2",
|
"type": "badgerV2",
|
||||||
"dataSource": "/etc/step-ca/db"
|
"dataSource": "/etc/step-ca/db"
|
||||||
|
|
|
@ -183,7 +183,7 @@ func (t *Template) Load() error {
|
||||||
// the template fails.
|
// the template fails.
|
||||||
func (t *Template) LoadBytes(b []byte) error {
|
func (t *Template) LoadBytes(b []byte) error {
|
||||||
t.backfill(b)
|
t.backfill(b)
|
||||||
tmpl, err := template.New(t.Name).Funcs(sprig.TxtFuncMap()).Parse(string(b))
|
tmpl, err := template.New(t.Name).Funcs(StepFuncMap()).Parse(string(b))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error parsing template %s", t.Name)
|
return errors.Wrapf(err, "error parsing template %s", t.Name)
|
||||||
}
|
}
|
||||||
|
@ -270,3 +270,12 @@ func mkdir(path string, perm os.FileMode) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StepFuncMap returns sprig.TxtFuncMap but removing the "env" and "expandenv"
|
||||||
|
// functions to avoid any leak of information.
|
||||||
|
func StepFuncMap() template.FuncMap {
|
||||||
|
m := sprig.TxtFuncMap()
|
||||||
|
delete(m, "env")
|
||||||
|
delete(m, "expandenv")
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue