【问题标题】:JAVA - Reading and encrypting large number of passwords from text - Any way to speed it up?JAVA - 从文本中读取和加密大量密码 - 有什么方法可以加快速度吗?
【发布时间】:2015-06-21 22:10:18
【问题描述】:

我正在尝试读取一个大文件,该文件的行由电子邮件:密码组合组成。然后我使用 Mega.co.nz 的 API 对它们进行加密。这很好用,但是加密过程太慢了,无法在合理的时间内处理 100+MB 的文本文件。

我最初是用 Python 做的,但是太慢了。我认为 JAVA 会大大加快速度,但事实并非如此。它确实加快了一点速度,但幅度不大。

这是适合多线程的任务吗?

谢谢!

import java.io.*;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.Arrays;

class RunnableDemo implements Runnable {
   private Thread t;
   private String threadName;
   private long[] password_aes;
   private String uh = "";
   private static final char[] CA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
   private static final int[] IA = new int[256];
   private String fileName = "C://combo_encrypted.txt";



    static {
        Arrays.fill(IA, -1);
        for (int i = 0, iS = CA.length; i < iS; i++)
            IA[CA[i]] = i;
        IA['='] = 0;
    }

   RunnableDemo( String name){
       threadName = name;
   }

   public void run() {


   FileWriter fileWriter;
try {
    fileWriter = new FileWriter(fileName, true);
    BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);

    String finalString = "";
    String[] parts = threadName.split(":");
    password_aes = prepare_key_pw(parts[1]);
    uh = stringhash(parts[0], password_aes);
    finalString = (parts[0] + ":" + parts[1] + ":" + uh + "\n");
    System.out.println(finalString);
    bufferedWriter.write(finalString);
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

   }



   public void start ()
   {
      System.out.println("Starting " +  threadName );
      if (t == null)
      {
         t = new Thread (this, threadName);
         t.start ();
      }
   }


public static long[] str_to_a32(String string) {
    if (string.length() % 4 != 0) {
        string += new String(new char[4 - string.length() % 4]);
    }
    long[] data = new long[string.length() / 4];

    byte[] part = new byte[8];
    for (int k = 0, i = 0; i < string.length(); i += 4, k++) {
        String sequence = string.substring(i, i + 4);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            baos.write(sequence.getBytes("ISO-8859-1"));
            System.arraycopy(baos.toByteArray(), 0, part, 4, 4);
            ByteBuffer bb = ByteBuffer.wrap(part);
            data[k] = bb.getLong();
        } catch (IOException e) {
            data[k] = 0;
        }
    }
    return data;
}

public static String a32_to_str(long[] data) {
    byte[] part = null;
    StringBuilder builder = new StringBuilder();
    ByteBuffer bb = ByteBuffer.allocate(8);
    for (int i = 0; i < data.length; i++) {
        bb.putLong(data[i]);
        part = Arrays.copyOfRange(bb.array(), 4, 8);
        bb.clear();
        ByteArrayInputStream bais = new ByteArrayInputStream(part);
        while (bais.available() > 0) {
            builder.append((char) bais.read());
        }
    }
    return builder.toString();
}

public static byte[] aes_cbc_encrypt(byte[] data, byte[] key) {
    String iv = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
    IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes());
    byte[] output = null;
    try {
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/NOPADDING");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
        output = cipher.doFinal(data);

    } catch (Exception e) {
        e.printStackTrace();
    }
    return output;
}

public static long[] aes_cbc_encrypt_a32(long[] idata, long[] ikey) {
    try {
        byte[] data = a32_to_str(idata).getBytes("ISO-8859-1");
        byte[] key = a32_to_str(ikey).getBytes("ISO-8859-1");
        byte[] encrypt = aes_cbc_encrypt(data, key);

        return str_to_a32(new String(encrypt, "ISO-8859-1"));

    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    return new long[0];
}

public static String base64_url_encode(String data) {

    try {
        data = new String(base64_url_encode_byte((data.getBytes("ISO-8859-1")),true), "ISO-8859-1");
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    data = data.replaceAll("\\+", "-");
    data = data.replaceAll("/", "_");
    data = data.replaceAll("=", "");

    return data;
}

public static String a32_to_base64(long[] a) {
    return base64_url_encode(a32_to_str(a));
}

public static String stringhash(String email, long[] aeskey) {
    long[] s32 = str_to_a32(email);
    long[] h32 = {0, 0, 0, 0};
    for (int i = 0; i < s32.length; i++) {
        h32[i % 4] ^= s32[i];
    }
    for (int r = 0; r < 0x4000; r++) {
        h32 = aes_cbc_encrypt_a32(h32, aeskey);
    }
    long[] h32Part = new long[2];
    h32Part[0] = h32[0];
    h32Part[1] = h32[2];
    return a32_to_base64(h32Part);
}

public static long[] prepare_key(long[] password) {
    long[] pkey = {0x93C467E3, 0x7DB0C7A4, 0xD1BE3F81, 0x0152CB56};
    for (int r = 0; r < 0x10000; r++) {
        for (int j = 0; j < password.length; j += 4) {
            long[] key = {0, 0, 0, 0};
            for (int i = 0; i < 4; i++) {
                if (i + j < password.length) {
                    key[i] = password[i + j];
                }
            }
            pkey = aes_cbc_encrypt_a32(pkey, key);
        }
    }
    return pkey;}

public static long[] prepare_key_pw(String password) {
    return prepare_key(str_to_a32(password));
}

   public final static byte[] base64_url_encode_byte(byte[] sArr, boolean lineSep){
        // Check special case
        int sLen = sArr != null ? sArr.length : 0;
        if (sLen == 0)
            return new byte[0];

        int eLen = (sLen / 3) * 3;                              // Length of even 24-bits.
        int cCnt = ((sLen - 1) / 3 + 1) << 2;                   // Returned character count
        int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array
        byte[] dArr = new byte[dLen];

        // Encode even 24-bits
        for (int s = 0, d = 0, cc = 0; s < eLen;) {
            // Copy next three bytes into lower 24 bits of int, paying attension to sign.
            int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff);

            // Encode the int into four chars
            dArr[d++] = (byte) CA[(i >>> 18) & 0x3f];
            dArr[d++] = (byte) CA[(i >>> 12) & 0x3f];
            dArr[d++] = (byte) CA[(i >>> 6) & 0x3f];
            dArr[d++] = (byte) CA[i & 0x3f];

            // Add optional line separator
            if (lineSep && ++cc == 19 && d < dLen - 2) {
                dArr[d++] = '\r';
                dArr[d++] = '\n';
                cc = 0;

            }
        }

        // Pad and encode last bits if source isn't an even 24 bits.
        int left = sLen - eLen; // 0 - 2.
        if (left > 0) {
            // Prepare the int
            int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0);

            // Set last four chars
            dArr[dLen - 4] = (byte) CA[i >> 12];
            dArr[dLen - 3] = (byte) CA[(i >>> 6) & 0x3f];
            dArr[dLen - 2] = left == 2 ? (byte) CA[i & 0x3f] : (byte) '=';
            dArr[dLen - 1] = '=';
        }
        return dArr;
    }

}


public class TestThread {


   final static String OUTPUT_FILE_NAME = "C:\\combo_encrypted.txt";

   public static void main(String args[]) throws IOException {





  File file1 = new File("File1.txt");
  File file2 = new File("File2.txt");
  File file3 = new File("File3.txt");
  File file4 = new File("File4.txt");

  FileInputStream fis1 = null;
  FileInputStream fis2 = null;
  FileInputStream fis3 = null;
  FileInputStream fis4 = null;

  BufferedInputStream bis1 = null;
  BufferedInputStream bis2 = null;
  BufferedInputStream bis3 = null;
  BufferedInputStream bis4 = null;

  DataInputStream dis1 = null;
  DataInputStream dis2 = null;
  DataInputStream dis3 = null;
  DataInputStream dis4 = null;

  fis1 = new FileInputStream(file1);    
  fis2 = new FileInputStream(file2);
  fis3 = new FileInputStream(file3);
  fis4 = new FileInputStream(file4);

  bis1 = new BufferedInputStream(fis1);
  bis2 = new BufferedInputStream(fis2);
  bis3 = new BufferedInputStream(fis3);
  bis4 = new BufferedInputStream(fis4);

  dis1 = new DataInputStream(bis1);
  dis2 = new DataInputStream(bis2);
  dis3 = new DataInputStream(bis3);
  dis4 = new DataInputStream(bis4);

  while ( (dis4.available() != 0) ) {
        String combo1 = dis1.readLine();
        String combo2 = dis2.readLine();
        String combo3 = dis3.readLine();
        String combo4 = dis4.readLine();

        RunnableDemo R1 = new RunnableDemo(combo1);
        RunnableDemo R2 = new RunnableDemo(combo2);
        RunnableDemo R3 = new RunnableDemo(combo3);
        RunnableDemo R4 = new RunnableDemo(combo4);

        R1.start();
        R2.start();
        R3.start();
        R4.start();
  }

  fis1.close();
  fis2.close();
  fis3.close();
  fis4.close();

  bis1.close();
  bis2.close();
  bis3.close();
  bis4.close();

  dis1.close();
  dis2.close(); 
  dis3.close(); 
  dis4.close(); 

   }   
}

【问题讨论】:

标签: java performance api encryption passwords


【解决方案1】:

你在做什么有趣的事情???您真的要加密文件吗?如果是这样,那就去做吧。文件是一个字节序列,而这正是 Cipher.updateCipher.doFinal 的作用。

读取字节,加密字节,写入字节。完毕。转换成字符串、base64、long[] 和其他无用的东西只会减慢速度。

也许您想要的不是文件加密,但您应该清楚地描述它。您似乎正在做一些可能需要的逐行处理,但我无法理解。

您可以通过避免从字节转换为字符串和类似的东西来加速逐行处理。此外,创建新密码和类似的东西也不会加快速度。

这是适合多线程的任务吗?

如果你真的需要逐行处理,我想是的。但是除非你有几十个内核,否则你可以通过先清理代码来获得更多。

【讨论】:

  • 感谢您的回复。我不想加密文件。我想从文本文件中的一行读取电子邮件地址和密码,并根据这两个解析字符串创建一个加密字符串。
  • @pelayo 我明白了(我应该阅读问题标题,而不仅仅是文本!)。加密算法看起来有点奇怪,万一是你自己发明的,问问它的安全性。 +++ 我关于避免在不同格式之间转换和重新创建密码等的部分答案仍然适用。
【解决方案2】:

由于加密是罪魁祸首,尝试使用多个线程来做加密阶段。您可以通过像这样流水线化您的程序来实现这一点:

然而,这假设结果列表的顺序无关紧要。如果确实很重要,您将需要在加密阶段之后进行某种排序。

【讨论】:

    【解决方案3】:

    代码很慢,因为代码实现了某种Key stretching故意很慢)。由于它受 CPU 限制,如果你能让它使用所有内核,它会运行得更快。

    但不要指望能找到真正加快速度的方法,而无需更改所使用的密码算法。

    【讨论】:

    • 您好,您回复时我正在编辑代码。谢谢您的答复。我让它使用多个线程,它现在可以非常快速地加密所有密码。我将文件拆分为四个较小的文本文件,并打开了四个流,每个文件一个。我同时将文本文件的每一行提供给工作人员。过去需要 30 多分钟的任务现在大约需要一分钟。我的新代码非常草率,但似乎可以正常工作。我现在遇到的问题是将加密的密码写入一个新的文本文件。我相信我的方法不正确。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-22
    • 1970-01-01
    • 2012-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-20
    相关资源
    最近更新 更多