/*!
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* 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.
*
* Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.reporting.libraries.base.util;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.spec.KeySpec;
public class SecurePasswordEncryption {
private static final String[] byteToText;
private static final byte[] SALT = { 0x12, 0x56, (byte) 0x89, (byte) 0xFE, (byte) 0xAB, (byte) 0xC5, 0x7F, 0x01 };
static {
byteToText = new String[ 256 ];
for ( int i = 0; i < 16; i++ ) {
byteToText[ i ] = '0' + Integer.toHexString( i );
}
for ( int i = 16; i < 256; i++ ) {
byteToText[ i ] = Integer.toHexString( i );
}
}
public String encryptPassword( final String password, final String key )
throws GeneralSecurityException, UnsupportedEncodingException {
final SecretKeyFactory factory = SecretKeyFactory.getInstance( "PBKDF2WithHmacSHA1" );
final KeySpec spec = new PBEKeySpec( key.toCharArray(), SALT, 1024, 128 );
final SecretKey tmp = factory.generateSecret( spec );
final SecretKey secret = new SecretKeySpec( tmp.getEncoded(), "AES" );
return encryptPassword( password, secret, "AES/CBC/PKCS5PADDING" );
}
public String encryptPassword( final String password, final SecretKey key, final String algorithm )
throws GeneralSecurityException, UnsupportedEncodingException {
final Cipher c = Cipher.getInstance( algorithm );
c.init( Cipher.ENCRYPT_MODE, key );
final byte[] encryptedData = c.doFinal( password.getBytes( "UTF-8" ) );
final StringBuffer b = new StringBuffer();
final byte[] iv = c.getIV();
appendAsHexString( intToByte( iv.length ), b );
appendAsHexString( iv, b );
appendAsHexString( intToByte( encryptedData.length ), b );
appendAsHexString( encryptedData, b );
return b.toString();
}
protected static void appendAsHexString( final byte[] encryptedData, final StringBuffer b ) {
for ( int i = 0; i < encryptedData.length; i++ ) {
final int b1 = ( 0xFF & ( encryptedData[ i ] ) );
b.append( byteToText[ b1 ] );
}
}
protected static byte[] intToByte( final int value ) {
final byte[] b = new byte[ 4 ];
b[ 0 ] = (byte) ( ( value >> 24 ) & 0xFF );
b[ 1 ] = (byte) ( ( value >> 16 ) & 0xFF );
b[ 2 ] = (byte) ( ( value >> 8 ) & 0xFF );
b[ 3 ] = (byte) ( ( value ) & 0xFF );
return b;
}
public String decryptPassword( final String password,
final String key ) throws UnsupportedEncodingException, GeneralSecurityException {
final SecretKeyFactory factory = SecretKeyFactory.getInstance( "PBKDF2WithHmacSHA1" );
final KeySpec spec = new PBEKeySpec( key.toCharArray(), SALT, 1024, 128 );
final SecretKey tmp = factory.generateSecret( spec );
final SecretKey secret = new SecretKeySpec( tmp.getEncoded(), "AES" );
return decryptPassword( password, secret, "AES/CBC/PKCS5PADDING" );
}
public String decryptPassword( final String password, final SecretKey key, final String algorithm )
throws UnsupportedEncodingException, GeneralSecurityException {
final byte[] b = stringToBytes( password );
if ( b == null ) {
return null;
}
final int ivLength = bytesToInt( b, 0 );
final byte[] iv = new byte[ ivLength ];
System.arraycopy( b, 4, iv, 0, ivLength );
final int dataLength = bytesToInt( b, 4 + ivLength );
final byte[] data = new byte[ dataLength ];
System.arraycopy( b, 8 + ivLength, data, 0, dataLength );
final Cipher c = Cipher.getInstance( algorithm );
c.init( Cipher.DECRYPT_MODE, key, new IvParameterSpec( iv ) );
final byte[] decryptedData = c.doFinal( data );
return new String( decryptedData, "UTF-8" );
}
protected static byte[] stringToBytes( final String password )
throws UnsupportedEncodingException {
final char[] chars = password.toCharArray();
if ( ( chars.length % 2 ) != 0 ) {
return null;
}
final byte[] b = new byte[ chars.length / 2 ];
for ( int i = 0; i < b.length; i++ ) {
final int c1 = PasswordObscurification.charToHex( chars[ i * 2 ] );
final int c2 = PasswordObscurification.charToHex( chars[ i * 2 + 1 ] );
final int encodedByte = ( c1 ) * 16 + c2;
final int encByte = ( 0xFF & encodedByte );
b[ i ] = (byte) ( encByte );
}
return b;
}
protected static int bytesToInt( final byte[] data, final int offset ) {
int retval = 0;
retval |= ( ( data[ offset ] & 0xFF ) << 24 );
retval |= ( ( data[ offset + 1 ] & 0xFF ) << 16 );
retval |= ( ( data[ offset + 2 ] & 0xFF ) << 8 );
retval |= ( ( data[ offset + 3 ] & 0xFF ) );
return retval;
}
}