View Javadoc
1   /*
2    * The contents of this file are subject to the terms of the Common Development and
3    * Distribution License (the License). You may not use this file except in compliance with the
4    * License.
5    *
6    * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
7    * specific language governing permission and limitations under the License.
8    *
9    * When distributing Covered Software, include this CDDL Header Notice in each file and include
10   * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
11   * Header, with the fields enclosed by brackets [] replaced by your own identifying
12   * information: "Portions copyright [year] [name of copyright owner]".
13   *
14   * Copyright 2016 ForgeRock AS.
15   */
16  
17  package org.forgerock.json.jose.jwe.handlers.encryption;
18  
19  import java.security.GeneralSecurityException;
20  import java.security.Key;
21  import java.security.NoSuchAlgorithmException;
22  import java.util.Arrays;
23  import java.util.logging.Level;
24  import java.util.logging.Logger;
25  
26  import javax.crypto.Cipher;
27  import javax.crypto.KeyGenerator;
28  import javax.crypto.spec.GCMParameterSpec;
29  
30  import org.forgerock.json.jose.exceptions.JweDecryptionException;
31  import org.forgerock.json.jose.exceptions.JweEncryptionException;
32  import org.forgerock.json.jose.jwe.EncryptionMethod;
33  import org.forgerock.json.jose.jwe.JweEncryption;
34  
35  /**
36   * Encrypts content using Galois Counter Mode (GCM), an efficient authenticated encryption with associated data
37   * (AEAD) mode. Compared to {@link AESCBCHMACSHA2ContentEncryptionHandler} this is a more efficient combined AEAD
38   * mode that does not require padding. On the flip side, if you ever repeat a key/IV pair then the security of the
39   * cipher is totally compromised.
40   */
41  final class AESGCMContentEncryptionHandler extends ContentEncryptionHandler {
42      private static final Logger LOGGER = Logger.getLogger(AESGCMContentEncryptionHandler.class.getName());
43      private static final int TAG_LENGTH = 128;
44      private static final int IV_LENGTH = 96 / 8;
45  
46      private final EncryptionMethod encryptionMethod;
47  
48      AESGCMContentEncryptionHandler(final EncryptionMethod encryptionMethod) {
49          this.encryptionMethod = encryptionMethod;
50      }
51  
52      @Override
53      JweEncryption encrypt(final Key key, final byte[] iv, final byte[] plainText, final byte[] additionalData) {
54          try {
55              final Cipher cipher = Cipher.getInstance(encryptionMethod.getTransformation());
56              cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(TAG_LENGTH, iv));
57              cipher.updateAAD(additionalData);
58              final byte[] cipherText = cipher.doFinal(plainText);
59  
60              final int tagStart = cipherText.length - TAG_LENGTH / 8;
61              return new JweEncryption(Arrays.copyOfRange(cipherText, 0, tagStart),
62                      Arrays.copyOfRange(cipherText, tagStart, cipherText.length));
63          } catch (GeneralSecurityException ex) {
64              throw new JweEncryptionException(ex);
65          }
66      }
67  
68      @Override
69      byte[] decrypt(final Key key, final byte[] iv, final JweEncryption cipherText, final byte[] additionalData) {
70          try {
71              final Cipher cipher = Cipher.getInstance(encryptionMethod.getTransformation());
72              cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(TAG_LENGTH, iv));
73              cipher.updateAAD(additionalData);
74              cipher.update(cipherText.getCiphertext());
75              return cipher.doFinal(cipherText.getAuthenticationTag());
76          } catch (GeneralSecurityException ex) {
77              if (LOGGER.isLoggable(Level.FINE)) {
78                  LOGGER.log(Level.FINE, "Decryption failed: " + ex, ex);
79              }
80              throw new JweDecryptionException();
81          }
82      }
83  
84      @Override
85      Key generateEncryptionKey() {
86          try {
87              final KeyGenerator encKeyGenerator = KeyGenerator.getInstance(encryptionMethod.getEncryptionAlgorithm());
88              encKeyGenerator.init(encryptionMethod.getKeySize());
89              return encKeyGenerator.generateKey();
90          } catch (NoSuchAlgorithmException e) {
91              throw new JweEncryptionException("Unsupported Encryption Algorithm, "
92                      + encryptionMethod.getEncryptionAlgorithm(), e);
93          }
94      }
95  
96      @Override
97      int getIVByteLength() {
98          return IV_LENGTH;
99      }
100 }