2015-10-06 27 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項目嗎? –

相關問題