net.alegen.datpass.library.crypto.CryptoManager.java Source code

Java tutorial

Introduction

Here is the source code for net.alegen.datpass.library.crypto.CryptoManager.java

Source

/*
 * EasyHash - command line utility for creating hashes
 * Copyright (C) 2013 - 2014 Alexandru Geana (alegen)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package net.alegen.datpass.library.crypto;

import java.io.UnsupportedEncodingException;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.KeySpec;
import java.util.Random;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

import net.alegen.datpass.library.configure.XMLConfigurator.XmlConstants;

import org.apache.commons.codec.binary.Base64;
import org.jdom2.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CryptoManager {

    private static final int DEFAULT_ITERATIONS = 10000;
    private static final int AES_KEY_LENGTH = 256;
    private static final Logger log = LoggerFactory.getLogger(CryptoManager.class);
    private static CryptoManager instance = null;

    public static CryptoManager getInstance() {
        if (instance == null)
            instance = new CryptoManager();
        return instance;
    }

    public SecretKey derivateKey(KeyDerivationFunctions function, String password, byte[] salt, int length,
            int iterations) {
        try {
            SecretKeyFactory factory = SecretKeyFactory.getInstance(function.toString());
            KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iterations, length);
            return factory.generateSecret(spec);
        } catch (NoSuchAlgorithmException e) {
            log.error("The required algorithm is not supported by the current JVM.");
            return null;
        } catch (InvalidKeySpecException e) {
            log.error("The key spec is invalid.");
            return null;
        }
    }

    public String decrypt(EncryptionOutput encryptionOutput, String password) {
        final String ctext = encryptionOutput.getCypherText();
        final String salt = encryptionOutput.getSalt();
        final String iv = encryptionOutput.getIV();
        try {
            // set up the decryption key
            byte[] saltBytes = Base64.decodeBase64(salt.getBytes("UTF-8"));
            SecretKey key = this.derivateKey(KeyDerivationFunctions.PBKDF2_HMAC_SHA1, password, saltBytes,
                    AES_KEY_LENGTH, DEFAULT_ITERATIONS);
            key = new SecretKeySpec(key.getEncoded(), "AES");

            // decrypt cipher text with AES using generated key
            byte[] cipherBytes = Base64.decodeBase64(ctext.getBytes("UTF-8"));
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            byte[] iVBytes = Base64.decodeBase64(iv.getBytes("UTF-8"));
            cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iVBytes));
            byte[] plaintext = cipher.doFinal(cipherBytes);

            // return plaintext
            return new String(plaintext, "UTF-8");
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException
                | BadPaddingException | UnsupportedEncodingException | InvalidAlgorithmParameterException e) {
            log.error("An error occured while decrypting the message.");
            return null;
        }
    }

    public EncryptionOutput encrypt(String plaintext, String password) {
        try {
            // set up the encryption key
            Random r = new Random(System.currentTimeMillis());
            byte[] salt = new byte[50];
            r.nextBytes(salt);
            SecretKey key = this.derivateKey(KeyDerivationFunctions.PBKDF2_HMAC_SHA1, password, salt,
                    AES_KEY_LENGTH, DEFAULT_ITERATIONS);
            salt = Base64.encodeBase64(salt);
            key = new SecretKeySpec(key.getEncoded(), "AES");

            // encrypt plain text with AES using generated key         
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, key);
            AlgorithmParameters params = cipher.getParameters();
            byte[] iv = Base64.encodeBase64(params.getParameterSpec(IvParameterSpec.class).getIV());
            byte[] ciphertext = Base64.encodeBase64(cipher.doFinal(plaintext.getBytes("UTF-8")));

            // package output and return
            return new EncryptionOutput(new String(ciphertext, "UTF-8"), new String(salt, "UTF-8"),
                    new String(iv, "UTF-8"));
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException
                | BadPaddingException | UnsupportedEncodingException | InvalidParameterSpecException e) {
            log.error("An error occured while encrypting the message.");
            return null;
        }
    }

    public String generateHmac(EncryptionOutput encryptionOutput, String password) {
        final String ctext = encryptionOutput.getCypherText();
        final String salt = encryptionOutput.getSalt();
        final String iv = encryptionOutput.getIV();
        try {
            // create the hmac key and message
            MessageDigest md = MessageDigest.getInstance("SHA-512");
            byte[] hmacKey = md.digest(password.getBytes("UTF-8"));
            byte[] hmacMessage = (iv + ctext + salt).getBytes("UTF-8");

            // generate hmac
            SecretKeySpec keySpec = new SecretKeySpec(hmacKey, "HmacSHA1");
            Mac mac = Mac.getInstance("HmacSHA1");
            mac.init(keySpec);
            byte[] result = mac.doFinal(hmacMessage);

            // return result
            result = Base64.encodeBase64(result);
            return new String(result, "UTF-8");
        } catch (NoSuchAlgorithmException | InvalidKeyException | UnsupportedEncodingException e) {
            log.error("An error occured while generating an HMAC value.");
            return null;
        }
    }

    public static class EncryptionOutput {

        private String cypherText;
        private String salt;
        private String iv;

        public EncryptionOutput(String cypherText, String salt, String iv) {
            this.cypherText = cypherText;
            this.salt = salt;
            this.iv = iv;
        }

        public EncryptionOutput(Element element) {
            this.cypherText = element.getChild(XmlConstants.CIPHERTEXT).getTextNormalize();
            this.salt = element.getChild(XmlConstants.ENC_SALT).getTextNormalize();
            this.iv = element.getChild(XmlConstants.ENC_IV).getTextNormalize();
        }

        public String getCypherText() {
            return this.cypherText;
        }

        public String getSalt() {
            return this.salt;
        }

        public String getIV() {
            return this.iv;
        }
    }
}