【问题标题】:In AES decryption, "Given final block not properly padded" occurred在 AES 解密中,出现“未正确填充最终块”
【发布时间】:2013-12-18 10:00:51
【问题描述】:

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

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)

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

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

服务器端:

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();


        }
}

客户端:

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");

    }

}

【问题讨论】:

  • 对不起,你的意思是我应该改变密码算法?对修复这个错误有帮助吗?
  • 不,它不会解决这个问题。使用 ecb 是个坏主意 - 请参阅有关密码模式的维基百科文章。

标签: java encryption aes


【解决方案1】:

您多次致电doFinal。或者至少尝试这样做。

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

但是当你这样做时,你每次都调用doFinal,而不是update。这是错误的,是错误的原因。相反,将doFinal 替换为update,然后在您完成读取所有数据后添加一个额外的doFinal(有一个doFinal(),正是出于这个原因,它不需要任何参数)。

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

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

【讨论】:

  • 非常感谢 Andrew :) 使用 update 确实有助于让整个程序运行,但是在最后调用 doFinal() 时,出现了新异常:“解密时输入长度必须是 16 的倍数带填充密码”。我尝试传输一些大文件,例如超过 100MB 的视频,传输后无法正常打开。我发现 doFinal() 是为了添加填充。那么我应该将此填充也编码为 Base64 吗?
  • 哦,我认为现在的问题是在调用 doFinal() 时(不带参数),它可能会生成一些东西但无法传输到远程端,不是吗?所以我能做的只是为我发送的整个文件的最后一部分调用 doFinal 。 doFinal() (不带参数)实际上与这个文件传输案例无关。对吗?
  • 对不起,我忘记了发送方。您需要发送所有内容,包括从 doFinal 返回的内容,但如果有帮助,您可以调用 doFinal(data...) 方法。或者只是在循环中使用更新,然后使用最终的 doFinal - 请记住 doFinal 可能(将!)返回数据并且必须传输数据。
  • 谢谢。我将 doFinal() 的返回字节数组发送到远程端,仍然存在同样的问题。那么我应该使用Base64编码和解码吗?有关系吗?
  • 不,编码应该无关紧要。你很可能只是有一个错误 - 没有刷新某些东西,或者没有从套接字正确读取,或类似的。
猜你喜欢
  • 2015-05-22
  • 2015-03-31
  • 2015-10-07
  • 1970-01-01
  • 2017-03-17
  • 1970-01-01
  • 2014-07-26
  • 2014-01-22
  • 1970-01-01
相关资源
最近更新 更多