如何解密用Laravel中的Crypt加密的Java(Android)文本?

时间:2015-10-06 17:18:26

标签: java php android encryption cryptography

我需要解密从服务器收到的一些数据,并且制作API的程序员将我引导到此Encrypter类,以查看他用于加密的内容。

现在基于该类,我发现使用的算法是AES128 CBC,我收到的字符串是Base64编码的,包含其他数据,而不仅仅是密文。

即如果我收到以下字符串:

eyJpdiI6InJsSzRlU3pDZTBBUVNwMzdXMjVcL0tBPT0iLCJ2YWx1ZSI6Ik5JOENsSVVWaWk2RGNhNlwvWjJNeG94UzVkclwvMGJOREQreWUyS1UzclRMND0iLCJtYWMiOiJhZTZkYjNkNGM2ZTliNmU0ZTc0MTRiNDBmMzFlZTJhNTczZWIxMjk4N2YwMjlhODA1NTIyMDEzODljNDY2OTk2In0

在base64解码后我得到:

{"iv":"rlK4eSzCe0AQSp37W25\/KA==","value":"NI8ClIUVii6Dca6\/Z2MxoxS5dr\/0bNDD+ye2KU3rTL4=","mac":"ae6db3d4c6e9b6e4e7414b40f31ee2a573eb12987f029a80552201389c466996"}

基于line 99类(Encrypter)的iv = base64_decode($payload['iv']);,我在ivvalue上执行了另一个base64解码,得到了{{}长度为16}。我作为参数传递给下面的函数:

iv

但我收到以下错误:

    public static String decrypt(String iv, String encryptedData) throws Exception {
    byte[] keyValue = "zy2dEd1pKG5i3WuWbvOBolFQR84AYbvN".getBytes();
    Key key = new SecretKeySpec(keyValue, "AES");        
    Cipher c = Cipher.getInstance("AES/CBC/PKCS7Padding");
    c.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv.getBytes()));
    byte[] decordedValue = Base64.decode(encryptedData.getBytes(), Base64.DEFAULT);
    byte[] decValue = c.doFinal(decordedValue);
    return new String(decValue);
}

注意:字符串10-06 19:13:33.601 12895-12895/? W/System.err: java.security.InvalidAlgorithmParameterException: expected IV length of 16 10-06 19:13:33.601 12895-12895/? W/System.err: at com.android.org.conscrypt.OpenSSLCipher.engineInitInternal(OpenSSLCipher.java:281) 10-06 19:13:33.601 12895-12895/? W/System.err: at com.android.org.conscrypt.OpenSSLCipher.engineInit(OpenSSLCipher.java:323) 10-06 19:13:33.601 12895-12895/? W/System.err: at javax.crypto.Cipher.init(Cipher.java:751) 10-06 19:13:33.601 12895-12895/? W/System.err: at javax.crypto.Cipher.init(Cipher.java:701) 10-06 19:13:33.601 12895-12895/? W/System.err: at com.example.kushtrim.testproject.MainActivity.decrypt(MainActivity.java:62) 10-06 19:13:33.601 12895-12895/? W/System.err: at com.example.kushtrim.testproject.MainActivity.onCreate(MainActivity.java:45) 10-06 19:13:33.601 12895-12895/? W/System.err: at android.app.Activity.performCreate(Activity.java:5990) 10-06 19:13:33.601 12895-12895/? W/System.err: at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106) 10-06 19:13:33.601 12895-12895/? W/System.err: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2278) 10-06 19:13:33.601 12895-12895/? W/System.err: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387) 10-06 19:13:33.601 12895-12895/? W/System.err: at android.app.ActivityThread.access$800(ActivityThread.java:151) 10-06 19:13:33.601 12895-12895/? W/System.err: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303) 10-06 19:13:33.602 12895-12895/? W/System.err: at android.os.Handler.dispatchMessage(Handler.java:102) 10-06 19:13:33.602 12895-12895/? W/System.err: at android.os.Looper.loop(Looper.java:135) 10-06 19:13:33.602 12895-12895/? W/System.err: at android.app.ActivityThread.main(ActivityThread.java:5254) 10-06 19:13:33.602 12895-12895/? W/System.err: at java.lang.reflect.Method.invoke(Native Method) 10-06 19:13:33.602 12895-12895/? W/System.err: at java.lang.reflect.Method.invoke(Method.java:372) 10-06 19:13:33.602 12895-12895/? W/System.err: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903) 10-06 19:13:33.602 12895-12895/? W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698) 的长度为16,但iv返回长度为26的数组。

有人能指出我哪里出错,我该如何解决它。 感谢/

修改
评论结束后,我做了一些修改,解决了上述错误:
在我使用base64解码iv.getBytes()之前,将字节转换为String,然后将该String传递给decrypt方法,后者在其上调用getBytes()。不知怎的,这使得字节数组的长度为26 将base64解码后得到的字节数组发送到解密方法修复了问题 现在方法如下:

iv

现在我有另一个奇怪的问题:
我首先加密的文字是public static String decrypt(byte[] iv, String encryptedData) throws Exception { byte[] keyValue = "zy2dEd1pKG5i3WuWbvOBolFQR84AYbvN".getBytes(); Key key = new SecretKeySpec(keyValue, "AES"); Cipher c = Cipher.getInstance("AES/CBC/PKCS7Padding"); c.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv)); byte[] decordedValue = Base64.decode(encryptedData.getBytes(), Base64.DEFAULT); byte[] decValue = c.doFinal(decordedValue); return new String(decValue); } ,但解密的文字是KushtrimPacaj。 其他部分来自哪里? 13或许代表s:13:"KushtrimPacaj";的长度?

修改
这是工作代码,万一有人需要它:
https://gist.github.com/KushtrimPacaj/43a383ab419fc222f80e

1 个答案:

答案 0 :(得分:2)

您可以在padAndMcrypt() function中看到,使用PHP serialize() function序列化了给定的$值。您可以在Java中re-implement unserialize()函数,或者如果您始终在PHP中加密字符串,则可以自行拆分字节数组。

int firstQuoteIndex = 0;
while(decValue[firstQuoteIndex] != (byte)'"') firstQuoteIndex++;
return new String(Arrays.copyOfRange(decValue, firstQuoteIndex + 1, decValue.length-2));

完整代码:

public static String decrypt(byte[] keyValue, String ivValue, String encryptedData) throws Exception {
    Key key = new SecretKeySpec(keyValue, "AES");
    byte[] iv = Base64.decode(ivValue.getBytes("UTF-8"), Base64.DEFAULT);
    byte[] decodedValue = Base64.decode(encryptedData.getBytes("UTF-8"), Base64.DEFAULT);

    Cipher c = Cipher.getInstance("AES/CBC/PKCS7Padding"); // or PKCS5Padding
    c.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
    byte[] decValue = c.doFinal(decodedValue);

    int firstQuoteIndex = 0;
    while(decValue[firstQuoteIndex] != (byte)'"') firstQuoteIndex++;
    return new String(Arrays.copyOfRange(decValue, firstQuoteIndex + 1, decValue.length-2));
}

验证MAC总是一个好主意,因为它可以防止某些攻击,例如padding oracle攻击。这也是检测密文的一般修改的一种非常好的方法。

MAC验证的完整代码:

public static String decrypt(byte[] keyValue, String ivValue, String encryptedData, String macValue) throws Exception {
    Key key = new SecretKeySpec(keyValue, "AES");
    byte[] iv = Base64.decode(ivValue.getBytes("UTF-8"), Base64.DEFAULT);
    byte[] decodedValue = Base64.decode(encryptedData.getBytes("UTF-8"), Base64.DEFAULT);

    SecretKeySpec macKey = new SecretKeySpec(keyValue, "HmacSHA256");
    Mac hmacSha256 = Mac.getInstance("HmacSHA256");
    hmacSha256.init(macKey);
    hmacSha256.update(ivValue.getBytes("UTF-8"));
    byte[] calcMac = hmacSha256.doFinal(encryptedData.getBytes("UTF-8"));
    byte[] mac = Hex.decodeHex(macValue.toCharArray());
    if (!secureEquals(calcMac, mac))
        return null; // or throw exception

    Cipher c = Cipher.getInstance("AES/CBC/PKCS7Padding"); // or PKCS5Padding
    c.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
    byte[] decValue = c.doFinal(decodedValue);

    int firstQuoteIndex = 0;
    while(decValue[firstQuoteIndex] != (byte)'"') firstQuoteIndex++;
    return new String(Arrays.copyOfRange(decValue, firstQuoteIndex + 1, decValue.length-2));
}

/* Constant-time compare to prevent timing attacks on invalid authentication tags. */
public static boolean secureEquals(final byte[] known, final byte[] user) {
    int knownLen = known.length;
    int userLen = user.length;

    int result = knownLen ^ userLen;
    for (int i = 0; i < knownLen; i++) {
        result |= known[i] ^ user[i % userLen];
    }
    return result == 0;
}