Encryption mismatch between Java and PHP

删除回忆录丶 提交于 2019-12-12 07:16:50

问题


I'm working on an encryption system that passes the data to a 3rd party application. The encryption is done in Java and the decryption is done in PHP. Despite several attempts I can't get the encrypted string to be opened by the PHP application.

For testing purposes I created a PHP script that also encrypts the data, so I could compare the Java and PHP encrypted strings. The results match up to the 21st character, and then they differ. This is what I have:

// Java - Encrypt
private String EncryptAES(String text,String key) throws Exception
    {
      SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES");

      // Instantiate the cipher
      Cipher cipher = Cipher.getInstance("AES");

      cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
      byte[] encrypted = cipher.doFinal(text.getBytes());

      String encrypttext = new BASE64Encoder().encode(encrypted);

      return encrypttext;
    }

RESULT: TeUZAFxoFoQy/roPm5tXyPzJP/TLAwR1aIGn2xHbZpsbY1qrKwXfO+F/DAqmeTwB0b8e6dsSM+Yy0zrQt22E2Q== 

and

// PHP - Encrypt
<?php

$encrypt =  $crypt = openssl_encrypt($toCrypt,"AES256","key-32-char-long");
echo $encrypt; 

?>

RESULT: TeUZAFxoFoQy/roPm5tXyC05wta1Z5YOXcq4OtgFoSbfVi/bHAuD6B5tDthT8EcGXQir08UAx0QvcqRJ2fJmbQ==

Obviously something is being done right because part of the strings match, but obviously not everything is correct because the rest does not match. Also, if I try to decrypt the Java string in PHP, nothing happens:

// PHP - Decrypt
<?php
$toDecrypt = "TeUZAFxoFoQy/roPm5tXyPzJP/TLAwR1aIGn2xHbZpsbY1qrKwXfO+F/DAqmeTwB0b8e6dsSM+Yy0zrQt22E2Q==";
$decrypt = openssl_decrypt($toDecrypt,"AES256","<key-32-char-long>");
echo $decrypt;

?>

RESULT: <nothing>

Does anyone have any ideas what might be happening?


回答1:


Since both encrypted strings start with the same characters, it looks like you're using ECB in one and CBC in the other.




回答2:


What about this (to decrypt within Your PHP):

$toDecrypt = "TeUZAFxoFoQy/roPm5tXyPzJP/TLAwR1aIGn2xHbZpsbY1qrKwXfO+F/DAqmeTwB0b8e6dsSM+Yy0zrQt22E2Q==";
$decrypt = openssl_decrypt(base64_decode($toDecrypt),"AES256","key-32-char-long");
echo $decrypt;

You should decrypt the base64 decoded string and for decrypting You should call openssl_decrypt not openssl_encrypt :-)




回答3:


you are using getBytes in java

instead use getBytes(Charset) method to ensure the same encoding of the key and plaintext as the one used in php

(dump the byte array in both and see if they match before you go on)




回答4:


Some remarks:

  • the key size is not explicit in the java program
  • did you check what is the block cipher modes of operation of both programs ? CBC, ECB, OFB, etc... ? Some can require an IV to passe with the encrypted data.
  • last idea: what is the padding used by the Java and PHP programs ?

This document can help you: you have all the valid combinations cipher / block cipher mode / padding mode / key size.




回答5:


I have just got this working. The thing to do is to encode the string as UTF-8 before padding and then send the byte array to encrypt. UTF-8 represents accented characters as two hex bytes c3 xx so the converted string will be longer if it contains characters that need to be encoded. BTW the c3 character is "A" with a "~" on top so if your decryption shows up with these odd characters then you haven't recoded the decryption.

import java.security.NoSuchAlgorithmException;

import javax.crypto.Cipher; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec;

import android.util.Log;

public class MCrypt {

private IvParameterSpec ivspec;
private SecretKeySpec keyspec;
private Cipher cipher;
private String iv = "cant hear you";
private String SecretKey = "top secret";

public MCrypt()
{
    ivspec = new IvParameterSpec(iv.getBytes());
    keyspec = new SecretKeySpec(SecretKey.getBytes(), "AES");                        
    try {            
        cipher = Cipher.getInstance("AES/CBC/NoPadding");
    } catch (NoSuchAlgorithmException e) {
         e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        e.printStackTrace();
    }
}

public byte[] encrypt(String text) throws Exception{
    if(text == null || text.length() == 0) throw new Exception("Empty string");

    byte[] bs = text.getBytes("UTF-8");

    byte[] toEncrypt = padBytes(bs);
    byte[] encrypted = null;
    try {
        cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
        encrypted = cipher.doFinal(toEncrypt);
    } catch (Exception e){                       
            throw new Exception("[encrypt] " + e.getMessage());
    }
    return encrypted;
}

public byte[] decrypt(String code) throws Exception{
    if(code == null || code.length() == 0)  throw new Exception("Empty string");        
    byte[] decrypted = null;
    try {
        cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);                
        decrypted = cipher.doFinal(hexToBytes(code));
    } catch (Exception e){
        throw new Exception("[decrypt] " + e.getMessage());
    }
    return decrypted;
}



    public static String bytesToHex(byte[] data){
        if (data==null){
            return null;
        }            
        int len = data.length;
        String str = "";
        for (int i=0; i<len; i++) {
            if ((data[i]&0xFF)<16)
                    str = str + "0" + java.lang.Integer.toHexString(data[i]&0xFF);
            else
                    str = str + java.lang.Integer.toHexString(data[i]&0xFF);
        }
        return str;
    }


    public static byte[] hexToBytes(String str) {
            if (str==null) {
                    return null;
            } else if (str.length() < 2) {
                    return null;
            } else {
                    int len = str.length() / 2;
                    byte[] buffer = new byte[len];
                    for (int i=0; i<len; i++) {
                            buffer[i] = (byte) Integer.parseInt(str.substring(i*2,i*2+2),16);
                    }
                    return buffer;
            }
    }



    private static byte[] padBytes(byte[] source){
        char paddingChar = ' ';
        int size = 16;
        int x = source.length % size;
        int padLength = size - x;
        int bufferLength = source.length + padLength;
        byte[] ret = new byte[bufferLength];
        int i = 0;
        for ( ; i < source.length; i++){
            ret[i] = source[i];
        }
        for ( ; i < bufferLength; i++){
            ret[i] = (byte)paddingChar;
        }

        return ret;
    }
} // class close

The pHp side is similar... ( I cannot figure out how to enter pHp code here!!!!

class MCrypt
{               
private $iv =  'adfdadfgfd'; #Same as in JAVA
private $key = 'adfadfdfdafadfa'; #Same as in JAVA


function __construct()
{
}

function encrypt($str) {
  $iv = $this->iv;
  $td = mcrypt_module_open('rijndael-128', '', 'cbc', $iv);
  mcrypt_generic_init($td, $this->key, $iv);
  $s = padString(utf8_encode($str));
  $encrypted = mcrypt_generic($td, $s);
    //echo $encrypted;
  mcrypt_generic_deinit($td);
  mcrypt_module_close($td);
  return bin2hex($encrypted);
}

function decrypt($code) {
  $code = $this->hex2bin($code);
  $iv = $this->iv;
  $td = mcrypt_module_open('rijndael-128', '', 'cbc', $iv);
  mcrypt_generic_init($td, $this->key, $iv);
  $decrypted = mdecrypt_generic($td, $code);
  mcrypt_generic_deinit($td);
  mcrypt_module_close($td);
  return (trim(utf8_decode($decrypted)));
}

protected function hex2bin($hexdata) {
  $bindata = '';
  for ($i = 0; $i < strlen($hexdata); $i += 2) {
    $bindata .= chr(hexdec(substr($hexdata, $i, 2)));
  }
  return $bindata;
}   
}

that's it, quite straightforward really... Oh by the way, if you do the padding yourself then it does not matter if the method is Padding or NoPadding!



来源:https://stackoverflow.com/questions/10451068/encryption-mismatch-between-java-and-php

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!