How to decrypt in Java (Android) text that was encrypted with Crypt in Laravel?(如何解密在 Laravel 中使用 Crypt 加密的 Java (Android) 文本?)
问题描述
我需要解密我从服务器接收到的一些数据,制作 API 的程序员将我引导到这个 Encrypter 类,看看他用来加密什么.
现在基于该类,我发现使用的算法是 AES128 CBC,并且我收到的字符串是 Base64 编码的,并且包含其他数据,而不仅仅是密文.
即如果我收到以下字符串:
<预> <代码> eyJpdiI6InJsSzRlU3pDZTBBUVNwMzdXMjVcL0tBPT0iLCJ2YWx1ZSI6Ik5JOENsSVVWaWk2RGNhNlwvWjJNeG94UzVkclwvMGJOREQreWUyS1UzclRMND0iLCJtYWMiOiJhZTZkYjNkNGM2ZTliNmU0ZTc0MTRiNDBmMzFlZTJhNTczZWIxMjk4N2YwMjlhODA1NTIyMDEzODljNDY2OTk2In0base64 解码后我得到:
{iv":rlK4eSzCe0AQSp37W25/KA==",值":NI8ClIUVii6Dca6/Z2MxoxS5dr/0bNDD+ye2KU3rTL4=",mac":"ae6db3d4c6e9b6e4e7414b40f31ee2a573eb12987f029a80552201389c466996"}
基于 Encrypter
类的 line 99
( iv = base64_decode($payload['iv']);
),我执行了另一个在 iv
和 value
上进行 base64 解码,得到长度为 16 的 iv
.我将这些作为参数传递给下面的函数:
public static String decrypt(String iv, String encryptedData) 抛出异常 {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);字节[] decValue = c.doFinal(decordedValue);返回新字符串(decValue);}
但我收到以下错误:
10-06 19:13:33.601 12895-12895/?W/System.err:java.security.InvalidAlgorithmParameterException:预期的 IV 长度为 1610-06 19:13:33.601 12895-12895/?W/System.err:在 com.android.org.conscrypt.OpenSSLCipher.engineInitInternal(OpenSSLCipher.java:281)10-06 19:13:33.601 12895-12895/?W/System.err:在 com.android.org.conscrypt.OpenSSLCipher.engineInit(OpenSSLCipher.java:323)10-06 19:13:33.601 12895-12895/?W/System.err:在 javax.crypto.Cipher.init(Cipher.java:751)10-06 19:13:33.601 12895-12895/?W/System.err:在 javax.crypto.Cipher.init(Cipher.java:701)10-06 19:13:33.601 12895-12895/?W/System.err:在 com.example.kushtrim.testproject.MainActivity.decrypt(MainActivity.java:62)10-06 19:13:33.601 12895-12895/?W/System.err:在 com.example.kushtrim.testproject.MainActivity.onCreate(MainActivity.java:45)10-06 19:13:33.601 12895-12895/?W/System.err:在 android.app.Activity.performCreate(Activity.java:5990)10-06 19:13:33.601 12895-12895/?W/System.err:在 android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106)10-06 19:13:33.601 12895-12895/?W/System.err:在 android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2278)10-06 19:13:33.601 12895-12895/?W/System.err:在 android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387)10-06 19:13:33.601 12895-12895/?W/System.err:在 android.app.ActivityThread.access$800(ActivityThread.java:151)10-06 19:13:33.601 12895-12895/?W/System.err:在 android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)10-06 19:13:33.602 12895-12895/?W/System.err:在 android.os.Handler.dispatchMessage(Handler.java:102)10-06 19:13:33.602 12895-12895/?W/System.err:在 android.os.Looper.loop(Looper.java:135)10-06 19:13:33.602 12895-12895/?W/System.err:在 android.app.ActivityThread.main(ActivityThread.java:5254)10-06 19:13:33.602 12895-12895/?W/System.err:在 java.lang.reflect.Method.invoke(Native Method)10-06 19:13:33.602 12895-12895/?W/System.err:在 java.lang.reflect.Method.invoke(Method.java:372)10-06 19:13:33.602 12895-12895/?W/System.err:在 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)10-06 19:13:33.602 12895-12895/?W/System.err:在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
注意:字符串 iv
的长度为 16,但 iv.getBytes()
返回一个长度为 26 的数组.
谁能指出我哪里出错了,我该如何解决.谢谢/
编辑
评论后,我做了一些更改,解决了上述错误:
在我对 iv
进行 base64 解码之前,将字节转换为字符串,然后将该字符串传递给解密方法,该方法反过来调用它的 getBytes().不知何故,这使得字节数组的长度为 26.
将我base64解码后得到的字节数组发送到decrypt方法解决了问题.
现在方法如下:
public static String decrypt(byte[] iv, String encryptedData) 抛出异常 {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);字节[] decValue = c.doFinal(decordedValue);返回新字符串(decValue);}
现在我又遇到了一个奇怪的问题:
我首先加密的文本是 KushtrimPacaj
,但解密后的文本是 s:13:"KushtrimPacaj";
.那另一部分是从哪里来的?13或许代表KushtrimPacaj
的长度?
编辑
这是工作代码,以防万一有人需要它:
https://gist.github.com/KushtrimPacaj/43a383ab419fc222f80e
可以在padAndMcrypt()
函数,即给定的 $value 使用 PHP 的 serialize()
函数.你可以重新实现 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) 抛出异常 {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);密码 c = Cipher.getInstance("AES/CBC/PKCS7Padding");//或 PKCS5Paddingc.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) 抛出异常 {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))返回空值;//或抛出异常密码 c = Cipher.getInstance("AES/CBC/PKCS7Padding");//或 PKCS5Paddingc.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));}/* 恒定时间比较以防止对无效身份验证标签的定时攻击.*/公共静态布尔secureEquals(最终字节[]已知,最终字节[]用户){int knownLen = known.length;int userLen = user.length;int 结果 = knownLen ^ userLen;for (int i = 0; i < knownLen; i++) {结果 |= 已知[i] ^ 用户[i % userLen];}返回结果 == 0;}
I need to decrypt some data that I receive from the server, and the programmer who made the API directed me to this Encrypter class, to see what he used to encrypt.
Now based on that class, I found that the algorithm used is AES128 CBC, and that the string I receive is Base64 encoded and contains other data, not just the ciphertext.
Namely that if I receive the following String:
eyJpdiI6InJsSzRlU3pDZTBBUVNwMzdXMjVcL0tBPT0iLCJ2YWx1ZSI6Ik5JOENsSVVWaWk2RGNhNlwvWjJNeG94UzVkclwvMGJOREQreWUyS1UzclRMND0iLCJtYWMiOiJhZTZkYjNkNGM2ZTliNmU0ZTc0MTRiNDBmMzFlZTJhNTczZWIxMjk4N2YwMjlhODA1NTIyMDEzODljNDY2OTk2In0
after base64 decoding I get:
{"iv":"rlK4eSzCe0AQSp37W25/KA==","value":"NI8ClIUVii6Dca6/Z2MxoxS5dr/0bNDD+ye2KU3rTL4=","mac":"ae6db3d4c6e9b6e4e7414b40f31ee2a573eb12987f029a80552201389c466996"}
Based on line 99
of Encrypter
class ( iv = base64_decode($payload['iv']);
), I performed another base64 decode on the iv
and the value
, and got an iv
of length 16. Those I passed as parameters to the function below:
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);
}
But I'm getting the following error:
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)
Note: The String iv
has length of 16, but iv.getBytes()
returns an array of length 26.
Could someone point me to where I went wrong, and how do I fix it. Thanks/
EDIT
After the comment, I made some changes, that resolved the above error:
Before I was base64 decoding iv
, converting the bytes to String, then passing that String to the decrypt method, which in return called the getBytes() on it. Somehow this made the byte array have a length of 26.
Sending the byte array I obtained after base64 decoding to the decrypt method fixed the problem.
Now the method is as follows:
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);
}
Now I have another weird problem:
The text I encrypted on the first place was KushtrimPacaj
, but the decrypted text is s:13:"KushtrimPacaj";
.
Where is that other part coming from ? 13 perhaps represents the length of KushtrimPacaj
?
Edit
Here's the working code, in case anyone needs it :
https://gist.github.com/KushtrimPacaj/43a383ab419fc222f80e
You can see in the padAndMcrypt()
function, that the given $value is serialized using PHP's serialize()
function. You can re-implement the unserialize()
function in Java or you can split the byte array yourself if you're always encrypting strings in PHP.
int firstQuoteIndex = 0;
while(decValue[firstQuoteIndex] != (byte)'"') firstQuoteIndex++;
return new String(Arrays.copyOfRange(decValue, firstQuoteIndex + 1, decValue.length-2));
Full code:
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));
}
Verifying the MAC is always a good idea, because it prevents some attacks such as the padding oracle attack. It is also a very good way to detect general modifications of ciphertexts.
Full code with MAC verification:
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;
}
这篇关于如何解密在 Laravel 中使用 Crypt 加密的 Java (Android) 文本?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:如何解密在 Laravel 中使用 Crypt 加密的 Java (Andro
基础教程推荐
- 如何使用 Eclipse 检查调试符号状态? 2022-01-01
- 在螺旋中写一个字符串 2022-01-01
- 如何使用 Stream 在集合中拆分奇数和偶数以及两者的总和 2022-01-01
- Spring Boot Freemarker从2.2.0升级失败 2022-01-01
- 由于对所需库 rt.jar 的限制,对类的访问限制? 2022-01-01
- 如何强制对超级方法进行多态调用? 2022-01-01
- 如何在不安装整个 WTP 包的情况下将 Tomcat 8 添加到 Eclipse Kepler 2022-01-01
- Java 中保存最后 N 个元素的大小受限队列 2022-01-01
- 首次使用 Hadoop,MapReduce Job 不运行 Reduce Phase 2022-01-01
- 如何对 HashSet 进行排序? 2022-01-01