I have a basic mis-understanding on how to encrypt/decrypt information.
I am using the class shown below.
For a given value and two different keys, I am getting the same
encrypted string. Yet I should be getting two different encrypted
strings since the keys are different, even though the value is the same.
For instance:
key = 5
value = abcd
encrypted = x765tra=
key = 4
value = abcd
encrypted = x765tra=
Any help would be appreciated.
-------------------------------------------
package ca.mycomp.utility.tools.encryption;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import ca.mycomp.utility.exceptions.DecryptionException;
import ca.mycomp.utility.exceptions.EncryptionException;
import ca.mycomp.utility.exceptions.EncryptionInitializationException;
import com.sun.crypto.provider.SunJCE;
public class Encryption
{
// DO NOT change these. They are saved in the database. Changes WILL
make the data unreadable!
private static final String ENCODING_SCHEME = "UTF8";
private static final String ENCRYPTION_SCHEME= "DES";
private static final int MIN_ENCRYPT_LENGTH = 10;
// must be at least MIN_ENCRYPT_LENGTH long
private static final String ENCRYPT_PADDING = "a6kskdkr56xcv3";
private static final int PADDING_LENGTH = ENCRYPT_PADDING.length();
// DO NOT change these. They are saved in the database. Changes WILL
make the data unreadable!
private boolean ivIsUsable = false;
private Cipher ivEncryptCipher;
private Cipher ivDecryptCipher;
private SecretKey ivKey;
private static Provider cvProvider;
private static boolean cvIsInitialized = false;
public Encryption( long key ) throws EncryptionInitializationException
{
this( "" + key );
}
public Encryption( String key ) throws EncryptionInitializationException
{
super();
setup( key );
}
/**
* This is only run once for all instances
*
*/
private static synchronized void initialize()
{
if (!cvIsInitialized)
{
cvProvider = new SunJCE();
Security.addProvider( cvProvider );
cvIsInitialized = true;
}
}
private void setup( String keyIn ) throws EncryptionInitializationException
{
String encryptKey;
KeySpec keySpec;
SecretKeyFactory secretKey;
byte[] keyBytes;
initialize();
try
{
// DO NOT change these. They are saved in the database. Changes WILL
make the data unreadable!
if (keyIn.length() < MIN_ENCRYPT_LENGTH)
encryptKey = keyIn + ENCRYPT_PADDING.substring( 0, PADDING_LENGTH -
keyIn.length() );
else
encryptKey = keyIn;
// DO NOT change these. They are saved in the database. Changes WILL
make the data unreadable!
keyBytes = encryptKey.getBytes( ENCODING_SCHEME );
keySpec = new DESKeySpec( keyBytes );
secretKey = SecretKeyFactory.getInstance( ENCRYPTION_SCHEME,
cvProvider );
ivKey = secretKey.generateSecret( keySpec );
ivEncryptCipher = Cipher.getInstance( ENCRYPTION_SCHEME, cvProvider );
ivDecryptCipher = Cipher.getInstance( ENCRYPTION_SCHEME, cvProvider );
ivEncryptCipher.init( Cipher.ENCRYPT_MODE, ivKey );
ivDecryptCipher.init( Cipher.DECRYPT_MODE, ivKey );
ivIsUsable = true; // all setup worked, so we can do things .....
}
catch (SecurityException e)
{
throw new EncryptionInitializationException( e.toString() );
}
catch (UnsupportedEncodingException e)
{
throw new EncryptionInitializationException( e.toString() );
}
catch (InvalidKeyException e)
{
throw new EncryptionInitializationException( e.toString() );
}
catch (InvalidKeySpecException e)
{
throw new EncryptionInitializationException( e.toString() );
}
catch (NoSuchAlgorithmException e)
{
throw new EncryptionInitializationException( e.toString() );
}
catch (NoSuchPaddingException e)
{
throw new EncryptionInitializationException( e.toString() );
}
}
public String decrypt( String text ) throws DecryptionException
{
BASE64Decoder decoder = new BASE64Decoder();
byte[] decodedBytes;
byte[] clearBytes;
String inClear = "";
initialize();
if (!ivIsUsable)
throw new DecryptionException( "Decryptor not Initialized" );
try
{
decodedBytes = decoder.decodeBuffer( text );
clearBytes = ivDecryptCipher.doFinal( decodedBytes );
inClear = new String( clearBytes, ENCODING_SCHEME );
}
catch (IOException e)
{
throw new DecryptionException( e.toString() );
}
catch (IllegalStateException e)
{
throw new DecryptionException( e.toString() );
}
catch (IllegalBlockSizeException e)
{
throw new DecryptionException( e.toString() );
}
catch (BadPaddingException e)
{
throw new DecryptionException( e.toString() );
}
return inClear;
}
public String encrypt( String text ) throws EncryptionException
{
BASE64Encoder encoder = new BASE64Encoder();
byte[] clearBytes;
byte[] encryptedBytes;
String encrypted = "";
initialize();
if (!ivIsUsable)
throw new EncryptionException( "Encryptor not Initialized" );
try
{
clearBytes = text.getBytes( ENCODING_SCHEME );
encryptedBytes = ivEncryptCipher.doFinal( clearBytes );
encrypted = encoder.encode( encryptedBytes );
}
catch (UnsupportedEncodingException e)
{
throw new EncryptionException( e.toString() );
}
catch (IllegalStateException e)
{
throw new EncryptionException( e.toString() );
}
catch (IllegalBlockSizeException e)
{
throw new EncryptionException( e.toString() );
}
catch (BadPaddingException e)
{
throw new EncryptionException( e.toString() );
}
return encrypted;
}
}
> I have a basic mis-understanding on how to encrypt/decrypt information.
> I am using the class shown below.
[quoted text clipped - 16 lines]
>
> private void setup( String keyIn ) throws
Why on earth would you specify a key as a String? If the parameter is
supposed to be a password rather than a key, then you should hash it to
get the key bytes.
> EncryptionInitializationException
> {
[quoted text clipped - 13 lines]
> PADDING_LENGTH - keyIn.length() );
> else
All keys less than 10 characters get padded to a full 14 characters.
> encryptKey = keyIn;
> // DO NOT change these. They are saved in the database.
> Changes WILL make the data unreadable!
>
> keyBytes = encryptKey.getBytes( ENCODING_SCHEME );
keyBytes is 14 bytes long. Since (single) DES needs at most 7 or 8
bytes of key material, which of these 14 bytes actually get used?
If it's the last 7 or 8 bytes, then all of the key material comes
from the padding for keys of up to 6 or 7 characters.
If it's the first 8 bytes, then you should realize that DES's 56 bits
of key material is taken from the 7 high-order bits of those bytes, and
your two keys "4" and "5" differ only in the low-order bit of one byte.
If it's the first 7 bytes, then more investigation is warranted.
> keySpec = new DESKeySpec( keyBytes );
> secretKey = SecretKeyFactory.getInstance( ENCRYPTION_SCHEME,
[quoted text clipped - 11 lines]
> ivIsUsable = true; // all setup worked, so we can do things
> .....
--Mike Amling
Wojtek Bok - 01 Feb 2006 16:53 GMT
>> private void setup( String keyIn ) throws
> Why on earth would you specify a key as a String? If the parameter is
> supposed to be a password rather than a key, then you should hash it to
> get the key bytes.
No, it is an actual key. The password would be encrypted.
The key is a unique column in the table, and is immutable, so each row
has its own unique key, which is used to encrypt/decrypt other columns
in that row.
>> keyBytes = encryptKey.getBytes( ENCODING_SCHEME );
>
> keyBytes is 14 bytes long. Since (single) DES needs at most 7 or 8
> bytes of key material, which of these 14 bytes actually get used?
Ok, now I understand why I needed to pad the key on the first place. DES
needs AT LEAST 7 (or 8) bytes.
> If it's the last 7 or 8 bytes, then all of the key material comes from
> the padding for keys of up to 6 or 7 characters.
> If it's the first 8 bytes, then you should realize that DES's 56 bits
> of key material is taken from the 7 high-order bits of those bytes, and
> your two keys "4" and "5" differ only in the low-order bit of one byte.
> If it's the first 7 bytes, then more investigation is warranted.
I tried appending the keyIn, rather than prepending it. Then I tried
both appending and prepending
>> ivKey = secretKey.generateSecret( keySpec );
Ok, ivKey.hashCode() returns the same value for both keyIn values, so
what I am running into is a hash collision.
Mike Amling - 03 Feb 2006 19:34 GMT
>>> private void setup( String keyIn ) throws
>>
[quoted text clipped - 7 lines]
> has its own unique key, which is used to encrypt/decrypt other columns
> in that row.
If the key is in a column the table, how can you prevent anyone who
reads a row from decrypting the data?
--Mike Amling
Wojtek Bok - 06 Feb 2006 15:03 GMT
>> The key is a unique column in the table, and is immutable, so each row
>> has its own unique key, which is used to encrypt/decrypt other columns
>> in that row.
>
> If the key is in a column the table, how can you prevent anyone who
> reads a row from decrypting the data?
The only people who would have accesss to the raw information in the DB
would also need physical access, plus login access to the DB server. All
other access is through a Web application.
The encryption is to prevent some DBA from simply copy/pasting values
from one place to another. If I used a common key, then that would
work. Since the key is different for each row, then you must use the
decryption/encryption routines to move the information.
Not impossible, but much harder.