1
16
17 package main
18
19 import (
20 "fmt"
21 "io/ioutil"
22 "os"
23
24 "gopkg.in/alecthomas/kingpin.v2"
25 "gopkg.in/square/go-jose.v2"
26 )
27
28 var (
29 app = kingpin.New("jose-util", "A command-line utility for dealing with JOSE objects.")
30
31 keyFile = app.Flag("key", "Path to key file (PEM or DER-encoded)").ExistingFile()
32 inFile = app.Flag("in", "Path to input file (stdin if missing)").ExistingFile()
33 outFile = app.Flag("out", "Path to output file (stdout if missing)").ExistingFile()
34
35 encryptCommand = app.Command("encrypt", "Encrypt a plaintext, output ciphertext.")
36 algFlag = encryptCommand.Flag("alg", "Key management algorithm (e.g. RSA-OAEP)").Required().String()
37 encFlag = encryptCommand.Flag("enc", "Content encryption algorithm (e.g. A128GCM)").Required().String()
38
39 decryptCommand = app.Command("decrypt", "Decrypt a ciphertext, output plaintext.")
40
41 signCommand = app.Command("sign", "Sign a payload, output signed message.")
42 sigAlgFlag = signCommand.Flag("alg", "Key management algorithm (e.g. RSA-OAEP)").Required().String()
43
44 verifyCommand = app.Command("verify", "Verify a signed message, output payload.")
45
46 expandCommand = app.Command("expand", "Expand JOSE object to full serialization format.")
47 formatFlag = expandCommand.Flag("format", "Type of message to expand (JWS or JWE, defaults to JWE)").String()
48
49 full = app.Flag("full", "Use full serialization format (instead of compact)").Bool()
50 )
51
52 func main() {
53 app.Version("v2")
54
55 command := kingpin.MustParse(app.Parse(os.Args[1:]))
56
57 var keyBytes []byte
58 var err error
59 if command != "expand" {
60 keyBytes, err = ioutil.ReadFile(*keyFile)
61 exitOnError(err, "unable to read key file")
62 }
63
64 switch command {
65 case "encrypt":
66 pub, err := LoadPublicKey(keyBytes)
67 exitOnError(err, "unable to read public key")
68
69 alg := jose.KeyAlgorithm(*algFlag)
70 enc := jose.ContentEncryption(*encFlag)
71
72 crypter, err := jose.NewEncrypter(enc, jose.Recipient{Algorithm: alg, Key: pub}, nil)
73 exitOnError(err, "unable to instantiate encrypter")
74
75 obj, err := crypter.Encrypt(readInput(*inFile))
76 exitOnError(err, "unable to encrypt")
77
78 var msg string
79 if *full {
80 msg = obj.FullSerialize()
81 } else {
82 msg, err = obj.CompactSerialize()
83 exitOnError(err, "unable to serialize message")
84 }
85
86 writeOutput(*outFile, []byte(msg))
87 case "decrypt":
88 priv, err := LoadPrivateKey(keyBytes)
89 exitOnError(err, "unable to read private key")
90
91 obj, err := jose.ParseEncrypted(string(readInput(*inFile)))
92 exitOnError(err, "unable to parse message")
93
94 plaintext, err := obj.Decrypt(priv)
95 exitOnError(err, "unable to decrypt message")
96
97 writeOutput(*outFile, plaintext)
98 case "sign":
99 signingKey, err := LoadPrivateKey(keyBytes)
100 exitOnError(err, "unable to read private key")
101
102 alg := jose.SignatureAlgorithm(*sigAlgFlag)
103 signer, err := jose.NewSigner(jose.SigningKey{Algorithm: alg, Key: signingKey}, nil)
104 exitOnError(err, "unable to make signer")
105
106 obj, err := signer.Sign(readInput(*inFile))
107 exitOnError(err, "unable to sign")
108
109 var msg string
110 if *full {
111 msg = obj.FullSerialize()
112 } else {
113 msg, err = obj.CompactSerialize()
114 exitOnError(err, "unable to serialize message")
115 }
116
117 writeOutput(*outFile, []byte(msg))
118 case "verify":
119 verificationKey, err := LoadPublicKey(keyBytes)
120 exitOnError(err, "unable to read public key")
121
122 obj, err := jose.ParseSigned(string(readInput(*inFile)))
123 exitOnError(err, "unable to parse message")
124
125 plaintext, err := obj.Verify(verificationKey)
126 exitOnError(err, "invalid signature")
127
128 writeOutput(*outFile, plaintext)
129 case "expand":
130 input := string(readInput(*inFile))
131
132 var serialized string
133 var err error
134 switch *formatFlag {
135 case "", "JWE":
136 var jwe *jose.JSONWebEncryption
137 jwe, err = jose.ParseEncrypted(input)
138 if err == nil {
139 serialized = jwe.FullSerialize()
140 }
141 case "JWS":
142 var jws *jose.JSONWebSignature
143 jws, err = jose.ParseSigned(input)
144 if err == nil {
145 serialized = jws.FullSerialize()
146 }
147 }
148
149 exitOnError(err, "unable to expand message")
150 writeOutput(*outFile, []byte(serialized))
151 writeOutput(*outFile, []byte("\n"))
152 }
153 }
154
155
156 func exitOnError(err error, msg string) {
157 if err != nil {
158 fmt.Fprintf(os.Stderr, "%s: %s\n", msg, err)
159 os.Exit(1)
160 }
161 }
162
163
164 func readInput(path string) []byte {
165 var bytes []byte
166 var err error
167
168 if path != "" {
169 bytes, err = ioutil.ReadFile(path)
170 } else {
171 bytes, err = ioutil.ReadAll(os.Stdin)
172 }
173
174 exitOnError(err, "unable to read input")
175 return bytes
176 }
177
178
179 func writeOutput(path string, data []byte) {
180 var err error
181
182 if path != "" {
183 err = ioutil.WriteFile(path, data, 0644)
184 } else {
185 _, err = os.Stdout.Write(data)
186 }
187
188 exitOnError(err, "unable to write output")
189 }
190
View as plain text