/*
* ProActive Parallel Suite(TM):
* The Open Source library for parallel and distributed
* Workflows & Scheduling, Orchestration, Cloud Automation
* and Big Data Analysis on Enterprise Grids & Clouds.
*
* Copyright (c) 2007 - 2017 ActiveEon
* Contact: contact@activeeon.com
*
* This library 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: version 3 of
* the License.
*
* 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/>.
*
* If needed, contact us to obtain a release under GPL Version 2 or 3
* or a different license than the AGPL.
*/
package org.ow2.proactive.authentication.crypto;
import java.io.UnsupportedEncodingException;
import java.security.KeyException;
import java.security.PrivateKey;
import java.security.PublicKey;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
/**
* Encrypt data with a symmetric key that is asymmetrically encrypted.
*/
public class HybridEncryptionUtil {
private static final String ENCRYPTED_STRING_CHARSET = "UTF-8";
/** symmetric encryption parameters */
// more than that breaks with default provider / config, it *is* secure nonetheless
private static final int AES_KEYSIZE = 128;
// should work fine with default providers
private static final String AES_ALGO = "AES";
// funny transformations require initial vector parameters, try to avoid them
private static final String AES_CIPHER = "AES";
private static final String STRING_ENCRYPTION_CIPHER = "RSA/ECB/PKCS1Padding";
public static byte[] decrypt(PrivateKey privateKey, String cipher, HybridEncryptedData encryptedData)
throws KeyException {
byte[] decryptedData;
byte[] decryptedSymmetricKey;
// recover clear AES key using the private key
try {
decryptedSymmetricKey = KeyPairUtil.decrypt(privateKey, cipher, encryptedData.getEncryptedSymmetricKey());
} catch (KeyException e) {
throw new KeyException("Could not decrypt symmetric key", e);
}
// recover clear credentials using the AES key
try {
decryptedData = KeyUtil.decrypt(new SecretKeySpec(decryptedSymmetricKey, AES_ALGO),
AES_CIPHER,
encryptedData.getEncryptedData());
} catch (KeyException e) {
throw new KeyException("Could not decrypt data", e);
}
return decryptedData;
}
public static HybridEncryptedData encrypt(PublicKey publicKey, String cipher, byte[] message) throws KeyException {
// generate symmetric key
SecretKey aesKey = KeyUtil.generateKey(AES_ALGO, AES_KEYSIZE);
byte[] encData;
byte[] encAes;
// encrypt AES key with public RSA key
try {
encAes = KeyPairUtil.encrypt(publicKey, cipher, aesKey.getEncoded());
} catch (KeyException e) {
throw new KeyException("Symmetric key encryption failed", e);
}
// encrypt clear credentials with AES key
try {
encData = KeyUtil.encrypt(aesKey, AES_CIPHER, message);
} catch (KeyException e) {
throw new KeyException("Message encryption failed", e);
}
return new HybridEncryptedData(encAes, encData);
}
public static String decryptString(HybridEncryptedData encryptedData, PrivateKey privateKey) throws KeyException {
try {
return new String(decrypt(privateKey, STRING_ENCRYPTION_CIPHER, encryptedData), ENCRYPTED_STRING_CHARSET);
} catch (UnsupportedEncodingException ignored) {
return null; // never happens, we control charset value
}
}
public static HybridEncryptedData encryptString(String value, PublicKey publicKey) throws KeyException {
try {
byte[] valueAsBytes = value.getBytes(ENCRYPTED_STRING_CHARSET);
return encrypt(publicKey, STRING_ENCRYPTION_CIPHER, valueAsBytes);
} catch (UnsupportedEncodingException ignored) {
return null; // never happens, we control charset value
}
}
public static String decryptBase64String(String encryptedString, PrivateKey privateKey, String separator)
throws KeyException {
String[] encryptedStrings = encryptedString.split(separator);
HybridEncryptedData encryptedData = new HybridEncryptedData(Base64.decodeBase64(encryptedStrings[1]),
Base64.decodeBase64(encryptedStrings[0]));
return decryptString(encryptedData, privateKey);
}
public static String encryptStringToBase64(String value, PublicKey publicKey, String separator)
throws KeyException {
HybridEncryptedData data = encryptString(value, publicKey);
return Base64.encodeBase64String(data.getEncryptedData()) + separator +
Base64.encodeBase64String(data.getEncryptedSymmetricKey());
}
public static class HybridEncryptedData {
private byte[] encryptedData;
private byte[] encryptedSymmetricKey;
public HybridEncryptedData(byte[] encryptedSymmetricKey, byte[] encryptedData) {
this.encryptedSymmetricKey = encryptedSymmetricKey;
this.encryptedData = encryptedData;
}
public byte[] getEncryptedData() {
return encryptedData;
}
public byte[] getEncryptedSymmetricKey() {
return encryptedSymmetricKey;
}
}
}