通过TCP传输视频时出现Java AES填充加密错误

时间:2017-10-13 08:20:46

标签: java encryption tcp cryptography video-streaming

我目前正在用Java做一个项目,通过TCP将视频文件从服务器传输到客户端。这个想法是服务器将继续运行并监听传入的连接。一旦有来自客户端的传入连接,服务器将自动将视频文件发送到客户端。 (截至目前,IP和文件名称是硬编码的)。这个想法是为了可以同时复制和播放文件

它在本地使用,将自动从接收计算机打开VLC以播放正在传输的文件。我做了转移部分没有任何问题。当我尝试加密/解密文件时,唯一的问题出现了。我的代码在下面

可运行的线程文件传输服务器

public class FileTransferServer { 

    public static void main(String[] args) throws Exception {
        //Initialize Sockets
        int i = 0;
        ServerSocket ssock = new ServerSocket(6012);

        while (true){
         ClientConnection CC;   
            CC = new ClientConnection(ssock.accept());
            Thread t = new Thread(CC);
            t.start();
        }        
    }
}

服务器Java文件

import java.io.BufferedInputStream;
import javax.crypto.Cipher;
import java.io.InputStream;
import java.io.OutputStream;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class ClientConnection implements Runnable 
{
private Socket socketPort;

public ClientConnection (Socket socketPort)
{
    this.socketPort = socketPort;
}

public void run()
{
    try {
        DataInputStream input = new DataInputStream(socketPort.getInputStream());
        String videoName = input.readUTF();


        // automatically get local ip
        InetAddress IA = InetAddress.getByName("10.0.0.1"); 

        String key = "Maryhadonecat111";
        byte[] keyByte = key.getBytes("UTF-8");
        System.out.println(keyByte);
        System.out.println(keyByte.toString());

        //Specify the file
        File file = new File("D:\\Temp\\"+videoName);
        FileInputStream fis = new FileInputStream(file);
        BufferedInputStream bis = new BufferedInputStream(fis); 

        //Get socket's output stream
        OutputStream os = socketPort.getOutputStream();

        //Read File Contents into contents array 
        byte[] contents;
        long fileLength = file.length(); 
        long current = 0;
        long start = System.nanoTime();

        while(current!=fileLength){ 
            int size = 1000000;
            if(fileLength - current >= size)
                current += size;    
            else{ 
                size = (int)(fileLength - current); 
                current = fileLength;
            } 
            contents = new byte[size]; 
            bis.read(contents, 0, size); 
            //os.write(contents);                 
            os.write(CryptoTest1.doEncrypt(contents,keyByte));                   
            System.out.print("Sending file to "+ socketPort.getInetAddress().toString() +" " +(current*100)/fileLength+"% complete!\n");                
        }   

        os.flush(); 
        //File transfer done. Close the socket connection!
        socketPort.close();
        // ssock.close();
        System.out.println("File sent succesfully!");

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

    }
}

客户端Java文件

import java.io.BufferedOutputStream;
import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;

import javax.crypto.Cipher;
import java.io.InputStream;
import java.io.OutputStream;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;

public class FileTransferClient { 

public static void main(String[] args) throws Exception{
    requestFile("10.0.0.1", "papa.avi");
}

public static void requestFile(String IP, String videoName) throws Exception{
    //Initialize socket
    Socket socket = new Socket(InetAddress.getByName(IP), 6012);
    DataOutputStream output = new DataOutputStream( socket.getOutputStream());
    output.writeUTF(videoName);



    String key = "Maryhadonecat111";
    byte[] keyByte = key.getBytes("UTF-8");

    byte[] contents = new byte[1000000];
    //Initialize the FileOutputStream to the output file's full path.
    FileOutputStream fos = new FileOutputStream("D:\\Temp2\\"+videoName);
    BufferedOutputStream bos = new BufferedOutputStream(fos);
    InputStream is = socket.getInputStream(); 

    System.out.println("Receiving File");
    ProcessBuilder pb = new ProcessBuilder("C:\\Program Files (x86)\\VideoLAN\\VLC\\vlc.exe", "D:\\Temp2\\"+videoName);
    Process start = pb.start(); 
    //No of bytes read in one read() call
    int bytesRead = 0; 


    while((bytesRead=is.read(contents))!=-1){
        System.out.println("Bytes Received: " + bytesRead);

        contents = (CryptoTest1.doDecrypt(contents,keyByte));              
        bos.write(contents, 0, bytesRead); 

    }
    bos.flush(); 
    socket.close(); 

    System.out.println("File saved successfully!");
}
}

CryptoTest1 Java文件

public class CryptoTest1
{
public static byte[] doEncrypt(byte[] msg, byte[] key) throws Exception {
    //prepare key
    SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");

    //prepare cipher
    String cipherALG = "AES/CBC/PKCS5Padding"; // use your preferred algorithm 
    Cipher cipher = Cipher.getInstance(cipherALG);
    String string = cipher.getAlgorithm();


    //as iv (Initial Vector) is only required for CBC mode
    if (string.contains("CBC")) {
        //IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); 
        IvParameterSpec ivParameterSpec = cipher.getParameters().getParameterSpec(IvParameterSpec.class);
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
    } else {
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
    }

    byte[] encMessage = cipher.doFinal(msg);        
    return encMessage;
}

public static byte[] doDecrypt(byte[] encMsgtoDec, byte[] key) throws Exception {
    //prepare key
    SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");

    //prepare cipher
    String cipherALG = "AES/CBC/PKCS5Padding"; // use your preferred algorithm 
    Cipher cipher = Cipher.getInstance(cipherALG);
    String string = cipher.getAlgorithm();

    //as iv (Initial Vector) is only required for CBC mode
    if (string.contains("CBC")) {
        //IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);   
        IvParameterSpec ivParameterSpec = cipher.getParameters().getParameterSpec(IvParameterSpec.class);
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
    } else {
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
    }

    byte[] decMsg = cipher.doFinal(encMsgtoDec);        
    return decMsg;
}
}

问题 我似乎没有加密文件的问题,它正在发送。问题是解密文件。我似乎无法让它工作。我已经尝试了很多次,大多数错误都归结为“填充异常”

我目前正在使用 AES / CBC / PKCS5Padding ,但我尝试了以下

  1. AES / CBC / PKCS5Padding
  2. AES / CBC / NoPadding
  3. AES / ECB / PKCS5Padding
  4. AES / ECB / NoPadding
  5. 如果我使用填充,我会得到一个例外

      

    javax.crypto.BadPaddingException:给定最终块未正确填充

    如果我不使用填充,我会得到

    的例外
      

    javax.crypto.IllegalBlockSizeException:输入长度不是16个字节的倍数。

    我在修补时遇到的其他一些例外

      

    缺少参数   java.security.InvalidKeyException:非法的密钥大小   java.security.InvalidKeyException:无效的AES密钥长度:64字节

    我想问一下,你是否有人愿意指出我正确的方向,我做错了什么。我还是Java新手,所以请假设我知之甚少。 我已经搜索了Stackoverflow很长一段时间,这里的大多数加密问题都是关于文本文件,而不是实际的视频。如果我使用的加密方法不适合视频,请告诉我是否有更好的视频。

1 个答案:

答案 0 :(得分:0)

加密和解密的IV必须相同。通常,在加密时创建随机IV,并且必须将其提供给解密方法。处理此问题的一种方法是使用IV为加密数据添加前缀,以便在解密时可以使用。

注意:

  1. AES是块密码,因此输入必须是块大小的精确倍数,这通常通过填充来完成。

  2. 错误的填充错误通常意味着密钥,IV或加密数据不正确,而不是填充不正确。填充不正确,因为解密失败。