org.xdi.oxauth.model.jwe.JweDecrypterImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.xdi.oxauth.model.jwe.JweDecrypterImpl.java

Source

/*
 * oxAuth is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text.
 *
 * Copyright (c) 2014, Gluu
 */

package org.xdi.oxauth.model.jwe;

import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.engines.AESWrapEngine;
import org.bouncycastle.crypto.modes.GCMBlockCipher;
import org.bouncycastle.crypto.params.AEADParameters;
import org.bouncycastle.crypto.params.KeyParameter;
import org.xdi.oxauth.model.crypto.encryption.BlockEncryptionAlgorithm;
import org.xdi.oxauth.model.crypto.encryption.KeyEncryptionAlgorithm;
import org.xdi.oxauth.model.crypto.signature.RSAPrivateKey;
import org.xdi.oxauth.model.exception.InvalidJweException;
import org.xdi.oxauth.model.exception.InvalidParameterException;
import org.xdi.oxauth.model.util.Base64Util;
import org.xdi.oxauth.model.util.Util;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.util.Arrays;

/**
 * @author Javier Rojas Blum
 * @version July 31, 2016
 */
public class JweDecrypterImpl extends AbstractJweDecrypter {

    private PrivateKey privateKey;
    private RSAPrivateKey rsaPrivateKey;
    private byte[] sharedSymmetricKey;

    public JweDecrypterImpl(byte[] sharedSymmetricKey) {
        if (sharedSymmetricKey != null) {
            this.sharedSymmetricKey = sharedSymmetricKey.clone();
        }
    }

    public JweDecrypterImpl(RSAPrivateKey rsaPrivateKey) {
        this.rsaPrivateKey = rsaPrivateKey;
    }

    public JweDecrypterImpl(PrivateKey privateKey) {
        this.privateKey = privateKey;
    }

    @Override
    public byte[] decryptEncryptionKey(String encodedEncryptedKey) throws InvalidJweException {
        if (getKeyEncryptionAlgorithm() == null) {
            throw new InvalidJweException("The key encryption algorithm is null");
        }
        if (encodedEncryptedKey == null) {
            throw new InvalidJweException("The encoded encryption key is null");
        }

        try {
            if (getKeyEncryptionAlgorithm() == KeyEncryptionAlgorithm.RSA_OAEP
                    || getKeyEncryptionAlgorithm() == KeyEncryptionAlgorithm.RSA1_5) {
                if (rsaPrivateKey == null && privateKey == null) {
                    throw new InvalidJweException("The RSA private key is null");
                }

                //Cipher cipher = Cipher.getInstance(getKeyEncryptionAlgorithm().getAlgorithm(), "BC");
                Cipher cipher = Cipher.getInstance(getKeyEncryptionAlgorithm().getAlgorithm());

                if (rsaPrivateKey != null) {
                    KeyFactory keyFactory = KeyFactory.getInstance(getKeyEncryptionAlgorithm().getFamily(), "BC");
                    RSAPrivateKeySpec privKeySpec = new RSAPrivateKeySpec(rsaPrivateKey.getModulus(),
                            rsaPrivateKey.getPrivateExponent());
                    java.security.interfaces.RSAPrivateKey privKey = (java.security.interfaces.RSAPrivateKey) keyFactory
                            .generatePrivate(privKeySpec);
                    cipher.init(Cipher.DECRYPT_MODE, privKey);
                } else {
                    cipher.init(Cipher.DECRYPT_MODE, privateKey);
                }

                byte[] decryptedKey = cipher.doFinal(Base64Util.base64urldecode(encodedEncryptedKey));

                return decryptedKey;
            } else if (getKeyEncryptionAlgorithm() == KeyEncryptionAlgorithm.A128KW
                    || getKeyEncryptionAlgorithm() == KeyEncryptionAlgorithm.A256KW) {
                if (sharedSymmetricKey == null) {
                    throw new InvalidJweException("The shared symmetric key is null");
                }
                if (sharedSymmetricKey.length != 16) { // 128 bit
                    MessageDigest sha = MessageDigest.getInstance("SHA-1");
                    sharedSymmetricKey = sha.digest(sharedSymmetricKey);
                    sharedSymmetricKey = Arrays.copyOf(sharedSymmetricKey, 16);
                }
                byte[] encryptedKey = Base64Util.base64urldecode(encodedEncryptedKey);
                SecretKeySpec keyEncryptionKey = new SecretKeySpec(sharedSymmetricKey, "AES");
                AESWrapEngine aesWrapEngine = new AESWrapEngine();
                CipherParameters params = new KeyParameter(keyEncryptionKey.getEncoded());
                aesWrapEngine.init(false, params);
                byte[] decryptedKey = aesWrapEngine.unwrap(encryptedKey, 0, encryptedKey.length);

                return decryptedKey;
            } else {
                throw new InvalidJweException("The key encryption algorithm is not supported");
            }
        } catch (NoSuchPaddingException e) {
            throw new InvalidJweException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new InvalidJweException(e);
        } catch (IllegalBlockSizeException e) {
            throw new InvalidJweException(e);
        } catch (BadPaddingException e) {
            throw new InvalidJweException(e);
        } catch (NoSuchProviderException e) {
            throw new InvalidJweException(e);
        } catch (InvalidKeyException e) {
            throw new InvalidJweException(e);
        } catch (InvalidKeySpecException e) {
            throw new InvalidJweException(e);
        } catch (InvalidCipherTextException e) {
            throw new InvalidJweException(e);
        }
    }

    @Override
    public String decryptCipherText(String encodedCipherText, byte[] contentMasterKey, byte[] initializationVector,
            byte[] authenticationTag, byte[] additionalAuthenticatedData) throws InvalidJweException {
        if (getBlockEncryptionAlgorithm() == null) {
            throw new InvalidJweException("The block encryption algorithm is null");
        }
        if (contentMasterKey == null) {
            throw new InvalidJweException("The content master key (CMK) is null");
        }
        if (initializationVector == null) {
            throw new InvalidJweException("The initialization vector is null");
        }
        if (authenticationTag == null) {
            throw new InvalidJweException("The authentication tag is null");
        }
        if (additionalAuthenticatedData == null) {
            throw new InvalidJweException("The additional authentication data is null");
        }

        try {
            if (getBlockEncryptionAlgorithm() == BlockEncryptionAlgorithm.A128GCM
                    || getBlockEncryptionAlgorithm() == BlockEncryptionAlgorithm.A256GCM) {
                final int MAC_SIZE_BITS = 128;
                byte[] cipherText = Base64Util.base64urldecode(encodedCipherText);

                KeyParameter key = new KeyParameter(contentMasterKey);
                AEADParameters aeadParameters = new AEADParameters(key, MAC_SIZE_BITS, initializationVector,
                        additionalAuthenticatedData);
                SecretKeySpec sks = new SecretKeySpec(contentMasterKey, "AES");

                BlockCipher blockCipher = new AESEngine();
                CipherParameters params = new KeyParameter(sks.getEncoded());
                blockCipher.init(false, params);
                GCMBlockCipher aGCMBlockCipher = new GCMBlockCipher(blockCipher);
                aGCMBlockCipher.init(false, aeadParameters);
                byte[] input = new byte[cipherText.length + authenticationTag.length];
                System.arraycopy(cipherText, 0, input, 0, cipherText.length);
                System.arraycopy(authenticationTag, 0, input, cipherText.length, authenticationTag.length);
                int len = aGCMBlockCipher.getOutputSize(input.length);
                byte[] out = new byte[len];
                int outOff = aGCMBlockCipher.processBytes(input, 0, input.length, out, 0);
                aGCMBlockCipher.doFinal(out, outOff);

                String plaintext = new String(out, Charset.forName(Util.UTF8_STRING_ENCODING));

                return plaintext;
            } else if (getBlockEncryptionAlgorithm() == BlockEncryptionAlgorithm.A128CBC_PLUS_HS256
                    || getBlockEncryptionAlgorithm() == BlockEncryptionAlgorithm.A256CBC_PLUS_HS512) {
                byte[] cipherText = Base64Util.base64urldecode(encodedCipherText);

                byte[] cek = KeyDerivationFunction.generateCek(contentMasterKey, getBlockEncryptionAlgorithm());
                Cipher cipher = Cipher.getInstance(getBlockEncryptionAlgorithm().getAlgorithm());
                IvParameterSpec ivParameter = new IvParameterSpec(initializationVector);
                cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(cek, "AES"), ivParameter);
                byte[] decodedPlainTextBytes = cipher.doFinal(cipherText);
                String decodedPlainText = new String(decodedPlainTextBytes,
                        Charset.forName(Util.UTF8_STRING_ENCODING));

                // Integrity check
                String securedInputValue = new String(additionalAuthenticatedData,
                        Charset.forName(Util.UTF8_STRING_ENCODING)) + "." + encodedCipherText;
                byte[] cik = KeyDerivationFunction.generateCik(contentMasterKey, getBlockEncryptionAlgorithm());
                SecretKey secretKey = new SecretKeySpec(cik,
                        getBlockEncryptionAlgorithm().getIntegrityValueAlgorithm());
                Mac mac = Mac.getInstance(getBlockEncryptionAlgorithm().getIntegrityValueAlgorithm());
                mac.init(secretKey);
                byte[] integrityValue = mac.doFinal(securedInputValue.getBytes(Util.UTF8_STRING_ENCODING));
                if (!Arrays.equals(integrityValue, authenticationTag)) {
                    throw new InvalidJweException("The authentication tag is not valid");
                }

                return decodedPlainText;
            } else {
                throw new InvalidJweException("The block encryption algorithm is not supported");
            }
        } catch (InvalidCipherTextException e) {
            throw new InvalidJweException(e);
        } catch (NoSuchPaddingException e) {
            throw new InvalidJweException(e);
        } catch (BadPaddingException e) {
            throw new InvalidJweException(e);
        } catch (InvalidAlgorithmParameterException e) {
            throw new InvalidJweException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new InvalidJweException(e);
        } catch (IllegalBlockSizeException e) {
            throw new InvalidJweException(e);
        } catch (UnsupportedEncodingException e) {
            throw new InvalidJweException(e);
        } catch (NoSuchProviderException e) {
            throw new InvalidJweException(e);
        } catch (InvalidKeyException e) {
            throw new InvalidJweException(e);
        } catch (InvalidParameterException e) {
            throw new InvalidJweException(e);
        }
    }
}