/////////////////////////////////////////////////////////////////////////////
//
// Project ProjectForge Community Edition
// www.projectforge.org
//
// Copyright (C) 2001-2014 Kai Reinhard (k.reinhard@micromata.de)
//
// ProjectForge is dual-licensed.
//
// This community edition 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; version 3 of the License.
//
// This community edition 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 org.projectforge.common;
import java.io.UnsupportedEncodingException;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.Logger;
/**
*
* @author Wolfgang Jung (W.Jung@micromata.de)
* @author Kai Reinhard (k.reinhard@micromata.de)
*
*/
public class Crypt
{
private final static Logger log = Logger.getLogger(Crypt.class);
private static final String CRYPTO_ALGORITHM = "AES/ECB/PKCS5Padding";
private static boolean initialized;
/**
* Encrypts the given str with AES. The password is first converted using SHA-256.
* @param password
* @param str
* @return The base64 encoded result (url safe).
*/
public static String encrypt(final String password, final String data)
{
initialize();
try {
// AES is sometimes not part of Java, therefore use bouncy castle provider:
final Cipher cipher = Cipher.getInstance(CRYPTO_ALGORITHM);
final byte[] keyValue = getPassword(password);
final Key key = new SecretKeySpec(keyValue, "AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
final byte[] encVal = cipher.doFinal(data.getBytes("UTF-8"));
final String encryptedValue = Base64.encodeBase64URLSafeString(encVal);
return encryptedValue;
} catch (final Exception ex) {
log.error("Exception encountered while trying to encrypt with Algorithm 'AES' and the given password: " + ex.getMessage(), ex);
return null;
}
}
/**
* @param password
* @param encryptedString
* @return
*/
public static String decrypt(final String password, final String encryptedString)
{
initialize();
try {
final Cipher cipher = Cipher.getInstance(CRYPTO_ALGORITHM);
final byte[] keyValue = getPassword(password);
final Key key = new SecretKeySpec(keyValue, "AES");
cipher.init(Cipher.DECRYPT_MODE, key);
final byte[] decordedValue = Base64.decodeBase64(encryptedString);
final byte[] decValue = cipher.doFinal(decordedValue);
final String decryptedValue = new String(decValue, "UTF-8");
return decryptedValue;
} catch (final Exception ex) {
log.error("Exception encountered while trying to encrypt with Algorithm 'AES' and the given password: " + ex.getMessage(), ex);
return null;
}
}
private static byte[] getPassword(final String password)
{
try {
final MessageDigest digester = MessageDigest.getInstance("MD5"); // 128 bit. 256 bit (SHA-256) doesn't work on Java versions without required security policy.
digester.update(password.getBytes("UTF-8"));
final byte[] key = digester.digest();
return key;
} catch (final NoSuchAlgorithmException ex) {
log.error("Exception encountered while trying to create a MD5 password: " + ex.getMessage(), ex);
return null;
} catch (final UnsupportedEncodingException ex) {
log.error("Exception encountered while trying to get bytes in UTF-8: " + ex.getMessage(), ex);
return null;
}
}
private static void initialize() {
synchronized (log) {
if (initialized == false) {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
initialized = true;
}
}
}
/**
* Encrypts the given String via SHA crypt algorithm.
* @param s
* @return
*/
public static String digest(final String s)
{
return encode(s, "SHA");
}
public static String digest(final String s, final String alg)
{
return encode(s, alg);
}
public static boolean check(final String pass, final String encoded)
{
final String alg = encoded.substring(0, encoded.indexOf('{'));
return encoded.equals(encode(pass, alg));
}
private static String encode(final String s, final String alg)
{
try {
final MessageDigest md = MessageDigest.getInstance(alg);
md.reset();
md.update(s.getBytes());
final byte[] d = md.digest();
String ret = "";
for (int val : d) {
final char[] hex = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
if (val < 0) {
val = 256 + val;
}
final char hi = hex[val / 16];
final char lo = hex[val % 16];
ret = hi + "" + lo + ret;
}
return md.getAlgorithm() + '{' + ret + '}';
} catch (final NoSuchAlgorithmException ex) {
log.fatal(ex);
return "NONE{" + s + "}";
}
}
}