This repository has been archived by the owner on Jan 24, 2020. It is now read-only.
forked from smallstep/cli
-
Notifications
You must be signed in to change notification settings - Fork 1
/
keypair.go
199 lines (168 loc) 路 4.83 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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
package crypto
import (
"github.com/pkg/errors"
"github.com/smallstep/cli/command"
"github.com/smallstep/cli/crypto/keys"
"github.com/smallstep/cli/crypto/pemutil"
"github.com/smallstep/cli/errs"
"github.com/smallstep/cli/flags"
"github.com/smallstep/cli/jose"
"github.com/smallstep/cli/ui"
"github.com/smallstep/cli/utils"
"github.com/urfave/cli"
)
func createKeyPairCommand() cli.Command {
return cli.Command{
Name: "keypair",
Action: command.ActionFunc(createAction),
Usage: "generate a public / private keypair in PEM format",
UsageText: `**step crypto keypair** <pub_file> <priv_file>
[**--kty**=<key-type>] [**--curve**=<curve>] [**--size**=<size>]
[**--password-file**=<file>] [**--no-password**]`,
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{
flags.KTY,
flags.Size,
flags.Curve,
cli.StringFlag{
Name: "from-jwk",
Usage: `Create a PEM representing the key encoded in an
existing <jwk-file> instead of creating a new key.`,
},
flags.PasswordFile,
flags.NoPassword,
flags.Insecure,
flags.Force,
},
}
}
func createAction(ctx *cli.Context) (err 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")
passwordFile := ctx.String("password-file")
if noPass && len(passwordFile) > 0 {
return errs.IncompatibleFlag(ctx, "no-password", "password-file")
}
if noPass && !insecure {
return errs.RequiredWithFlag(ctx, "insecure", "no-password")
}
// Read password if necessary
var password string
if len(passwordFile) > 0 {
password, err = utils.ReadStringPasswordFromFile(passwordFile)
if err != nil {
return err
}
}
var pub, priv interface{}
fromJWK := ctx.String("from-jwk")
if len(fromJWK) > 0 {
switch {
case ctx.IsSet("kty"):
return errs.IncompatibleFlagWithFlag(ctx, "from-jwk", "kty")
case ctx.IsSet("curve"):
return errs.IncompatibleFlagWithFlag(ctx, "from-jwk", "curve")
case ctx.IsSet("size"):
return errs.IncompatibleFlagWithFlag(ctx, "from-jwk", "size")
}
var jwk *jose.JSONWebKey
jwk, err = jose.ParseKey(fromJWK)
if err != nil {
return err
}
if jwk.IsPublic() {
pub = jwk.Key
} else {
pub = jwk.Public().Key
priv = jwk.Key
}
} else {
var (
kty, crv string
size int
)
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 err
}
}
_, err = pemutil.Serialize(pub, pemutil.ToFile(pubFile, 0600))
if err != nil {
return err
}
if priv == nil {
ui.Printf("Your public key has been saved in %s.\n", pubFile)
ui.Println("Only the public PEM was generated.")
ui.Println("Cannot retrieve a private key from a public one.")
return nil
}
if noPass {
_, err = pemutil.Serialize(priv, pemutil.ToFile(privFile, 0600))
if err != nil {
return err
}
} else {
var pass []byte
pass, err = ui.PromptPassword("Please enter the password to encrypt the private key", ui.WithValue(password))
if err != nil {
return errors.Wrap(err, "error reading password")
}
_, err = pemutil.Serialize(priv, pemutil.WithPassword(pass),
pemutil.ToFile(privFile, 0600))
if err != nil {
return err
}
}
ui.Printf("Your public key has been saved in %s.\n", pubFile)
ui.Printf("Your private key has been saved in %s.\n", privFile)
return nil
}