001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2015-2016 ForgeRock AS.
015 */
016
017package org.forgerock.json.jose.jwe.handlers.encryption;
018
019import static java.security.spec.MGF1ParameterSpec.SHA256;
020import static javax.crypto.spec.PSource.PSpecified.DEFAULT;
021import static org.forgerock.json.jose.jwe.JweAlgorithm.RSA_OAEP_256;
022import static org.forgerock.json.jose.jwe.JweAlgorithmType.RSA;
023import static org.forgerock.util.Reject.checkNotNull;
024
025import java.security.GeneralSecurityException;
026import java.security.Key;
027import java.security.interfaces.RSAPublicKey;
028import java.security.spec.AlgorithmParameterSpec;
029
030import javax.crypto.Cipher;
031import javax.crypto.spec.OAEPParameterSpec;
032
033import org.forgerock.json.jose.exceptions.JweDecryptionException;
034import org.forgerock.json.jose.exceptions.JweEncryptionException;
035import org.forgerock.json.jose.jwe.EncryptionMethod;
036import org.forgerock.json.jose.jwe.JweAlgorithm;
037import org.forgerock.json.jose.jwe.JweEncryption;
038import org.forgerock.util.Reject;
039
040/**
041 * Abstract base class for implementations of the RSAES-PKCS1-v1_5 and RSA-OAEP encryption schemes.
042 *
043 * @see <a href="https://tools.ietf.org/html/rfc7518#section-4.2">RFC 7518 Section 4.2 and 4.3</a>
044 */
045public final class RSAEncryptionHandler implements EncryptionHandler {
046    private static final OAEPParameterSpec RSA_OAEP_256_PARAMS = new OAEPParameterSpec("SHA-256", "MGF1", SHA256,
047            DEFAULT);
048    private final EncryptionMethod encryptionMethod;
049    private final ContentEncryptionHandler contentEncryptionHandler;
050    private final JweAlgorithm jweAlgorithm;
051    private final AlgorithmParameterSpec parameterSpec;
052
053    /**
054     * Constructs a new RSAEncryptionHandler instance.
055     *
056     * @param encryptionMethod the content encryption method. Must not be null.
057     * @param jweAlgorithm the JWE algorithm. Must not be null. Must be an RSA encryption algorithm.
058     */
059    public RSAEncryptionHandler(EncryptionMethod encryptionMethod, final JweAlgorithm jweAlgorithm) {
060        this.encryptionMethod = checkNotNull(encryptionMethod, "EncryptionMethod must not be null");
061        this.jweAlgorithm = checkNotNull(jweAlgorithm, "JweAlgorithm must not be null");
062        Reject.ifFalse(jweAlgorithm.getAlgorithmType() == RSA, "JweAlgorithm type must be RSA");
063        this.contentEncryptionHandler = ContentEncryptionHandler.getInstance(encryptionMethod);
064        // RSA-OAEP-256 requires non-default algorithm parameters to conform to the JWE spec. The JRE defaults are
065        // correct for all other modes, so leave as null.
066        this.parameterSpec = jweAlgorithm == RSA_OAEP_256 ? RSA_OAEP_256_PARAMS : null;
067    }
068
069    /**
070     * Creates a Content Encryption Key (CEK) by generating a random key value with a length equal to the
071     * EncryptionMethod A128CBC_HS256 key size.
072     * <p>
073     * See point 2 in <a href="http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption-11#section-5.1">
074     *     Section 5.1</a> of the JWE Specification.
075     *
076     * @return {@inheritDoc}
077     */
078    @Override
079    public Key getContentEncryptionKey() {
080        return contentEncryptionHandler.generateEncryptionKey();
081    }
082
083    /**
084     * Generates the JWE Encrypted Key by encrypting the Content Encryption Key (CEK) using the JweAlgorithm
085     * RSAES_PCKCS1_V1_5.
086     * <p>
087     * See point 4 in <a href="http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption-11#section-5.1">
088     *     Section 5.1</a> of the JWE Specification.
089     *
090     * @param key {@inheritDoc}
091     * @param contentEncryptionKey {@inheritDoc}
092     * @return {@inheritDoc}
093     */
094    @Override
095    public byte[] generateJWEEncryptedKey(Key key, Key contentEncryptionKey) {
096        return encryptKey((RSAPublicKey) key, contentEncryptionKey);
097    }
098
099    /**
100     * Generates a random JWE Initialisation Vector of the correct size for the encryption algorithm.
101     * <p>
102     * See points 9 in <a href="http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption-11#section-5.1">
103     *     Section 5.1</a> of the JWE Specification.
104     *
105     * @return {@inheritDoc}
106     */
107    @Override
108    public byte[] generateInitialisationVector() {
109        return contentEncryptionHandler.generateInitialisationVector();
110    }
111
112    /**
113     * {@inheritDoc}
114     */
115    @Override
116    public JweEncryption encryptPlaintext(Key contentEncryptionKey, byte[] initialisationVector, byte[] plaintext,
117                                          byte[] additionalAuthenticatedData) {
118
119        return contentEncryptionHandler.encrypt(contentEncryptionKey, initialisationVector, plaintext,
120                additionalAuthenticatedData);
121    }
122
123    /**
124     * Decrypts the JWE Encrypted Key to produce the Content Encryption Key (CEK).
125     * <p>
126     * See points 10 in <a href="http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption-11#section-5.2">
127     *     Section 5.2</a> of the JWE Specification.
128     *
129     * @param key {@inheritDoc}
130     * @param encryptedContentEncryptionKey {@inheritDoc}
131     * @return {@inheritDoc}
132     */
133    @Override
134    public Key decryptContentEncryptionKey(Key key, byte[] encryptedContentEncryptionKey) {
135        try {
136            final Cipher cipher = Cipher.getInstance(jweAlgorithm.getAlgorithm());
137            cipher.init(Cipher.UNWRAP_MODE, key, parameterSpec);
138            return cipher.unwrap(encryptedContentEncryptionKey, encryptionMethod.getEncryptionAlgorithm(),
139                    Cipher.SECRET_KEY);
140        } catch (GeneralSecurityException e) {
141            throw new JweDecryptionException();
142        }
143    }
144
145    /**
146     * {@inheritDoc}
147     */
148    @Override
149    public byte[] decryptCiphertext(Key contentEncryptionKey, byte[] initialisationVector, byte[] ciphertext,
150                                    byte[] authenticationTag, byte[] additionalAuthenticatedData) {
151        return contentEncryptionHandler.decrypt(contentEncryptionKey, initialisationVector,
152                new JweEncryption(ciphertext, authenticationTag), additionalAuthenticatedData);
153    }
154
155    private byte[] encryptKey(final RSAPublicKey keyEncryptionKey, final Key contentKey) {
156        try {
157            final Cipher cipher = Cipher.getInstance(jweAlgorithm.getAlgorithm());
158            cipher.init(Cipher.WRAP_MODE, keyEncryptionKey, parameterSpec);
159            return cipher.wrap(contentKey);
160        } catch (GeneralSecurityException e) {
161            throw new JweEncryptionException(e);
162        }
163    }
164}