简体   繁体   English

javax.crypto.BadPaddingException

[英]javax.crypto.BadPaddingException

I am working on AES algorithm, and I have this exception which I couldn't solve. 我正在研究AES算法,我有这个例外,我无法解决。

javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)

the exception happens in the decryption part. 异常发生在解密部分。 I initialize the key in a different place from where the decryption algorithm is 我在与解密算法不同的地方初始化密钥

KeyGenerator kgen = KeyGenerator.getInstance("AES");//key generation for AES
kgen.init(128); // 192 and 256 bits may not be available

then I pass it with the cipher text which I read from file to the following method 然后我用我从文件中读取的密文传递给下面的方法

 public String decrypt(String message, SecretKey skey) {

    byte[] raw = skey.getEncoded();
    SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
    // Instantiate the cipher
    Cipher cipher;

    byte[] original = null;
    try {
        cipher = Cipher.getInstance("AES");

        cipher.init(Cipher.DECRYPT_MODE, skeySpec);
        System.out.println("Original string: "
                + message);
        original = cipher.doFinal(message.trim().getBytes());  //here where I got the exception
        String originalString = new String(original);
       }
 //catches

EDIT here's the encryption method. 编辑这里是加密方法。

public String encrypt(String message, SecretKey skey) {
    byte[] raw = skey.getEncoded();
    SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");

    // Instantiate the cipher

    Cipher cipher;
    byte[] encrypted = null;
    try {
        cipher = Cipher.getInstance("AES");

        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);

        encrypted = cipher.doFinal(message.getBytes());
        System.out.println("raw is " + encrypted);

    } catches
    return asHex(encrypted);
}

and here's the asHex method 这是asHex方法

  public static String asHex(byte buf[]) {
    StringBuffer strbuf = new StringBuffer(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();
}

Here's where I read the cipher text form the file 这是我从文件中读取密文的地方

static public String readFile(String filePath) {
    StringBuilder file = new StringBuilder();
    String line = null;
    try {
        FileReader reader = new FileReader(filePath);
        BufferedReader br = new BufferedReader(reader);
        if (br != null) {
            line = br.readLine();
            while (line != null) {
                file.append(line);
                //      System.out.println("line is " + line);
                line = br.readLine();

            }
        }
        br.close();
        reader.close();
    } catch (IOException ex) {
        Logger.getLogger(FileManagement.class.getName()).log(Level.SEVERE, null, ex);
    }
    System.out.println("line is " + file.toString());
    return String.valueOf(file);

}

can someone help? 有人可以帮忙吗?

Ok, so the problem is that you are converting the encrypted bytes to a hex string (using the asHex method) but are not converting the hex string back to a byte array correctly for decryption. 好的,问题是您正在将加密的字节转换为十六进制字符串(使用asHex方法),但是没有正确地将十六进制字符串转换回字节数组以进行解密。 You can't use getBytes . 你不能使用getBytes

You can use the following method to convert a hex string to a byte array: 您可以使用以下方法将十六进制字符串转换为字节数组:

public static byte[] fromHexString(String s) {
    int len = s.length();
    byte[] data = new byte[len / 2];
    for (int i = 0; i < len; i += 2) {
        data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                             + Character.digit(s.charAt(i+1), 16));
    }
    return data;
}

and then change your decrypt method to use: 然后更改您的解密方法以使用:

original = cipher.doFinal(fromHexString(message));

I did have a Bad Padding Exception and have not been able to find on the internet a solution to my problem. 我确实有一个Bad Padding Exception并且无法在互联网上找到我的问题的解决方案。 Since I found it after some hard-working hours, I give it here. 自从我在一些艰苦的工作时间后发现它,我在这里给它。

My problem was, I was reading a file on my hard drive, and encrypting it through a buffer, always calling the doFinal() method instead of update() method. 我的问题是,我正在读取硬盘上的文件,并通过缓冲区加密,始终调用doFinal()方法而不是update()方法。 So when decrypting it, I had padding errors 所以在解密时,我有填充错误

    input = new FileInputStream(file);
    output = new FileOutputStream(newFile);

    Cipher cipher = Cipher.getInstance("DES");
    cipher.init(Cipher.ENCRYPT_MODE, mySecretKey);

    byte[] buf = new byte[1024];

    count = input.read(buf);
    while (count >= 0) {
        output.write(cipher.update(buf, 0, count)); // HERE I WAS DOING doFinal() method
        count = input.read(buf);
    }
    output.write(cipher.doFinal()); // AND I DID NOT HAD THIS LINE BEFORE
    output.flush();

And when decrypting, with the same method, but with a Cipher init with DECRYPT_MODE 并且在解密时,使用相同的方法,但使用带有DECRYPT_MODE的Cipher init

    input = new FileInputStream(file);
    output = new FileOutputStream(newFile);

    Cipher cipher = Cipher.getInstance("DES");
    cipher.init(Cipher.DECRYPT_MODE, mySecretKey);

    byte[] buf = new byte[1024];

    count = input.read(buf);

    while (count >= 0) {
        output.write(cipher.update(buf, 0, count)); // HERE I WAS DOING doFinal() method

        //AND HERE WAS THE BadPaddingExceotion -- the first pass in the while structure

        count = input.read(buf);
    }
    output.write(cipher.doFinal()); // AND I DID NOT HAD THIS LINE BEFORE
    output.flush();

With the code written, I no longer have any BadPaddingException. 编写代码后,我不再有任何BadPaddingException。

I may precise that this exception only appears when the original clear file length (obtained through file.length()) is bigger than the buffer. 我可以确切地说,只有当原始清除文件长度(通过file.length()获得)大于缓冲区时才会出现此异常。 Else, we do not need to pass several times in the while structure, and we can encrypt in one pass with a doFinal() call. 否则,我们不需要在while结构中多次传递,并且我们可以通过doFinal()调用在一次传递中加密。 That justify the random character of the exception following the size of the file you try to encrypt. 这证明了在您尝试加密的文件大小之后的异常的随机特性。

I hope you had a good reading! 我希望你读得好!

I guess the expression message.trim().getBytes() does not return the same bytes which are generated when you encrypted the message. 我想表达式message.trim().getBytes()不会返回加密消息时生成的相同字节。 Specially the trim() method could delete the bytes which were added as padding in the encrypted message. 特别是trim()方法可以删除在加密消息中添加为填充的字节。

Verify that both the returned array of the doFinal() method during the encryption and the returned array of message.trim().getBytes() : 验证加密期间返回的doFinal()方法数组和返回的message.trim().getBytes()数组:

  1. got the same number of bytes (array length) 得到相同的字节数(数组长度)
  2. got the same bytes in the array 在数组中得到相同的字节
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(512);
KeyPair rsaKeyPair = kpg.genKeyPair();
byte[] txt = "This is a secret message.".getBytes();
System.out.println("Original clear message: " + new String(txt));

// encrypt
Cipher cipher;
try
{
    cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    cipher.init(Cipher.ENCRYPT_MODE, rsaKeyPair.getPublic());
    txt = cipher.doFinal(txt);
}
catch (Throwable e)
{
    e.printStackTrace();
    return;
}
System.out.println("Encrypted message: " + new String(txt));

// decrypt
try
{
    cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    cipher.init(Cipher.DECRYPT_MODE, rsaKeyPair.getPrivate());
    txt = cipher.doFinal(txt);
}
catch (Throwable e)
{
    e.printStackTrace();
    return;
}
System.out.println("Decrypted message: " + new String(txt));

Here is a solution I was able to piece together using a jks keystore with RSA encryption 这是一个解决方案,我可以使用带有RSA加密的jks密钥库拼凑在一起

import javax.crypto.Cipher;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.cert.Certificate;

public class Main {

    public static void main(String[] args) {

        byte[] txt = "This is a secret message for your own eyes only".getBytes();
        byte[] encText;
        try{

            // Load the keystore
            KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
            char[] password = "keystorePassword".toCharArray();
            java.io.FileInputStream fis = new java.io.FileInputStream("/path/to/keystore/myKeyStore.jks");
            ks.load(fis, password);
            fis.close();

            Key rsakey = ks.getKey("mykeyalias", password);
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");

            // Encrypt
            Certificate cert = ks.getCertificate("mykeyalias");
            try
            {
                cipher.init(Cipher.ENCRYPT_MODE, cert.getPublicKey());
                encText = cipher.doFinal(txt);
                System.out.println(encText.toString());
            }
            catch (Throwable e)
            {
                e.printStackTrace();
                return;
            }

            // Decrypt
            cipher.init(Cipher.DECRYPT_MODE, rsakey);
            String decrypted = new String(cipher.doFinal(encText));
            System.out.println(decrypted);


        } catch (Exception e) {
            System.out.println("error" + e);
        }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM