【问题标题】:How to handle "last block incomplete in decryption"如何处理“解密中的最后一个块不完整”
【发布时间】:2011-01-04 21:57:20
【问题描述】:

我有一个简单的类来尝试包装加密以在我的程序的其他地方使用。

import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;

public final class StupidSimpleEncrypter
{
    public static String encrypt(String key, String plaintext)
    {
        byte[] keyBytes = key.getBytes();
        byte[] plaintextBytes = plaintext.getBytes();
        byte[] ciphertextBytes = encrypt(keyBytes, plaintextBytes);
        return new String(ciphertextBytes);
    }

    public static byte[] encrypt(byte[] key, byte[] plaintext)
    {
        try
        {
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            SecretKeySpec spec = new SecretKeySpec(getRawKey(key), "AES");
            cipher.init(Cipher.ENCRYPT_MODE, spec);
            return cipher.doFinal(plaintext);
        }
        catch(Exception e)
        {
            // some sort of problem, return null because we can't encrypt it.
            Utility.writeError(e);
            return null;
        }
    }

    public static String decrypt(String key, String ciphertext)
    {
        byte[] keyBytes = key.getBytes();
        byte[] ciphertextBytes = ciphertext.getBytes();
        byte[] plaintextBytes = decrypt(keyBytes, ciphertextBytes);
        return new String(plaintextBytes);
    }

    public static byte[] decrypt(byte[] key, byte[] ciphertext)
    {
        try
        {
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            SecretKeySpec spec = new SecretKeySpec(getRawKey(key), "AES");
            cipher.init(Cipher.DECRYPT_MODE, spec);
            return cipher.doFinal(ciphertext);
        }
        catch(Exception e)
        {
            // some sort of problem, return null because we can't encrypt it.
            Utility.writeError(e);
            return null;
        }
    }

    private static byte[] getRawKey(byte[] key)
    {
        try
        {
            KeyGenerator gen = KeyGenerator.getInstance("AES");
            SecureRandom rand = SecureRandom.getInstance("SHA1PRNG");
            rand.setSeed(key);
            gen.init(256, rand);
            return gen.generateKey().getEncoded();
        }
        catch(Exception e)
        {
            return null;
        }
    }
}

它似乎可以正确处理加密,但在解密时却没有这么多,这会在突出显示的行抛出 javax.crypto.IllegalBlockSizeException“解密中的最后一个块不完整”。这是堆栈跟踪:

位置:com.xxxxxx.android.StupidSimpleEncrypter.decrypt ln:49 最后一个块解密不完整 javax.crypto.IllegalBlockSizeException:最后一个块在解密中不完整 在 org.bouncycastle.jce.provider.JCEBlockCipher.engineDoFinal(JCEBlockCipher.java:711) 在 javax.crypto.Cipher.doFinal(Cipher.java:1090) 在 com.xxxxxx.android.StupidSimpleEncrypter.decrypt(StupidSimpleEncrypter.java:44) 在 com.xxxxxx.android.StupidSimpleEncrypter.decrypt(StupidSimpleEncrypter.java:34)

我已经多次将头撞在桌子上以试图弄清楚这一点,但如果我得到任何结果,它最终会成为一个不同的例外。我似乎也无法通过搜索找到很多东西。

我错过了什么?我将不胜感激。

【问题讨论】:

    标签: android encryption aes


    【解决方案1】:

    我不知道这是否是IllegalBlockSizeException 的问题,但您不应该将密钥编码为String,尤其是在没有指定字符编码的情况下。如果您想这样做,请使用 Base-64 之类的东西,它旨在对任何“二进制”数据进行编码,而不是仅将某些字节映射到字符的字符编码。

    通常,密钥将包含与默认平台编码中的字符不对应的字节值。在这种情况下,当您创建String 时,该字节将被转换为“替换字符”,U+FFFD (�),并且正确的值将无法挽回地丢失。

    稍后尝试使用损坏的String 表示密钥将阻止明文被恢复;它可能会导致IllegalBlockSizeException,但我怀疑无效填充异常的可能性更大。

    另一种可能是源平台和目标平台字符编码不同,“解码”密文导致字节太少。例如,源编码是 UTF-8,将输入中的两个字节解释为单个字符,而目标编码是 ISO-Latin-1,它将该字符表示为单个字节。

    【讨论】:

    • 这是我的问题的根本原因,但它与有效负载有关,而不是在我的情况下始终是标准字符串的密钥。我在字符串加密方法中改变了它,返回的密文从字节数组编码为Base64,而在解密方法中密文字符串被Base64解码为字节数组。这意味着数据不再丢失,代码现在可以工作了。
    • 加密字符串本身也是如此。它必须存储为 byte[]。
    【解决方案2】:

    在“bad base 64”和“last block不完整”错误之间,我为此大发雷霆……当然,这是不对称的。以下是我最终如何做到这一点的本质,希望能比我试图解释的更多内容:

    public String crypto(SecretKey key, String inString, boolean decrypt){
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        byte[] inputByte = inString.getBytes("UTF-8");
        if (decrypt){
            cipher.init(Cipher.DECRYPT_MODE, key);
            return new String (cipher.doFinal(Base64.decode(inputByte, Base64.DEFAULT)));
        } else {
            cipher.init(Cipher.ENCRYPT_MODE, key);
            return new String (Base64.encode(cipher.doFinal(inputByte), Base64.DEFAULT));
        }
    }
    

    【讨论】:

    • 只为你扯掉的头发。
    【解决方案3】:

    您的getKeySpec() 方法错误。您为加密和解密方向生成一个新的随机密钥。您必须为两者使用相同的密钥。您应该已经注意到,您没有对该方法使用 key 参数。

    【讨论】:

    • 这是问题的一部分,我需要一段时间才能注意到。谢谢。
    【解决方案4】:

    如果您正在处理字节数组,那么您必须使用相同的缓冲区大小。比如有一个字节数组,大小是1000,加密后这个大小变成2000。(这些不是真实值)。 如果你使用缓冲区读取所有加密文件,那么你应该选择缓冲区大小为2000。我用这种方式解决了同样的问题。

    【讨论】:

      【解决方案5】:

      对我来说,当要解密的数据损坏(缺少 1 个字符)时,我注意到了这个问题。这可能是由于通过 WiFi 传输数据所致。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-06-11
        • 2017-05-12
        • 2012-06-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多