001package com.nimbusds.jose.crypto;
002
003
004import java.security.SecureRandom;
005
006import javax.crypto.SecretKey;
007
008import net.jcip.annotations.ThreadSafe;
009
010import com.nimbusds.jose.EncryptionMethod;
011import com.nimbusds.jose.JOSEException;
012import com.nimbusds.jose.JWEAlgorithm;
013import com.nimbusds.jose.JWECryptoParts;
014import com.nimbusds.jose.JWEEncrypter;
015import com.nimbusds.jose.JWEHeader;
016import com.nimbusds.jose.util.Base64URL;
017import com.nimbusds.jose.util.StringUtils;
018
019
020/**
021 * Direct encrypter of {@link com.nimbusds.jose.JWEObject JWE objects} with a
022 * shared symmetric key. This class is thread-safe.
023 *
024 * <p>Supports the following JWE algorithms:
025 *
026 * <ul>
027 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#DIR}
028 * </ul>
029 *
030 * <p>Supports the following encryption methods:
031 *
032 * <ul>
033 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256}
034 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384}
035 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512}
036 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM}
037 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM}
038 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM}
039 * </ul>
040 *
041 * @author Vladimir Dzhuvinov
042 * @version $version$ (2014-08-20)
043 */
044@ThreadSafe
045public class DirectEncrypter extends DirectCryptoProvider implements JWEEncrypter {
046
047
048        /**
049         * Creates a new direct encrypter.
050         *
051         * @param key The shared symmetric key. Its algorithm must be "AES".
052         *            Must be 128 bits (16 bytes), 192 bits (24 bytes), 256
053         *            bits (32 bytes), 384 bits (48 bytes) or 512 bits
054         *            (64 bytes) long. Must not be {@code null}.
055         *
056         * @throws JOSEException If the key length or algorithm are unexpected,
057         *                       or if the underlying secure random generator 
058         *                       couldn't be instantiated.
059         */
060        public DirectEncrypter(final SecretKey key)
061                throws JOSEException {
062
063                super(key);
064        }
065
066
067        /**
068         * Creates a new direct encrypter.
069         *
070         * @param keyBytes The shared symmetric key, as a byte array. Must be 
071         *                 128 bits (16 bytes), 192 bits (24 bytes), 256 bits
072         *                 (32 bytes), 384 bits (48 bytes) or 512 bits (64
073         *                 bytes) long. Must not be {@code null}.
074         *
075         * @throws JOSEException If the key length or algorithm are unexpected,
076         *                       or if the underlying secure random generator 
077         *                       couldn't be instantiated.
078         */
079        public DirectEncrypter(final byte[] keyBytes)
080                throws JOSEException {
081
082                super(keyBytes);
083        }
084
085
086        @Override
087        public JWECryptoParts encrypt(final JWEHeader readOnlyJWEHeader, final byte[] bytes)
088                throws JOSEException {
089
090                JWEAlgorithm alg = readOnlyJWEHeader.getAlgorithm();
091
092                if (! alg.equals(JWEAlgorithm.DIR)) {
093
094                        throw new JOSEException("Unsupported JWE algorithm, must be \"dir\"");
095                }
096
097                // Check key length matches matches encryption method
098                EncryptionMethod enc = readOnlyJWEHeader.getEncryptionMethod();
099
100                if (enc.cekBitLength() != getKey().getEncoded().length * 8) {
101
102                        throw new JOSEException("The Content Encryption Key (CEK) length must be " + enc.cekBitLength() + " bits for " + enc + " encryption");
103                }
104
105                final Base64URL encryptedKey = null; // The second JWE part
106
107
108                // Apply compression if instructed
109                byte[] plainText = DeflateHelper.applyCompression(readOnlyJWEHeader, bytes);
110
111
112                // Compose the AAD
113                byte[] aad = StringUtils.toByteArray(readOnlyJWEHeader.toBase64URL().toString());
114                
115
116                // Encrypt the plain text according to the JWE enc
117                byte[] iv;
118                AuthenticatedCipherText authCipherText;
119                SecureRandom randomGen = getSecureRandom();
120
121                if (enc.equals(EncryptionMethod.A128CBC_HS256) || enc.equals(EncryptionMethod.A192CBC_HS384) || enc.equals(EncryptionMethod.A256CBC_HS512)) {
122
123                        iv = AESCBC.generateIV(randomGen);
124
125                        authCipherText = AESCBC.encryptAuthenticated(getKey(), iv, plainText, aad, contentEncryptionProvider, macProvider);
126
127                } else if (enc.equals(EncryptionMethod.A128GCM) || enc.equals(EncryptionMethod.A192GCM) || enc.equals(EncryptionMethod.A256GCM)) {
128
129                        iv = AESGCM.generateIV(randomGen);
130
131                        authCipherText = AESGCM.encrypt(getKey(), iv, plainText, aad, contentEncryptionProvider);
132
133                } else {
134
135                        throw new JOSEException("Unsupported encryption method, must be A128CBC_HS256, A192CBC_HS384, A256CBC_HS512, A128GCM, A192GCM or A128GCM");
136                }
137
138                return new JWECryptoParts(encryptedKey,  
139                                          Base64URL.encode(iv), 
140                                          Base64URL.encode(authCipherText.getCipherText()),
141                                          Base64URL.encode(authCipherText.getAuthenticationTag()));
142        }
143}