0

I have problem convert php code with example of decrypting into java. Here is the php code:

function decrypt($encrypted, $password, $salt='2#g+XK^Sc3"4ABXbvwF8CPD%en%;9,c(') {
    // Build a 256-bit $key which is a SHA256 hash of $salt and $password.
    $key = hash('SHA256', $salt . $password, true);
    // Retrieve $iv which is the first 22 characters plus ==, base64_decoded.
    $iv = base64_decode(substr($encrypted, 0, 22) . '==');
    // Remove $iv from $encrypted.
    $encrypted = substr($encrypted, 22);
    // Decrypt the data.  rtrim won't corrupt the data because the last 32 characters are the md5 hash; thus any \0 character has to be padding.
    $decrypted = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($encrypted), MCRYPT_MODE_CBC, $iv), "\0\4");
    // Retrieve $hash which is the last 32 characters of $decrypted.
    $hash = substr($decrypted, -32);
    // Remove the last 32 characters from $decrypted.
    $decrypted = substr($decrypted, 0, -32);
    // Integrity check.  If this fails, either the data is corrupted, or the password/salt was incorrect.
    if (md5($decrypted) != $hash) return false;
    // Yay!
    return $decrypted;
}

And here is my java code what I have done. But this is not working.

private static String password = "AxkbK2jZ5PMaeNZWfn8XRLUWF2waGwH2EkAXxBDU6aZ";
private static String salt = "2#g+XK^Sc3\"4ABXbvwF8CPD%en%;9,c(";
private static String text = "Fm+Zfufqe3DjRQtWcYdw9g9oXriDjrAkRrBLhEfu7fCtT4BzD0gw7D+8KxrcbbgJm26peTUWHU2k4YJ4KqCSRQN3NPzuXwlJ4mC4444Edg3Q==";

public String decrypt(String pass, String encr) {

    try {
        int i = 0;

        String key = hash();
        byte[] iv = Base64.decodeBase64(text.substring(0, 22) + "==");

        Cipher cipher = Cipher.getInstance("DES");
        SecretKeySpec keySpec = new SecretKeySpec(password.getBytes(), "DES");
        IvParameterSpec ivSpec = new IvParameterSpec(salt.getBytes());
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
        ByteArrayInputStream fis = new ByteArrayInputStream(iv);
        CipherInputStream cis = new CipherInputStream(fis, cipher);
        ByteArrayOutputStream fos = new ByteArrayOutputStream();
        // decrypting
        byte[] b = new byte[8];
        while ((i = cis.read(b)) != -1) {
            fos.write(b, 0, i);
        }
        fos.flush();
        fos.close();
        cis.close();
        fis.close();

        return fos.toString();
    } catch (Exception e) {
        e.printStackTrace();
    }

    return null;
}

private String hash() {
    StringBuffer sb = new StringBuffer();

    try {
        MessageDigest md = null;
        md = MessageDigest.getInstance("SHA-256");
        md.update((password + salt).getBytes());
        byte byteData[] = md.digest();

        for (int i = 0; i < byteData.length; i++) {
            sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1));
        }
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } finally {
        return sb.toString();
    }

}
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
deadfish
  • 11,996
  • 12
  • 87
  • 136

1 Answers1

2

There are some problems with your code.

  • MCRYPT_RIJNDAEL_128 is AES not DES. Those are two entirely different algorithms.
  • Cipher.getInstance("DES"); probably defaults to Cipher.getInstance("DES/ECB/PKCS5Padding"); depending on your default JCE provider
  • You're not using the key that you generated, but use directly the password which is not a key. Also, your key should not be hex encoded, since you also use a raw key in PHP.
  • Salt is not the IV.
  • You're not slicing off the hash from the end of the recovered plaintext to verify it.

Some things that are cringe-worthy in your original PHP code:

  • A single pass of SHA-256 is not good enough nowadays. Your password needs to be really long (50+ random characters) in order for it to be secure. Use PBKDF2, bcrypt, scrypt with high cost or iterations in order to be able to use shorter passwords.
  • It seems that the IV is actually appended to the Base64-encoded ciphertext as as a Base64-encoded string without padding bytes. This is very unusual and might lead to misunderstandings.
  • Using MCrypt's Zero Padding instead of utilizing PKCS#7 padding.
  • Checking the integrity of the plaintext is good, but you should check the integrity of the ciphertext instead, because encrypt-then-MAC is generally better. MD5 is also not good enough. You should use at least HMAC-SHA256.
Community
  • 1
  • 1
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
  • Also, `md5($decrypted) != $hash` there are so many things wrong here. – Scott Arciszewski Aug 18 '15 at 14:31
  • @Scott `md5($decrypted) != $hash` is somewhat fine since it is at least some kind of integrity check similar to MAC-then-encrypt. Encrypt-then-(actual-)MAC would have been better. The problem is of course that it isn't at all replicated in the Java code. – Artjom B. Aug 18 '15 at 14:37
  • I don't disagree, I just wanted to add that as an addendum. :) – Scott Arciszewski Aug 18 '15 at 14:47