2015-10-06 19 views
5

Tôi cần phải giải mã một số dữ liệu mà tôi nhận được từ máy chủ và người lập trình đã đưa API hướng tôi đến lớp Encrypter này, để xem những gì anh ta sử dụng để mã hóa.Làm thế nào để giải mã trong văn bản Java (Android) đã được mã hóa bằng Crypt trong Laravel?

Bây giờ dựa trên lớp đó, tôi thấy rằng thuật toán được sử dụng là AES128 CBC và chuỗi tôi nhận được được mã hóa Base64 và chứa dữ liệu khác, không chỉ là bản mã.

Cụ thể rằng nếu tôi nhận được chuỗi sau:

eyJpdiI6InJsSzRlU3pDZTBBUVNwMzdXMjVcL0tBPT0iLCJ2YWx1ZSI6Ik5JOENsSVVWaWk2RGNhNlwvWjJNeG94UzVkclwvMGJOREQreWUyS1UzclRMND0iLCJtYWMiOiJhZTZkYjNkNGM2ZTliNmU0ZTc0MTRiNDBmMzFlZTJhNTczZWIxMjk4N2YwMjlhODA1NTIyMDEzODljNDY2OTk2In0 

sau base64 giải mã tôi nhận được:

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

Dựa trên line 99 của Encrypter lớp (iv = base64_decode($payload['iv']);), tôi thực hiện một decode base64 trên ivvalue và có độ dài iv độ dài 16. Những thông tin này tôi đã chuyển làm thông số cho hàm bên dưới:

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

Nhưng tôi nhận được lỗi sau:

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) 

Lưu ý: Các chuỗi iv có chiều dài 16, nhưng iv.getBytes() trả về một mảng có độ dài 26.

có thể ai đó điểm tôi đến nơi tôi đã đi sai, và làm thế nào để sửa chữa nó. Thanks/

EDIT
Sau khi bình luận, tôi đã thực hiện một số thay đổi, đó là giải quyết các lỗi trên:
Trước khi tôi là base64 giải mã iv, chuyển đổi các byte để String, sau đó đi qua Chuỗi rằng với phương pháp giải mã , mà đổi lại gọi là getBytes() trên đó. Bằng cách nào đó điều này làm cho mảng byte có chiều dài là 26.
Gửi mảng byte mà tôi nhận được sau khi giải mã base64 cho phương thức giải mã đã khắc phục được sự cố.
Bây giờ phương pháp này như sau:

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

Bây giờ tôi có một vấn đề khác lạ:
Văn bản tôi mã hóa trên địa điểm đầu tiên là KushtrimPacaj, nhưng văn bản giải mã là s:13:"KushtrimPacaj";. Phần kia đến từ đâu? 13 có lẽ đại diện cho chiều dài KushtrimPacaj?

Sửa
Dưới đây là đoạn code làm việc, trong trường hợp bất cứ ai cần nó:
https://gist.github.com/KushtrimPacaj/43a383ab419fc222f80e

+0

Vui lòng cung cấp một ví dụ đầy đủ. Nếu 'iv' là một String, giá trị của nó là gì? Bạn đã quên giải mã chưa? Tôi không thấy lý do nào chiều dài sẽ là 26. Hãy nhớ rằng bạn không thể chuyển dữ liệu nhị phân/không thể in ra dưới dạng Chuỗi. Bạn sẽ cần phải sử dụng một mảng byte. –

+0

@ArtjomB. Cảm ơn bạn đã bình luận, bạn đã cho tôi một ý tưởng dẫn đến vấn đề về độ dài. Mặc dù bây giờ tôi có một cái khác lạ (xem câu hỏi đã chỉnh sửa). Bất kỳ ý tưởng về cách sửa chữa nó? –

Trả lời

2

Bạn có thể nhìn thấy trong padAndMcrypt() function, rằng $ giá trị nhất định được đăng bằng PHP serialize() function. Bạn có thể re-implement chức năng unserialize() trong Java hoặc bạn có thể tự tách mảng byte nếu bạn luôn mã hóa chuỗi trong PHP.

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

Full mã:

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

Thẩm định MAC luôn luôn là một ý tưởng tốt, vì nó ngăn chặn một số cuộc tấn công như tấn công đệm oracle. Nó cũng là một cách rất tốt để phát hiện các sửa đổi chung của các thuật toán mã hóa.

Toàn mã xác minh 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

Cảm ơn người đàn ông. Tôi biết cách chia Chuỗi nhưng không dám, bởi vì tôi không biết tại sao nó lại xảy ra, và liệu nó có luôn luôn theo cùng một khuôn mẫu hay không. Bạn cũng có thể chỉnh sửa câu trả lời và tạo tham số thứ hai trong copyOfRange: "firstQuoteIndex + 1", vì chỉ mục bắt đầu được bao gồm. Stackoverflow sẽ không cho phép tôi tạo một bản chỉnh sửa có ít hơn 6 ký tự. –

+0

Tôi đã thêm xác minh MAC. Bạn có thể kiểm tra xem nó có hoạt động như mong đợi không? –

+0

'// TODO: sử dụng so sánh thời gian không đổi' Bạn có muốn tạo dự án Github cho điều này không? –

Các vấn đề liên quan