Java tutorial
/* * 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; } } }