2015-10-06 11 views
5

Ho bisogno di decifrare alcuni dati che ricevo dal server, e il programmatore che ha fatto l'API mi ha indirizzato a questa classe Encrypter, per vedere cosa ha usato per crittografare.Come decodificare in testo Java (Android) che è stato crittografato con Crypt in Laravel?

Ora basato su quella classe, ho trovato che l'algoritmo utilizzato è AES128 CBC e che la stringa che ricevo è codificata Base64 e contiene altri dati, non solo il testo cifrato.

Vale a dire che se ricevo la seguente stringa:

eyJpdiI6InJsSzRlU3pDZTBBUVNwMzdXMjVcL0tBPT0iLCJ2YWx1ZSI6Ik5JOENsSVVWaWk2RGNhNlwvWjJNeG94UzVkclwvMGJOREQreWUyS1UzclRMND0iLCJtYWMiOiJhZTZkYjNkNGM2ZTliNmU0ZTc0MTRiNDBmMzFlZTJhNTczZWIxMjk4N2YwMjlhODA1NTIyMDEzODljNDY2OTk2In0 

dopo la decodifica base64 ottengo:

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

Sulla base line 99 di Encrypter classe (iv = base64_decode($payload['iv']);), ho eseguito un altro decodifica base64 sulla iv e value, e ottenuto un iv di lunghezza 16. Quelli che ho passato come parametri per la funzione di seguito:

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); 
} 

Ma sto ottenendo il seguente errore:

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) 

Nota: Lo String iv ha lunghezza di 16, ma iv.getBytes() restituisce un array di lunghezza 26.

Potrebbe punto qualcuno a dove sono andato storto, e come lo aggiusto. Grazie/

EDIT
Dopo il commento, ho fatto alcune modifiche, che risolvono l'errore precedente:
Prima ero decodifica base64 iv, convertendo i byte di stringa, poi passando che String al metodo decrypt , che in cambio chiama getBytes() su di esso. In qualche modo questo ha reso l'array di byte 26.
L'invio dell'array di byte che ho ottenuto dopo la decodifica in base64 al metodo decrypt ha risolto il problema.
Ora il metodo è il seguente:

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); 
} 

Ora ho un altro problema strano:
Il testo ho crittografato sul primo posto era KushtrimPacaj, ma il testo decifrato è s:13:"KushtrimPacaj";. Da dove viene quell'altra parte? 13 rappresenta forse la lunghezza di KushtrimPacaj?

Modifica
Ecco il codice di lavoro, nel caso qualcuno ne ha bisogno:
https://gist.github.com/KushtrimPacaj/43a383ab419fc222f80e

+0

Si prega di fornire un esempio completo. Se 'iv' è una stringa, qual è il suo valore? Hai dimenticato di decodificarlo? Non vedo alcun motivo per la lunghezza 26. Ricorda che non è possibile passare dati binari/non stampabili come stringa. Dovresti usare un array di byte. –

+0

@ArtjomB. Grazie per il commento, mi hai dato un'idea che ha portato al problema della lunghezza. Anche se ora ne ho un altro strano (vedi la domanda modificata). Qualche idea su come risolverlo? –

risposta

2

Si può vedere nel padAndMcrypt() function, che il dato valore $ viene serializzato utilizzando PHP di serialize() function. È possibile re-implement la funzione unserialize() in Java oppure è possibile dividere l'array di byte da soli se si crittografa sempre le stringhe in PHP.

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

codice completo:

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)); 
} 

Verifica il MAC è sempre una buona idea, perché impedisce alcuni attacchi come l'attacco imbottitura oracolo. È anche un ottimo modo per rilevare le modifiche generali dei testi cifrati.

codice completa con la verifica 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

Grazie amico. Sapevo come dividere la corda ma non osavo, perché non sapevo perché stava accadendo e se avrebbe sempre seguito lo stesso schema. Inoltre, puoi modificare la risposta e creare il secondo parametro in copyOfRange: "firstQuoteIndex + 1", poiché l'indice iniziale è inclusivo. StackOverflow non mi consente di apportare modifiche che siano meno di 6 caratteri. –

+0

Ho aggiunto la verifica MAC. Puoi verificare se funziona come previsto? –

+0

'// TODO: usa confronto costante di tempo 'Preferiresti fare un progetto Github per questo? –