Android和PHP之间加密/解密的问题

时间:2012-03-20 10:55:58

标签: php android encryption

你好其他程序员,

我按照http://www.androidsnippets.com/encrypt-decrypt-between-android-and-php上的教程将PHP加密的String发送回我的A​​ndroid客户端,我想再次解密它。加密/解密部分在我的PHP服务器上像魅力一样运行,我也能够将加密的字符串传递到我的Android客户端,但我无法弄清楚如何在Android中解密字符串......我得到的只是是一些神秘的(哈哈)标志......

问题描述: 我将通过REST Api在Android客户端上生成的IV和加密密钥发送到我的PHP服务器,该服务器处理一些数据库查询并使用给定的IV和加密密钥加密字符串,然后将其返回到Android客户端。 客户端能够读出返回的值并获取加密的字符串,但加密不是等待...

我严格遵循上层教程,但我无法弄清楚问题会是什么......

这是PHP加密的代码:

class MCrypt {
private $iv;
private $key;
private $mode;

function __construct() {
    $this->mode = 'cbc';
}

public function setIV($iv) {
    $this->iv = $iv;
}

public function setKey($key) {
    //$this->key = $this->hex2bin($key);
    $this->key = $key;
}

public function getIV() {
    return $this->iv;
}

public function getKey() {
    return $this->key;
}

public function encrypt($value) {
    $iv = $this->iv;
    $td = mcrypt_module_open('rijndael-128', '', $this->mode, $iv);

    mcrypt_generic_init($td, $this->key, $iv);

    $encrypted = mcrypt_generic($td, $value);

    mcrypt_generic_deinit($td);
    mcrypt_module_close($td);

    return bin2hex($encrypted);
}


public function decrypt($value) {
    $value = $this->hex2bin($value);
    $iv = $this->iv;

    $td = mcrypt_module_open('rijndael-128', '', $this->mode, $iv);

    mcrypt_generic_init($td, $this->key, $iv);
    $decrypted = mdecrypt_generic($td, $value);

    mcrypt_generic_deinit($td);
    mcrypt_module_close($td);

    return utf8_encode(trim($decrypted));
}


protected function hex2bin($hexdata) {
    $bindata = '';

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

    return $bindata;
}

}

以下是我实施它的方式:

$value = "some data I want to read out";
// create new MCrypt object
$mcrypt = new MCrypt();
// set IV to hash
$mcrypt->setIV($data['iv']);
// set key to salt
$mcrypt->setKey($data['key']);
// encrypt the key
$enc = $mcrypt->encrypt($value);

return array("data"=>$enc);

因此,这将返回正确编码的字符串(使用PHP解密函数进行测试,该函数将字符串解码为等待结果)。

这是JAVA实现的样子: import java.security.NoSuchAlgorithmException; import javax.crypto.Cipher; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec;

import android.util.Log;

公共课MCrypt {     private static final String TAG = MCrypt.class.getSimpleName();

private String iv;
private String key;
private IvParameterSpec mIvParameterSpec;
private SecretKeySpec mSecretKeySpec;
private Cipher mCipher;

public MCrypt(String iv, String key) {
    Log.v(TAG, "IV Bytes.length=" + iv.getBytes().length);
    this.iv = cut(iv, 16);
    Log.i(TAG, "IV = " + this.iv + ", Bytelength=" + this.iv.getBytes().length);
    this.key = key;

    mIvParameterSpec = new IvParameterSpec(this.iv.getBytes());
    mSecretKeySpec = new SecretKeySpec(this.key.getBytes(), "AES");

    try {
        mCipher = Cipher.getInstance("AES/CBC/NoPadding");
    } catch (NoSuchAlgorithmException e) {
        Log.e(TAG, "Got Exception while initializing mCipher: " + e.toString(), e);
    } catch (NoSuchPaddingException e) {
        Log.e(TAG, "Got Exception while initializing mCipher: " + e.toString(), e);
    }
}

public String getIV() {
    return this.iv;
}

public byte[] decrypt(String code) throws Exception {
    if(code == null || code.length() == 0) {
        throw new Exception("Emtpy string given");
    }

    byte[] decrypted = null;

    try {
        mCipher.init(Cipher.DECRYPT_MODE, mSecretKeySpec, mIvParameterSpec);
        decrypted = mCipher.doFinal(hexToBytes(code));
    } catch(Exception e) {
        throw new Exception("[decrypt] " + e.getMessage());
    }

    return decrypted;
}

private 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 String cut(String s, int n) {
    byte[] sBytes = s.getBytes();

    if(sBytes.length < n) {
        n = sBytes.length;
    }

    int n16 = 0;
    boolean extraLong = false;

    int i = 0;

    while(i < n) {
        n16 += (extraLong) ? 2 : 1;
        extraLong = false;

        if((sBytes[i] & 0x80) == 0) {
            i += 1;
        }
        else if((sBytes[i] & 0xC0) == 0x80) {
            i += 2;
        }
        else if((sBytes[i] & 0xE0) == 0xC0) {
            i += 3;
        } else {
            i += 4;
            extraLong = true;
        }
    }
    return s.substring(0, n16);
}
}

如果我现在想要/解密一个字符串(例如“Hello World”),我会得到这个字符串:af8d89fa962794d5b945b74c4b6fd9b6,由PHP实现解码为“Hello World”,但JAVA只是给出了一些有趣的迹象。 ..

我知道这是很多代码,但我对此问题的任何帮助表示感谢!

提前感谢您的耐心和时间阅读主题;)


所以,到目前为止,我得到的是这些'神秘的'符号而不是预期的解密输出实际上是字节数组本身的地址,即使我尝试使用String str = new String(byteArray);从这个字节数组创建一个新的String我得到一个看起来像[B@44e8c0c8的输出......这里发生了什么?任何想法??

2 个答案:

答案 0 :(得分:1)

所以,经过长时间的研究和(最后)重启我的电脑后,我找到了我想与你分享的问题解决方案:

首先,我将hex2Bin(String str)函数更改为以下内容:

private byte[] hex2ByteArray(String s) {
    int len = s.length();
    byte[] data = new byte[len / 2];
    for (int i = 0; i < len; i += 2) {
        data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                             + Character.digit(s.charAt(i+1), 16));
    }
    return data;
}

之后,我仍然一遍又一遍地得到同样的错误,Android只会显示字节数组的地址而不是解密的字符串,所以我咨询了哈利波特,他告诉我魔法能够得到这个排序现在,我希望与您分享......最重要的是......如果您每天使用Eclipse / Motodev Studio超过几个小时,重新启动计算机 - 它将为您节省一些时间和金钱,当然;)

答案 1 :(得分:0)

您遇到此问题可能是因为当您在PHP中调用encrypt方法时,它使用一个密钥进行加密,但在Java代码中解密加密字符串时,您必须生成密钥这将与PHP密钥的不同,因此您将在解密的字符串中获取这些特殊字符

您可以将对象从PHP传递到Java代码或使用PHP-Java桥接