Skip to content

Commit 28e3ac9

Browse files
authored
Merge pull request #72 from smallstep/truststore
Support for step certificate install.
2 parents 509daab + d0261e2 commit 28e3ac9

File tree

5 files changed

+294
-4
lines changed

5 files changed

+294
-4
lines changed

Gopkg.lock

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Gopkg.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,7 @@ required = [
6666
[[constraint]]
6767
branch = "master"
6868
name = "github.com/smallstep/certificates"
69+
70+
[[constraint]]
71+
branch = "master"
72+
name = "github.com/smallstep/truststore"

command/ca/bootstrap.go

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/smallstep/cli/flags"
1616
"github.com/smallstep/cli/ui"
1717
"github.com/smallstep/cli/utils"
18+
"github.com/smallstep/truststore"
1819
"github.com/urfave/cli"
1920
)
2021

@@ -23,7 +24,7 @@ func bootstrapCommand() cli.Command {
2324
Name: "bootstrap",
2425
Action: command.ActionFunc(bootstrapAction),
2526
Usage: "initialize the environment to use the CA commands",
26-
UsageText: "**step ca bootstrap** [**--ca-url**=<uri>] [**--fingerprint**=<fingerprint>]",
27+
UsageText: `**step ca bootstrap** [**--ca-url**=<uri>] [**--fingerprint**=<fingerprint>] [**--install**]`,
2728
Description: `**step ca bootstrap** downloads the root certificate from the certificate
2829
authority and sets up the current environment to use it.
2930
@@ -33,7 +34,14 @@ url, the root certificate location and its fingerprint.
3334
3435
After the bootstrap, ca commands do not need to specify the flags
3536
--ca-url, --root or --fingerprint if we want to use the same environment.`,
36-
Flags: []cli.Flag{caURLFlag, fingerprintFlag, flags.Force},
37+
Flags: []cli.Flag{
38+
caURLFlag,
39+
fingerprintFlag,
40+
cli.BoolFlag{
41+
Name: "install",
42+
Usage: "Install the root certificate into the system truststore.",
43+
},
44+
flags.Force},
3745
}
3846
}
3947

@@ -104,5 +112,15 @@ func bootstrapAction(ctx *cli.Context) error {
104112
}
105113

106114
ui.Printf("Your configuration has been saved in %s.\n", configFile)
115+
116+
if ctx.Bool("install") {
117+
ui.Printf("Installing the root certificate in the system truststore... ")
118+
if err := truststore.InstallFile(rootFile); err != nil {
119+
ui.Println()
120+
return err
121+
}
122+
ui.Println("done.")
123+
}
124+
107125
return nil
108126
}

command/certificate/certificate.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ of private keys.
2020
2121
## EXAMPLES
2222
23-
Create a root certifciate and private key using the default parameters (EC P-256 curve):
23+
Create a root certificate and private key using the default parameters (EC P-256 curve):
2424
'''
2525
$ step certificate create foo foo.crt foo.key --profile root-ca
2626
'''
@@ -69,7 +69,16 @@ Extract the public key from a PEM encoded certificate:
6969
'''
7070
$ step certificate key foo.crt
7171
'''
72-
`,
72+
73+
Install a root certificate in the system truststore:
74+
'''
75+
$ step certificate install root-ca.crt
76+
'''
77+
78+
Uninstall a root certificate from the system truststore:
79+
'''
80+
$ step certificate uninstall root-ca.crt
81+
'''`,
7382

7483
Subcommands: cli.Commands{
7584
bundleCommand(),
@@ -81,6 +90,8 @@ $ step certificate key foo.crt
8190
signCommand(),
8291
verifyCommand(),
8392
keyCommand(),
93+
installCommand(),
94+
uninstallCommand(),
8495
},
8596
}
8697

command/certificate/install.go

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
package certificate
2+
3+
import (
4+
"strings"
5+
6+
"github.com/pkg/errors"
7+
"github.com/smallstep/cli/command"
8+
"github.com/smallstep/cli/crypto/pemutil"
9+
"github.com/smallstep/cli/errs"
10+
"github.com/smallstep/cli/ui"
11+
"github.com/smallstep/truststore"
12+
"github.com/urfave/cli"
13+
)
14+
15+
func installCommand() cli.Command {
16+
return cli.Command{
17+
Name: "install",
18+
Action: command.ActionFunc(installAction),
19+
Usage: "install a root certificate in the system truststore",
20+
UsageText: `**step certificate install** <crt-file>
21+
[**--prefix**=<name>] [**--all**]
22+
[**--java**] [**--firefox**] [**--no-system**]`,
23+
Description: `**step certificate install** installs a root certificate in the system
24+
truststore.
25+
26+
Java and Firefox truststores are also supported via the respective flags.
27+
28+
## POSITIONAL ARGUMENTS
29+
30+
<crt-file>
31+
: Certificate to install in the system truststore
32+
33+
## EXAMPLES
34+
35+
Install a certificate in the system truststore:
36+
'''
37+
$ step certificate install root-ca.pem
38+
'''
39+
40+
Install a certificate in all the supported truststores:
41+
'''
42+
$ step certificate install --all root-ca.pem
43+
'''
44+
45+
Install a certificate in Firefox and the system trustore:
46+
'''
47+
$ step certificate install --firefox root--ca.pem
48+
'''
49+
50+
Install a certificate in Java and the system trustore:
51+
'''
52+
$ step certificate install --java root-ca.pem
53+
'''
54+
55+
Install a certificate in Firefox, Java, but not in the system trustore:
56+
'''
57+
$ step certificate install --firefox --java --no-system root-ca.pem
58+
'''`,
59+
Flags: []cli.Flag{
60+
cli.StringFlag{
61+
Name: "prefix",
62+
Usage: `The prefix used to <name> the CA in the truststore. Defaults to the
63+
certificate common name.`,
64+
},
65+
cli.BoolFlag{
66+
Name: "java",
67+
Usage: "install on the Java key store",
68+
},
69+
cli.BoolFlag{
70+
Name: "firefox",
71+
Usage: "install on the Firefox NSS security database",
72+
},
73+
cli.BoolFlag{
74+
Name: "no-system",
75+
Usage: "disables the install on the system truststore",
76+
},
77+
cli.BoolFlag{
78+
Name: "all",
79+
Usage: "install on the system, Firefox and Java truststores",
80+
},
81+
},
82+
}
83+
}
84+
85+
func uninstallCommand() cli.Command {
86+
return cli.Command{
87+
Name: "uninstall",
88+
Action: command.ActionFunc(uninstallAction),
89+
Usage: "uninstall a root certificate from the system truststore",
90+
UsageText: `**step certificate uninstall** <crt-file>
91+
[**--prefix**=<name>] [**--all**]
92+
[**--java**] [**--firefox**] [**--no-system**]`,
93+
Description: `**step certificate install** uninstalls a root certificate from the system
94+
truststore.
95+
96+
Java and Firefox truststores are also supported via the respective flags.
97+
98+
## POSITIONAL ARGUMENTS
99+
100+
<crt-file>
101+
: Certificate to uninstall from the system truststore
102+
103+
## EXAMPLES
104+
105+
Uninstall from only the system truststore:
106+
'''
107+
$ step certificate uninstall root-ca.pem
108+
'''
109+
110+
Uninstall a certificate from all the supported truststores:
111+
'''
112+
$ step certificate uninstall =-all root-ca.pem
113+
'''
114+
115+
Uninstall a certificate from Firefox and the system trustore:
116+
'''
117+
$ step certificate uninstall --firefox root--ca.pem
118+
'''
119+
120+
Uninstall a certificate infrom Java and the system trustore:
121+
'''
122+
$ step certificate uninstall --java root-ca.pem
123+
'''
124+
125+
Uninstall a certificate from Firefox, Java, but not from the system:
126+
'''
127+
$ step certificate uninstall --firefox --java --no-system root-ca.pem
128+
'''`,
129+
Flags: []cli.Flag{
130+
cli.StringFlag{
131+
Name: "prefix",
132+
Usage: `The prefix used to <name> the CA in the truststore. Defaults to the
133+
certificate common name.`,
134+
},
135+
cli.BoolFlag{
136+
Name: "java",
137+
Usage: "uninstall from the Java key store",
138+
},
139+
cli.BoolFlag{
140+
Name: "firefox",
141+
Usage: "uninstall from the Firefox NSS security database",
142+
},
143+
cli.BoolFlag{
144+
Name: "no-system",
145+
Usage: "disables the uninstall from the system truststore",
146+
},
147+
cli.BoolFlag{
148+
Name: "all",
149+
Usage: "uninstall from the system, Firefox and Java truststores",
150+
},
151+
},
152+
}
153+
}
154+
155+
func installAction(ctx *cli.Context) error {
156+
if err := errs.NumberOfArguments(ctx, 1); err != nil {
157+
return err
158+
}
159+
160+
filename := ctx.Args().Get(0)
161+
opts, err := getTruststoreOptions(ctx)
162+
if err != nil {
163+
return err
164+
}
165+
166+
if err := truststore.InstallFile(filename, opts...); err != nil {
167+
switch err := err.(type) {
168+
case *truststore.CmdError:
169+
return errors.Errorf("failed to execute \"%s\" failed with: %s", strings.Join(err.Cmd().Args, " "), err.Err())
170+
default:
171+
return errors.Wrapf(err, "failed to install %s", filename)
172+
}
173+
}
174+
175+
ui.Printf("Certificate %s has been installed.", filename)
176+
return nil
177+
}
178+
179+
func uninstallAction(ctx *cli.Context) error {
180+
if err := errs.NumberOfArguments(ctx, 1); err != nil {
181+
return err
182+
}
183+
184+
filename := ctx.Args().Get(0)
185+
opts, err := getTruststoreOptions(ctx)
186+
if err != nil {
187+
return err
188+
}
189+
190+
if err := truststore.UninstallFile(filename, opts...); err != nil {
191+
switch err := err.(type) {
192+
case *truststore.CmdError:
193+
return errors.Errorf("failed to execute \"%s\" failed with: %s", strings.Join(err.Cmd().Args, " "), err.Err())
194+
default:
195+
return errors.Wrapf(err, "failed to uninstall %s", filename)
196+
}
197+
}
198+
199+
ui.Printf("Certificate %s has been removed.", filename)
200+
return nil
201+
}
202+
203+
func getTruststoreOptions(ctx *cli.Context) ([]truststore.Option, error) {
204+
cert, err := pemutil.ReadCertificate(ctx.Args().Get(0))
205+
if err != nil {
206+
return nil, err
207+
}
208+
209+
if !cert.IsCA || cert.CheckSignatureFrom(cert) != nil {
210+
return nil, errors.Errorf("certificate %s is not a root CA", ctx.Args().Get(0))
211+
}
212+
213+
prefix := ctx.String("prefix")
214+
if prefix == "" {
215+
if len(cert.Subject.CommonName) > 0 {
216+
prefix = cert.Subject.CommonName + " "
217+
} else {
218+
prefix = "Smallstep Development CA "
219+
}
220+
}
221+
222+
opts := []truststore.Option{
223+
truststore.WithPrefix(prefix),
224+
}
225+
226+
if ctx.Bool("all") {
227+
opts = append(opts, truststore.WithJava(), truststore.WithFirefox())
228+
} else {
229+
if ctx.Bool("java") {
230+
opts = append(opts, truststore.WithJava())
231+
}
232+
if ctx.Bool("firefox") {
233+
opts = append(opts, truststore.WithFirefox())
234+
}
235+
}
236+
if ctx.Bool("no-system") {
237+
opts = append(opts, truststore.WithNoSystem())
238+
}
239+
return opts, nil
240+
}

0 commit comments

Comments
 (0)