在Android上使用AES加密文件

13

我正在为自己做一个个人项目,尝试在手机上加密文件,这些文件可以是任何东西,比如文档、照片等。现在我正在努力使这个项目正常运行。每当我运行加密时,它似乎都能正常工作并加密文件。但是,当我运行解密时,有时它可以正常工作,而有时却无法正常工作。当它失败时,通常会出现“Error while finalizing cipher, pad block corrupted”错误。我也没有使用不同的测试文件,所以不像有些文件可用而其他文件不可用。每次我尝试的都是同样的两个文件。

public static void encryptfile(String path,String Pass) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException {
    FileInputStream fis = new FileInputStream(path);
    FileOutputStream fos = new FileOutputStream(path.concat(".crypt"));
    byte[] key = (salt + Pass).getBytes("UTF-8");
    MessageDigest sha = MessageDigest.getInstance("SHA-1");
    key = sha.digest(key);
    key = Arrays.copyOf(key,16);
    SecretKeySpec sks = new SecretKeySpec(key, "AES");
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.ENCRYPT_MODE, sks);
    CipherOutputStream cos = new CipherOutputStream(fos, cipher);
    int b;
    byte[] d = new byte[8];
    while((b = fis.read(d)) != -1) {
        cos.write(d, 0, b);
    }
    cos.flush();
    cos.close();
    fis.close();
}

public static void decrypt(String path,String Pass) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException {
    FileInputStream fis = new FileInputStream(path);
    FileOutputStream fos = new FileOutputStream(path.replace(".crypt",""));
    byte[] key = (salt + Pass).getBytes("UTF-8");
    MessageDigest sha = MessageDigest.getInstance("SHA-1");
    key = sha.digest(key);
    key = Arrays.copyOf(key,16);
    SecretKeySpec sks = new SecretKeySpec(key, "AES");
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.DECRYPT_MODE, sks);
    CipherInputStream cis = new CipherInputStream(fis, cipher);
    int b;
    byte[] d = new byte[8];
    while((b = cis.read(d)) != -1) {
        fos.write(d, 0, b);
    }
    fos.flush();
    fos.close();
    cis.close();
}

目前Salt和Password是静态的,为了测试目的而不改变。但仍然有大约一半的时间会出现错误。

有人对此有何想法吗?我已经搜索了一些解决方案,但都没有奏效。我在以下问题中寻找解决方法:

Android解密:在完成密码时发生错误

使用CipherInputStream / CipherOutputStream时最后一个块不完整,即使使用填充AES / CBC / PKCS5Padding

Android 4.2加密错误

解密错误:“当预期一个IV时没有设置IV”

如何处理“解密时最后一个块不完整”

图像文件的加密和解密

使用AES在Java中加密/解密图像的提示

非常感谢任何帮助!我想我只是缺少了一些简单的东西......

更新!

人们说得对,这是关于盐的问题。当我去掉盐时,问题得到了解决...再深入一点发现,问题出在盐+ Pass上,但因为盐是byte[]而Pass是字符串。我将salt更改为String,然后使用salt.concat(Pass),问题就解决了!


我个人很想看看你尝试实现了什么。这也可以帮助其他人,因为他们可以看到哪些选项已经被尝试过(即使它们被错误地实现了)。 - Bono
@Bono 我添加了我认为对我有帮助的链接,但最终并没有太大帮助。我尝试过一些方法,比如让它不使用填充,改用CBC而不是EBC(无论如何我都想这样做)。有一次,我甚至让它转换Base64 :P - Linux4Hope
似乎盐是最有可能的问题,因为盐由随机字节组成,但它作为字符串给出。你是如何创建这个盐的? - Maarten Bodewes
@MaartenBodewes 我希望是这样,但Salt目前已经固定用于测试 :/ - Linux4Hope
这个错误可能是由于错误的密钥、乱码的密文或者 - 仅限于相当短的密文 - 错误的 IV 值引起的。如果您输入了错误的密码,这将是唯一检测到它的方法(如果没有认证标签存在)。 - Maarten Bodewes
@MaartenBodewes 目前我只是使用设置变量来存储密码和盐。这样我可以测试加密并确保我没有那些问题。我可以通过记录密钥来进行双重检查。还有其他想法吗? - Linux4Hope
1个回答

10

也许我漏掉了什么,但在我的电脑上它可以正常工作。 您可以尝试简单更改fileToBeCrypted、fileToBeDecrypted和fileDecryptedOutput变量的值来查看是否有效?

package test;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

public class TestCrypt{

    private static final String salt = "t784";
    private static final String cryptPassword = "873147cbn9x5'2 79'79314";
    private static final String fileToBeCrypted = "c:\\Temp\\sampleFile.conf";
    private static final String fileToBeDecrypted = "c:\\Temp\\sampleFile.conf.crypt";
    private static final String fileDecryptedOutput = "c:\\Temp\\sampleFile.conf.decrypted";

    public static void main(String[] args) throws Exception
    {
        for (int i=0; i<100; i++)
        {
            encryptfile(fileToBeCrypted, cryptPassword);
            decrypt(fileToBeDecrypted, cryptPassword, fileDecryptedOutput);
            System.out.println(i);
        }
    }

    public static void encryptfile(String path,String password) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException {
        FileInputStream fis = new FileInputStream(path);
        FileOutputStream fos = new FileOutputStream(path.concat(".crypt"));
        byte[] key = (salt + password).getBytes("UTF-8");
        MessageDigest sha = MessageDigest.getInstance("SHA-1");
        key = sha.digest(key);
        key = Arrays.copyOf(key,16);
        SecretKeySpec sks = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, sks);
        CipherOutputStream cos = new CipherOutputStream(fos, cipher);
        int b;
        byte[] d = new byte[8];
        while((b = fis.read(d)) != -1) {
            cos.write(d, 0, b);
        }
        cos.flush();
        cos.close();
        fis.close();
    }

    public static void decrypt(String path,String password, String outPath) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException {
        FileInputStream fis = new FileInputStream(path);
        FileOutputStream fos = new FileOutputStream(outPath);
        byte[] key = (salt + password).getBytes("UTF-8");
        MessageDigest sha = MessageDigest.getInstance("SHA-1");
        key = sha.digest(key);
        key = Arrays.copyOf(key,16);
        SecretKeySpec sks = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.DECRYPT_MODE, sks);
        CipherInputStream cis = new CipherInputStream(fis, cipher);
        int b;
        byte[] d = new byte[8];
        while((b = cis.read(d)) != -1) {
            fos.write(d, 0, b);
        }
        fos.flush();
        fos.close();
        cis.close();
    }

}

我可以多次迭代而不出现错误! 我正在使用Oracle JDK 1.8,但以1.7兼容模式运行。

希望这可以帮助你。 再见 皮耶罗


结果证明是盐的问题...唯一的问题是我不确定为什么。我正在做更多的挖掘 :) - Linux4Hope
同意Linux,salt不能包含特殊字符。 - Michalsx
无法处理视频文件。加密和解密后,视频无法播放。请建议一些解决方案。 - Anurag Srivastava
嗨,处理二进制文件(如视频)没有特殊的配置或更改:您将它们存储在哪里? - Piero Ottuzzi
我们可以使用任何字符作为盐吗? - Cecil Paul
不仅SALT可以由任何字符创建,而且如果您查看代码,您会发现SALT被用作字节数组:这意味着SALT可以由任何字节数组制成(因此即使是非可打印字符,如\r或\n)。 - Piero Ottuzzi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接