forked from smallstep/cli
-
Notifications
You must be signed in to change notification settings - Fork 0
/
decrypt.go
147 lines (126 loc) · 3.98 KB
/
decrypt.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
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
}