forked from smallstep/cli
-
Notifications
You must be signed in to change notification settings - Fork 0
/
keypair.go
184 lines (148 loc) · 4.47 KB
/
keypair.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
package crypto
import (
"github.com/pkg/errors"
"github.com/smallstep/cli/crypto/keys"
"github.com/smallstep/cli/crypto/pemutil"
"github.com/smallstep/cli/errs"
"github.com/smallstep/cli/utils"
"github.com/urfave/cli"
)
func createKeyPairCommand() cli.Command {
return cli.Command{
Name: "keypair",
Action: cli.ActionFunc(createAction),
Usage: "generate a public /private keypair in PEM format.",
UsageText: `**step crypto keypair** <pub_file> <priv_file>
[**--curve**=<curve>] [**--no-password**] [**--size**=<size>]
[**--kty**=<key-type>]`,
Description: `**step crypto keypair** generates a raw public /
private keypair in PEM format. These keys can be used by other operations
to sign and encrypt data, and the public key can be bound to an identity
in a CSR and signed by a CA to produce a certificate.
Private keys are encrypted using a password. You'll be prompted for this
password automatically when the key is used.
## POSITIONAL ARGUMENTS
<pub_file>
: The path to write the public key.
<priv_file>
: The path to write the private key.
## EXIT CODES
This command returns 0 on success and \>0 if any error occurs.
## EXAMPLES
Create an RSA public / private key pair with 4096 bits:
'''
$ step crypto keypair foo.pub foo.key --kty RSA --size 4096
'''
Create an RSA public / private key with fewer than the recommended number of
bits (recommended >= 2048 bits):
'''
$ step crypto keypair foo.pub foo.key --kty RSA --size 1024 --insecure
'''
Create an EC public / private key pair with curve P-521:
'''
$ step crypto keypair foo.pub foo.key --kty EC --curve "P-521"
'''
Create an EC public / private key pair but do not encrypt the private key file:
'''
$ step crypto keypair foo.pub foo.key --kty EC --curve "P-256" \
--no-password --insecure
'''
Create an Octet Key Pair with curve Ed25519:
'''
$ step crypto keypair foo.pub foo.key --kty OKP --curve Ed25519
'''
`,
Flags: []cli.Flag{
cli.StringFlag{
Name: "kty",
Value: "EC",
Usage: `The <kty> (key type) to create.
If unset, default is EC.
: <kty> is a case-sensitive string and must be one of:
**EC**
: Create an **elliptic curve** keypair
**OKP**
: Create an octet key pair (for **"Ed25519"** curve)
**RSA**
: Create an **RSA** keypair
`,
},
cli.IntFlag{
Name: "size",
Usage: `The <size> (in bits) of the key for RSA and oct key types. RSA keys require a
minimum key size of 2048 bits. If unset, default is 2048 bits for RSA keys and 128 bits for oct keys.`,
},
cli.StringFlag{
Name: "crv, curve",
Usage: `The elliptic <curve> to use for EC and OKP key types. Corresponds
to the **"crv"** JWK parameter. Valid curves are defined in JWA [RFC7518]. If
unset, default is P-256 for EC keys and Ed25519 for OKP keys.
: <curve> is a case-sensitive string and must be one of:
**P-256**
: NIST P-256 Curve
**P-384**
: NIST P-384 Curve
**P-521**
: NIST P-521 Curve
**Ed25519**
: Ed25519 Curve
`,
},
cli.BoolFlag{
Name: "insecure",
Hidden: true,
},
cli.BoolFlag{
Name: "no-password",
Usage: `Do not ask for a password to encrypt the private key.
Sensitive key material will be written to disk unencrypted. This is not
recommended. Requires **--insecure** flag.`,
},
},
}
}
func createAction(ctx *cli.Context) error {
if err := errs.NumberOfArguments(ctx, 2); err != nil {
return err
}
pubFile := ctx.Args().Get(0)
privFile := ctx.Args().Get(1)
if pubFile == privFile {
return errs.EqualArguments(ctx, "PUB_FILE", "PRIV_FILE")
}
insecure := ctx.Bool("insecure")
noPass := ctx.Bool("no-password")
if noPass && !insecure {
return errs.RequiredWithFlag(ctx, "insecure", "no-password")
}
kty, crv, size, err := utils.GetKeyDetailsFromCLI(ctx, insecure, "kty",
"curve", "size")
if err != nil {
return err
}
pub, priv, err := keys.GenerateKeyPair(kty, crv, size)
if err != nil {
return errors.WithStack(err)
}
_, err = pemutil.Serialize(pub, pemutil.ToFile(pubFile, 0600))
if err != nil {
return errors.WithStack(err)
}
if noPass {
_, err = pemutil.Serialize(priv, pemutil.ToFile(privFile, 0600))
if err != nil {
return errors.WithStack(err)
}
} else {
pass, err := utils.ReadPassword("Please enter the password to encrypt the private key: ")
if err != nil {
return errors.Wrap(err, "error reading password")
}
_, err = pemutil.Serialize(priv, pemutil.WithEncryption(pass),
pemutil.ToFile(privFile, 0600))
if err != nil {
return errors.WithStack(err)
}
}
return nil
}