Search code examples
javaphpencryptionencryption-symmetric3des

JAVA to PHP 7 Encryption


I want to replicate the encryption/decryption from JAVA to PHP. But my problem the result is not match. I have no idea about java so I'm trying to understand each line of codes from java and write in PHP.

JAVA


    secretkey: thisisasecretkey
    import java.security.MessageDigest;
    import java.util.Arrays;
    import javax.crypto.Cipher;
    import javax.crypto.SecretKey;
    import javax.crypto.spec.SecretKeySpec;

    import org.apache.commons.codec.binary.Base64;

    public class TDESEncrypter {

    public String _encrypt(String message, String secretKey) throws Exception {
    
        MessageDigest md = MessageDigest.getInstance("SHA-1");
        byte[] digestOfPassword = md.digest(secretKey.getBytes("utf-8"));
        byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
            
        SecretKey key = new SecretKeySpec(keyBytes, "DESede");
        Cipher cipher = Cipher.getInstance("DESede");
        cipher.init(Cipher.ENCRYPT_MODE, key);
            
        byte[] plainTextBytes = message.getBytes("utf-8");
        byte[] buf = cipher.doFinal(plainTextBytes);
        byte [] base64Bytes = Base64.encodeBase64(buf);
        String base64EncryptedString = new String(base64Bytes);
            
        return base64EncryptedString;
    }

    public String _decrypt(String encryptedText, String secretKey) throws Exception {
    
        byte[] message = Base64.decodeBase64(encryptedText.getBytes("utf-8"));
            
        MessageDigest md = MessageDigest.getInstance("SHA-1");
        byte[] digestOfPassword = md.digest(secretKey.getBytes("utf-8"));
        byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
        SecretKey key = new SecretKeySpec(keyBytes, "DESede");
            
        Cipher decipher = Cipher.getInstance("DESede");
        decipher.init(Cipher.DECRYPT_MODE, key);
            
        byte[] plainText = decipher.doFinal(message);
            
        return new String(plainText, "UTF-8");
    }
   
}

Here's the steps using java that might be useful to replicate the functions in PHP.

  1. create a hash using sha1
  2. convert the secretkey(from credentials) to bytes array with utf-8 encoding
  3. padd step 2 with xeroes, truncating or padding with zeros (if necessary) so the copy has the specified length of 24
  4. initialize Secretkey with keybytes from step3 using DESede

  5. create cipher with DESede instance

  6. initialize cipher with mode to encrypt using the key from step4
  7. convert data(username/password) to encrypt to byte array with utf-8 encoding
  8. encrypt step 7 using step 6 cipher
  9. encode step8 in base 64 format
  10. convert step9 to string for final string encrypted message

What I have done so far,

function encrypt($data, $secret)  { 



    $key = sha1(utf8_encode($secret), true); <-- Step 1 & 2
    $iv = utf8_encode("jvz8bUAx"); <-- Do I use initialise vector on it?

    $key .= substr($key, 0, 8); 

    $method = 'des-ede3-cbc'; //<-- Is this cypher method correct from the above?


    if (strlen($data) % 8) {
        $data = str_pad($data, strlen($data) + 8 - strlen($data) % 8, "\0");
    }

    $encrypted = openssl_encrypt($data, $method, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv); //Force zero padding.

    $encrypted = urlencode(base64_encode($encrypted)); // Added the urlencode.....
    return $encrypted;
} 

Solution

  • In the Java-code, the encryption-algorithm is specified as DESede. This corresponds to DESede/ECB/PKCS5Padding, i.e. the ECB-mode and PKCS5-padding are used. This means for the PHP-code:

    • des-ede3 must be applied
    • all code portions related to an IV must be removed (since the ECB-mode doesn't use an IV)
    • the custom padding (which is by no means PKCS5-Padding) must be removed (since openssl_encrypt uses PKCS5-padding by default)

    In the Java-code, the SHA1-hash (with a size of 20 bytes) is extended to 24 bytes by appending 0-values. This extension must also be done in the PHP-code.

    A possible PHP-counterpart to the Java _encrypt-method is:

    function encrypt($data, $secret)  {
        $key = sha1(mb_convert_encoding($secret, "UTF-8"), true);                   // Create SHA-1 hash (20 byte) 
        $key = str_pad($key, 24, "\0");                                             // Extend to 24 byte by appending 0-values (would also happen automatically on openssl_encrypt-call)
        $encrypted = openssl_encrypt($data, 'DES-EDE3', $key, OPENSSL_RAW_DATA);    // Encryption: DESede (24 byte key), ECB-mode, PKCS5-Padding
        return base64_encode($encrypted);                                           // Base64-encoding
    }
    

    Finally: The Java-code has a number of disadvantages, e.g.

    • Triple-DES is used. A better choice would be AES, see e.g. here
    • the ECB-mode is used, which is unsafe per se, see e.g. here. A better choice would be CBC or GCM (the latter under AES).
    • SHA-1 is used as KDF (which gives a too short key, 20 bytes instead of the 24 bytes actually needed). A better choice would be PBKDF2.

    The latter just for the sake of completeness, as it's probably legacy code that can't be changed for any reason.