We are Borg
We are Borg

Reputation: 5311

Generating and using two keys for encryption and decryption in Java

I am working on a Java application which requires I use two keys generated from different Strings for encrypting and decrypting. One String is coming from user and other is master key. I looked on net and found a few references regarding it. I would really like some help in knowing how to implement this. I will show what I have now.

So as you can see from code, I used some code from other stackoverflow post and modified it a bit. I just don't know how to generate the 2 keys from 2 Strings and from where I can get the SecretKey desKey used for decryption.

Code :

public class Encryption {

public void doStuff() {

    String plaintext = "abc";

    SecretKey k1 = generateDESkey();
    SecretKey k2 = generateDESkey();


    String firstEncryption = desEncryption(plaintext, k1);
    String decryption = desDecryption(firstEncryption, k2);
    String secondEncryption = desEncryption(decryption, k1);

    System.out.println(firstEncryption);
    System.out.println(decryption);
    System.out.println(secondEncryption);
}

public static SecretKey generateDESkey() {
    KeyGenerator keyGen = null;
    try {
        keyGen = KeyGenerator.getInstance("DESede");
    } catch (NoSuchAlgorithmException ex) {
        Logger.getLogger(Test.class
                .getName()).log(Level.SEVERE, null, ex);
    }
    try {
        assert keyGen != null;
        keyGen.init(112); // key length 56
        return keyGen.generateKey();
    } catch (NullPointerException ex){
        return null;
    }
}


public static String desEncryption(String strToEncrypt, SecretKey desKey) {
    try {
        Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, desKey);
        return Base64.encode(cipher.doFinal(strToEncrypt.getBytes()));
    } catch (NoSuchAlgorithmException | NoSuchPaddingException |
            IllegalBlockSizeException | BadPaddingException |
            InvalidKeyException ex) {
        Logger.getLogger(Test.class
                .getName()).log(Level.SEVERE, null, ex);
    }
    return null;
}


public static String desDecryption(String strToDecrypt, SecretKey desKey) {
    try {
        Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, desKey);
        return new String(cipher.doFinal(Base64.decode(strToDecrypt)));

    } catch (NoSuchAlgorithmException |  BadPaddingException | IllegalBlockSizeException
            | InvalidKeyException | NoSuchPaddingException ex) {
        Logger.getLogger(Test.class
                .getName()).log(Level.SEVERE, null, ex);
    }
    return null;
}

}

If there is any confusion or doubts about it. Kindly let me know.

Upvotes: 0

Views: 3091

Answers (1)

Artjom B.
Artjom B.

Reputation: 61892

Answer for the code before it was changed.

You're trying to do DESede with only two keys instead of three.

That might generally work, but not as you've written. The problem is the padding. In the second step, you try to decrypt the ciphertext with an other key than with what it was encrypted, so the decryption will fail more than 255 out of 256 times, because the padding will be wrong (also because you use Base64 encoding where it is not necessary).

If you really want to do it, you will have to decrypt without padding and without Base64 encoding. The good thing is that the unencoded ciphertext is already a multiple of the blocksize, so there is no stopping you to use "DES/ECB/NoPadding".

public static void main(String[] args) {
    // First I would like to create keys by giving Strings
    SecretKey k1 = generateDESkey();
    SecretKey k2 = generateDESkey();

    // encryption
    byte[] firstEncryption = desEncryption("plaintext".getBytes("UTF-8"), k1, false);
    byte[] decryption = desDecryption(firstEncryption, k2, true);
    byte[] secondEncryption = desEncryption(decryption, k1, true);

    // decryption
    byte[] firstDecryption = desDecryption(secondEncryption, k1, true);
    byte[] encryption = desEncryption(firstDecryption, k2, true);
    byte[] secondDecryption = desDecryption(encryption, k1, false);

    System.out.println(new String(secondDecryption)); // plaintext
}

public static byte[] desEncryption(byte[] strToEncrypt, SecretKey desKey, boolean noPadding) {
    try {
        Cipher cipher = Cipher.getInstance(noPadding ? "DES/ECB/NoPadding" : "DES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, desKey);
        return cipher.doFinal(strToEncrypt);
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return null;
}

public static byte[] desDecryption(byte[] strToDecrypt, SecretKey desKey, boolean noPadding) {
    try {
        Cipher cipher = Cipher.getInstance(noPadding ? "DES/ECB/NoPadding" : "DES/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, desKey);
        return cipher.doFinal(strToDecrypt);
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return null;
}

This is actually an equivalent implementation of DESede with two keys when the general key is constructed in this way:

SecretKey k1 = generateDESkey();
SecretKey k2 = generateDESkey();

byte[] edeKeyBytes = new byte[24];
System.arraycopy(k1.getEncoded(), 0, edeKeyBytes, 0, 8);
System.arraycopy(k2.getEncoded(), 0, edeKeyBytes, 8, 8);
System.arraycopy(k1.getEncoded(), 0, edeKeyBytes, 16, 8);

edeKey = new SecretKeySpec(edeKeyBytes, "DESede");

Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, edeKey);

System.out.println(Base64.encode(cipher.doFinal("plaintext".getBytes("UTF-8"))));

DESede uses three keys which we will call k1, k2 and k3. All of them are concatenated into a single byte array. In your case k1 is used a second time in place of k3.

Upvotes: 3

Related Questions