2015-10-06 93 views
5

我需要解密从服务器收到的一些数据,并且让API编程的程序员指示我去看这个Encrypter类,以查看他用来加密的内容。如何在使用Laravel中的Crypt加密的Java(Android)文本中解密?

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

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

eyJpdiI6InJsSzRlU3pDZTBBUVNwMzdXMjVcL0tBPT0iLCJ2YWx1ZSI6Ik5JOENsSVVWaWk2RGNhNlwvWjJNeG94UzVkclwvMGJOREQreWUyS1UzclRMND0iLCJtYWMiOiJhZTZkYjNkNGM2ZTliNmU0ZTc0MTRiNDBmMzFlZTJhNTczZWIxMjk4N2YwMjlhODA1NTIyMDEzODljNDY2OTk2In0 

BASE64解码后获得:基于Encrypter类(iv = base64_decode($payload['iv']);)的line 99

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

,我执行另一BASE64解码上ivvalue,并得到了长度为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) 

注:弦乐iv有16的长度,但iv.getBytes()返回长度为26

可能有人点的阵列我到我出错的地方,我该如何解决。 感谢/

EDIT
注释后,我做了一些改变,即解决了上述错误:
以前,我是BASE64解码iv,字节转换为字符串,然后使该字符串的解密方法,它作为回报在其上调用getBytes()。不知何故,这使得字节数组的长度为26.
将base64解码后获得的字节数组发送到解密方法解决了问题。
现在的方法如下:

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,但解密的文字是s:13:"KushtrimPacaj";。 其他部分来自哪里? 13也许代表KushtrimPacaj的长度?

编辑
这里是工作的代码,如果有人需要它:
https://gist.github.com/KushtrimPacaj/43a383ab419fc222f80e

+0

请提供一个完整的示例。如果'iv'是一个字符串,它的价值是什么?你忘了解码吗?我认为没有理由长度将是26.请记住,您不能将二进制/不可打印的数据作为字符串传递。你需要使用一个字节数组。 –

+0

@ ArtjomB。感谢评论,你给了我一个导致长度问题的想法。虽然现在我有另一个奇怪的(见编辑的问题)。任何想法如何解决它? –

回答

2

可以在padAndMcrypt() function看到,给定$值是使用PHP的serialize() function序列化。您可以在Java中使用re-implementunserialize()函数,或者如果您始终使用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始终是一个好主意,因为它可以防止一些攻击如填充神谕攻击。这也是一种检测密文的一般修改的好方法。

完整代码与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; 
} 
+0

谢谢你。我知道如何拆分字符串但不敢,因为我不知道它为什么会发生,以及它是否总是遵循相同的模式。也可以编辑答案,并在copyOfRange中创建第二个参数:“firstQuoteIndex + 1”,因为起始索引是包含性的。 Stackoverflow不会让我编辑它少于6个字符。 –

+0

我已添加MAC验证。你能检查它是否按预期工作? –

+0

'// TODO:use time-constant compare'你愿意为此制作一个Github项目吗? –

相关问题