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