Skip to Main Content

Java Security

Announcement

For appeals, questions and feedback about Oracle Forums, please email oracle-forums-moderators_us@oracle.com. Technical questions should be asked in the appropriate category. Thank you!

Interested in getting your voice heard by members of the Developer Marketing team at Oracle? Check out this post for AppDev or this post for AI focus group information.

How to encrypt/decrypt passwords?

843810Feb 14 2003 — edited Jul 5 2007
Hi!
I want to store some usernames and passwords in a file.
I want to use the MD5 or the SHA-1 algorithm.
What classes in the JAVA Cryptography API should I use?
I thought about a MessageDigest, but then I recognized that I can not decrypt the data with a MessageDigest! What else can I use?

I would be very glad if you could give me some hints.
Thx

Comments

843810
Maybe this is an easier explaination of my problem:
I just want to encrypt and decrypt some strings without the use of any Key. Because when I encrypt the data with e.g. a PrivateKey, I have to store the key somewhere through an ObjectOutputStream and then I have to read it back when I want to decrypt the data. But reading a key takes a lot of time, so my program would not be very performant if I do so! I dont�t know if there�s an alternative? Pease help me it�s urgent!
Thx
843810
import java.io.*;
import java.security.*;
import java.security.cert.*;
import javax.crypto.*;
import javax.crypto.spec.*;

public class SHADigester
{
public byte[] digest(byte[] bytes) throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance("SHA");
digest.update(bytes);
return digest.digest();
}
}

No guarantees on this compiling!
843810
As I said before, it is not possible to decode data with a MessageDigest! But thank you! I need another solution please!
Thx
843810
ooh, ok. I just saw the last part of your posting. You need to use an encryption/decryption algorithm. I find Blowfish to be a good balance of speed and security. So:

KeyGenerator kgen = KeyGenerator.getInstance("Blowfish");
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "Blowfish");

Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal("This is just an example".getBytes());

This example uses a generated key. Obviously you would need to store your key somewhere so you can decrypt again. When decrypting, you just initialise the cipher with Cipher.DECRYPT_MODE instead of ENCRYPT_MODE. Otherwise it's the same.

You can either store your key in a keystore, or just hard-code it in the Java class (not sure of the security of this second option, anyone else with an opinion?)

Have a look at:

http://java.sun.com/j2se/1.4/docs/guide/security/jce/JCERefGuide.html

If you need more help, let me know. I have some working code somewhere...

843810
but he also suggested "encryption" without a key.


hmmm.
843810
The easiest way to do this is to code the key as a byte array IN the class that handles the encryption.

Just construct a byte array with random bytes, of a "valid" bit length (depending on the algorithm. Eg, Blowfish can use 128 and 448 bit keys), then use the SecretKeySpec class (SecretKey interface) to construct the actual key.

EG:

byte[] key = new byte[16]; // 128 bit key
key[0] = 24;
key[1] = -10;
... etc etc...

SecretKey sKey = new SecretKeySpec(key, "Blowfish");
843810
Hi!
I want to store some usernames and passwords in a
file.
I want to use the MD5 or the SHA-1 algorithm.
What classes in the JAVA Cryptography API should I
use?
I thought about a MessageDigest, but then I recognized
that I can not decrypt the data with a MessageDigest!
What else can I use?

I would be very glad if you could give me some hints.
Thx
Just to explain some basics.. The MessageDigest class is the class you want to use. The way you code it is that you Hash the password and store it hashed. Then when a user logs in you Hash his supplied password and compare hashes. This way you never have to store the encryption key or the actual password.
I'll show you some example code in a minute.. just have to look it up.

Rival
843810
Here is the code.. AccessDescriptor stores the usernames and passwords and has a few methods for storing and requesting users and passwords. PasswordEncrypt provides a better interface for comparing passwords and storing new users and passwords. The actual hashing code is in this class.

--------------------------Cut here--------------------------
import java.io.*;
import java.security.*;

class PasswordEncrypt
{
  private AccessDescriptor access;

  public PasswordEncrypt()
  {
    access = readPassword("C:\\temp\\password.obj");
  }

  public boolean doValidate(String username, String password)
  {
    if (access.checkUserName(username))
      return access.getPassword(username).equals(getEncrypted(password));
    else
      return false;
  }

  public boolean storeNewPassword(String username, String password, String newPassword)
  {
    if (!access.checkUserName(username))
      return false;
    if (access.getPassword(username).equals(getEncrypted(password)))
    {
      access.setPassword(username, getEncrypted(newPassword));
      writePassword(access, "D:\\password.obj");
      return true;
    }
    return false;
  }

  private String getEncrypted(String password)
  {
    try
    {
      byte[] encrypt = password.getBytes();

      MessageDigest md = MessageDigest.getInstance("MD5");
      md.update(encrypt);
      return new String(md.digest());
    }
    catch(Exception e)
    {
      System.out.println("Error while encrypting: " + e.toString());
      return null;
    }
  }

  private AccessDescriptor readPassword(String file)
  {
    try
    {
      ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
      AccessDescriptor temp = (AccessDescriptor) ois.readObject();
      ois.close();
      return temp;
    }
    catch (Exception e)
    {
      System.out.println("Error while reading password file: " + e.toString());
      return null;
    }
  }

  private void writePassword(AccessDescriptor access, String file)
  {
    try
    {
      ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
      oos.writeObject(access);
      oos.close();
    }
    catch (Exception e)
    {
      System.out.println("Error while writing password file: " + e.toString());
    }
  }
}
--------------------------Cut here--------------------------
import java.io.*;

class AccessDescriptor implements Serializable
{
  private String[] users  = {"Administrator", "Marco", "Wim", "Bert", "Marianne", "Piet"};
  private String[] passes = {"", "", "", "", "", ""};

  public AccessDescriptor() { }
  
  public boolean checkUserName(String username)
  {
    for (int i = 0; i < users.length; i++)
      if (username.equals(users))
return true;
return false;
}

public String getPassword(String username)
{
for (int i = 0; i < users.length; i++)
if (username.equals(users[i]))
return passes[i];
return "";
}

public void setPassword(String username, String password)
{
for (int i = 0; i < users.length; i++)
if (username.equals(users[i]))
passes[i] = password;
}

public void addUser(String username, String password)
{
String[] newUsers = new String[users.length + 1];
for (int i = 0; i < users.length; i++)
newUsers[i] = users[i];
newUsers[users.length] = username;
users = newUsers;

String[] newPasses = new String[passes.length + 1];
for (int i = 0; i < passes.length; i++)
newPasses[i] = passes[i];
newPasses[passes.length] = password;
passes = newPasses;
}
}
843810
Thank you very much!
This is exatly what I have searched!
843810
Hi, Rival

Three remarks on your code:

1.
      byte[] encrypt = password.getBytes();

      MessageDigest md = MessageDigest.getInstance("MD5");
      md.update(encrypt);
This is a misnomer: You did not encrypt the password. You just hashed it. Encryption would involve a key and there is no key used.

2.

Specify the character set in the getBytes() method. If you don't and the user types characters like "�" or "�" you will get different hashes on different platforms. I.e. Java on Windows uses a different default character set than Java on Unix.

The best character set is "UTF-8". It is available on every platform and "knows" all characters you can think of.

3.

The method you use is prone to a dictionary attack. If I get hold of the list of hashed passwords I could set up a dictionary of common and generated passwords with their correspondig hash value. If I find the same hash in your password list I have found the password. This is because a certain password always yields the same hash. I.e. MD5("Ferrari") = 6C5B90CE A858312B 4A7F0704 48F8CD1F.

So, you would add a "salt" to each password before hashing it. I.e.
      md.update(salt);
      md.update(password.getBytes("UTF-8"));
You would store the salt together with the password hash. It is not necessary to keep the salt secret. It just has to be unique for every user.


Regards,

Frank
843810
Hello FHS1962, I have a question I am currently using MD5 to hash passwords and Base64Encoder to encode the string before inserting it in the database. I have a probelm though, I used your UTF-8 suggestion to make the password platform independent for both Java on Windows and Java on Unix. However, it is not working...when the user creates a user on the Unix platform and tries to log in with that userid/password which was hashed/encoded on Unix platform, in a Windows platform, the password is different from what was stored on the Unix platform and what is passed in from the user hashed and encoded on the Windows platform. Do you have a solution for this. Here is my code below. Please HELP! Thanks so much!



public String doIt(String test) {
try {
sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder();


secret = new String(encrypt(test));
//System.out.println("the encrypted result : " + secret);
encoded=encoder.encode(secret.getBytes());
System.out.print("Here is the encoded string in LIMDBO" + encoded);
boolean ok = true;
//String s = "";

} catch (Exception e) {
e.printStackTrace();
}

return encoded;
}

static byte[] encrypt(String x) throws Exception {
java.security.MessageDigest d = null; //comment
d = java.security.MessageDigest.getInstance("MD5"); //comment when released
// d = java.security.MessageDigest.getInstance("SHA"); uncomment when released
d.reset();
d.update(x.getBytes("UTF-8"));
return d.digest();
}
843810
Hi chilo63
... I used your UTF-8 suggestion to make
the password platform independent for both Java on
Windows and Java on Unix. However, it is not
working...
Well, that is no surprise...

You have the line
   secret = new String(encrypt(test));
in your code. When you refer to the documentation your read the following regarding "String":

String(byte[] bytes): Constructs a new String by decoding the specified array of bytes using the platform's default charset.

That's it: You use the default character set of the platform!

So, don't stringify the output of the MD5 hash. It is a byte array, not a string. And the Base64 encoder needs a byte array anyway.


Regards,

Frank
843810
Hi chilo63

I just saw that you posted your question on three threads! This is considered very unpolite behaviour. Please don't do this again. Otherwise some people might be offended and report this as an abuse.


Regards,

Frank
843810
Hello FHS1962,

Sorry about that....I have a deadline and I am getting stresed, but all that aside. Thanks for your help by the way! I changed the code to the following, do you see any probelms with this code:
public String doIt(String test) {
try {

byte[] dataBuffer = test.getBytes("UTF-8");


byte[] theDigest = digestIt(dataBuffer);
mdArray=displayBase64(digestIt(theDigest));


boolean ok = true;

} catch (Exception e) {
e.printStackTrace();
}

return mdArray;
}



//This method generates and returns a digest for an
// incoming array of bytes.
static byte[] digestIt(byte[] dataIn){
byte[] theDigest = null;
try{
//Create a MessageDigest object implementing
// the SHA algorithm, as supplied by SUN
java.security.MessageDigest messageDigest =
java.security.MessageDigest.getInstance("MD5");
//Feed the byte array to the digester. Can
// accommodate multiple calls if needed
messageDigest.update(dataIn);
//Complete the digestion and save the result
theDigest = messageDigest.digest();
}catch(Exception e){System.out.println(e);}
//Return the digest value to the calling method
return theDigest;
}//end digestIt()
//-----------------------------------------------------//

//Method to display an array of bytes in base 64 format
static String displayBase64(byte[] data){
sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder();
String encoded = encoder.encodeBuffer(data);
return encoded;
}
843810
Hi Jason,

Could you post the whole java source? I am working on the same type issue and I have found that storing the key in a file is taking way too long to process. I would be interested in seeing your solution.

Thanks!
Mike
843811
Hi Rival,

I had the exact same question and your explanation of the basics was very helpful. Thanks.
EJP
except that it's not encryption, it is digesting, and it will corrupt the data here:
return new String(md.digest());
and/or at the converse operation. String is not a container for binary data.
1 - 17
Locked Post
New comments cannot be posted to this locked post.

Post Details

Locked on Aug 1 2007
Added on Feb 14 2003
17 comments
511 views