CryptoService.java

/*
 * Copyright (c) 2002-2017, Mairie de Paris
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice
 *     and the following disclaimer.
 *
 *  2. Redistributions in binary form must reproduce the above copyright notice
 *     and the following disclaimer in the documentation and/or other materials
 *     provided with the distribution.
 *
 *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * License 1.0
 */
package fr.paris.lutece.util.keydiversification;

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;

/**
 * This service encrypts / decrypts strings
 */
public class CryptoService
{

    private static final String UNICODE_FORMAT = "UTF8";
    private static final String DESEDE_ENCRYPTION_SCHEME = "DESede";

    private final Cipher _cipher;
    private final SecretKey _key;

    /**
     * Constructor
     *
     * @param strEncryptionKey
     *            An encryption key
     * @throws KeyDiversificationException
     *             If an error occurs
     * @throws InvalidKeyException
     * @throws NoSuchAlgorithmException
     * @throws NoSuchPaddingException
     * @throws InvalidKeySpecException
     */
    public CryptoService( String strEncryptionKey ) throws KeyDiversificationException
    {
        try
        {
            byte [ ] arrayBytes = new byte [ DESedeKeySpec.DES_EDE_KEY_LEN];

            for ( int i = 0; i < DESedeKeySpec.DES_EDE_KEY_LEN; i++ )
            {
                if ( i < strEncryptionKey.length( ) )
                {
                    arrayBytes [i] = (byte) strEncryptionKey.charAt( i );
                }
            }
            KeySpec keySpec = new DESedeKeySpec( arrayBytes );
            SecretKeyFactory skf = SecretKeyFactory.getInstance( DESEDE_ENCRYPTION_SCHEME );
            _cipher = Cipher.getInstance( DESEDE_ENCRYPTION_SCHEME );
            _key = skf.generateSecret( keySpec );
        }
        catch( InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeySpecException e )
        {
            throw new KeyDiversificationException( "Error during the initialisation of encryption!", e );
        }
    }

    /**
     * Encrypts the specified string
     *
     * @param strSource
     *            the string to encrypt
     * @return The encrypted string
     * @throws KeyDiversificationException
     *             if there is an error during the treatment
     */
    public String encrypt( String strSource ) throws KeyDiversificationException
    {
        try
        {
            String strEncrypted;
            _cipher.init( Cipher.ENCRYPT_MODE, _key );
            byte [ ] plainText = strSource.getBytes( UNICODE_FORMAT );
            byte [ ] encryptedText = _cipher.doFinal( plainText );
            strEncrypted = new String( Base64.getEncoder( ).encode( encryptedText ), UNICODE_FORMAT );
            return strEncrypted;
        }
        catch( InvalidKeyException | UnsupportedEncodingException | IllegalBlockSizeException | BadPaddingException e )
        {
            throw new KeyDiversificationException( "Error during the encryption of '" + strSource + "'", e );
        }
    }

    /**
     * Decrypts the specified string
     *
     * @param strEncrypted
     *            The encrypted string
     * @return The decrypted string
     * @throws KeyDiversificationException
     *             if there is an error during the treatment
     */
    public String decrypt( String strEncrypted ) throws KeyDiversificationException
    {
        try
        {
            String strDecrypted;
            _cipher.init( Cipher.DECRYPT_MODE, _key );
            byte [ ] encryptedText = Base64.getDecoder( ).decode( strEncrypted );
            byte [ ] plainText = _cipher.doFinal( encryptedText );
            strDecrypted = new String( plainText, UNICODE_FORMAT );
            return strDecrypted;
        }
        catch( IllegalBlockSizeException | BadPaddingException | InvalidKeyException | UnsupportedEncodingException e )
        {
            throw new KeyDiversificationException( "Error during the decryption of '" + strEncrypted + "'", e );
        }
    }

}