/*
* © Copyright IBM Corp. 2012-2013
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.ibm.commons.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import com.ibm.commons.log.CommonsLogger;
import com.ibm.commons.log.LogMgr;
import com.ibm.commons.util.io.base64.Base64InputStream;
import com.ibm.commons.util.io.base64.Base64OutputStream;
/**
* Trivial utility class for transient encryption operations.
* This class should not be used for persistent storage of data as the key
* generation and cipher used may be subject to change.
* This class is intended for securing temporary data, such as
* session-related artefacts.
*
* Usage:
* <pre>
Key key = EncryptionUtil.generateKey();
byte[] data = "Hello, World!".getBytes("UTF-8");
byte[] cyphertext = EncryptionUtil.encrypt(key, data);
byte[] plaintext = EncryptionUtil.decrypt(key, cyphertext);
if(data.length!=plaintext.length) {
throw new IllegalStateException();
}
for(int i = 0; i < data.length; i++) {
if(data[i]!=plaintext[i]) {
throw new IllegalStateException();
}
}
* </pre>
*
* This class makes use of the Java cryptography extensions.
*
* @ibm-not-published
*/
public class EncryptionUtil {
private static final KeyGenerator KEY_GENERATOR = createKeyGenerator();
private static final String ERROR_CODE = "ENC"+EncryptionUtil.class.hashCode(); //$NON-NLS-1$
private static final String ALGORITHM = "DESede"; //DESede==Triple-DES //$NON-NLS-1$
private static final String MODE = "ECB"; //$NON-NLS-1$
private static final String PADDING = "PKCS5Padding"; //$NON-NLS-1$
private static final String CIPHER = ALGORITHM + '/' + MODE + '/' + PADDING;
private static final LogMgr log = CommonsLogger.ENCRYPTION;
/**
* Creates a private key generator.
*/
private static final KeyGenerator createKeyGenerator() {
try {
KeyGenerator gen = KeyGenerator.getInstance(ALGORITHM); //$NON-NLS-1$
return gen;
} catch(Exception e) {
try {
if(log.isErrorEnabled()) {
String msg = "{0}: Error creating key generator for algorithm {1}"; // $NLE-EncryptionUtil.0Errorcreatingkeygeneratorforalgo-1$
log.error(e, msg, new Object[]{ERROR_CODE, ALGORITHM});
}
} catch(Exception ex) {
//FAILSAFE
e.printStackTrace();
ex.printStackTrace();
}
return null;
}
}
/**
* Creates a private key.
* @return a new private key for use in symmetrical encryption.
* @throws GeneralSecurityException
*/
public static synchronized Key generateKey() throws GeneralSecurityException {
if(KEY_GENERATOR==null) {
String msg = "Key generator failed to initialize. For cause, search logs for error code {0}."; // $NLS-EncryptionUtil.KeygeneratorfailedtoinitializeFor-1$
msg = StringUtil.format(msg, ERROR_CODE);
throw new GeneralSecurityException(msg);
}
return KEY_GENERATOR.generateKey();
}
private static Cipher createCipher(int cipherMode, Key encryptionKey) throws GeneralSecurityException {
if(encryptionKey==null) {
throw new NullPointerException();
}
Cipher cipher = Cipher.getInstance(CIPHER); //$NON-NLS-1$
if(cipherMode==Cipher.ENCRYPT_MODE) {
cipher.init(cipherMode, encryptionKey);
} else if(cipherMode==Cipher.DECRYPT_MODE) {
cipher.init(cipherMode, encryptionKey);
} else {
throw new IllegalArgumentException(""+cipherMode); //$NON-NLS-1$
}
return cipher;
}
/**
* Encrypts a finite block of data.
* The key should be persisted for decryption.
* @see generateKey()
* @param key a private key for symmetric encryption
* @param data the data to be encrypted
* @return the data in cyphertext form
* @throws GeneralSecurityException
*/
public static byte[] encrypt(Key key, byte[] data) throws GeneralSecurityException {
if(key==null || data==null) {
throw new IllegalArgumentException();
}
Cipher cipher = createCipher(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(data);
}
/**
* Decrypts a finite block of data.
* @see generateKey()
* @param key the private key used to encrypt the data
* @param encryptedData the encrypted data
* @return the data in plaintext form
* @throws GeneralSecurityException
*/
public static byte[] decrypt(Key key, byte[] encryptedData) throws GeneralSecurityException {
if(key==null || encryptedData==null) {
throw new IllegalArgumentException();
}
Cipher cipher = createCipher(Cipher.DECRYPT_MODE, key);
return cipher.doFinal(encryptedData);
}
/*public static void main(String[] args) throws Exception {
String test = "Hello, World!";
{
Key key = generateKey();
byte[] data = test.getBytes("UTF-8");
byte[] cyphertext = encrypt(key, data);
byte[] plaintext = decrypt(key, cyphertext);
if(data.length!=plaintext.length) {
throw new IllegalStateException();
}
for(int i=0; i<data.length; i++) {
if(data[i]!=plaintext[i]) {
throw new IllegalStateException();
}
}
System.out.println("OK");
}
{
Key key = generateKey();
System.out.println(test);
String cypherText = encryptString(key, test);
System.out.println(cypherText);
String plainText = decryptString(key, cypherText);
System.out.println(plainText);
if(!test.equals(plainText)) {
throw new IllegalStateException();
}
}
}*/
/**
* Encrypts a string.
*/
public static String encryptString(Key key, String plaintext) throws GeneralSecurityException {
if(key==null || plaintext==null) {
throw new NullPointerException();
}
try {
byte[] data = plaintext.getBytes("UTF-8"); //$NON-NLS-1$
byte[] cyphertext = encrypt(key, data);
ByteArrayOutputStream barrout = new ByteArrayOutputStream(cyphertext.length*3/2);
Base64OutputStream base64 = new Base64OutputStream(barrout);
base64.write(cyphertext);
base64.flush();
barrout.flush();
//Base64 is ASCII safe
String ret = barrout.toString("ASCII"); //$NON-NLS-1$
return ret;
} catch(IOException e) {
throw new RuntimeException(e);
}
}
/**
* Decrypts a string encrypted with encryptString(key, plaintext).
*/
public static String decryptString(Key key, String cyphertext) throws GeneralSecurityException {
if(key==null || cyphertext==null) {
throw new NullPointerException();
}
try {
byte[] base64 = cyphertext.getBytes("ASCII"); //$NON-NLS-1$
ByteArrayInputStream bin = new ByteArrayInputStream(base64);
Base64InputStream base64in = new Base64InputStream(bin);
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
byte[] barr = new byte[cyphertext.length()];
while(true) {
int r = base64in.read(barr);
if(r<=0) break;
buffer.write(barr, 0, r);
}
byte[] encrypted = buffer.toByteArray();
byte[] plaintext = decrypt(key, encrypted);
String ret = new String(plaintext, "UTF-8"); //$NON-NLS-1$
return ret;
} catch(IOException e) {
throw new RuntimeException(e);
}
}
}