函数解密抛出 javax.crypto.BadPaddingException: pad block 在 android 中的 SimpleCrypto 类中损坏
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20063239/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
function decrypt throws javax.crypto.BadPaddingException: pad block corrupted in class SimpleCrypto in android
提问by Opax Web
I am writing an android app that decrypts some text stored in a file. I used the following code -
我正在编写一个 android 应用程序,它可以解密存储在文件中的一些文本。我使用了以下代码 -
public class SimpleCrypto {
public static String encrypt(String seed, String cleartext)
throws Exception {
byte[] rawKey = getRawKey(seed.getBytes());
byte[] result = encrypt(rawKey, cleartext.getBytes());
return toHex(result);
}
public static String decrypt(String seed, String encrypted)
throws Exception {
byte[] rawKey = getRawKey(seed.getBytes());
byte[] enc = toByte(encrypted);
byte[] result = decrypt(rawKey, enc);
return new String(result);
}
private static byte[] getRawKey(byte[] seed) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
sr.setSeed(seed);
kgen.init(128, sr); // 192 and 256 bits may not be available
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
return raw;
}
private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
private static byte[] decrypt(byte[] raw, byte[] encrypted)
throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);//this is the line that throws error
return decrypted;
}
public static String toHex(String txt) {
return toHex(txt.getBytes());
}
public static String fromHex(String hex) {
return new String(toByte(hex));
}
public static byte[] toByte(String hexString) {
int len = hexString.length() / 2;
byte[] result = new byte[len];
for (int i = 0; i < len; i++)
result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2),
16).byteValue();
return result;
}
public static String toHex(byte[] buf) {
if (buf == null)
return "";
StringBuffer result = new StringBuffer(2 * buf.length);
for (int i = 0; i < buf.length; i++) {
appendHex(result, buf[i]);
}
return result.toString();
}
private final static String HEX = "0123456789ABCDEF";
private static void appendHex(StringBuffer sb, byte b) {
sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));
}
}
EDIT: This is the text that I am trying to decrypt -
编辑:这是我试图解密的文本 -
39D4CA73AAF2D42C32659FDC5D1848EA
with this key -
用这把钥匙——
thebestsecret153
if that helps. it should show in153
.
如果这有帮助。它应该显示in153
。
EDIT: The line that throws the error in android. -
编辑:在android中引发错误的行。——
byte[] decrypted = cipher.doFinal(encrypted);
I also created a project in swing to read from a file and got the following error -
我还在swing中创建了一个项目以从文件中读取并收到以下错误-
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:313)
at javax.crypto.Cipher.doFinal(Cipher.java:2087)
at decrypt.Decrypt.decrypt(Decrypt.java:43)
at decrypt.MainForm.jButton1MouseClicked(MainForm.java:91)
at decrypt.MainForm.access##代码##0(MainForm.java:20)
at decrypt.MainForm.mouseClicked(MainForm.java:46)
at java.awt.AWTEventMulticaster.mouseClicked(AWTEventMulticaster.java:270)
at java.awt.Component.processMouseEvent(Component.java:6508)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3320)
at java.awt.Component.processEvent(Component.java:6270)
at java.awt.Container.processEvent(Container.java:2229)
at java.awt.Component.dispatchEventImpl(Component.java:4861)
at java.awt.Container.dispatchEventImpl(Container.java:2287)
at java.awt.Component.dispatchEvent(Component.java:4687)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4832)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4501)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4422)
at java.awt.Container.dispatchEventImpl(Container.java:2273)
at java.awt.Window.dispatchEventImpl(Window.java:2719)
at java.awt.Component.dispatchEvent(Component.java:4687)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:735)
at java.awt.EventQueue.access0(EventQueue.java:103)
at java.awt.EventQueue.run(EventQueue.java:694)
at java.awt.EventQueue.run(EventQueue.java:692)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.security.ProtectionDomain.doIntersectionPrivilege(ProtectionDomain.java:87)
at java.awt.EventQueue.run(EventQueue.java:708)
at java.awt.EventQueue.run(EventQueue.java:706)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:705)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:91)
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at java.lang.String.<init>(String.java:556)
at decrypt.MainForm.jButton1MouseClicked(MainForm.java:91)
at decrypt.MainForm.access##代码##0(MainForm.java:20)
at decrypt.MainForm.mouseClicked(MainForm.java:46)
at java.awt.AWTEventMulticaster.mouseClicked(AWTEventMulticaster.java:270)
at java.awt.Component.processMouseEvent(Component.java:6508)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3320)
at java.awt.Component.processEvent(Component.java:6270)
at java.awt.Container.processEvent(Container.java:2229)
at java.awt.Component.dispatchEventImpl(Component.java:4861)
at java.awt.Container.dispatchEventImpl(Container.java:2287)
at java.awt.Component.dispatchEvent(Component.java:4687)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4832)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4501)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4422)
at java.awt.Container.dispatchEventImpl(Container.java:2273)
at java.awt.Window.dispatchEventImpl(Window.java:2719)
at java.awt.Component.dispatchEvent(Component.java:4687)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:735)
at java.awt.EventQueue.access0(EventQueue.java:103)
at java.awt.EventQueue.run(EventQueue.java:694)
at java.awt.EventQueue.run(EventQueue.java:692)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.security.ProtectionDomain.doIntersectionPrivilege(ProtectionDomain.java:87)
at java.awt.EventQueue.run(EventQueue.java:708)
at java.awt.EventQueue.run(EventQueue.java:706)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:705)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:91)
It works perfectly if encryption and decryption is done in same program.
如果加密和解密在同一个程序中完成,它就可以完美地工作。
Any help is appreciated. Thanks in advance.
任何帮助表示赞赏。提前致谢。
采纳答案by Maarten Bodewes
TL;DR: SecureRandom.getInstance("SHA1PRNG")
may not always return the same results evenwhen seeded with the same seed.
TL;DR:即使使用相同的种子进行播种,SecureRandom.getInstance("SHA1PRNG")
也不一定总是返回相同的结果。
You have walked in the trap set up by some person that thought a Pseudo Random Number Generator (PRNG) is the same thing as a Key Derivation Function (KDF). And in general, they are pretty close in operation. The only problem is that with (some implementations of) Android's crypto provider implementation, the setSeed
method addsthe seed to the random, even when it is the first call. So you will generate a different key each time. Different keys mean random ciphertext that may never be decrypted.
您已经步入了某些人设置的陷阱,认为伪随机数生成器 (PRNG) 与密钥派生函数 (KDF) 是一回事。总的来说,它们在操作上非常接近。唯一的问题是,对于(某些实现)Android 的加密提供程序实现,该setSeed
方法将种子添加到随机数中,即使它是第一次调用也是如此。因此,您每次都会生成不同的密钥。不同的密钥意味着可能永远不会被解密的随机密文。
Random ciphertext means that the padding is very likely to be incorrect as ECB or CBC decryption with the wrong key still returns plaintext - it's just random just like the ciphertext. The unpadding routine is likelyto fail on randomized plaintext, so the decryption will fail with a padding exception. Note that authenticated ciphers such as GCM mode encryption will on the other hand alwaysfail if the wrong key is used.
随机密文意味着填充很可能不正确,因为使用错误密钥的 ECB 或 CBC 解密仍然返回明文 - 它就像密文一样随机。unpadding 例程可能会在随机明文上失败,因此解密将失败并出现填充异常。请注意,如果使用了错误的密钥,另一方面,经过身份验证的密码(例如 GCM 模式加密)将始终失败。
The solution: use a true random AES key, or use the build-in PBKDF2 functionality to derive a key from a password (and salt). You can view an examples of how to do this here.
解决方案:使用真正的随机 AES 密钥,或使用内置的 PBKDF2 功能从密码(和盐)派生密钥。您可以在此处查看如何执行此操作的示例。
Issues with using SecureRandom for key derivation functions:
将 SecureRandom 用于密钥派生函数的问题:
- the
SecureRandom
algorithm may (and does) differ per provider/runtime; - the
SecureRandom
implementation may differ per provider/runtime; - the
SecureRandom
implementation may either add or replace the seed during the first call ofSecureRandom
;
- 该
SecureRandom
算法可能(并且确实)因提供者/运行时而异; SecureRandom
每个提供者/运行时的实现可能不同;- 该
SecureRandom
实现可以添加或的第一呼叫期间更换种子SecureRandom
;
Finally some providers return a completely different random number generator when "SHA1PRNG"
is used as many people seem to do this by default. When creating a random number generator simply use new SecureRandom
instead and remember that "SHA1PRNG"
does not denote a specific algorithm.
最后,一些提供者在"SHA1PRNG"
使用时返回一个完全不同的随机数生成器,因为许多人似乎默认这样做。在创建随机数生成器时,只需使用new SecureRandom
并记住它"SHA1PRNG"
并不表示特定算法。
With provider I mean a cryptographic service provider as explained in the Java Cryptography Architecture, JCA; i.e. a java.security.Provider
.
提供者是指在 Java Cryptography Architecture, JCA 中解释的加密服务提供者;即一个java.security.Provider
。