Java 在 AES 解密中,发生“给定的最终块未正确填充”

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20332636/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 01:08:19  来源:igfitidea点击:

In AES decryption, "Given final block not properly padded" occurred

javaencryptionaes

提问by Han

I'm doing a simple encryption file transfer system and now stopped by a run time exception:

我正在做一个简单的加密文件传输系统,现在因运行时异常而停止:

Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811)
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676)
    at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:313)
    at javax.crypto.Cipher.doFinal(Cipher.java:2087)
    at ftpclient.FTPClient.main(FTPClient.java:82)

I tried to debug my code using a string to encrypt and decrypt with the same key and it works. However, when I tried to transfer stream from the file, this exception always comes.

我尝试使用字符串来调试我的代码以使用相同的密钥进行加密和解密,并且它可以工作。但是,当我尝试从文件传输流时,总是会出现此异常。

Here are the codes of both sides. At first they will exchange symmetric key (AES key) via RSA and then transfer large files via AES encryption. We can focus on the last part of each code where the files are encrypted and decrypted by AES key.

这是双方的代码。首先他们将通过 RSA 交换对称密钥(AES 密钥),然后通过 AES 加密传输大文件。我们可以关注每个代码的最后一部分,其中文件由 AES 密钥加密和解密。

Server Side:

服务器端:

package ftpserver;

import java.io.*;
import java.net.*;
import javax.crypto.*;
import java.security.*;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64; 
/**
 *
 * @author Han
 */
public class FTPServer {
    public static void main(String[] args) throws Exception {

        //generate symmetric key and initialize cipher for AES
        SecretKey skey = null;
        Cipher aes = Cipher.getInstance("AES/ECB/PKCS5Padding");

        KeyGenerator kg = KeyGenerator.getInstance("AES");
        kg.init(128);
        skey = kg.generateKey();

        //get public key of the receive side
        final String PUBLIC_KEY_PATH = "key_b.public";
        PublicKey publickey = null;
        try {
        FileInputStream fis;
        fis = new FileInputStream(PUBLIC_KEY_PATH);
        ObjectInputStream oin = new ObjectInputStream(fis);
        publickey = (PublicKey) oin.readObject();
        oin.close();
        } catch (FileNotFoundException e) {
        e.printStackTrace();
        } catch (IOException e) {
        e.printStackTrace();
        } catch (ClassNotFoundException e) {
        e.printStackTrace();
        };

        //encrypte symmetric key with own private key and send out
        Cipher rsa = Cipher.getInstance("RSA");
        rsa.init(Cipher.ENCRYPT_MODE, publickey);
        byte cipherSKey[] = rsa.doFinal(skey.getEncoded());
        //System.out.println(skey); //debug

    //create tcp server socket
        ServerSocket tcp = new ServerSocket(2000);
        Socket client = tcp.accept();

        //get input&output stream from the TCP connection
        InputStream in = client.getInputStream();
    OutputStream out = client.getOutputStream();

        //generate a file input stream to get stream from file
    File sentFile = new File("F:\test.txt");
        FileInputStream fin = new FileInputStream(sentFile);

        //send encrypted symmetric key first
        out.write("Symmetric Key:\r\n".getBytes());
        out.write(cipherSKey);

        DataInputStream din = new DataInputStream(in);
        while(true)
        {
            if(din.readLine().equals("Received."))
            {
                System.out.println("Send key successfully.");
                break;
            }

        };

        //send files
    int count;
        byte[] bytearray = new byte[8192];
        byte[] cipherbuffer;
        while((count = fin.read(bytearray))>0)
    {
        cipherbuffer = Base64.encodeBase64(aes.doFinal(bytearray));
        out.write(cipherbuffer,0,cipherbuffer.length);
        System.out.println(count+" bytes have been sent.");
        };

        out.flush();
        out.close();

        client.close();


        }
}

Client Side:

客户端:

package ftpclient;

import java.io.*;
import java.net.*;
import java.security.PrivateKey;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
/**
 *
 * @author Han
 */
public class FTPClient {

public static void main(String[] args) throws Exception 
    {
            //get the private key of this side


        final String PUBLIC_KEY_PATH = "key_b.privat";
        PrivateKey privatkey = null;
        try {
        FileInputStream fis;
        fis = new FileInputStream(PUBLIC_KEY_PATH);
        ObjectInputStream oin = new ObjectInputStream(fis);
        privatkey = (PrivateKey) oin.readObject();
        oin.close();
        } catch (FileNotFoundException e) {
        e.printStackTrace();
        } catch (IOException e) {
        e.printStackTrace();
        } catch (ClassNotFoundException e) {
        e.printStackTrace();
        };

        Cipher rsa = Cipher.getInstance("RSA");
        rsa.init(Cipher.DECRYPT_MODE, privatkey);

        //create tcp client socket
        Socket tcp = new Socket("192.168.1.185",2000);        
    InputStream in = tcp.getInputStream();
        OutputStream out = tcp.getOutputStream();
        DataInputStream din = new DataInputStream(in);

        //receive symmetric key from server
        byte keybuffer[] = new byte[128];
        SecretKey skey = null;

        while(true)
        {
            if(din.readLine().equals("Symmetric Key:"))
            {
                System.out.println("Start to receiving key...");
                in.read(keybuffer);
                byte[] skeycode = rsa.doFinal(keybuffer);
                skey = new SecretKeySpec(skeycode, 0, skeycode.length, "AES");
                out.write("Received.\r\n".getBytes());
                break;
            }
        };
        //create cipher for symmetric decryption
        Cipher aes = Cipher.getInstance("AES/ECB/PKCS5Padding");
        aes.init(Cipher.DECRYPT_MODE, skey);
        //System.out.println(skey); //debug

        //create file stream
        FileOutputStream fos = new FileOutputStream("E:\test_cp.txt");

    int count;
        int i = 0;
        byte[] bytearray = new byte[8192];
        byte[] buffer;
        while((count = in.read(bytearray)) > 0)
        {
            buffer = (aes.doFinal(Base64.decodeBase64(bytearray)));
                        fos.write(buffer,0,buffer.length);   
                        i +=count;
                        System.out.println(i+" bytes have been received.");
        };


        fos.flush();
        fos.close();
        in.close();

        tcp.close();
        System.out.println("File Transfer completed");

    }

}

采纳答案by andrew cooke

you are calling doFinalmultiple times. or at least trying to.

你打了doFinal很多次电话。或者至少尝试。

when you read data, not all data arrives or is read into the buffer at once. so you decrypt some and then read again. that is all ok.

读取数据时,并非所有数据都会立即到达或读入缓冲区。所以你解密一些然后再读一遍。没关系。

but when you do that, you are calling doFinaleach time, instead of update. this is wrong and is the cause of the error. instead, replace doFinalwith updateand then add an extra doFinalonce you have finished reading all data (there is a doFinal()that takes no arguments for exactly this reason).

但是当你这样做时,你doFinal每次都在打电话,而不是update. 这是错误的,并且是错误的原因。取而代之的是,在读取完所有数据后替换doFinalupdate,然后添加一个额外的doFinaldoFinal()正是出于这个原因,有一个不带参数)。

see http://docs.oracle.com/javase/7/docs/api/javax/crypto/Cipher.html

http://docs.oracle.com/javase/7/docs/api/javax/crypto/Cipher.html

also see http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_.28ECB.29for why ecb mode is often not a good idea (look at the penguin pictures).

另请参阅http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_.28ECB.29了解为什么 ecb 模式通常不是一个好主意(看看企鹅图片)。