javax.crypto.IllegalBlockSizeException:解密时最后一个块不完整

时间:2013-12-13 08:40:51

标签: java android encryption cryptography aes

我正在开发一个Android项目,我必须从PHP服务器中的文件解密字符串,使用PHP完成加密。(AES 128算法)

加密代码:

 function aes128Encrypt($key, $data) {
      $key = md5($key);
      return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, str_repeat("\0", 16));
    }

我从URL获取字符串的代码:

String myUri = "http://www.example.com/lib2.php";
HttpClient httpClient = new DefaultHttpClient();
HttpGet get = new HttpGet(myUri);

HttpResponse response = httpClient.execute(get);

String bodyHtml = "";
String xmlFile = "";
bodyHtml = EntityUtils.toString(response.getEntity());
System.out.println(bodyHtml);
xmlFile = decrypt(bodyHtml);
System.out.println(xmlFile);

decrypt()方法:

public static String decrypt(String encData) throws Exception
    {
        Key key = generateKey();

        Cipher c = Cipher.getInstance(algo + "/CBC/PKCS7Padding");

        //Cipher c = Cipher.getInstance(algo);

        IvParameterSpec ivSpec = new IvParameterSpec(ivbytes);

        c.init(Cipher.DECRYPT_MODE, key , ivSpec);

        //byte[] decValue = Base64.decode(encData , Base64.NO_PADDING);

        //byte[] decFin = Base64.decode(decValue, Base64.NO_PADDING);

        //byte[] decFinal = c.doFinal(decValue);

        byte[] decFinal = c.doFinal(encData.getBytes());

        String getAns = new String(decFinal, "UTF8");

        return getAns;
    }

在generateKey()中:

public static Key generateKey()
    {

        try {
            MessageDigest digest = java.security.MessageDigest
                    .getInstance("MD5");

            digest.update(myChar.getBytes());

            key1 = digest.digest();

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }



        Key key = new SecretKeySpec(key1,algo);     
        return key;
    }
}

Logcat显示以下异常:

javax.crypto.IllegalBlockSizeException: last block incomplete in decryption

在第c.doFinal()

我注释掉的代码也是我用来检查的代码。这段代码有什么问题?任何帮助将不胜感激。

3 个答案:

答案 0 :(得分:2)

你的狙击手期待填充“/ CBC / PKCS7Padding”。这与消息编码无关(BASE64)。要么在java端更改预期的chipher,要么在php端使用padding进行加密。

指定Chipers algorithm/mode/paddingalgortihm,有关详细信息,请参阅http://docs.oracle.com/javase/7/docs/api/javax/crypto/Cipher.html

您正在指定带填充的chiper但实际上在php端执行零填充,

摘自http://www.php.net/manual/de/function.mcrypt-encrypt.php

# creates a cipher text compatible with AES (Rijndael block size = 128)
# to keep the text confidential 
# only suitable for encoded input that never ends with value 00h
# (because of default zero padding)
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key,
                             $plaintext, MCRYPT_MODE_CBC, $iv);

答案 1 :(得分:1)

问题可能是您将PHP服务器中的值视为Java String的方式。 PHP mcrypt_encrypt函数返回二进制字符串,其中每个字节可以包含任何值。 PHP mcrypt_encrypt不使用任何字符编码。因此,使用EntityUtils.toString()已经是一个错误 - 使用String.getBytes()而不指定编码通常也是如此。

您只需使用getContent()writeTo(OutputStream outstream)来获取二进制密文。如果您选择第一个选项,则可以在其周围包裹CipherInputStream。使用"AES/CBC/NoPadding"密码对其进行初始化,您可以直接解密纯文本字符串。请注意,您可能必须从结果中修剪一定数量的00值字节才能获得明文。

答案 2 :(得分:0)

我通过避免重用密码来解决我的问题。每次我再次启动它。刚刚开始就足够了。

 try {
            SecretKeyFactory factory = SecretKeyFactory
                    .getInstance(SECRET_KEY_ALGORITHM);
            KeySpec spec = new PBEKeySpec(ps, salt, ITERATIONS, KEY_LENGTH);
            SecretKey tmp = factory.generateSecret(spec);
            SecretKey secret = new SecretKeySpec(tmp.getEncoded(), ALGORITHM);

            // Build encryptor and get IV
            ecipher = Cipher.getInstance(TRANSFORMATION);
            ecipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(iv));

            // Build decryptor
            dcipher = Cipher.getInstance(TRANSFORMATION);
            dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
        } catch (Exception e) {
            e.printStackTrace();
        }