14

I need to generate a Key from a string, such that I can always create the same key from the same string. (Specifically a Key object, so that I can use it to create a Cipher in turn to create a SealedObject)

Is this possible in Java, and what class/method combination should I be looking at to do so?

3
  • won't hashCode() do for you? if not - why? Commented Mar 2, 2012 at 16:32
  • en.wikipedia.org/wiki/… Commented Mar 2, 2012 at 16:34
  • Not as far as I'm aware, because I'm trying to create a SealedObject in order to encapsulate an object for transmission: I'm not trying to obfuscate the plaintext string into a hash, I'm trying to create a Key (object) Commented Mar 2, 2012 at 16:51

4 Answers 4

22

For AES encryption:

SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);

byte[] iv = cipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
byte[] ciphertext = cipher.doFinal("Hello, World!".getBytes("UTF-8"));

// reinit cypher using param spec
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));

Similarly for the deprecated PBKDF1 and insecure DES for communicating with legacy systems or learning purposes:

byte[] salt = {
    (byte)0xc7, (byte)0x73, (byte)0x21, (byte)0x8c,
    (byte)0x7e, (byte)0xc8, (byte)0xee, (byte)0x99
};

int count = 20;

PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, count);
PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray());
SecretKeyFactory keyFac = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);

Cipher cipher = Cipher.getInstance("PBEWithMD5AndDES");
cipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);

SealedObject sealed = new SealedObject(object, cipher);
...

Note that the iteration count is too low as well in the last example.

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks. Just to be clear, if I performed the same on the server side with the same password, it would produce a cipher that could be used to decrypt the SealedObject?
That is correct. As long as you're using the same param and key specs, you will have the same key.
This would have been a better answer if you removed the first half. DES is completely broken today, and it is dangerous to use it even as an example (people might copy it without knowing that it was unsafe).
DES is even for these type of things very very outdated. It is both breakable and if you ever user it for anything which has security requirments the presence of straight des will likely give you trouble
Thanks for the extra details, guys. I'll be sure to use the AES version.
|
5

You want to use PBKDF2 or bcrypt for this. The former is more widely used in my experience. It appears, based on this comment, that java does support this.

SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");

Comments

2

This is all outdated. The only remcoomended algorithm ist Argon2id. It is in the newer Versions of Bouncycastle: https://www.bouncycastle.org/latest_releases.html

If you run out of memory, use "-Xmx8G" in the execution arguments.

private SecretKey genKey(char[] passwordChars, byte[] saltBytes) {
SecretKey aesKey;
    int aesKeyLen = 16; //key len in bytes
    int version = Argon2Parameters.ARGON2_VERSION_13;
    int iterations = 1;
    int memory = 22; // 20 = 1 GB -> 22=4GB
    int parallelism = 16; //double CPU core
    Argon2Parameters.Builder builder = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id)
            .withVersion(version).withIterations(iterations).withMemoryPowOfTwo(memory) // use 2^(memory) KB
            .withParallelism(parallelism).withSalt(saltBytes);
    Argon2BytesGenerator gen = new Argon2BytesGenerator();
    gen.init(builder.build());
    byte[] result = new byte[aesKeyLen];
    gen.generateBytes(passwordChars, result, 0, result.length);
    aesKey = new SecretKeySpec(result, "AES");
//clear to free RAM
    builder = null;
    gen = null;
    System.gc();
return aesKey;
}

1 Comment

I mean, it’s a 9 year old question.... it’s not entirely surprising that the answers are outdated now
0

You can achieve this by Encryption of Java.

At first you need two jars:

  1. bcmail-jdk16-1.46.jar
  2. bcprov-jdk16-1.46.jar

Here is complete example of how to Data Encryption Standard in Java:

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;

import org.bouncycastle.util.encoders.Base64;


public class KeyGen {
    private SecretKey key;
    private Cipher ecipher;
    private Cipher dcipher;
    private static KeyGen keyGen;

    private KeyGen() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException{
        key = KeyGenerator.getInstance("DES").generateKey();
        ecipher = Cipher.getInstance("DES");
        dcipher = Cipher.getInstance("DES");
        ecipher.init(Cipher.ENCRYPT_MODE, key);
        dcipher.init(Cipher.DECRYPT_MODE, key);
    }

    public static KeyGen getInstance() throws NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException {
        if(keyGen == null) {
            keyGen = new KeyGen();
        }
        return keyGen;
    }

    public String encrypt(String str) throws UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException {
        byte[] utf8 = str.getBytes("UTF8");
        byte[] enc = ecipher.doFinal(utf8);
        return new String(Base64.encode(enc));
    }

    public String decrypt(String str) throws IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
        byte[] dec = Base64.decode(str);
        byte[] utf8 = dcipher.doFinal(dec);
        return new String(utf8, "UTF8");
    }

    public static void main(String[] args) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException {
        KeyGen keyGen = KeyGen.getInstance();
        String string = "JOYMAA";
        String enc = keyGen.encrypt(string);
        System.out.println(enc);
        String dec = keyGen.decrypt(enc);
        System.out.println(dec);
    }
}

Usage:

KeyGen keyGen = KeyGen.getInstance();
String string = "JOYMAA";
String enc = keyGen.encrypt(string);
System.out.println(enc);
String dec = keyGen.decrypt(enc);
System.out.println(dec);

Hope this will help you.

1 Comment

Neither does ECB mode encryption. Having a class named KeyGen performing encryption/decryption does not give much hope either.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.