【问题标题】:Multithread AES encryption in JavaJava中的多线程AES加密
【发布时间】:2020-09-14 00:12:56
【问题描述】:

正如标题所说,我正在尝试使用 2 个线程加密文件并使用 1 个线程解密。 由于加密不是线程安全的,我使用FileChannel 来指定在第二个线程中读取的位置。 我正在使用一个 512 字节的缓冲区,因此我将文件大小除以 1024,从而获得在 while 中执行的迭代以到达文件前半部分的末尾。 第二个线程在第一个线程结束后立即开始并迭代直到文件结束。 解密函数一次只读取一个文件,将第二个附加到第一个文件的末尾。

我已经用图像进行了测试,但不幸的是,解密后只能看到图像的前半部分,其余部分是白色的。 在解密结束时我得到Error while decrypting: java.io.IOException: javax.crypto.BadPaddingException

EDIT1:我想我已经向前迈进了一步,现在我可以通过在 2 个不同的地方读取文件来实现多线程加密,但解密后我仍然得到Error while decrypting: java.io.IOException: javax.crypto.BadPaddingException

    public void encrypt(File inputFile, File outputFile, String secret, int threadNum, long iteration) 
    {       
        try
        {
            setKey(secret);
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            
            FileInputStream fis = new FileInputStream(inputFile);
            FileOutputStream fos = new FileOutputStream(outputFile);
            
            byte[] buffer = new byte[512];
            long val = inputFile.length()/100, numIteration = 0;
            int count;
            if(threadNum == 1)
            {
                while(true)
                {
                    if(numIteration == iteration)
                    {
                        fis.read(buffer);
                        fos.write(cipher.doFinal(buffer));
                        System.out.println("Dofinal 1°");
                        break;
                    }
                    else {
                        fis.read(buffer);
                        fos.write(cipher.update(buffer));
                    }
                    System.out.println("Num iteration: "+numIteration);
                    numIteration++;
                }
            }
            else if(threadNum == 2)
            {
                while(numIteration <= iteration)
                {
                    if(numIteration <= iteration/2)
                    {
                        fis.skip(512);
                    }
                    else if(numIteration >= iteration/2) {
                        fis.read(buffer);
                        fos.write(cipher.update(buffer));
                        System.out.println("Iteration"+numIteration);
                    }
                    else if(numIteration == iteration)
                    {
                        fis.read(buffer);
                        fos.write(cipher.doFinal(new byte[(int) inputFile.length()%512]));
                        break;
                    }
                    System.out.println("NumIteration: "+numIteration);
                    numIteration++;
                }
            }
        }
        catch (Exception e) 
        {
            System.out.println("Error while encrypting: " + e.toString());
        }
    }

    public void decrypt(File inputFile[], File outputFile, String secret) 
    {
        System.out.println("Decryption");
        try
        {
            setKey(secret);
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            
            for(int i = 0; i < 2; i++)
            {
                CipherInputStream in = new CipherInputStream(new FileInputStream(inputFile[i]), cipher);
                CountingInputStream cis = new CountingInputStream(in);
                
                FileOutputStream out;
                
                if(i == 0)
                    out = new FileOutputStream(outputFile);
                else 
                    out = new FileOutputStream(outputFile, true);

                CountingOutputStream cos = new CountingOutputStream(out);

                int count;
                double val = (inputFile[i].length()/100);
                byte[] buffer = new byte[512];
                while((count = cis.read(buffer)) != -1)
                {
                    cos.write(buffer, 0, count);
                    cos.flush();
                }
                cis.close();
                cos.close();
            }
        } 
        catch (Exception e) 
        {
            System.out.println("Error while decrypting: " + e.toString());
        }
    }

【问题讨论】:

  • FileChannel 也不是线程安全的,并且由于磁盘本身不是多线程的,因此多线程加密没有任何好处。
  • 为什么你认为“加密”不是线程安全的?密码对象不是线程安全的,因此您在每个线程中创建和使用密码对象。
  • @MarquisofLorne 我使用 nvme ssd 作为存储,所以应该可以实现多线程读取,但此时我不知道是什么原因导致解密期间出现此问题,如果未设置文件通道正常或其他的东西
  • 我也会质疑这种工作方式,但让我们假装吧。由于使用了 ECB(密码处理块不需要先前的状态)和 PKCS5(相同的原因),这可能起作用。但只要 1) 你有线程不安全的计算/发布/重置 keysecretKey 静态变量 2) 你有无序写入(块 N 在块 N-1 之前完成?),3 ) 你没有将你的读取限制在块大小上,4) 没有人控制对 Cipher 上doFinal() 的调用(这将很难)。 Ultim:它有多安全?

标签: java aes filechannel


【解决方案1】:

我设法实现了双线程加密,使用 i5 10210u 4c 8t 将 48.5 mb 文件(从 0.57 到 0.41 秒)的性能提高了约 28%。 应该可以根据CPU的核心数增加线程数来获得更高的性能

代码如下:

    public void encrypt(File inputFile, File outputFile, String secret, int threadNum, long iteration) 
    {       
        try
        {
            setKey(secret);
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            
            FileInputStream fis = new FileInputStream(inputFile);
            FileOutputStream fos = new FileOutputStream(outputFile);
            
            byte[] buffer = new byte[512];
            long numIteration = 1;
            
            if(threadNum == 1)
            {
                while(true)
                {
                    if(numIteration == iteration)
                    {
                        fis.read(buffer);
                        fos.write(cipher.doFinal(buffer));
                        break;
                    }
                    else 
                    {
                        fis.read(buffer);
                        fos.write(cipher.update(buffer));
                    }
                    numIteration++;
                }
            }
            else if(threadNum == 2)
            {
                while(numIteration <= iteration)
                {
                    if(numIteration == iteration)
                    {
                        byte[] f = new byte[(int) inputFile.length()%512];
                        fis.read(f);
                        fos.write(cipher.doFinal(f));
                        break;
                    }
                    else if(numIteration < iteration/2)
                    {
                        fis.skip(512);
                    }
                    else if(numIteration >= iteration/2 && numIteration < iteration) 
                    {
                        fis.read(buffer);
                        fos.write(cipher.update(buffer));
                    }
                    numIteration++;
                }
            }
            fis.close();
            fos.close();
        }
        catch (Exception e) 
        {
            System.out.println("Error while encrypting: " + e.toString());
        }
    }

    public void decrypt(File inputFile[], File outputFile, String secret) 
    {
        try
        {
            setKey(secret);
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            
            for(int i = 0; i < 2; i++)
            {               
                CipherInputStream in = new CipherInputStream(new FileInputStream(inputFile[i]), cipher);
                CountingInputStream cis = new CountingInputStream(in);
                
                FileOutputStream out;
                
                if(i == 0)
                    out = new FileOutputStream(outputFile);
                else 
                    out = new FileOutputStream(outputFile, true);

                CountingOutputStream cos = new CountingOutputStream(out);

                int count;
                double val = (inputFile[i].length()/100);
                byte[] buffer = new byte[512];
                
                while((count = cis.read(buffer)) != -1)
                {
                    cos.write(buffer, 0, count);
                    cos.flush();
                    System.out.println("Percentuale: "+cos.getCount()/val+"%");
                }
                cis.close();
                cos.close();
            }
        } 
        catch (Exception e) 
        {
            System.out.println("Error while decrypting: " + e.toString());
        }
    }

【讨论】:

  • 我宁愿说“大多数时候代码不会失败”。在密钥的设置中仍然存在竞争条件。没有任何保护措施可以使读取达到所需的长度(512 字节),因此可以保持块大小的完整性。输出是两个单独的文件,其中有一个输入,这将花费时间。这不会动态扩展到可用的 CPU 能力(这将创建更多要合并的文件),也不会扩展到其他键大小、块模式或填充模式。这作为一个练习很好,但我不相信它。
  • @GPI 我显然计划使用多线程加密来处理大文件,该示例是使用 43 mb 之一,但使用更大的,时间改进接近 33%(显然只考虑加密) 所以理论上块大小问题并没有真正影响到我。在解密期间,文件被附加(未解密然后合并),因此没有时间差(解密)。该程序也可以在 CBC 模式下工作,添加IvParameterSpec。关于密钥的竞态条件,您能详细说明一下吗?
猜你喜欢
  • 2011-06-05
  • 2012-02-17
  • 2012-03-28
  • 2012-06-01
  • 2017-05-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-19
相关资源
最近更新 更多