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), 256 bits (32 bytes) or 512 
050         *            bits (64 bytes) long. Must not be {@code null}.
051         *
052         * @throws JOSEException If the key length or algorithm are unexpected,
053         *                       or if the underlying secure random generator 
054         *                       couldn't be instantiated.
055         */
056        public DirectEncrypter(final SecretKey key)
057                throws JOSEException {
058
059                super(key);
060        }
061
062
063        /**
064         * Creates a new direct encrypter.
065         *
066         * @param keyBytes The shared symmetric key, as a byte array. Must be 
067         *                 128 bits (16 bytes), 256 bits (32 bytes) or 512 bits
068         *                 (64 bytes) long. Must not be {@code null}.
069         *
070         * @throws JOSEException If the key length or algorithm are unexpected,
071         *                       or if the underlying secure random generator 
072         *                       couldn't be instantiated.
073         */
074        public DirectEncrypter(final byte[] keyBytes)
075                throws JOSEException {
076
077                super(keyBytes);
078        }
079
080
081        @Override
082        public JWECryptoParts encrypt(final ReadOnlyJWEHeader readOnlyJWEHeader, final byte[] bytes)
083                throws JOSEException {
084
085                JWEAlgorithm alg = readOnlyJWEHeader.getAlgorithm();
086
087                if (! alg.equals(JWEAlgorithm.DIR)) {
088
089                        throw new JOSEException("Unsupported JWE algorithm, must be \"dir\"");
090                }
091
092                // Check key length matches matches encryption method
093                EncryptionMethod enc = readOnlyJWEHeader.getEncryptionMethod();
094
095                if (enc.cekBitLength() != getKey().getEncoded().length * 8) {
096
097                        throw new JOSEException("The Content Encryption Key (CEK) length must be " + enc.cekBitLength() + " bits for " + enc + " encryption");
098                }
099
100                final Base64URL encryptedKey = null; // The second JWE part
101
102
103                // Apply compression if instructed
104                byte[] plainText = DeflateHelper.applyCompression(readOnlyJWEHeader, bytes);
105
106
107                // Compose the AAD
108                byte[] aad = StringUtils.toByteArray(readOnlyJWEHeader.toBase64URL().toString());
109                
110
111                // Encrypt the plain text according to the JWE enc
112                byte[] iv;
113                AuthenticatedCipherText authCipherText;
114                SecureRandom randomGen = getSecureRandom();
115
116                if (enc.equals(EncryptionMethod.A128CBC_HS256) || enc.equals(EncryptionMethod.A192CBC_HS384) || enc.equals(EncryptionMethod.A256CBC_HS512)) {
117
118                        iv = AESCBC.generateIV(randomGen);
119
120                        authCipherText = AESCBC.encryptAuthenticated(getKey(), iv, plainText, aad, contentEncryptionProvider, macProvider);
121
122                } else if (enc.equals(EncryptionMethod.A128GCM) || enc.equals(EncryptionMethod.A192GCM) || enc.equals(EncryptionMethod.A256GCM)) {
123
124                        iv = AESGCM.generateIV(randomGen);
125
126                        authCipherText = AESGCM.encrypt(getKey(), iv, plainText, aad, contentEncryptionProvider);
127
128                } else {
129
130                        throw new JOSEException("Unsupported encryption method, must be A128CBC_HS256, A192CBC_HS384, A256CBC_HS512, A128GCM, A192GCM or A128GCM");
131                }
132
133                return new JWECryptoParts(encryptedKey,  
134                                          Base64URL.encode(iv), 
135                                          Base64URL.encode(authCipherText.getCipherText()),
136                                          Base64URL.encode(authCipherText.getAuthenticationTag()));
137        }
138}