org.apigw.commons.crypto.ApigwCrypto.java Source code

Java tutorial

Introduction

Here is the source code for org.apigw.commons.crypto.ApigwCrypto.java

Source

/**
 *   Copyright 2013 Stockholm County Council
 *
 *   This file is part of APIGW
 *
 *   APIGW is free software; you can redistribute it and/or modify
 *   it under the terms of version 2.1 of the GNU Lesser General Public
 *   License as published by the Free Software Foundation.
 *
 *   APIGW is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public
 *   License along with APIGW; if not, write to the
 *   Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 *   Boston, MA 02111-1307  USA
 *
 */

package org.apigw.commons.crypto;

import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;

import javax.annotation.PostConstruct;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.*;
import java.security.cert.CertificateException;

/**
 * Created by martin on 26/12/14.
 */
public class ApigwCrypto {

    protected Logger log;

    protected static final int IV_LENGTH = 16;
    protected static final String TRANSFORMATION = "AES/CBC/PKCS7PADDING";
    protected static final String KEY_ALGORITHM = "AES";

    @Value("${crypto.useEncryption}")
    protected boolean useEncryption;

    @Value("${crypto.keyStore.file}")
    private String keyStoreFile;

    @Value("${crypto.keyStore.password}")
    private String keyStorePassword;

    @Value("${crypto.keyStore.type}")
    private String keyStoreType;

    @Value("${crypto.keyStore.alias}")
    private String alias;

    @Value("${crypto.saltKeyStore.file}")
    private String saltKeyKeyStoreFile;

    @Value("${crypto.saltKeyStore.password}")
    private String saltKeyKeyStorePassword;

    @Value("${crypto.saltKeyStore.type}")
    private String saltKeyKeyStoreType;

    @Value("${crypto.saltKey.password}")
    private String saltKeyPassword;

    @Value("${crypto.saltKey.alias}")
    private String saltKeyAlias;

    @Value("${crypto.encodedSalt}")
    private String encodedEncryptedSalt;

    private Provider securityProvider;
    private byte[] salt;
    private KeyStore keyStore;
    private KeyStore saltKeyStore;

    @PostConstruct
    public void init() throws Exception {
        log.debug("Initializing...");
        if (useEncryption) {
            securityProvider = new BouncyCastleProvider();
            Security.addProvider(securityProvider);
            keyStore = initKeyStore(keyStoreFile, keyStorePassword, keyStoreType);
            saltKeyStore = initKeyStore(saltKeyKeyStoreFile, saltKeyKeyStorePassword, saltKeyKeyStoreType);
            salt = initSalt();
            Key key = keyStore.getKey(alias, keyStorePassword.toCharArray());
            validateKey(key);
            String algorithm = key.getAlgorithm();
            int size = key.getEncoded().length * 8;
            log.debug("operations will be performed using {} key with size {}", algorithm, size);

        } else {
            keyStore = null;
            log.warn("No keystore file specified, will not encrypt messages");
        }
        log.debug("Finished initializing");
    }

    /**
     *
     * @return The SecretKeySpec to be used for encryption/decryption of messages
     * @throws UnrecoverableKeyException
     * @throws NoSuchAlgorithmException
     * @throws KeyStoreException
     */
    protected SecretKeySpec getSecretKeySpec()
            throws UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException {
        Key key = keyStore.getKey(alias, keyStorePassword.toCharArray());
        return new SecretKeySpec(key.getEncoded(), KEY_ALGORITHM);
    }

    /**
     *
     * @return IvParameterSpec of the global salt to be used for encryption/decryption
     */
    protected IvParameterSpec getIvParameterSpec() {
        return new IvParameterSpec(salt);
    }

    /**
     *
     * @return a new instance of Cipher to be used for encryption/decryption
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     */
    protected Cipher getCipher() throws NoSuchPaddingException, NoSuchAlgorithmException {
        return Cipher.getInstance(TRANSFORMATION, securityProvider);
    }

    protected void validateKey(Key key) throws InvalidKeyException, NoSuchAlgorithmException {
        String algorithm = key.getAlgorithm();
        int size = key.getEncoded().length * 8;
        if (!KEY_ALGORITHM.equalsIgnoreCase(algorithm)) {
            String msg = "Expected key of type: " + KEY_ALGORITHM + ", instead it was: " + algorithm;
            log.error(msg);
            throw new InvalidKeyException(msg);
        } else if (size > Cipher.getMaxAllowedKeyLength(KEY_ALGORITHM)) {
            String msg = "Illegal key size, max platform support for " + KEY_ALGORITHM + " keys is "
                    + Cipher.getMaxAllowedKeyLength(KEY_ALGORITHM);
            log.error(msg);
            throw new InvalidKeyException(msg);
        }
    }

    /**
     * Will init the global salt / IV, this salt should not be stored together with encrypted values.
     */
    private byte[] initSalt() throws UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException,
            NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException,
            IllegalBlockSizeException {
        byte[] encryptedSalt = Base64.decodeBase64(encodedEncryptedSalt.getBytes());
        Key saltKey = saltKeyStore.getKey(saltKeyAlias, saltKeyPassword.toCharArray());
        validateKey(saltKey);
        String algorithm = saltKey.getAlgorithm();
        int size = saltKey.getEncoded().length * 8;
        log.debug("initializing salt using {} key with size {}", algorithm, size);
        SecretKeySpec skeySpec = new SecretKeySpec(saltKey.getEncoded(), KEY_ALGORITHM);

        IvParameterSpec ivParameterSpec = new IvParameterSpec(getIV(encryptedSalt));
        Cipher decryptCipher = Cipher.getInstance(TRANSFORMATION, securityProvider);
        decryptCipher.init(Cipher.DECRYPT_MODE, skeySpec, ivParameterSpec);
        byte[] decryptedSalt = decryptCipher.doFinal(encryptedSalt);
        return removeIV(decryptedSalt);
    }

    /**
     * Will return a new byte[] containing all the bytes except for the first number of bytes corresponding to IV_LENGTH
     * @param ivPrependedEncryptedBytes
     * @return a new byte[] containing all the bytes except for the first number of bytes corresponding to IV_LENGTH
     */
    private byte[] removeIV(byte[] ivPrependedEncryptedBytes) {
        byte[] encryptedWithoutIV = new byte[ivPrependedEncryptedBytes.length - IV_LENGTH];
        System.arraycopy(ivPrependedEncryptedBytes, IV_LENGTH, encryptedWithoutIV, 0,
                ivPrependedEncryptedBytes.length - IV_LENGTH);
        return encryptedWithoutIV;
    }

    /**
     * Will extract the first number fo bytes from a byte[] which should represent an IV
     *
     * @param ivPrependedEncryptedBytes byte array where the first number of bytes (corresponding to IV_LENGTH) contains an IV
     * @return
     */
    private byte[] getIV(byte[] ivPrependedEncryptedBytes) {
        byte[] iv = new byte[IV_LENGTH];
        System.arraycopy(ivPrependedEncryptedBytes, 0, iv, 0, IV_LENGTH);
        return iv;
    }

    /**
     *
     * @param keyStoreFile
     * @param keyStorePassword
     * @param keyStoreType
     * @return a new KeyStore corresponding to provided values
     * @throws IOException
     * @throws KeyStoreException
     * @throws CertificateException
     * @throws NoSuchAlgorithmException
     */
    private KeyStore initKeyStore(String keyStoreFile, String keyStorePassword, String keyStoreType)
            throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException {
        InputStream inputStream;
        KeyStore keyStore;
        try {
            inputStream = new ClassPathResource(keyStoreFile).getInputStream();
        } catch (FileNotFoundException e) {
            log.warn("unable to load {} from classpath, will try filesystem", keyStoreFile);
            inputStream = new FileSystemResource(keyStoreFile).getInputStream();
        }
        keyStore = KeyStore.getInstance(keyStoreType);
        keyStore.load(inputStream, keyStorePassword.toCharArray());
        inputStream.close();
        return keyStore;
    }

    public void setKeyStoreFile(String keyStoreFile) {
        this.keyStoreFile = keyStoreFile;
    }

    public void setKeyStorePassword(String keyStorePassword) {
        this.keyStorePassword = keyStorePassword;
    }

    public void setKeyStoreType(String keyStoreType) {
        this.keyStoreType = keyStoreType;
    }

    public void setAlias(String alias) {
        this.alias = alias;
    }

    public void setUseEncryption(boolean useEncryption) {
        this.useEncryption = useEncryption;
    }

    public void setSaltKeyKeyStoreFile(String saltKeyKeyStoreFile) {
        this.saltKeyKeyStoreFile = saltKeyKeyStoreFile;
    }

    public void setSaltKeyKeyStorePassword(String saltKeyKeyStorePassword) {
        this.saltKeyKeyStorePassword = saltKeyKeyStorePassword;
    }

    public void setSaltKeyKeyStoreType(String saltKeyKeyStoreType) {
        this.saltKeyKeyStoreType = saltKeyKeyStoreType;
    }

    public void setSaltKeyAlias(String saltKeyAlias) {
        this.saltKeyAlias = saltKeyAlias;
    }

    public void setSaltKeyPassword(String saltKeyPassword) {
        this.saltKeyPassword = saltKeyPassword;
    }

    public void setEncodedEncryptedSalt(String encodedEncryptedSalt) {
        this.encodedEncryptedSalt = encodedEncryptedSalt;
    }

}