java - 加快android DES加密/解密

标签 java android encryption cryptography

我必须在 Android 上加密和解密文件。为此,我编写了以下类:

package blabla.fileencrypter;

import alotofclasses;

/**
 * The FileEncoder class provides an interface to allow for easy encrypting and decrypting of files. To use this class, first call both {@link #setSalts(String, String)} and {@link #setFolders(String, String)}.
 * @author Daniël van den Berg
 * @since Nov 26, 2015
 *
 */
public class FileEncrypter {
    private static String encryptedFolder = "";
    private static String decryptedFolder = "";
    private static byte[] salt = null;
    private static IvParameterSpec iv = null;
    private static String encryptedPostfix = "";

    /**
     * Sets the folders the documents have to be placed in.
     * @param encryptedFolder The folder encrypted files have to be placed in.
     * @param decryptedFolder The folder decrypted files have to be placed in.
     */
    public static void setFolders(String encryptedFolder, String decryptedFolder){
        FileEncrypter.encryptedFolder = encryptedFolder;
        FileEncrypter.decryptedFolder = decryptedFolder;
    }

    /**
     * A postfix to give to encrypted files. Can be "" for no postfix.
     * @param extension The postfix to append to encrypted files.
     */
    public static void setEncryptedPostfix(String extension){
        if (extension != null){
            FileEncrypter.encryptedPostfix = extension;
        }else{
            FileEncrypter.encryptedPostfix = "";
        }
    }

    /**
     * The salts to use when encrypting/decrypting files.
     * @param salt The salt to use.
     * @param ivParameterSpec The buffer with the IV.
     * @throws FileEncryptingException When the ivParameterSpec is smaller than 16 bytes.
     */
    public static void setSalts(String salt, String ivParameterSpec) throws FileEncryptingException{
        FileEncrypter.salt = salt.getBytes();
        if (ivParameterSpec.length() < 16){
            throw new FileEncryptingException("ivParameterSpec not long enough. Should be at least 16 bytes.");
        }
        FileEncrypter.iv = new IvParameterSpec(ivParameterSpec.getBytes(),0,16);
    }

    /**
     * Encode the given inputFile with the given key.
     * @param inputFile The file to encrypt.
     * @param key The key to use for encrypting the file.
     * @return The encrypted file.
     * @throws FileEncryptingException
     * @throws NoSuchAlgorithmException
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException
     * @throws IOException
     * @throws InvalidKeySpecException
     * @throws InvalidAlgorithmParameterException
     */
    public static File encrypt(File inputFile, String key) throws FileEncryptingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IOException, InvalidKeySpecException, InvalidAlgorithmParameterException{
        if (isEncrypted(inputFile)){
            throw new FileEncryptingException("File not decrypted: "+inputFile.getAbsolutePath());
        }

        FileInputStream inputStream = new FileInputStream(inputFile);

        File outputFile = new File(inputFile.getAbsolutePath().replace(decryptedFolder,encryptedFolder) + encryptedPostfix);
        outputFile.getParentFile().mkdirs();
        FileOutputStream outputStream = new FileOutputStream(outputFile);
        processStream(Cipher.ENCRYPT_MODE,key,inputStream,outputStream);
        inputStream.close();
        outputStream.close();
        return outputFile;
    }

    /**
     * Decrypt the given inputFile with the given key.
     * @param inputFile The file to decrypt.
     * @param key The key to use for decrypting the file.
     * @return The decrypted file.
     * @throws FileEncryptingException
     * @throws IOException
     * @throws NoSuchAlgorithmException
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException
     * @throws InvalidKeySpecException
     * @throws InvalidAlgorithmParameterException
     */
    public static File decrypt(File inputFile, String key) throws FileEncryptingException, IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidKeySpecException, InvalidAlgorithmParameterException{
        if (!isEncrypted(inputFile)){
            throw new FileEncryptingException("File not encrypted: "+inputFile.getAbsolutePath());
        }

        if (!inputFile.exists() && !inputFile.getAbsolutePath().contains(encryptedPostfix)){
            inputFile = new File(inputFile.getAbsolutePath()+encryptedPostfix);
        }

        FileInputStream inputStream = new FileInputStream(inputFile);

        File outputFile = new File(inputFile.getAbsolutePath().replace(encryptedPostfix, "").replace(encryptedFolder,decryptedFolder));
        outputFile.getParentFile().mkdirs();
        FileOutputStream outputStream = new FileOutputStream(outputFile);
        processStream(Cipher.DECRYPT_MODE,key,inputStream,outputStream);
        inputStream.close();
        outputStream.close();
        return outputFile;
    }

    /**
     * Used for generating a cipher.
     * @param cipherMode The cipher mode to use. Either <code>Cipher.DECRYPT_MODE</code> or <code>Cipher.ENCRYPT_MODE</code>
     * @param key The key to generate the cipher with.
     * @return The generated cipher.
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException
     * @throws InvalidAlgorithmParameterException
     */
    private static Cipher getCipher(int cipherMode, String key) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException {
        SecretKeyFactory factory = SecretKeyFactory.getInstance("DES");
        KeySpec spec;
        try {
            spec = new DESKeySpec((key+salt).getBytes("UTF8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return null;
        }
        SecretKey secret = factory.generateSecret(spec);

        Cipher c = Cipher.getInstance("DES");
        c.init(cipherMode, secret,iv);
        return c;
    }

    /**
     * Process a stream. This will encrypt or decrypt the stream, depending on the given cipherMode. The output will be available in the given {@link OutputStream}.
     * @param cipherMode The cipher mode to use. Either <code>Cipher.DECRYPT_MODE</code> or <code>Cipher.ENCRYPT_MODE</code>
     * @param key The key to use for decryption/encryption.
     * @param inputStream The stream to read from.
     * @param outputStream The stream to write the encrypted/decrypted result to.
     * @throws InvalidKeyException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws NoSuchPaddingException
     * @throws InvalidAlgorithmParameterException
     * @throws IOException
     */
    public static void processStream(int cipherMode, String key, InputStream inputStream, OutputStream outputStream) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidAlgorithmParameterException, IOException{
        Cipher c = getCipher(cipherMode,key);
        CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, c);
        int b = 0;
        while ((b = inputStream.read()) !=-1){
            cipherOutputStream.write(b);
        }
        cipherOutputStream.close();
    }

    /**
     * Returns a file, no matter whether it's encrypted or not. See {@link #isEncrypted(File)} to detect if the file is encrypted.
     * @param filenameDecrypted The filename the decrypted file would have.
     * @return The file that corresponds with the given filename.
     */
    public static File getFile(String filenameDecrypted){
        if (!filenameDecrypted.contains(decryptedFolder) && !filenameDecrypted.contains(encryptedFolder)){
            filenameDecrypted = decryptedFolder + filenameDecrypted;
        }
        File file = new File(filenameDecrypted);
        if (!file.exists()){
            file = new File(filenameDecrypted.replace(decryptedFolder, encryptedFolder)+encryptedPostfix);
        }
        return file;
    }

    /**
     * Checks if the file is encrypted or not.
     * @param file The file to check.
     * @return True if the file is encrypted, false otherwise.
     */
    public static boolean isEncrypted(File file){
        return file.getAbsolutePath().contains(encryptedFolder) || (!encryptedPostfix.isEmpty() && file.getAbsolutePath().contains(encryptedPostfix));
    }
}

一开始我实现这个类是用AES加密解密的。但是,这样做的速度太慢了,所以我们改用 DES。但是,这似乎仍然非常慢。大约 1MB 的文件加密大约需要半分钟。

有什么方法可以显着加快速度吗?

最佳答案

归功于 Wagner Tsuchiya感谢您在这方面的帮助。

解决方案主要有两点。

  1. 使用 BufferedReader/Writer,如函数中所示 encrypt()decrypt。这将速度从 3MB/2:16 分钟更改为大约 3MB/0:59 分钟。
  2. 使用不同的加密算法。 DES 结果比 AES 还要慢,但对于我目前的要求,一个更简单的算法 RC4 就足够了。这再次将加密和解密所花费的时间减半。可以在 http://www.javamex.com/tutorials/cryptography/ciphers.shtml 上找到不同算法的比较。 .

这都导致了以下代码:

package blabla.fileencrypter;

import lots;

/**
 * The FileEncoder class provides an interface to allow for easy encrypting and decrypting of files. To use this class, first call both {@link #setSalts(String, String)} and {@link #setFolders(String, String)}.
 * @author Daniël van den Berg
 * @since Nov 26, 2015
 *
 */
public class FileEncrypter {
    private static String encryptedFolder = "";
    private static String decryptedFolder = "";
    private static byte[] salt = null;
    private static String encryptedPostfix = "";
    private static final HashMap<Integer, HashMap<String,Cipher>> ciphers = new HashMap<Integer, HashMap<String, Cipher>>();

    /**
     * Sets the folders the documents have to be placed in.
     * @param encryptedFolder The folder encrypted files have to be placed in.
     * @param decryptedFolder The folder decrypted files have to be placed in.
     */
    public static void setFolders(String encryptedFolder, String decryptedFolder){
        FileEncrypter.encryptedFolder = encryptedFolder;
        FileEncrypter.decryptedFolder = decryptedFolder;
    }

    /**
     * A postfix to give to encrypted files. Can be "" for no postfix.
     * @param extension The postfix to append to encrypted files.
     */
    public static void setEncryptedPostfix(String extension){
        if (extension != null){
            FileEncrypter.encryptedPostfix = extension;
        }else{
            FileEncrypter.encryptedPostfix = "";
        }
    }

    /**
     * The salts to use when encrypting/decrypting files.
     * @param salt The salt to use.
     * @param ivParameterSpec The buffer with the IV.
     * @throws FileEncryptingException When the ivParameterSpec is smaller than 16 bytes.
     */
    public static void setSalts(String salt) throws FileEncryptingException{
        FileEncrypter.salt = salt.getBytes();
    }

    /**
     * Encode the given inputFile with the given key.
     * @param inputFile The file to encrypt.
     * @param key The key to use for encrypting the file.
     * @return The encrypted file.
     * @throws FileEncryptingException
     * @throws NoSuchAlgorithmException
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException
     * @throws IOException
     * @throws InvalidKeySpecException
     * @throws InvalidAlgorithmParameterException
     */
    public static File encrypt(File inputFile, String key) throws FileEncryptingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IOException, InvalidKeySpecException, InvalidAlgorithmParameterException{
        if (isEncrypted(inputFile)){
            throw new FileEncryptingException("File not decrypted: "+inputFile.getAbsolutePath());
        }

        FileInputStream inputStream = new FileInputStream(inputFile);
        BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);

        File outputFile = new File(inputFile.getAbsolutePath().replace(decryptedFolder,encryptedFolder) + encryptedPostfix);
        outputFile.getParentFile().mkdirs();
        FileOutputStream outputStream = new FileOutputStream(outputFile);
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
        processStream(Cipher.ENCRYPT_MODE,key,bufferedInputStream,bufferedOutputStream);
        inputStream.close();
        outputStream.close();
        bufferedInputStream.close();
        bufferedOutputStream.close();
        return outputFile;
    }

    /**
     * Decrypt the given inputFile with the given key.
     * @param inputFile The file to decrypt.
     * @param key The key to use for decrypting the file.
     * @return The decrypted file.
     * @throws FileEncryptingException
     * @throws IOException
     * @throws NoSuchAlgorithmException
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException
     * @throws InvalidKeySpecException
     * @throws InvalidAlgorithmParameterException
     */
    public static File decrypt(File inputFile, String key) throws FileEncryptingException, IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidKeySpecException, InvalidAlgorithmParameterException{
        if (!isEncrypted(inputFile)){
            throw new FileEncryptingException("File not encrypted: "+inputFile.getAbsolutePath());
        }

        if (!inputFile.exists() && !inputFile.getAbsolutePath().contains(encryptedPostfix)){
            inputFile = new File(inputFile.getAbsolutePath()+encryptedPostfix);
        }

        FileInputStream inputStream = new FileInputStream(inputFile);
        BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);

        File outputFile = new File(inputFile.getAbsolutePath().replace(encryptedPostfix, "").replace(encryptedFolder,decryptedFolder));
        outputFile.getParentFile().mkdirs();
        FileOutputStream outputStream = new FileOutputStream(outputFile);
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
        processStream(Cipher.DECRYPT_MODE,key,bufferedInputStream,bufferedOutputStream);
        inputStream.close();
        outputStream.close();
        bufferedInputStream.close();
        bufferedOutputStream.close();
        return outputFile;
    }

    /**
     * Used for generating a cipher.
     * @param cipherMode The cipher mode to use. Either <code>Cipher.DECRYPT_MODE</code> or <code>Cipher.ENCRYPT_MODE</code>
     * @param key The key to generate the cipher with.
     * @return The generated cipher.
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException
     * @throws InvalidAlgorithmParameterException
     */
    private static Cipher getCipher(int cipherMode, String key) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException {
        if (!ciphers.containsKey(cipherMode)){
            synchronized (ciphers){
                if (!ciphers.containsKey(cipherMode)){
                    ciphers.put(cipherMode, new HashMap<String,Cipher>());
                }
            }
        }
        HashMap<String, Cipher> hashMap = ciphers.get(cipherMode);
        if (!hashMap.containsKey(key)){
            synchronized (hashMap) {
                if (!hashMap.containsKey(key)){
                    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
                    KeySpec spec = new PBEKeySpec(key.toCharArray(), salt, 65536, 128);
                    SecretKey tmp = factory.generateSecret(spec);
                    SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "RC4");

                    Cipher c = Cipher.getInstance("RC4");
                    c.init(cipherMode, secret);
                    hashMap.put(key, c);
                }
            }
        }
        return hashMap.get(key);
    }

    /**
     * Process a stream. This will encrypt or decrypt the stream, depending on the given cipherMode. The output will be available in the given {@link OutputStream}.
     * @param cipherMode The cipher mode to use. Either <code>Cipher.DECRYPT_MODE</code> or <code>Cipher.ENCRYPT_MODE</code>
     * @param key The key to use for decryption/encryption.
     * @param inputStream The stream to read from.
     * @param outputStream The stream to write the encrypted/decrypted result to.
     * @throws InvalidKeyException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws NoSuchPaddingException
     * @throws InvalidAlgorithmParameterException
     * @throws IOException
     */
    public static void processStream(int cipherMode, String key, InputStream inputStream, OutputStream outputStream) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidAlgorithmParameterException, IOException{
        Cipher c = getCipher(cipherMode,key);
        CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, c);
        int b = 0;
        while ((b = inputStream.read()) !=-1){
            cipherOutputStream.write(b);
        }
        cipherOutputStream.close();
    }

    /**
     * Returns a file, no matter whether it's encrypted or not. See {@link #isEncrypted(File)} to detect if the file is encrypted.
     * @param filenameDecrypted The filename the decrypted file would have.
     * @return The file that corresponds with the given filename.
     */
    public static File getFile(String filenameDecrypted){
        if (!filenameDecrypted.contains(decryptedFolder) && !filenameDecrypted.contains(encryptedFolder)){
            filenameDecrypted = decryptedFolder + filenameDecrypted;
        }
        File file = new File(filenameDecrypted);
        if (!file.exists()){
            file = new File(filenameDecrypted.replace(decryptedFolder, encryptedFolder)+encryptedPostfix);
        }
        return file;
    }

    /**
     * Checks if the file is encrypted or not.
     * @param file The file to check.
     * @return True if the file is encrypted, false otherwise.
     */
    public static boolean isEncrypted(File file){
        return file.getAbsolutePath().contains(encryptedFolder) || (!encryptedPostfix.isEmpty() && file.getAbsolutePath().contains(encryptedPostfix));
    }
}

关于java - 加快android DES加密/解密,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35034350/

相关文章:

java - 找不到 hibernate 属性

java - Try-with-resources api pre 19

sql-server - SQL Server 2005 数据加密和 LINQ TO SQL

ssl - 解密 TLS 和/或比较 TLS 负载

java - ccnx android java :not found

php mysql_connect 安全

java - JScrollpane 需要缩小它的宽度

java - 如何让用户在读取csv文件时出现一次?

java - JAXB 2.x 与 MOXy (Eclipselink 2.1.2) : persisting XHTML as element value

android - 修复了带有 CoordinatorLayout 的 header 在滚动时显示阴影