1

I need to produce the same encrypted string in Java as the one built with the Ruby encrypted_strings library. I've tried many different ways but my Java code keeps returning a different output, and I'm unable to understand what I'm doing wrong.

Below is the ruby script that produces the desired output that I can't get right in Java.

#!/usr/bin/ruby
require 'encrypted_strings'

data = 'Whackabad'
password = 'bAJLyifeUJUBFWdHzVbykfDmPHtLKLMzViHW9aHGmyTLD8hGYZ'

encrypted_data = data.encrypt(:symmetric, :password => password)
printf "Data: #{data}\n"
printf "Encrypted Data: #{encrypted_data}"

Output:

Data: Whackabad
Encrypted Data: AEsDXVcgh2jsTjlDgh+REg==

I had a look at the library, and it seems to be using DES-EDE3-CBC as the default algorithm for encryption. I deduce from here that I should use DESede or TripleDES algorithm and CBC mode. As the padding option, I'm using PKCS5Padding cause the library is calling pkcs5_keyivgen.

Below is the Java code that tries to reproduce the same output unsuccessfully.

package ...

import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;

public class SymmetricDESedeCipher {
    private static final String DATA = "Whackabad";
    private static final String key = "bAJLyifeUJUBFWdHzVbykfDmPHtLKLMzViHW9aHGmyTLD8hGYZ";
    private static final String ALGORITHM = "DESede";
    private static final String XFORM = "DESede/CBC/PKCS5Padding";

    private static byte[] iv = new byte[8];

    private static byte[] encrypt(byte[] inpBytes,
                                  SecretKey key, String XFORM) throws Exception {
        Cipher cipher = Cipher.getInstance(XFORM);
        IvParameterSpec ips = new IvParameterSpec(iv);
        cipher.init(Cipher.ENCRYPT_MODE, key, ips);
        return cipher.doFinal(inpBytes);
    }

    public static void main(String[] unused) throws Exception {
        byte[] keyBytes = key.getBytes();
        DESedeKeySpec desKeySpec = new DESedeKeySpec(keyBytes);
        SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(ALGORITHM);

        SecretKey secretKey = secretKeyFactory.generateSecret(desKeySpec);

        byte[] dataBytes = DATA.getBytes();
        byte[] encBytes = encrypt(dataBytes, secretKey, XFORM);

        System.out.println("Data: " + DATA);
        System.out.println("Encrypted Data: " + new BASE64Encoder().encode(encBytes));
    }
}

Output

Data: Whackabad
Encrypted Data: ScPTKQBsR9Ni1nJ1tsMaaQ==

I've seen people encrypting data from Java to be decrypted from Ruby and vice-versa with different algorithms so I think this can be achieved but I can't see what's wrong. Do you have an idea? If so, that'd be of much help!

Thanks

duckhunt
  • 333
  • 1
  • 6
  • 23
  • Start by ensuring you're using the same bytes. String representations vary widely between languages. – chrylis -cautiouslyoptimistic- Feb 22 '17 at 23:42
  • 1
    Are you absolutely sure you want to use that ruby encryption library? The documentation seems to indicate it derives the Initialization Vector from a password, which is a terrible idea -- [IV's should be random](http://crypto.stackexchange.com/a/82) – dnault Feb 22 '17 at 23:53
  • Yes @dnault, it's legacy what I'm trying to translate into Java so that method is the one in use at this moment and the one I want to replicate. – duckhunt Feb 22 '17 at 23:57
  • @chrylis I'm not sure which string representation is Ruby using so I can't reproduce it in Java. If you know, that would be of help too. – duckhunt Feb 23 '17 at 00:01
  • 1
    If you want bug-for-bug compatibility, I would investigate how the ruby library derives the IV and then do the same thing in Java (instead of using an all-zero IV as your code does now). – dnault Feb 23 '17 at 00:05
  • 1
    3DES (an algorithm past it's prime) takes a 24 byte key, you are supplying a 50 character string, that is not correct. – zaph Feb 23 '17 at 01:41
  • @zaph you are right as I was using a password as a key in my Java code. I found my way to get the key and iv from the password so thanks for driving me into the solution – duckhunt Feb 24 '17 at 11:15
  • Thanks! @dnault Your comment helped to end up with a solution to what I was trying to achieve – duckhunt Feb 24 '17 at 11:16
  • So what was the solution? :-) Please post it as an "answer" so it is more visible to help the next guy. – Leigh Feb 27 '17 at 18:15
  • @leigh I just posted that. Hope it helps someone – duckhunt Mar 14 '17 at 23:27
  • @rakemous - Great, thanks! – Leigh Mar 14 '17 at 23:51

1 Answers1

1

The first thing to do is to derive the IV and key from the given password.

From the link above, you will get an encoded IV and KEY that corresponds with "VDiJjncs4ak=" and "s9e42J3PpmQv8n5T8L3zzuFaGdrzK/wU" respectively. This means that the key and IV vector used in the Java code are wrong as it was said in the comments.

Below is the resulting Java code:

public class SymmetricDESedeCipher {
    private static final String DATA = "Whackabad";
    private static final String ALGORITHM = "DESede";
    private static final String XFORM = "DESede/CBC/PKCS5Padding";
    private static final String KEY = "s9e42J3PpmQv8n5T8L3zzuFaGdrzK/wU";
    private static final String IV = "VDiJjncs4ak=";

    private static byte[] encrypt(String data,
                                  SecretKey key, String XFORM, byte[] iv) throws Exception {
        Cipher cipher = Cipher.getInstance(XFORM);
        cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
        return cipher.doFinal(data.getBytes());
    }

    public static void main(String[] unused) throws Exception {
        DESedeKeySpec spec = new DESedeKeySpec(new BASE64Decoder().decodeBuffer(KEY));
        SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(ALGORITHM);
        SecretKey secretKey = secretKeyFactory.generateSecret(spec);

        byte[] encBytes = encrypt(DATA, secretKey, XFORM,  new BASE64Decoder().decodeBuffer(IV));

        System.out.println("Data: " + DATA);
        System.out.println("Encrypted Data: " + new BASE64Encoder().encode(encBytes));
    }
}

Output:

Data: Whackabad
Encrypted Data: AEsDXVcgh2jsTjlDgh+REg==
Community
  • 1
  • 1
duckhunt
  • 333
  • 1
  • 6
  • 23