/**
* Copyright (C) 2001-2017 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.com
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU Affero 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
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.tools.cipher;
import com.rapidminer.io.Base64;
//
// import sun.misc.BASE64Decoder;
// import sun.misc.BASE64Encoder;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.xml.bind.DatatypeConverter;
/**
* This class can be used to encrypt and decrypt given strings based on a key generated by the user.
* Please note that classes using this tool class should first ensure that a user key is available
* by invoking the method isKeyAvailable().
*
* @author Ingo Mierswa, Marco Boeck
*/
@SuppressWarnings("deprecation")
public class CipherTools {
/** algorithm used to encrypt/decrypt */
private static final String CIPHER_TYPE = "DESede";
/** String used to flag new encoded passwords */
private static final String FLAG_NEW_ENCODE = "_";
/** the minimum length of string to encode to Base64 before 6.0.003 */
private static final int FORMER_MINLENGTH_TO_ENCODE = 4;
/**
* Returns whether an encryption {@link Key} is available or not.
*
* @return <code>true</code> if a cipher {@link Key} is available; <code>false</code> otherwise
*/
public static boolean isKeyAvailable() {
try {
KeyGeneratorTool.getUserKey();
return true;
} catch (IOException e) {
return false;
}
}
/**
* Encrypt the given {@link String} with the current {@link KeyGeneratorTool#getUserKey()} and
* encode the result in Base64.
*
* @param text
* @return
* @throws CipherException
*/
public static String encrypt(String text) throws CipherException {
Key key = null;
try {
key = KeyGeneratorTool.getUserKey();
} catch (IOException e) {
throw new CipherException("Cannot retrieve key, probably no one was created: " + e.getMessage());
}
return encrypt(text, key);
}
/**
* Encrypt the given {@link String} with the specified {@link Key} and encode the result in
* Base64.
*
* @param text
* @param key
* @return
* @throws CipherException
*/
public static String encrypt(String text, Key key) throws CipherException {
try {
Cipher cipher = Cipher.getInstance(CIPHER_TYPE);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] outputBytes = cipher.doFinal(text.getBytes());
String base64;
if (text.length() < FORMER_MINLENGTH_TO_ENCODE) {
// use new way to encode strings with less than 4 characters which was impossible
// before 6.0.003
base64 = DatatypeConverter.printBase64Binary(outputBytes) + FLAG_NEW_ENCODE;
} else {
// use old way for full backwards compatibility
base64 = Base64.encodeBytes(outputBytes);
}
return base64;
} catch (NoSuchAlgorithmException e) {
throw new CipherException("Failed to encrypt text: " + e.getMessage());
} catch (NoSuchPaddingException e) {
throw new CipherException("Failed to encrypt text: " + e.getMessage());
} catch (InvalidKeyException e) {
throw new CipherException("Failed to encrypt text: " + e.getMessage());
} catch (IllegalBlockSizeException e) {
throw new CipherException("Failed to encrypt text: " + e.getMessage());
} catch (BadPaddingException e) {
throw new CipherException("Failed to encrypt text: " + e.getMessage());
}
}
/**
* Decrypt the given Base64 encoded and encrypted {@link String} with the current
* {@link KeyGeneratorTool#getUserKey()}.
*
* @param text
* @return
* @throws CipherException
*/
public static String decrypt(String text) throws CipherException {
Key key = null;
try {
key = KeyGeneratorTool.getUserKey();
} catch (IOException e) {
throw new CipherException("Cannot retrieve key, probably no one was created: " + e.getMessage());
}
return decrypt(text, key);
}
/**
* Decrypt the given Base64 encoded and encrypted {@link String} with the specified {@link Key}.
*
* @param text
* @param key
* @return
* @throws CipherException
*/
public static String decrypt(String text, Key key) throws CipherException {
try {
byte[] encrypted;
if (text.endsWith(FLAG_NEW_ENCODE)) {
// encrypted strings with less than 4 characters which have been encrypted with
// version 6.0.003 onwards use are encoded via DataTypeConverter instead
encrypted = DatatypeConverter.parseBase64Binary(text.subSequence(0, text.length() - 1).toString());
} else {
encrypted = Base64.decode(text);
}
Cipher cipher = Cipher.getInstance(CIPHER_TYPE);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] outputBytes = cipher.doFinal(encrypted);
String ret = new String(outputBytes);
return ret;
} catch (NoSuchAlgorithmException e) {
throw new CipherException("Failed to decrypt text: " + e.getMessage());
} catch (NoSuchPaddingException e) {
throw new CipherException("Failed to decrypt text: " + e.getMessage());
} catch (IOException e) {
throw new CipherException("Failed to decrypt text: " + e.getMessage());
} catch (InvalidKeyException e) {
throw new CipherException("Failed to decrypt text: " + e.getMessage());
} catch (IllegalBlockSizeException e) {
throw new CipherException("Failed to decrypt text: " + e.getMessage());
} catch (BadPaddingException e) {
throw new CipherException("Failed to decrypt text: " + e.getMessage());
}
}
}