Java AES错误:给定的最终块未正确填充

Java AES错误:给定的最终块未正确填充,java,cryptography,aes,Java,Cryptography,Aes,我需要帮助解决这个错误:给定的最后一个块没有正确填充。正如你从标题中看到的,我正在与AES合作 以下是错误所在行的代码: byte[] decrypted = cipher.doFinal(bytes); 以下是完整的代码: public class AESCrypt { private final Cipher cipher; private final SecretKeySpec key; private String encryptedText, decryptedText; pub

我需要帮助解决这个错误:给定的最后一个块没有正确填充。正如你从标题中看到的,我正在与AES合作

以下是错误所在行的代码:

 byte[] decrypted = cipher.doFinal(bytes);
以下是完整的代码:

public class AESCrypt {
private final Cipher cipher;
private final SecretKeySpec key;
private String encryptedText, decryptedText;

public AESCrypt(String password) throws Exception {
    // hash password with SHA-256 and crop the output to 128-bit for key
    MessageDigest digest = MessageDigest.getInstance("SHA-256");
    digest.update(password.getBytes("UTF-8"));
    byte[] keyBytes = new byte[16];
    System.arraycopy(digest.digest(), 0, keyBytes, 0, keyBytes.length);

    cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    key = new SecretKeySpec(keyBytes, "AES");
}

public String encrypt(String plainText) throws Exception {
    byte[] iv = new byte[cipher.getBlockSize()];
    new SecureRandom().nextBytes(iv);
    AlgorithmParameterSpec spec = new IvParameterSpec(iv);
    cipher.init(Cipher.ENCRYPT_MODE, key, spec);
    byte[] encrypted = cipher.doFinal(plainText.getBytes());
    encryptedText = asHex(encrypted);
    return encryptedText;
}

public String decrypt(String cryptedText) throws Exception {
    byte[] iv = new byte[cipher.getBlockSize()];
    AlgorithmParameterSpec spec = new IvParameterSpec(iv);
    cipher.init(Cipher.DECRYPT_MODE, key, spec);
    // decrypt the message
    byte[] bytes = cryptedText.getBytes("UTF-8");
    byte[] decrypted = cipher.doFinal(bytes);
    decryptedText = asHex(decrypted);
    System.out.println("Desifrovani tekst: " + decryptedText + "\n");

    return decryptedText;
}

public static String asHex(byte buf[]) {
    StringBuilder strbuf = new StringBuilder(buf.length * 2);
    int i;
    for (i = 0; i < buf.length; i++) {
        if (((int) buf[i] & 0xff) < 0x10) {
            strbuf.append("0");
        }
        strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
    }
    return strbuf.toString();
}

public static void main(String[] args) throws Exception {

    System.out.print("....AES....\n");

    String message = "MESSAGE";
    String password = "PASSWORD";

    System.out.println("MSG:" + message);

    AESCrypt aes = new AESCrypt(password);
    String encryptedText = aes.encrypt(message).toString();
    System.out.println("SIFROVANA PORUKA: " + encryptedText);
    String decryptedText = aes.decrypt(encryptedText).toString();       
    System.out.print("DESIFROVANA PORUKA: " + decryptedText);
}
公共类AESCrypt{
专用最终密码;
私有最终SecretKeySpec密钥;
私有字符串encryptedText,decryptedText;
公共AESCrypt(字符串密码)引发异常{
//使用SHA-256散列密码,并将输出裁剪为128位作为密钥
MessageDigest=MessageDigest.getInstance(“SHA-256”);
摘要.更新(密码.getBytes(“UTF-8”);
字节[]键字节=新字节[16];
System.arraycopy(digest.digest(),0,keyBytes,0,keyBytes.length);
cipher=cipher.getInstance(“AES/CBC/PKCS5Padding”);
key=新的SecretKeySpec(keyBytes,“AES”);
}
公共字符串加密(字符串明文)引发异常{
byte[]iv=新字节[cipher.getBlockSize()];
新SecureRandom().nextBytes(iv);
AlgorithmParameterSpec=新的IvParameterSpec(iv);
cipher.init(cipher.ENCRYPT_模式,密钥,规范);
byte[]encrypted=cipher.doFinal(明文.getBytes());
encryptedText=asHex(加密);
返回加密文本;
}
公共字符串解密(字符串加密文本)引发异常{
byte[]iv=新字节[cipher.getBlockSize()];
AlgorithmParameterSpec=新的IvParameterSpec(iv);
cipher.init(cipher.DECRYPT_模式,密钥,规范);
//解密消息
byte[]bytes=cryptedText.getBytes(“UTF-8”);
byte[]decrypted=cipher.doFinal(字节);
decryptedText=asHex(已解密);
System.out.println(“Desifrovani-tekst:“+decryptedText+”\n”);
返回解密文本;
}
公共静态字符串asHex(字节buf[]{
StringBuilder strbuf=新StringBuilder(基本长度*2);
int i;
对于(i=0;i

}您有两个问题,首先将输出编码为十六进制字符串,但不使用decode方法将其解码回来。第二,生成一个随机IV,但不要再次使用它进行解码

public byte[] encrypt(String plainText) throws Exception {
   byte[] iv = new byte[cipher.getBlockSize()];    
   AlgorithmParameterSpec spec = new IvParameterSpec(iv);
   cipher.init(Cipher.ENCRYPT_MODE, key, spec);
   return cipher.doFinal(plainText.getBytes());
}

public String decrypt(byte[] cryptedText) throws Exception {
   byte[] iv = new byte[cipher.getBlockSize()];
   AlgorithmParameterSpec spec = new IvParameterSpec(iv);
   cipher.init(Cipher.DECRYPT_MODE, key, spec);
   // decrypt the message
   byte[] decrypted = cipher.doFinal(cryptedText);
   decryptedText = new String(decrypted, "UTF-8");
   return decryptedText;
}



String decryptedText = aes.decrypt(aes.encrypt(message)).toString();     

根据你的评论,你很快就能让加密程序工作了

您需要将IV生成代码从加密/解密方法移动到其他地方,如

然后将该ivspec同时传递给encrypt和decrypt方法(使它们看起来像
encrypt(String,AlgorithmParameterSpec)
),这样加密和解密都有相同的iv


另外,不要对decryptedByteArray调用
printBase64Binary
,而是调用
new String(decryptedByteArray,“UTF-8”)

第一个问题(不是解码十六进制输出)是问题中错误的原因,但第二个问题(IV问题)将在解密的第一块明文上导致乱码明文输出。也就是说,此答案中的此代码有两个问题:1,它对IV使用全零字符串,这从安全角度来看是不好的。2,调用
getBytes()
,它没有指定字符串编码,这在解密另一端的文本时可能会导致问题,使用此代码时,加密文本的输出始终相同。@Peter yes all null IV从安全角度看是不好的,我的目的是显示工作代码。当然,你需要一个独特的iv在生产中,但你也必须跟踪它进行解密。+1为了指出尝试直接解密十六进制字符串的问题,我建议你使用apache commons编解码器类,而不是滚动你自己的十六进制编码器和解码器。我在互联网上看到了很多例子,人们在哪里使用Base64解码器和Base64编码器?但我似乎不能使用它,因为它们在sun名称空间中,并且可能不一定在所有JDK实现中都存在,所以使用它们并不是一种最佳实践。如果您希望在没有库的情况下进行Base64编码和解码,并且您支持Java 6+,请使用JAXB,如中所述,如果您可以使用改进的代码编写答案,我将接受您的答案,因为这是您应得的。SHA-256不是为派生密钥而设计的,您的密码密钥空间可能没有128位那么大,并且只有一个哈希可能会被破解,而且已经存在sha-256的大型预计算哈希表,可以用来更快地从密文中查找密码。一旦您初始化这个类,它的IV就被修复了。这种构造违反了CBC中每个密钥的IV规则,即IV不能重复,并且IV不能预测。因此它在语义上是不安全的。对,更好的构造是使用加密或解密方法传入IV,或者传递附加在一个位置或另一个位置的IV。这还存在一个问题,即程序的两次单独运行将有两个单独的IVs,这将导致解密损坏。我已经编辑了这个问题,以反映一种更好的方法来做,itIve试图用这个改进的代码实现类似的东西,但我再次得到相同的错误,关于给定的最后一个块没有正确填充。代码如下:您的代码将生成两个不同的IVs。。。您需要在
encrypt
decrypt
之外调用它,并将IVParameterSpec作为参数传递到方法中,以便确保使用相同的值,
public AlgorithmParameterSpec getIV() {
AlgorithmParameterSpec ivspec;
byte[] iv = new byte[cipher.getBlockSize()];
new SecureRandom().nextBytes(iv);
ivspec = new IvParameterSpec(iv);
}