1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.forgerock.json.jose.jwe.handlers.encryption;
18
19 import java.nio.ByteBuffer;
20 import java.nio.ByteOrder;
21 import java.security.GeneralSecurityException;
22 import java.security.Key;
23 import java.security.NoSuchAlgorithmException;
24 import java.util.Arrays;
25 import java.util.logging.Level;
26 import java.util.logging.Logger;
27
28 import javax.crypto.Cipher;
29 import javax.crypto.KeyGenerator;
30 import javax.crypto.Mac;
31 import javax.crypto.SecretKey;
32 import javax.crypto.spec.IvParameterSpec;
33 import javax.crypto.spec.SecretKeySpec;
34
35 import org.forgerock.json.jose.exceptions.JweDecryptionException;
36 import org.forgerock.json.jose.exceptions.JweEncryptionException;
37 import org.forgerock.json.jose.jwe.EncryptionMethod;
38 import org.forgerock.json.jose.jwe.JweEncryption;
39 import org.forgerock.json.jose.utils.Utils;
40
41
42
43
44 final class AESCBCHMACSHA2ContentEncryptionHandler extends ContentEncryptionHandler {
45 private static final Logger LOGGER = Logger.getLogger(AESCBCHMACSHA2ContentEncryptionHandler.class.getName());
46 private static final String RAW_KEY_FORMAT = "RAW";
47 private final EncryptionMethod method;
48
49 AESCBCHMACSHA2ContentEncryptionHandler(final EncryptionMethod method) {
50 this.method = method;
51 }
52
53 @Override
54 public JweEncryption encrypt(final Key key, final byte[] iv, final byte[] plainText, final byte[] additionalData) {
55
56 final Key macKey = macKey(key, method);
57 final Key encryptionKey = encKey(key, method);
58
59 try {
60 final Cipher cipher = Cipher.getInstance(method.getTransformation());
61 cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, new IvParameterSpec(iv));
62 final byte[] cipherText = cipher.doFinal(plainText);
63
64 long alLength = additionalData.length * 8L;
65 byte[] al = ByteBuffer.allocate(8).order(ByteOrder.BIG_ENDIAN).putLong(alLength).array();
66
67 final Mac mac = Mac.getInstance(method.getMacAlgorithm());
68 mac.init(macKey);
69 mac.update(additionalData);
70 mac.update(iv);
71 mac.update(cipherText);
72 mac.update(al);
73
74 byte[] authenticationTag = Arrays.copyOf(mac.doFinal(), method.getKeyOffset());
75
76 return new JweEncryption(cipherText, authenticationTag);
77 } catch (GeneralSecurityException e) {
78 throw new JweEncryptionException(e);
79 }
80 }
81
82 @Override
83 public byte[] decrypt(final Key key, final byte[] iv, final JweEncryption cipherText, final byte[] additionalData) {
84 final Key macKey = macKey(key, method);
85 final Key encKey = encKey(key, method);
86
87 long alLength = additionalData.length * 8L;
88 byte[] al = ByteBuffer.allocate(8).order(ByteOrder.BIG_ENDIAN).putLong(alLength).array();
89
90 try {
91 final Mac mac = Mac.getInstance(method.getMacAlgorithm());
92 mac.init(macKey);
93 mac.update(additionalData);
94 mac.update(iv);
95 mac.update(cipherText.getCiphertext());
96 mac.update(al);
97
98 final byte[] tag = Arrays.copyOf(mac.doFinal(), method.getKeyOffset());
99
100 final boolean macValid = Utils.constantEquals(tag, cipherText.getAuthenticationTag());
101
102 final Cipher cipher = Cipher.getInstance(method.getTransformation());
103 cipher.init(Cipher.DECRYPT_MODE, encKey, new IvParameterSpec(iv));
104 final byte[] plainText = cipher.doFinal(cipherText.getCiphertext());
105
106 if (!macValid) {
107 throw new GeneralSecurityException("MAC verification failed");
108 }
109
110 return plainText;
111
112 } catch (GeneralSecurityException ex) {
113 if (LOGGER.isLoggable(Level.FINE)) {
114 LOGGER.log(Level.FINE, "Decryption failed: " + ex, ex);
115 }
116 throw new JweDecryptionException();
117 }
118 }
119
120 @Override
121 Key generateEncryptionKey() {
122
123
124
125
126
127
128
129
130 try {
131
132 final int macKeySize = method.getKeyOffset() * 8;
133 final KeyGenerator macKeyGenerator = KeyGenerator.getInstance(method.getMacAlgorithm());
134 macKeyGenerator.init(macKeySize);
135 final Key macKey = macKeyGenerator.generateKey();
136 if (!RAW_KEY_FORMAT.equals(macKey.getFormat())) {
137 throw new IllegalStateException("HMAC KeyGenerator returned non-RAW key material!");
138 }
139
140 final int encKeySize = method.getKeySize() - macKeySize;
141 final KeyGenerator encKeyGenerator = KeyGenerator.getInstance(method.getEncryptionAlgorithm());
142 encKeyGenerator.init(encKeySize);
143 final Key encKey = encKeyGenerator.generateKey();
144 if (!RAW_KEY_FORMAT.equals(macKey.getFormat())) {
145 throw new IllegalStateException("AES KeyGenerator returned non-RAW key material!");
146 }
147
148 final byte[] combinedKey = ByteBuffer.allocate(method.getKeySize() / 8)
149 .put(macKey.getEncoded())
150 .put(encKey.getEncoded())
151 .array();
152
153 return new SecretKeySpec(combinedKey, method.getEncryptionAlgorithm());
154 } catch (NoSuchAlgorithmException e) {
155 throw new JweEncryptionException("Unsupported Encryption Algorithm, "
156 + method.getEncryptionAlgorithm(), e);
157 }
158
159 }
160
161 private static SecretKey macKey(final Key combinedKey, final EncryptionMethod method) {
162 return new SecretKeySpec(combinedKey.getEncoded(), 0, method.getKeyOffset(), method.getMacAlgorithm());
163 }
164
165 private static SecretKey encKey(final Key combinedKey, final EncryptionMethod method) {
166 return new SecretKeySpec(combinedKey.getEncoded(), method.getKeyOffset(), method.getKeyOffset(),
167 method.getEncryptionAlgorithm());
168 }
169 }