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
}
|