Encrypting passwords using Java

A

Andy Grove

I've just spent the best part of an hour tracking down the right
information to get password encryption/decryption and
storage/retrieval of secret keys working. I've created this utility
class which now serves my needs and I am posting it here in the hope
that it saves other people some time. Enjoy ;-)

Andy Grove.
http://www.codefutures.com/weblog/andygrove


/**
* Utility class to encrypt and decrypt passwords using Java
cryptography.
*/

package com.codefutures.security;

import sun.misc.BASE64Encoder;
import sun.misc.BASE64Decoder;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
import java.io.IOException;

public class PasswordUtil
{
private static final String CIPHER_TYPE = "DES/ECB/PKCS5Padding";

public static String encrypt(String password, Key key)
{
try {
Cipher cipher = Cipher.getInstance(CIPHER_TYPE);
cipher.init(Cipher.ENCRYPT_MODE, key);

byte[] outputBytes = cipher.doFinal( password.getBytes()
);

BASE64Encoder encoder = new BASE64Encoder();
String base64 = encoder.encode(outputBytes);

return base64;
}
catch (Exception e) {
throw new RuntimeException( "Failed to encrypt password",
e );
}
}

public static String decrypt(String password, Key key)
{
try {
BASE64Decoder decoder = new BASE64Decoder();
byte encrypted[] = decoder.decodeBuffer(password);

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 (Exception e) {
throw new RuntimeException( "Failed to decrypt password",
e );
}
}

/**
* Create a key for use in the cipher code
*/
public static Key generateRandomKey()
throws NoSuchAlgorithmException
{
KeyGenerator keyGenerator = KeyGenerator.getInstance("DES");
keyGenerator.init(new SecureRandom());
SecretKey secretKey = keyGenerator.generateKey();
return secretKey;
}

/**
* Encode a secret key as a string that can be stored for later
use.
*
* @param key
* @return
*/
public static String encodeKey(Key key)
{
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(key.getEncoded());
}

/**
* Reconstruct a secret key from a string representation.
*
* @param encodedKey
* @return
* @throws IOException
*/
public static Key decodeKey(String encodedKey)
throws IOException
{
BASE64Decoder decoder = new BASE64Decoder();
byte raw[] = decoder.decodeBuffer(encodedKey);
SecretKey key = new SecretKeySpec( raw, "DES" );
return key;
}


}
 
A

Andy Grove

Yes, the very same! I'll drop you a line separately ...


SPG said:
Not *the* andy grove from Orbware? Did some time at Knowhow in Woking??

Andy Grove said:
I've just spent the best part of an hour tracking down the right
information to get password encryption/decryption and
storage/retrieval of secret keys working. I've created this utility
class which now serves my needs and I am posting it here in the hope
that it saves other people some time. Enjoy ;-)

Andy Grove.
http://www.codefutures.com/weblog/andygrove


/**
* Utility class to encrypt and decrypt passwords using Java
cryptography.
*/

package com.codefutures.security;

import sun.misc.BASE64Encoder;
import sun.misc.BASE64Decoder;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
import java.io.IOException;

public class PasswordUtil
{
private static final String CIPHER_TYPE = "DES/ECB/PKCS5Padding";

public static String encrypt(String password, Key key)
{
try {
Cipher cipher = Cipher.getInstance(CIPHER_TYPE);
cipher.init(Cipher.ENCRYPT_MODE, key);

byte[] outputBytes = cipher.doFinal( password.getBytes()
);

BASE64Encoder encoder = new BASE64Encoder();
String base64 = encoder.encode(outputBytes);

return base64;
}
catch (Exception e) {
throw new RuntimeException( "Failed to encrypt password",
e );
}
}

public static String decrypt(String password, Key key)
{
try {
BASE64Decoder decoder = new BASE64Decoder();
byte encrypted[] = decoder.decodeBuffer(password);

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 (Exception e) {
throw new RuntimeException( "Failed to decrypt password",
e );
}
}

/**
* Create a key for use in the cipher code
*/
public static Key generateRandomKey()
throws NoSuchAlgorithmException
{
KeyGenerator keyGenerator = KeyGenerator.getInstance("DES");
keyGenerator.init(new SecureRandom());
SecretKey secretKey = keyGenerator.generateKey();
return secretKey;
}

/**
* Encode a secret key as a string that can be stored for later
use.
*
* @param key
* @return
*/
public static String encodeKey(Key key)
{
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(key.getEncoded());
}

/**
* Reconstruct a secret key from a string representation.
*
* @param encodedKey
* @return
* @throws IOException
*/
public static Key decodeKey(String encodedKey)
throws IOException
{
BASE64Decoder decoder = new BASE64Decoder();
byte raw[] = decoder.decodeBuffer(encodedKey);
SecretKey key = new SecretKeySpec( raw, "DES" );
return key;
}


}
 
J

Jon A. Cruz

Andy Grove wrote:

byte[] outputBytes = cipher.doFinal( password.getBytes()

BIG problem right there.

You've just made a call that will, given the same inputs, return
different results depending on the time of day, users whim, etc.

Especially with cryptography, never call String.getBytes(). Instead
always use String.getBytes(String) and feed it a known, proper encoding.

byte[] outputBytes = cipher.doFinal( encrypted );
String ret = new String( outputBytes );

And the same thing applies here. Instead of leaving things up to the
whim of the default platform encoding of the moment, instead give it an
explicit encoding. Use new String(byte[], String) instead of new
String(byte[]).
 
M

Matt Parker

Jon said:
Andy Grove wrote:

byte[] outputBytes = cipher.doFinal( password.getBytes()

BIG problem right there.

You've just made a call that will, given the same inputs, return
different results depending on the time of day, users whim, etc.

Especially with cryptography, never call String.getBytes(). Instead
always use String.getBytes(String) and feed it a known, proper encoding.

byte[] outputBytes = cipher.doFinal( encrypted );
String ret = new String( outputBytes );

And the same thing applies here. Instead of leaving things up to the
whim of the default platform encoding of the moment, instead give it an
explicit encoding. Use new String(byte[], String) instead of new
String(byte[]).

Also, the OP shouldn't really be using the sun.misc.* packages. These are
not part of the API and are subject to change at the whim of Sun.

Matt
 
A

Anton Spaans

Andy Grove said:
I've just spent the best part of an hour tracking down the right
information to get password encryption/decryption and
storage/retrieval of secret keys working. I've created this utility
class which now serves my needs and I am posting it here in the hope
that it saves other people some time. Enjoy ;-)

Andy Grove.
http://www.codefutures.com/weblog/andygrove


/**
* Utility class to encrypt and decrypt passwords using Java
cryptography.
*/

package com.codefutures.security;

import sun.misc.BASE64Encoder;
import sun.misc.BASE64Decoder;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
import java.io.IOException;

public class PasswordUtil
{
private static final String CIPHER_TYPE = "DES/ECB/PKCS5Padding";

public static String encrypt(String password, Key key)
{
try {
Cipher cipher = Cipher.getInstance(CIPHER_TYPE);
cipher.init(Cipher.ENCRYPT_MODE, key);

byte[] outputBytes = cipher.doFinal( password.getBytes()
);

BASE64Encoder encoder = new BASE64Encoder();
String base64 = encoder.encode(outputBytes);

return base64;
}
catch (Exception e) {
throw new RuntimeException( "Failed to encrypt password",
e );
}
}

public static String decrypt(String password, Key key)
{
try {
BASE64Decoder decoder = new BASE64Decoder();
byte encrypted[] = decoder.decodeBuffer(password);

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 (Exception e) {
throw new RuntimeException( "Failed to decrypt password",
e );
}
}

/**
* Create a key for use in the cipher code
*/
public static Key generateRandomKey()
throws NoSuchAlgorithmException
{
KeyGenerator keyGenerator = KeyGenerator.getInstance("DES");
keyGenerator.init(new SecureRandom());
SecretKey secretKey = keyGenerator.generateKey();
return secretKey;
}

/**
* Encode a secret key as a string that can be stored for later
use.
*
* @param key
* @return
*/
public static String encodeKey(Key key)
{
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(key.getEncoded());
}

/**
* Reconstruct a secret key from a string representation.
*
* @param encodedKey
* @return
* @throws IOException
*/
public static Key decodeKey(String encodedKey)
throws IOException
{
BASE64Decoder decoder = new BASE64Decoder();
byte raw[] = decoder.decodeBuffer(encodedKey);
SecretKey key = new SecretKeySpec( raw, "DES" );
return key;
}


}

I think it's better to use Java's MessageDigesters to generate hash-values
out of passwords (SHA, MD5 digesters). These hash-values can not be
decrypted ever (with reasonable resources), but they satisfy the 'equal'
constraint (if two passwords (strings) are the same, their hash-value is the
same). You get the encrypted (using SHA or MD5) hash-value from the user. If
this hash-value is the same as the hash-value you've stored in your
database, then the user can be authenticated.

For protection against network eaves-dropping (and if you don't mind storing
passwords in decryptable form in your database), you could modify original
passwords with a random string (make a hashvalues out of
"<password>+<modifier>", for example). Each request will generate a new
randomly generated modifier.

-- Anton Spaans.
 
A

Andy Grove

Jon A. Cruz said:
Andy Grove wrote:

byte[] outputBytes = cipher.doFinal( password.getBytes()

BIG problem right there.

You've just made a call that will, given the same inputs, return
different results depending on the time of day, users whim, etc.

Especially with cryptography, never call String.getBytes(). Instead
always use String.getBytes(String) and feed it a known, proper encoding.

byte[] outputBytes = cipher.doFinal( encrypted );
String ret = new String( outputBytes );

And the same thing applies here. Instead of leaving things up to the
whim of the default platform encoding of the moment, instead give it an
explicit encoding. Use new String(byte[], String) instead of new
String(byte[]).


Thanks for pointing that one out. I've introduced a DEFAULT_ENCODING
constant with a value of "UTF-8" and I'm passing that into the methods
you highlighted.

Cheers,

Andy Grove
http://www.codefutures.com/weblog/andygrove
 
J

Jon A. Cruz

Andy said:
Thanks for pointing that one out. I've introduced a DEFAULT_ENCODING
constant with a value of "UTF-8" and I'm passing that into the methods
you highlighted.

Glad I could help.

Next minor gotchas:

1) On older VM's, the string used was "UTF8" and not the proper "UTF-8". :)

2) On some systems, where it's still a requirement by the specs, many
vendors don't support that at all. This is mainly J2ME.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,902
Latest member
Elena68X5

Latest Threads

Top