如何处理"解密时最后一个块不完整"

Jak*_*ile 12 encryption android aes

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

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Location:com.xxxxxx.android.StupidSimpleEncrypter.decrypt ln:49
last block incomplete in decryption
javax.crypto.IllegalBlockSizeException: last block incomplete in decryption
     at org.bouncycastle.jce.provider.JCEBlockCipher.engineDoFinal(JCEBlockCipher.java:711)
     at javax.crypto.Cipher.doFinal(Cipher.java:1090)
     at com.xxxxxx.android.StupidSimpleEncrypter.decrypt(StupidSimpleEncrypter.java:44)
     at com.xxxxxx.android.StupidSimpleEncrypter.decrypt(StupidSimpleEncrypter.java:34)

我已经对我的桌子做了大量的敲击试图弄清楚这一点,但如果我到处都是,它最终会成为一个不同的例外.通过搜索我似乎也找不到多少.

我错过了什么?我将不胜感激任何帮助.

eri*_*son 19

我不知道这是否有问题IllegalBlockSizeException,但你不应该把密钥编码为a String,特别是不指定字符编码.如果你想这样做,可以使用像Base-64这样的东西来设计编码任何"二进制"数据,而不是字符编码,它只将某些字节映射到字符.

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

试图在以后使用String该密钥的腐败表示将阻止明文被恢复; 它可能会导致IllegalBlockSizeException,但我怀疑更有可能出现无效的填充异常.

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


小智 7

我正在为此烦恼,在“错误的基础 64”和“最后一个块不完整”之间的错误......当然,这是不对称的。这是我最终如何做的本质,希望比我试图解释的更能增加讨论:

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));
    }
}
Run Code Online (Sandbox Code Playgroud)


Jam*_*olk 6

你的getKeySpec()方法是错的.您为加密和解密方向生成新的随机密钥.您必须为两者使用相同的密钥.你应该注意到你没有使用该key方法的参数.