github.com/lestrrat-go/jwx/v2@v2.0.21/docs/03-jwe.md (about) 1 # Working with JWE 2 3 In this document we describe how to work with JWK using `github.com/lestrrat-go/jwx/v2/jwe` 4 5 * [Parsing](#parsing) 6 * [Parse a JWE message stored in memory](#parse-a-jwe-message-stored-in-memory) 7 * [Parse a JWE message stored in a file](#parse-a-jwe-message-stored-in-a-file) 8 * [Encrypting](#encrypting) 9 * [Generating a JWE message in compact serialization format](#generating-a-jwe-message-in-compact-serialization-format) 10 * [Generating a JWE message in JSON serialization format](#generating-a-jwe-message-in-json-serialization-format) 11 * [Generating a JWE message with detached payload](#generating-a-jwe-message-with-detached-payload) 12 * [Including arbitrary headers](#including-arbitrary-headers) 13 * [Decrypting](#decryptingG) 14 * [Decrypting using a single key](#decrypting-using-a-single-key) 15 * [Decrypting using a JWKS](#decrypting-using-a-jwks) 16 17 # Parsing 18 19 Parsing a JWE message means taking either a JWE message serialized in JSON or Compact form and loading it into a `jwe.Message` object. No decryption is performed, and therefore you cannot access the raw payload as when you use `jwe.Decrypt()` to decrypt the message. 20 21 Also, be aware that a `jwe.Message` is not meant to be used for either decryption nor encryption. It is only provided so that it can be inspected -- there is no way to decrypt or sign using an already parsed `jwe.Message`. 22 23 ## Parse a JWE message stored in memory 24 25 You can parse a JWE message in memory stored as `[]byte` into a [`jwe.Message`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwe#Message) object. In this mode, there is no decryption performed. 26 27 <!-- INCLUDE(examples/jwe_parse_example_test.go) --> 28 ```go 29 package examples_test 30 31 import ( 32 "encoding/json" 33 "fmt" 34 "os" 35 36 "github.com/lestrrat-go/jwx/v2/jwe" 37 ) 38 39 func ExampleJWE_Parse() { 40 const src = `eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.KrFTaMKVY_iUKYYk905QjbUf_fpBXvXCzIAfbPoPMGViDzxtgz5qnch8waV7wraVDfzpW7JfPOw6Nz_-XRwN3Vbud48bRYFw92GkC0M6kpKFpl_xgZxGN47ggNk9hzgqd7mFCuyufeYdn5c2fPoRZAV4UxvakLozEYcQo-eZaFmoYS4pyoC-IKKRikobW8n__LksMzXc_Vps1axn5kdpxsKQ4k1oayvUrgWX2PMxKn_TcLEKHtCN7qRlJ5hkKbZAXAdd34zGWcFV5gc1tcLs6HFhnebo8GUgItTYWBKSKzF6MyLJNRSUPFVq9q-Jxi1juXIlDrv_7rHVsdokQmBfvA.bK7z7Z3gEzFDgDQvNen0Ww.2hngnAVrmucUpJKLgIzYcg.CHs3ZP7JtG430Dl9YAKLMAl` 41 42 msg, err := jwe.Parse([]byte(src)) 43 if err != nil { 44 fmt.Printf("failed to parse JWE message: %s\n", err) 45 return 46 } 47 48 json.NewEncoder(os.Stdout).Encode(msg) 49 // OUTPUT: 50 // {"ciphertext":"2hngnAVrmucUpJKLgIzYcg","encrypted_key":"KrFTaMKVY_iUKYYk905QjbUf_fpBXvXCzIAfbPoPMGViDzxtgz5qnch8waV7wraVDfzpW7JfPOw6Nz_-XRwN3Vbud48bRYFw92GkC0M6kpKFpl_xgZxGN47ggNk9hzgqd7mFCuyufeYdn5c2fPoRZAV4UxvakLozEYcQo-eZaFmoYS4pyoC-IKKRikobW8n__LksMzXc_Vps1axn5kdpxsKQ4k1oayvUrgWX2PMxKn_TcLEKHtCN7qRlJ5hkKbZAXAdd34zGWcFV5gc1tcLs6HFhnebo8GUgItTYWBKSKzF6MyLJNRSUPFVq9q-Jxi1juXIlDrv_7rHVsdokQmBfvA","header":{"alg":"RSA1_5"},"iv":"bK7z7Z3gEzFDgDQvNen0Ww","protected":"eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0","tag":"CHs3ZP7JtG430Dl9YAKLMAk"} 51 } 52 ``` 53 source: [examples/jwe_parse_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwe_parse_example_test.go) 54 <!-- END INCLUDE --> 55 56 ## Parse a JWE message stored in a file 57 58 To parse a JWE stored in a file, use [`jwe.ReadFile()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwe#ReadFile). [`jwe.ReadFile()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwe#ReadFile) accepts the same options as [`jwe.Parse()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwe#Parse). 59 60 <!-- INCLUDE(examples/jwe_readfile_example_test.go) --> 61 ```go 62 package examples_test 63 64 import ( 65 "encoding/json" 66 "fmt" 67 "os" 68 69 "github.com/lestrrat-go/jwx/v2/jwe" 70 ) 71 72 func ExampleJWE_ReadFile() { 73 const src = `eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.KrFTaMKVY_iUKYYk905QjbUf_fpBXvXCzIAfbPoPMGViDzxtgz5qnch8waV7wraVDfzpW7JfPOw6Nz_-XRwN3Vbud48bRYFw92GkC0M6kpKFpl_xgZxGN47ggNk9hzgqd7mFCuyufeYdn5c2fPoRZAV4UxvakLozEYcQo-eZaFmoYS4pyoC-IKKRikobW8n__LksMzXc_Vps1axn5kdpxsKQ4k1oayvUrgWX2PMxKn_TcLEKHtCN7qRlJ5hkKbZAXAdd34zGWcFV5gc1tcLs6HFhnebo8GUgItTYWBKSKzF6MyLJNRSUPFVq9q-Jxi1juXIlDrv_7rHVsdokQmBfvA.bK7z7Z3gEzFDgDQvNen0Ww.2hngnAVrmucUpJKLgIzYcg.CHs3ZP7JtG430Dl9YAKLMAl` 74 75 f, err := os.CreateTemp(``, `jwe_readfile_example-*.jwe`) 76 if err != nil { 77 fmt.Printf("failed to create temporary file: %s\n", err) 78 return 79 } 80 defer os.Remove(f.Name()) 81 82 f.Write([]byte(src)) 83 f.Close() 84 85 msg, err := jwe.ReadFile(f.Name()) 86 if err != nil { 87 fmt.Printf("failed to parse JWE message from file %q: %s\n", f.Name(), err) 88 return 89 } 90 91 json.NewEncoder(os.Stdout).Encode(msg) 92 // OUTPUT: 93 // {"ciphertext":"2hngnAVrmucUpJKLgIzYcg","encrypted_key":"KrFTaMKVY_iUKYYk905QjbUf_fpBXvXCzIAfbPoPMGViDzxtgz5qnch8waV7wraVDfzpW7JfPOw6Nz_-XRwN3Vbud48bRYFw92GkC0M6kpKFpl_xgZxGN47ggNk9hzgqd7mFCuyufeYdn5c2fPoRZAV4UxvakLozEYcQo-eZaFmoYS4pyoC-IKKRikobW8n__LksMzXc_Vps1axn5kdpxsKQ4k1oayvUrgWX2PMxKn_TcLEKHtCN7qRlJ5hkKbZAXAdd34zGWcFV5gc1tcLs6HFhnebo8GUgItTYWBKSKzF6MyLJNRSUPFVq9q-Jxi1juXIlDrv_7rHVsdokQmBfvA","header":{"alg":"RSA1_5"},"iv":"bK7z7Z3gEzFDgDQvNen0Ww","protected":"eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0","tag":"CHs3ZP7JtG430Dl9YAKLMAk"} 94 } 95 ``` 96 source: [examples/jwe_readfile_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwe_readfile_example_test.go) 97 <!-- END INCLUDE --> 98 99 # Encrypting 100 101 ## Generating a JWE message in compact serialization format 102 103 To encrypt an arbitrary payload as a JWE message in compact serialization format, use `jwt.Encrypt()`. 104 105 Note that this would be [slightly different if you are encrypting JWTs](01-jwt.md#serialize-using-jws), as you would be 106 using functions from the `jwt` package instead of `jws`. 107 108 <!-- INCLUDE(examples/jwe_encrypt_example_test.go) --> 109 ```go 110 package examples_test 111 112 import ( 113 "crypto/rand" 114 "crypto/rsa" 115 "fmt" 116 117 "github.com/lestrrat-go/jwx/v2/jwa" 118 "github.com/lestrrat-go/jwx/v2/jwe" 119 "github.com/lestrrat-go/jwx/v2/jwk" 120 ) 121 122 func ExampleJWE_Encrypt() { 123 rawprivkey, err := rsa.GenerateKey(rand.Reader, 2048) 124 if err != nil { 125 fmt.Printf("failed to create raw private key: %s\n", err) 126 return 127 } 128 privkey, err := jwk.FromRaw(rawprivkey) 129 if err != nil { 130 fmt.Printf("failed to create private key: %s\n", err) 131 return 132 } 133 134 pubkey, err := privkey.PublicKey() 135 if err != nil { 136 fmt.Printf("failed to create public key:%s\n", err) 137 return 138 } 139 140 const payload = `Lorem ipsum` 141 encrypted, err := jwe.Encrypt([]byte(payload), jwe.WithKey(jwa.RSA_OAEP, pubkey)) 142 if err != nil { 143 fmt.Printf("failed to encrypt payload: %s\n", err) 144 return 145 } 146 147 decrypted, err := jwe.Decrypt(encrypted, jwe.WithKey(jwa.RSA_OAEP, privkey)) 148 if err != nil { 149 fmt.Printf("failed to decrypt payload: %s\n", err) 150 return 151 } 152 fmt.Printf("%s\n", decrypted) 153 // OUTPUT: 154 // Lorem ipsum 155 } 156 ``` 157 source: [examples/jwe_encrypt_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwe_encrypt_example_test.go) 158 <!-- END INCLUDE --> 159 160 ## Generating a JWE message in JSON serialization format 161 162 Generally the only time you need to use a JSON serialization format is when you have to generate multiple recipients (encrypted keys) for a given payload using multiple encryption algorithms and keys. 163 164 When this need arises, use the [`jwe.Encrypt()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jws#Encrypt) function with the `jwe.WithJSON()` option and multiple `jwe.WithKey()` options: 165 166 <!-- INCLUDE(examples/jwe_encrypt_json_example_test.go) --> 167 ```go 168 package examples_test 169 170 import ( 171 "crypto/rand" 172 "crypto/rsa" 173 "fmt" 174 175 "github.com/lestrrat-go/jwx/v2/jwa" 176 "github.com/lestrrat-go/jwx/v2/jwe" 177 "github.com/lestrrat-go/jwx/v2/jwk" 178 ) 179 180 func ExampleJWE_EncryptJSON() { 181 rawprivkey, err := rsa.GenerateKey(rand.Reader, 2048) 182 if err != nil { 183 fmt.Printf("failed to create raw private key: %s\n", err) 184 return 185 } 186 privkey, err := jwk.FromRaw(rawprivkey) 187 if err != nil { 188 fmt.Printf("failed to create private key: %s\n", err) 189 return 190 } 191 192 pubkey, err := privkey.PublicKey() 193 if err != nil { 194 fmt.Printf("failed to create public key:%s\n", err) 195 return 196 } 197 198 const payload = `Lorem ipsum` 199 encrypted, err := jwe.Encrypt([]byte(payload), jwe.WithJSON(), jwe.WithKey(jwa.RSA_OAEP, pubkey)) 200 if err != nil { 201 fmt.Printf("failed to encrypt payload: %s\n", err) 202 return 203 } 204 205 decrypted, err := jwe.Decrypt(encrypted, jwe.WithKey(jwa.RSA_OAEP, privkey)) 206 if err != nil { 207 fmt.Printf("failed to decrypt payload: %s\n", err) 208 return 209 } 210 fmt.Printf("%s\n", decrypted) 211 // OUTPUT: 212 // Lorem ipsum 213 } 214 215 func ExampleJWE_EncryptJSONMulti() { 216 var privkeys []jwk.Key 217 var pubkeys []jwk.Key 218 219 for i := 0; i < 3; i++ { 220 rawprivkey, err := rsa.GenerateKey(rand.Reader, 2048) 221 if err != nil { 222 fmt.Printf("failed to create raw private key: %s\n", err) 223 return 224 } 225 privkey, err := jwk.FromRaw(rawprivkey) 226 if err != nil { 227 fmt.Printf("failed to create private key: %s\n", err) 228 return 229 } 230 privkeys = append(privkeys, privkey) 231 232 pubkey, err := privkey.PublicKey() 233 if err != nil { 234 fmt.Printf("failed to create public key:%s\n", err) 235 return 236 } 237 pubkeys = append(pubkeys, pubkey) 238 } 239 240 options := []jwe.EncryptOption{jwe.WithJSON()} 241 for _, key := range pubkeys { 242 options = append(options, jwe.WithKey(jwa.RSA_OAEP, key)) 243 } 244 245 const payload = `Lorem ipsum` 246 encrypted, err := jwe.Encrypt([]byte(payload), options...) 247 if err != nil { 248 fmt.Printf("failed to encrypt payload: %s\n", err) 249 return 250 } 251 252 for _, key := range privkeys { 253 decrypted, err := jwe.Decrypt(encrypted, jwe.WithKey(jwa.RSA_OAEP, key)) 254 if err != nil { 255 fmt.Printf("failed to decrypt payload: %s\n", err) 256 return 257 } 258 fmt.Printf("%s\n", decrypted) 259 } 260 // OUTPUT: 261 // Lorem ipsum 262 // Lorem ipsum 263 // Lorem ipsum 264 } 265 ``` 266 source: [examples/jwe_encrypt_json_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwe_encrypt_json_example_test.go) 267 <!-- END INCLUDE --> 268 269 ## Including arbitrary headers 270 271 By default, only some header fields are included in the result from `jwe.Encrypt()`. 272 273 For global protected headers, you can use the `jwe.WithProtectedHeaders()` option. 274 275 In order to provide extra headers to the encrypted message such as `apu` and `apv`, you will need to use 276 `jwe.WithKey()` option with the `jwe.WithPerRecipientHeaders()` suboption. 277 278 279 <!-- INCLUDE(examples/jwe_encrypt_with_headers_example_test.go) --> 280 ```go 281 package examples_test 282 283 import ( 284 "crypto/rand" 285 "crypto/rsa" 286 "fmt" 287 "os" 288 289 "github.com/lestrrat-go/jwx/v2/internal/json" 290 "github.com/lestrrat-go/jwx/v2/jwa" 291 "github.com/lestrrat-go/jwx/v2/jwe" 292 ) 293 294 func ExampleJWE_SignWithHeaders() { 295 privkey, err := rsa.GenerateKey(rand.Reader, 2048) 296 if err != nil { 297 fmt.Printf("failed to create private key: %s\n", err) 298 return 299 } 300 const payload = "Lorem ipsum" 301 302 hdrs := jwe.NewHeaders() 303 hdrs.Set(`x-example`, true) 304 encrypted, err := jwe.Encrypt([]byte(payload), jwe.WithKey(jwa.RSA_OAEP, privkey.PublicKey, jwe.WithPerRecipientHeaders(hdrs))) 305 if err != nil { 306 fmt.Printf("failed to encrypt payload: %s\n", err) 307 return 308 } 309 310 msg, err := jwe.Parse(encrypted) 311 if err != nil { 312 fmt.Printf("failed to parse message: %s\n", err) 313 return 314 } 315 316 // NOTE: This is a bit tricky. Even though we specified a per-recipient 317 // header when executing jwe.Encrypt, the headers end up being in the 318 // global protected headers section. This is... by the books. JWE 319 // in Compact serialization asks us to shove the per-recipient 320 // headers in the protected header section, because there is nowhere 321 // else to store this information. 322 // 323 // If this were a full JWE JSON message, you might have to juggle 324 // between the global protected headers, global unprotected headers, 325 // and per-recipient unprotected headers 326 json.NewEncoder(os.Stdout).Encode(msg.ProtectedHeaders()) 327 328 // OUTPUT: 329 // {"alg":"RSA-OAEP","enc":"A256GCM","x-example":true} 330 } 331 ``` 332 source: [examples/jwe_encrypt_with_headers_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwe_encrypt_with_headers_example_test.go) 333 <!-- END INCLUDE --> 334 335 # Decrypting 336 337 ## Decrypting using a single key 338 339 To decrypt a JWE message using a single key, use `jwe.Decrypt()` with the `jwe.WithKey()` option. 340 It will automatically do the right thing whether it's serialized in compact form or JSON form. 341 342 The `alg` must be explicitly specified. 343 344 <!-- INCLUDE(examples/jwe_decrypt_with_key_example_test.go) --> 345 ```go 346 package examples_test 347 348 import ( 349 "fmt" 350 351 "github.com/lestrrat-go/jwx/v2/jwa" 352 "github.com/lestrrat-go/jwx/v2/jwe" 353 ) 354 355 func ExampleJWE_VerifyWithKey() { 356 const payload = "Lorem ipsum" 357 encrypted, err := jwe.Encrypt([]byte(payload), jwe.WithKey(jwa.RSA_OAEP, jwkRSAPublicKey)) 358 if err != nil { 359 fmt.Printf("failed to sign payload: %s\n", err) 360 return 361 } 362 363 decrypted, err := jwe.Decrypt(encrypted, jwe.WithKey(jwa.RSA_OAEP, jwkRSAPrivateKey)) 364 if err != nil { 365 fmt.Printf("failed to sign payload: %s\n", err) 366 return 367 } 368 fmt.Printf("%s\n", decrypted) 369 // OUTPUT: 370 // Lorem ipsum 371 } 372 ``` 373 source: [examples/jwe_decrypt_with_key_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwe_decrypt_with_key_example_test.go) 374 <!-- END INCLUDE --> 375 376 ## Decrypting using a JWKS 377 378 To decrypt a payload using JWKS, by default you will need your payload and JWKS to have matching `alg` field. 379 380 The `alg` field's requirement is the same for using a single key. See "[Why don't you automatically infer the algorithm for `jwe.Decrypt`?](99-faq.md#why-dont-you-automatically-infer-the-algorithm-for-jwsdecrypt-)" 381 382 Note that unlike in JWT, the `kid` is not required by default, although you _can_ make it so 383 by passing `jwe.WithRequireKid(true)`. 384 385 For more discussion on why/how `alg`/`kid` values work, please read the [relevant section in the JWT documentation](01-jwt.md#parse-and-decrypt-a-jwt-with-a-key-set-matching-kid) 386 387 <!-- INCLUDE(examples/jwe_decrypt_with_keyset_example_test.go) --> 388 ```go 389 package examples_test 390 391 import ( 392 "crypto/rand" 393 "crypto/rsa" 394 "fmt" 395 396 "github.com/lestrrat-go/jwx/v2/jwa" 397 "github.com/lestrrat-go/jwx/v2/jwe" 398 "github.com/lestrrat-go/jwx/v2/jwk" 399 ) 400 401 func ExampleJWE_VerifyWithJWKSet() { 402 privkey, err := rsa.GenerateKey(rand.Reader, 2048) 403 if err != nil { 404 fmt.Printf("failed to create private key: %s\n", err) 405 return 406 } 407 const payload = "Lorem ipsum" 408 encrypted, err := jwe.Encrypt([]byte(payload), jwe.WithKey(jwa.RSA_OAEP, privkey.PublicKey)) 409 if err != nil { 410 fmt.Printf("failed to sign payload: %s\n", err) 411 return 412 } 413 414 // Create a JWK Set 415 set := jwk.NewSet() 416 // Add some bogus keys 417 k1, _ := jwk.FromRaw([]byte("abracadabra")) 418 set.AddKey(k1) 419 k2, _ := jwk.FromRaw([]byte("opensesame")) 420 set.AddKey(k2) 421 // Add the real thing 422 k3, _ := jwk.FromRaw(privkey) 423 k3.Set(jwk.AlgorithmKey, jwa.RSA_OAEP) 424 set.AddKey(k3) 425 426 // Up to this point, you probably will replace with a simple jwk.Fetch() 427 428 if _, err := jwe.Decrypt(encrypted, jwe.WithKeySet(set, jwe.WithRequireKid(false))); err != nil { 429 fmt.Printf("Failed to decrypt using jwk.Set: %s", err) 430 } 431 432 // OUTPUT: 433 } 434 ``` 435 source: [examples/jwe_decrypt_with_keyset_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwe_decrypt_with_keyset_example_test.go) 436 <!-- END INCLUDE -->