All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fluidbpm.ws.client.v1.user.AES256Local Maven / Gradle / Ivy

/*
 * Koekiebox CONFIDENTIAL
 *
 * [2012] - [2017] Koekiebox (Pty) Ltd
 * All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property
 * of Koekiebox and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Koekiebox
 * and its suppliers and may be covered by South African and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material is strictly
 * forbidden unless prior written permission is obtained from Koekiebox.
 */

package com.fluidbpm.ws.client.v1.user;

import java.security.*;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import com.fluidbpm.program.api.util.UtilGlobal;
import com.fluidbpm.ws.client.FluidClientException;


/**
 * Utility class for AES-256 cryptography.
 *
 * @author jasonbruwer
 * @since v1.0
 *
 * @see Mac
 * @see Cipher
 */
public class AES256Local {

    public static final int IV_SIZE_BYTES = 16;
    public static final int SEED_SIZE_BYTES = 32;

    private static final String HMAC_ALGO = "HmacSHA256";

    private static final String ALGO_CBC = "AES/CBC/PKCS5Padding";
    private static final String KEY_ALGO = "AES";

    private static SecureRandom secureRandom;

    /**
     * Generate a new initialization vector with a {@code seedParam} byte
     * count.
     *
     * @param seedParam Size of the random bytes.
     * @return The software randomly generated bytes.
     */
    public static byte[] generateRandom(int seedParam) {

        if (AES256Local.secureRandom == null)
        {
            AES256Local.secureRandom = new SecureRandom();
        }

        return new IvParameterSpec(AES256Local.secureRandom.generateSeed(seedParam)).getIV();
    }

    /**
     * Generates an HMAC from {@code encryptedDataParam}.
     *
     * @param hMacKeyParam The symmetric key to use.
     * @param encryptedDataParam The encrypted data.
     * @return The HMAC result.
     */
    public static byte[] hmacSha256(byte[] hMacKeyParam, byte[] encryptedDataParam) {
        try {
            // hmac
            Mac hmac = Mac.getInstance(HMAC_ALGO);
            hmac.init(new SecretKeySpec(hMacKeyParam, HMAC_ALGO));

            return hmac.doFinal(encryptedDataParam);
        }
        //Changed for Java 1.6 compatibility...
        catch (NoSuchAlgorithmException except) {
            throw new FluidClientException("Unable to create HMAC from key. " + except.getMessage(), except,
                    FluidClientException.ErrorCode.AES_256);
        }
        catch (InvalidKeyException except) {
            throw new FluidClientException("Unable to create HMAC from key. " + except.getMessage(), except,
                    FluidClientException.ErrorCode.AES_256);
        }
    }

    /**
     * Generate a derived HMAC from {@code encryptedDataParam} using
     * other params.
     *
     * @param encryptedDataParam The encrypted data to generate HMAC from.
     * @param passwordParam Password (clear text) used to derive the key from.
     * @param saltParam The password salt as passed by the Init request.
     * @param seedParam Seed to be poisoned
     * @return Derived HMAC.
     */
    public static byte[] generateLocalHMAC(
            byte[] encryptedDataParam, String passwordParam, String saltParam, byte[] seedParam){

        byte[] poisonedSeed = poisonBytes(seedParam);

        byte[] passwordSha256 = sha256(
                passwordParam.concat(saltParam).getBytes());

        //Add the seed to the password and SHA-256...
        byte[] derivedKey = sha256(UtilGlobal.addAll(passwordSha256, poisonedSeed));

        return hmacSha256(derivedKey, encryptedDataParam);
    }

    /**
     * Generate a derived HMAC from {@code encryptedDataParam} using {@code keyParam}.
     *
     * @param encryptedDataParam The encrypted data to generate HMAC from.
     * @param keyParam Key used to derive the key from.
     * @param seedParam Seed to be poisoned
     * @return The HMAC for {@code encryptedDataParam}.
     */
    public static byte[] generateLocalHMACForReqToken(
            byte[] encryptedDataParam, byte[] keyParam, byte[] seedParam){

        byte[] poisonedSeed = poisonBytes(seedParam);

        //Add the seed to the password and SHA-256...
        byte[] derivedKey = sha256(UtilGlobal.addAll(keyParam, poisonedSeed));

        return hmacSha256(derivedKey, encryptedDataParam);
    }

    /**
     * Creates a derived version of {@code bytesToPoisonParam}.
     *
     * @param bytesToPoisonParam The bytes to poison.
     * @return The poisoned bytes.
     */
    private static byte[] poisonBytes(byte[] bytesToPoisonParam) {
        if (bytesToPoisonParam == null) {
            return null;
        }

        byte[] returnVal = new byte[bytesToPoisonParam.length];

        for (int index = 0; index < bytesToPoisonParam.length; index++) {
            byte poisoned = (byte) (bytesToPoisonParam[index] ^ 222);

            returnVal[index] = poisoned;
        }

        return returnVal;
    }

    /**
     * Decrypts the encrypted data.
     *
     * @param encryptedDataParam The Base64 data to decrypt.
     * @param passwordParam Password in the clear.
     * @param saltParam The password salt as passed by the Init request.
     * @param ivParam IV value used during packet encryption
     * @param seedParam The random seed.
     * @return Decrypted bytes / Clear text.
     */
    public static byte[] decryptInitPacket(
            byte[] encryptedDataParam,
            String passwordParam,
            String saltParam,
            byte[] ivParam,
            byte[] seedParam){

        //Stored like this in the database, so we have to get the password as stored in the database so that the
        // SHa256 and SALT combination will be valid...
        byte[] passwordSha256 = sha256(passwordParam.concat(saltParam).getBytes());

        //Add the seed to the password and SHA-256...
        byte[] derivedKey = sha256(UtilGlobal.addAll(passwordSha256, seedParam));

        //Decrypt with the derived key.
        return decrypt(derivedKey, encryptedDataParam, ivParam);
    }

    /**
     * Decrypt {@code dataToDecryptParam} using the {@code keyParam} key.
     *
     * @param keyParam The symmetric key.
     * @param dataToDecryptParam The encrypted data to decrypt.
     * @param ivParam The random IV.
     * @return Clear text.
     */
    public static byte[] decrypt(byte[] keyParam, byte[] dataToDecryptParam, byte[] ivParam) {

        Key key = new SecretKeySpec(keyParam, KEY_ALGO);

        try {
            Cipher cipher = Cipher.getInstance(ALGO_CBC);
            cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ivParam));

            return cipher.doFinal(dataToDecryptParam);
        }
        //Changed for Java 1.6 compatibility...
        catch (InvalidKeyException except) {

            throw new FluidClientException("Key: Unable to decrypt data. " +
                    except.getMessage(), except, FluidClientException.ErrorCode.AES_256);
        } catch (InvalidAlgorithmParameterException except) {

            throw new FluidClientException("Algo: Unable to decrypt data. " +
                    except.getMessage(), except, FluidClientException.ErrorCode.AES_256);
        } catch (IllegalBlockSizeException except) {

            throw new FluidClientException("Block: Unable to decrypt data. " +
                    except.getMessage(), except, FluidClientException.ErrorCode.AES_256);
        } catch (NoSuchPaddingException except) {

            throw new FluidClientException("NoPadding: Unable to decrypt data. " +
                    except.getMessage(), except, FluidClientException.ErrorCode.AES_256);
        } catch (NoSuchAlgorithmException except) {

            throw new FluidClientException("NoAlgo: Unable to decrypt data. " +
                    except.getMessage(), except, FluidClientException.ErrorCode.AES_256);
        } catch (BadPaddingException except) {

            throw new FluidClientException("BadPadding: Unable to decrypt data. " +
                    except.getMessage(), except, FluidClientException.ErrorCode.AES_256);
        }
    }

    /**
     * Encrypts the {@code dataToEncryptParam} data using key {@code keyParam}.
     *
     * @param keyParam Key to encrypt with.
     * @param dataToEncryptParam Data to encrypt.
     * @param ivParam - 16 bytes for AES-256.
     * @return Cipher text.
     */
    public static byte[] encrypt(byte[] keyParam, byte[] dataToEncryptParam, byte[] ivParam) {
        if (dataToEncryptParam == null) {
            throw new FluidClientException("No data to encrypt provided. ",
                    FluidClientException.ErrorCode.AES_256);
        }

        Key key = new SecretKeySpec(keyParam, KEY_ALGO);

        try {
            Cipher cipher = Cipher.getInstance(ALGO_CBC);

            cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(ivParam));

            return cipher.doFinal(dataToEncryptParam);
        }
        //Changed for Java 1.6 compatibility...
        catch (InvalidKeyException except) {
            throw new FluidClientException("Key: Unable to encrypt data. " + except.getMessage(), except,
                    FluidClientException.ErrorCode.AES_256);
        } catch (InvalidAlgorithmParameterException except) {
            throw new FluidClientException("Algo: Unable to encrypt data. " + except.getMessage(), except,
                    FluidClientException.ErrorCode.AES_256);
        } catch (IllegalBlockSizeException except) {
            throw new FluidClientException("Block: Unable to encrypt data. " + except.getMessage(), except,
                    FluidClientException.ErrorCode.AES_256);
        } catch (NoSuchPaddingException except) {
            throw new FluidClientException("NoPadding: Unable to encrypt data. " + except.getMessage(), except,
                    FluidClientException.ErrorCode.AES_256);
        } catch (NoSuchAlgorithmException except) {
            throw new FluidClientException("NoAlgo: Unable to encrypt data. " + except.getMessage(), except,
                    FluidClientException.ErrorCode.AES_256);
        } catch (BadPaddingException except) {
            throw new FluidClientException("BadPadding: Unable to encrypt data. " + except.getMessage(), except,
                    FluidClientException.ErrorCode.AES_256);
        }
    }

    /**
     * Compute SHA256 digest from {@code dataParam}.
     *
     * @param dataParam The data to SHA-256.
     * @return SHA256 digest.
     */
    public static byte[] sha256(final byte[] dataParam) {
        if (dataParam == null || dataParam.length == 0) {
            return new byte[] {};
        }

        try {
            final MessageDigest digest = MessageDigest.getInstance("SHA-256");
            return digest.digest(dataParam);
        }
        //
        catch (final NoSuchAlgorithmException e) {
            throw new IllegalStateException(e);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy