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