/* * Rapid Beans Framework: CryptoHelper.java * * Copyright (C) 2009 Martin Bluemel * * Creation Date: 06/07/2008 * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU Lesser 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 Lesser General Public License for more details. * You should have received a copies of the GNU Lesser General Public License and the * GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>. */ package org.rapidbeans.security; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.security.DigestInputStream; import java.security.GeneralSecurityException; import java.security.Key; import java.security.MessageDigest; import java.util.Arrays; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.CipherOutputStream; import javax.crypto.spec.DESedeKeySpec; import javax.crypto.spec.SecretKeySpec; import org.rapidbeans.core.exception.RapidBeansRuntimeException; import org.rapidbeans.presentation.ApplicationManager; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; /** * Simple encryption and decryption. * * @author Martin Bluemel */ public class CryptoHelper { private static final BASE64Encoder BENC = new BASE64Encoder(); private static final BASE64Decoder BDEC = new BASE64Decoder(); private static final int BUFSIZE = 8192; private static final int HASH_LEN = 20; private static final String HASH_ALG = "SHA1"; private static final String ENC_ALG = "TripleDES"; /** * Encrypt a single string. * * @param sIn * the string to encrypt * * @return the encrypted string */ public static String encrypt(final String sIn) { return encrypt(sIn, getDefaultKeyPhrase()); } /** * Encrypt a single string. * * @param sIn * the string to encrypt * @param keyPhrase * the keyPhrase * * @return the encrypted string */ public static String encrypt(final String sIn, final String keyPhrase) { try { final MessageDigest md = MessageDigest.getInstance(HASH_ALG); final DigestInputStream digestIn = new DigestInputStream(new ByteArrayInputStream(sIn.getBytes()), md); final byte[] buffer = new byte[BUFSIZE]; while (digestIn.read(buffer) != -1) ; digestIn.close(); final Cipher cipher = getCipher(true, keyPhrase); final ByteArrayInputStream srcStream = new ByteArrayInputStream(sIn.getBytes()); final ByteArrayOutputStream out = new ByteArrayOutputStream(); final CipherOutputStream cipherStream = new CipherOutputStream(out, cipher); cipherStream.write(md.digest()); int len; while ((len = srcStream.read(buffer)) != -1) cipherStream.write(buffer, 0, len); srcStream.close(); cipherStream.close(); return BENC.encode(out.toByteArray()); } catch (IOException e) { throw new RapidBeansRuntimeException(e); } catch (GeneralSecurityException e) { throw new RapidBeansRuntimeException(e); } } /** * Decrypt a string. * * @param keyPhrase * the key phrase * * @return the decrypted string */ public static String decrypt(final String sIn) { return decrypt(sIn, getDefaultKeyPhrase()); } /** * Decrypt a string. * * @param keyPhrase * the key phrase * @param sIn * the string to decrypt * * @return the decrypted string */ public static String decrypt(final String sIn, final String keyPhrase) { try { final byte[] originalHash = new byte[HASH_LEN]; final Cipher cipher = getCipher(false, keyPhrase); final CipherInputStream cipherStream = new CipherInputStream(new ByteArrayInputStream( BDEC.decodeBuffer(sIn)), cipher); cipherStream.read(originalHash); final MessageDigest md = MessageDigest.getInstance(HASH_ALG); final DigestInputStream digestStream = new DigestInputStream(cipherStream, md); final ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] buffer = new byte[BUFSIZE]; int len; while ((len = digestStream.read(buffer)) != -1) out.write(buffer, 0, len); digestStream.close(); // final byte[] computedHash = md.digest(); // if (!(Arrays.equals(originalHash, computedHash))) { // throw new RapidBeansRuntimeException("hashs do not equal"); // } return out.toString(); } catch (IOException e) { throw new RapidBeansRuntimeException(e); } catch (GeneralSecurityException e) { throw new RapidBeansRuntimeException(e); } } private static String getDefaultKeyPhrase() { if (ApplicationManager.getApplication() != null) { return ApplicationManager.getApplication().getClass().getName(); } return "1Q2w3E4r"; } /** * * @param keyPhrase * @param srcFileName * @param dstFileName * @return * @throws IOException * @throws GeneralSecurityException */ public static void encryptFile(final String keyPhrase, final String srcFileName, final String dstFileName) throws IOException, GeneralSecurityException { final MessageDigest md; final CipherOutputStream cipherStream; // MessageDigest-Exemplar erzeugen md = MessageDigest.getInstance(HASH_ALG); // Einen Stream mit diesem Exemplar erzeugen final DigestInputStream digestStream = new DigestInputStream(new FileInputStream(srcFileName), md); // Datei einmal einlesen, um den Hash zu berechnen byte[] buffer = new byte[BUFSIZE]; while (digestStream.read(buffer) != -1) ; digestStream.close(); // Hash abrufen byte[] hash = md.digest(); // Cipher-Objekt initialisieren Cipher cipher = getCipher(true, keyPhrase); final FileInputStream srcStream = new FileInputStream(srcFileName); // Cipher-Stream ueber einem FileOutputStream erzeugen cipherStream = new CipherOutputStream(new FileOutputStream(dstFileName), cipher); // Hash verschluesselt in die Datei schreiben cipherStream.write(hash); int len; // Daten aus der Quelldatei in den Cipher-Stream schreiben while ((len = srcStream.read(buffer)) != -1) cipherStream.write(buffer, 0, len); srcStream.close(); cipherStream.close(); } /** * Decrypt a file. * * @param keyPhrase * @param srcFileName * @param dstFileName * @return * @throws IOException * @throws GeneralSecurityException */ public static boolean decryptFile(final String keyPhrase, final String srcFileName, final String dstFileName) throws IOException, GeneralSecurityException { CipherInputStream cipherStream; FileOutputStream dstStream; MessageDigest md; DigestInputStream digestStream; byte[] originalHash = new byte[HASH_LEN]; byte[] computedHash = new byte[HASH_LEN]; Cipher cipher = getCipher(false, keyPhrase); cipherStream = new CipherInputStream(new FileInputStream(srcFileName), cipher); cipherStream.read(originalHash); md = MessageDigest.getInstance(HASH_ALG); digestStream = new DigestInputStream(cipherStream, md); dstStream = new FileOutputStream(dstFileName); byte[] buffer = new byte[BUFSIZE]; int len; while ((len = digestStream.read(buffer)) != -1) dstStream.write(buffer, 0, len); digestStream.close(); dstStream.close(); computedHash = md.digest(); return Arrays.equals(originalHash, computedHash); } /** * create a cipher for symmetric encryption or decryption. * * @param encrypt * determines if the cipher should be used for encryption or * decryption * @param keyPhrase * the key phrase * @return the cipher * @throws IOException * if IO fails * @throws GeneralSecurityException * if a general security problem occurs */ protected static Cipher getCipher(final boolean encrypt, final String keyPhrase) throws IOException, GeneralSecurityException { byte[] rawKey = getSymmetricKey(keyPhrase); final Key key = new SecretKeySpec(new DESedeKeySpec(rawKey).getKey(), ENC_ALG); final Cipher cipher = Cipher.getInstance(ENC_ALG); if (encrypt) { cipher.init(Cipher.ENCRYPT_MODE, key); } else { cipher.init(Cipher.DECRYPT_MODE, key); } return cipher; } /** * Compute hash and prepare key. * * @param keyPhrase * the key phrase * @return a byte array that serves a symmetric key for encryption or * decryption * @throws IOException * in case of IO problems * @throws GeneralSecurityException * in case of problems in the security library */ protected static byte[] getSymmetricKey(final String keyPhrase) throws IOException, GeneralSecurityException { MessageDigest md = MessageDigest.getInstance(HASH_ALG); byte[] digest = md.digest(keyPhrase.getBytes()); byte[] rawKey = new byte[24]; System.arraycopy(digest, 0, rawKey, 0, 8); System.arraycopy(digest, 8, rawKey, 8, 8); System.arraycopy(digest, 0, rawKey, 16, 8); return rawKey; } }