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
|
package jwe
import (
"fmt"
"os"
"github.com/smallstep/cli/errs"
"github.com/smallstep/cli/ui"
"github.com/pkg/errors"
"github.com/smallstep/cli/jose"
"github.com/smallstep/cli/utils"
"github.com/urfave/cli"
)
func decryptCommand() cli.Command {
return cli.Command{
Name: "decrypt",
Action: cli.ActionFunc(decryptAction),
Usage: "verify a JWE and decrypt ciphertext",
UsageText: `**step crypto jwe decrypt**
[**--key**=<path>] [**--jwks**=<jwks>] [**--kid**=<kid>]`,
Description: `**step crypto jwe decrypt** verifies a JWE read from STDIN and decrypts the
ciphertext printing it to STDOUT. If verification fails a non-zero failure
code is returned. If verification succeeds the command returns 0.
For examples, see **step help crypto jwe**.`,
Flags: []cli.Flag{
cli.StringFlag{
Name: "key",
Usage: `The <path> to the JWE recipient's private key. The argument should be the name of a file
containing a private JWK (or a JWK encrypted as a JWE payload) or a PEM encoded
private key (or a private key encrypted using the modes described on RFC 1423 or
with PBES2+PBKDF2 described in RFC 2898).`,
},
cli.StringFlag{
Name: "jwks",
Usage: `The JWK Set containing the recipient's private key. The <jwks> argument should
be the name of a file. The file contents should be a JWK Set or a JWE with a
JWK Set payload. The **--jwks** flag requires the use of the **--kid** flag to
specify which key to use.`,
},
cli.StringFlag{
Name: "kid",
Usage: `The ID of the recipient's private key. <kid> is a case-sensitive string. When
used with **--key** the <kid> value must match the **"kid"** member of the JWK. When
used with **--jwks** (a JWK Set) the KID value must match the **"kid"** member of
one of the JWKs in the JWK Set.`,
},
},
}
}
func decryptAction(ctx *cli.Context) error {
if err := errs.NumberOfArguments(ctx, 0); err != nil {
return err
}
data, err := utils.ReadAll(os.Stdin)
if err != nil {
return err
}
key := ctx.String("key")
jwks := ctx.String("jwks")
kid := ctx.String("kid")
obj, err := jose.ParseEncrypted(string(data))
if err != nil {
return errors.Wrap(err, "error parsing data")
}
alg := jose.KeyAlgorithm(obj.Header.Algorithm)
var isPBES2 bool
switch alg {
case jose.PBES2_HS256_A128KW, jose.PBES2_HS384_A192KW, jose.PBES2_HS512_A256KW:
isPBES2 = true
}
switch {
case isPBES2 && key != "":
return errors.Errorf("flag '--key' cannot be used with JWE algorithm '%s'", alg)
case isPBES2 && jwks != "":
return errors.Errorf("flag '--jwks' cannot be used with JWE algorithm '%s'", alg)
case !isPBES2 && key == "" && jwks == "":
return errs.RequiredOrFlag(ctx, "key", "jwk")
case key != "" && jwks != "":
return errs.MutuallyExclusiveFlags(ctx, "key", "jwks")
case jwks != "" && kid == "":
return errs.RequiredWithFlag(ctx, "kid", "jwks")
}
// Add parse options
var options []jose.Option
options = append(options, jose.WithUse("enc"))
if len(kid) > 0 {
options = append(options, jose.WithKid(kid))
}
// Read key from --key or --jwks
var pbes2Key []byte
var jwk *jose.JSONWebKey
switch {
case key != "":
jwk, err = jose.ParseKey(key, options...)
case jwks != "":
jwk, err = jose.ParseKeySet(jwks, options...)
case isPBES2:
pbes2Key, err = ui.PromptPassword("Please enter the password to decrypt the content encryption key")
default:
return errs.RequiredOrFlag(ctx, "key", "jwk")
}
if err != nil {
return err
}
var decryptKey interface{}
if isPBES2 {
decryptKey = pbes2Key
} else {
// Private keys are used for decryption
if jwk.IsPublic() {
return errors.New("cannot use a public key for decryption")
}
if jwk.Use == "sig" {
return errors.New("invalid jwk use: found 'sig' (signature), expecting 'enc' (encryption)")
}
// Validate jwk
if err = jose.ValidateJWK(jwk); err != nil {
return err
}
decryptKey = jwk.Key
}
decrypted, err := obj.Decrypt(decryptKey)
if err != nil {
return errors.Wrap(err, "error decrypting data")
}
fmt.Print(string(decrypted))
return nil
}
|