org.lsc.utils.security.SymmetricEncryption.java Source code

Java tutorial

Introduction

Here is the source code for org.lsc.utils.security.SymmetricEncryption.java

Source

/*
 ****************************************************************************
 * Ldap Synchronization Connector provides tools to synchronize
 * electronic identities from a list of data sources including
 * any database with a JDBC connector, another LDAP directory,
 * flat files...
 *
 *                  ==LICENSE NOTICE==
 *
 * Copyright (c) 2008 - 2011 LSC Project
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
    
 *  * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *   * Neither the name of the LSC Project 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 OWNER
 * 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 NOTICE==
 *
 *               (c) 2008 - 2011 LSC Project
 *         Sebastien Bahloul <seb@lsc-project.org>
 *         Thomas Chemineau <thomas@lsc-project.org>
 *         Jonathan Clarke <jon@lsc-project.org>
 *         Remy-Christophe Schermesser <rcs@lsc-project.org>
 ****************************************************************************
 */
package org.lsc.utils.security;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.Security;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.lsc.Configuration;
import org.lsc.configuration.EncryptionType;
import org.lsc.configuration.LscConfiguration;
import org.lsc.exception.LscException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>This new class allows symmetric encryption. You should have BouncyCastle
 * installed. Three new configuration parameters could be added to the
 * configuration:</p>
 * <ul>
 *   <li>lsc>security>encryption>keyfile: the path to the file used to
 *   encrypt/decrypt data</li>
 *   <li>lsc>security>encryption>algorithm: the algorithm to use</li>
 *   <li>lsc>security>encryption>strength: the strength in bits</li>
 * </ul>
 */
public class SymmetricEncryption {

    private static final Logger LOGGER = LoggerFactory.getLogger(SymmetricEncryption.class);

    public static final int DEFAULT_CIPHER_STRENGTH = 128;
    public static final String DEFAULT_CIPHER_ALGORITHM = "AES";
    private int strength;
    private String algorithm;
    private String keyPath;
    private Cipher cipherDecrypt;
    private Cipher cipherEncrypt;
    private Provider securityProvider;

    /**
     * New SymmetricEncryption object with default values.
     * @throws java.security.GeneralSecurityException
     */
    public SymmetricEncryption() throws GeneralSecurityException {
        this.securityProvider = new BouncyCastleProvider();
        Security.addProvider(this.securityProvider);
    }

    /**
     * New SymmetricEncryption object.
     * @param encryption the encryption required structure
     * @throws java.security.GeneralSecurityException
     */
    public SymmetricEncryption(EncryptionType encryption) throws GeneralSecurityException {
        if (encryption == null) {
            throw new RuntimeException("lsc>security>encryption node of the LSC configuration cannot be null !");
        } else if (encryption.getKeyfile() == null) {
            throw new RuntimeException(
                    "lsc>security>encryption>keyfile node of the LSC configuration cannot be null !");
        } else if (encryption.getAlgorithm() == null) {
            throw new RuntimeException(
                    "lsc>security>encryption>algorithm node of the LSC configuration cannot be null !");
        }

        this.securityProvider = new BouncyCastleProvider();
        this.algorithm = encryption.getAlgorithm();
        this.strength = encryption.getStrength();
        this.keyPath = encryption.getKeyfile();

        Security.addProvider(this.securityProvider);
    }

    /**
     * Encrypt bytes.
     * @param toEncrypt
     * @return Encrypted bytes.
     * @throws java.security.GeneralSecurityException
     */
    public byte[] encrypt(byte[] toEncrypt) throws GeneralSecurityException {
        return cipherEncrypt.doFinal(toEncrypt);
    }

    /**
     * Decrypt bytes.
     * @param toDecrypt
     * @return Decrypted bytes.
     * @throws java.security.GeneralSecurityException
     */
    public byte[] decrypt(byte[] toDecrypt) throws GeneralSecurityException {
        return cipherDecrypt.doFinal(toDecrypt);
    }

    /**
     * Generate a random key file with default value
     * @return boolean false if an error occurred
     * @throws NoSuchAlgorithmException 
     * @throws NoSuchProviderException 
     */
    public boolean generateDefaultRandomKeyFile() throws NoSuchAlgorithmException, NoSuchProviderException {
        File keypath = new File(Configuration.getConfigurationDirectory(), "lsc.key");
        if (keypath.exists()) {
            LOGGER.error("Existing key file in {}. Please move it away before generating a new key !",
                    keypath.getAbsolutePath());
            return false;
        }
        boolean status = this.generateRandomKeyFile(keypath.getAbsolutePath(), this.algorithm, this.strength);
        this.keyPath = keypath.getAbsolutePath();
        return status;
    }

    /**
     * Generate a random key file.
     * @param keyPath The filename where to write the key
     * @param algo The supported algorithm to use
     * @param strength The encryption strength
     * @return boolean false if an error occurred
     * @throws NoSuchAlgorithmException 
     * @throws NoSuchProviderException 
     */
    public boolean generateRandomKeyFile(String keyPath, String algo, int strength)
            throws NoSuchAlgorithmException, NoSuchProviderException {
        OutputStream os = null;
        try {
            KeyGenerator kg = KeyGenerator.getInstance(algo, securityProvider.getName());
            SecretKey cipherKey = kg.generateKey();
            SecureRandom sr = new SecureRandom();
            kg.init(strength, sr);
            os = new FileOutputStream(keyPath);
            os.write(cipherKey.getEncoded());
        } catch (IOException e) {
            LOGGER.error("Unable to write new generated key in " + keyPath + ". Encountered exception is : "
                    + e.getLocalizedMessage(), e);
            return false;
        } finally {
            try {
                if (os != null) {
                    os.close();
                }
            } catch (IOException e1) {
            }
        }
        return true;
    }

    /**
     * Return the default filename of the key to use.
     * The filename could be specified in the configuration file through the
     * lsc.security.encryption.keyfile property.
     * @return A filename
     */
    public static String getKeyPath() {
        return LscConfiguration.getSecurity().getEncryption().getKeyfile();
    }

    /**
     * Return the default supported algorithm to use.
     * The algorithm could be specified in the configuration file through the
     * lsc.security.encryption.algorithm property. See constant values defined
     * in this class which specified supported algorithms.
     * @return A supported algorithm
     */
    public static String getAlgorithm() {
        return LscConfiguration.getSecurity().getEncryption().getAlgorithm();
    }

    /**
     * Return the default encryption strength.
     * The encryption strength could be specified in the configuration file
     * through the lsc.security.encryption.strength property.
     * @return int
     */
    public static int getStrength() {
        return LscConfiguration.getSecurity().getEncryption().getStrength();
    }

    /**
     * Initialize encryption object from the configuration file.
     * @return boolean (always true if no exception)
     * @throws GeneralSecurityException 
     */
    public boolean initialize() throws GeneralSecurityException {
        InputStream input = null;
        boolean fail = false;
        try {
            input = new FileInputStream(new File(this.keyPath));
            byte[] data = new byte[strength / Byte.SIZE];
            input.read(data);

            Key key = new SecretKeySpec(data, this.algorithm);
            this.cipherEncrypt = Cipher.getInstance(this.algorithm);
            this.cipherEncrypt.init(Cipher.ENCRYPT_MODE, key);
            this.cipherDecrypt = Cipher.getInstance(this.algorithm);
            this.cipherDecrypt.init(Cipher.DECRYPT_MODE, key);

        } catch (IOException e) {
            fail = true;
        } finally {
            try {
                if (input != null) {
                    input.close();
                }
            } catch (IOException e) {
            }
        }

        if (fail) {
            LOGGER.error("Error reading the key for SymmetricEncryption! ({})", this.keyPath);
            return false;
        }

        return true;
    }

    /**
     * This main allow user to generate random key file.
     * @param argv
     */
    public static void main(String argv[]) {
        try {
            Options options = new Options();
            options.addOption("f", "cfg", true, "Specify configuration directory");
            CommandLine cmdLine = new GnuParser().parse(options, argv);

            if (cmdLine.getOptions().length > 0 && cmdLine.hasOption("f")) {
                // if a configuration directory was set on command line, use it to set up Configuration
                Configuration.setUp(cmdLine.getOptionValue("f"), false);
            } else {
                HelpFormatter formatter = new HelpFormatter();
                formatter.printHelp("lsc", options);
                System.exit(1);
            }
        } catch (ParseException e) {
            StringBuilder sbf = new StringBuilder();
            for (String arg : argv) {
                sbf.append(arg).append(" ");
            }

            LOGGER.error("Unable to parse options : {}({})", sbf.toString(), e);
            System.exit(1);
        } catch (LscException e) {
            LOGGER.error("Something goes wrong while loading configuration: " + e.toString(), e);
            System.exit(2);
        }

        try {
            if (LscConfiguration.getSecurity() == null) {
                throw new RuntimeException("lsc>security node of the LSC configuration cannot be null !");
            } else if (LscConfiguration.getSecurity().getEncryption() == null) {
                throw new RuntimeException(
                        "lsc>security>encryption node of the LSC configuration cannot be null !");
            }
            SymmetricEncryption se = new SymmetricEncryption(LscConfiguration.getSecurity().getEncryption());
            if (se.generateDefaultRandomKeyFile()) {
                LOGGER.info(
                        "Key generated: {}. Do not forget to check the lsc>security>encryption>keyfile node value in your configuration file !",
                        se.keyPath);
            }
        } catch (GeneralSecurityException ex) {
            LOGGER.debug(ex.toString(), ex);
        }
    }
}