【问题标题】:Java Android - Encrypt/Decrypt file contentsJava Android - 加密/解密文件内容
【发布时间】:2017-04-01 14:13:12
【问题描述】:

所以目前我正在尝试将一些加密文本写入文件,然后能够将其读回,解密并将其显示给用户。我目前正在使用 AES-256 和 PBKDF2 密码派生,因为我希望能够使用用户的密码来加密/解密文件。这些文件是简单的文本文件。我目前用来加密一些文本并将其保存到文件的代码如下。据我所知,从使用 adb 看,这可以正常工作。

FileOutputStream out = new FileOutputStream(mypath);
String defaultMessage = "Empty File";
int iterationCount = 1000;
int keyLength = 256;
int saltLength = keyLength / 8;
SecureRandom randomGenerator = new SecureRandom();
byte[] salt = new byte[saltLength];
randomGenerator.nextBytes(salt);
KeySpec keySpec = new PBEKeySpec(submittedPassword.toCharArray(), salt, iterationCount, keyLength);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
SecretKey key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = new byte[cipher.getBlockSize()];
randomGenerator.nextBytes(iv);
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, key, ivParams);
byte[] ciphertext = cipher.doFinal(defaultMessage.getBytes("UTF-8"));
String finalMessage = ciphertext.toString() + "]" + iv.toString() + "]" + salt.toString();
out.write(finalMessage.getBytes());
out.close();

P.S 以上是在 Try/Except 之内。

下面的代码是我当前尝试用来读取文件然后解密的代码,但是,当我尝试通过最后的测试视图显示解密的内容时,它没有显示出来。

FileInputStream fileInputStream = new FileInputStream(mypath);
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
StringBuffer stringBuffer = new StringBuffer();
while ((fileContents = bufferedReader.readLine()) != null) {
    stringBuffer.append(fileContents + "\n");
}
String fileContentsString = stringBuffer.toString();
String[] fileContentsList = fileContentsString.split("]");
byte[] cipherText = fileContentsList[0].getBytes();
Toast.makeText(getApplicationContext(), fileContentsList[0], Toast.LENGTH_LONG).show();
byte[] iv = fileContentsList[1].getBytes();
byte[] salt = fileContentsList[2].getBytes();
KeySpec keySpec = new PBEKeySpec(submittedPassword.toCharArray(), salt, 1000, 256);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
SecretKey key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, key, ivParams);
byte[] plaintext = cipher.doFinal(cipherText);
String plainrStr = new String(plaintext , "UTF-8");
textEdit.setText(plainrStr);

希望有人可以在这里为我提供一些帮助。同样,第二个代码段位于 Try/Except 语句中。

【问题讨论】:

  • 如果您在进行任何加密或解密之前继续读取和写入完整的文件内容(不推荐),您不妨使用以安全方式执行此操作的库。喜欢这个:github.com/tozny/java-aes-crypto
  • @ArtjomB。不,想法是文件内容被加密,然后保存。当用户想要查看内容时,从文件中读取,然后解密。
  • 还是希望得到答案,还没解决!!!
  • 你试过那个库吗?
  • 我没有,因为根据您的描述,这不是我要找的。 Atm 我的主要问题是解密时来自 doFinal。

标签: java android file encryption


【解决方案1】:

您的代码存在多个问题。

加密

这段代码

String finalMessage = ciphertext.toString() + "]" + iv.toString() + "]" + salt.toString();

产生密文。见这里:Java: Syntax and meaning behind "[B@1ef9157"? Binary/Address?

IV和salt的大小是固定的,所以可以放在密文前面。写完整个密文后,您需要使用Base64 或十六进制之类的东西来获取字符串。像 AES 这样的现代密码产生的密文可以包含任何值的字节,这些字节并不总是构成有效的字符编码,例如 UTF-8。字符串不是任意byte[] 内容的容器。

ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(iv);
baos.write(salt);
baos.write(ciphertext);
String finalMessage = Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT);

但是你根本不需要那个,因为你可以直接将你的密文写入文件中:

out.write(iv);
out.write(salt);
out.write(ciphertext);

解密

不要将InputStream<b>Reader</b>Buffered<b>Reader</b><b>String</b>Buffer 用于二进制数据。否则,你会破坏你的二进制密文。

你只需要这个:

byte[] iv = new byte[16];
byte[] salt = new byte[32];
byte[] ctChunk = new byte[8192]; // not for whole ciphertext, just a buffer

if (16 != fileInputStream.read(iv) || 32 != fileInputStream.read(salt)) {
    throw new Exception("IV or salt too short");
}

KeySpec keySpec = new PBEKeySpec(submittedPassword.toCharArray(), salt, 1000, 256);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
SecretKey key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, key, ivParams);

int read;
ByteArrayOutputStream ctBaos = new ByteArrayOutputStream();
while((read = fileInputStream.read(ctChunk)) > 0) {
    ctBaos.write(cipher.update(cipherText, 0, read));
}
ctBaos.write(cipher.doFinal());

String plainrStr = new String(ctBaos.toByteArray(), "UTF-8");
textEdit.setText(plainrStr);

这可以正确处理随机化,但不提供完整性。如果您想检测对密文的(恶意)操作(通常您希望这样做以防止某些攻击),您需要使用像 GCM 或 EAX 这样的身份验证模式,或者使用 encrypt-then-MAC 方案像 HMAC-SHA256 这样的强大 MAC。

使用像 tozny/java-aes-crypto 这样的库来使用良好的默认值。

【讨论】:

    【解决方案2】:

    如何将一些加密文本写入文件,然后再将其读回?

    public static byte[] generateKey(String password) throws Exception
    {
        byte[] keyStart = password.getBytes("UTF-8");
    
        KeyGenerator kgen = KeyGenerator.getInstance("AES");
        SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
        sr.setSeed(keyStart);
        kgen.init(128, sr);
        SecretKey skey = kgen.generateKey();
        return skey.getEncoded();
    }
    
        public static byte[] encodeFile(byte[] key, byte[] fileData) throws Exception
        {
    
            SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
    
            byte[] encrypted = cipher.doFinal(fileData);
    
            return encrypted;
        }
    
        public static byte[] decodeFile(byte[] key, byte[] fileData) throws Exception
        {
            SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec);
    
            byte[] decrypted = cipher.doFinal(fileData);
    
            return decrypted;
        }
    

    要将加密文件保存到 sd,请执行以下操作:

    File file = new File(Environment.getExternalStorageDirectory() + File.separator + "your_folder_on_sd", "file_name");
    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
    byte[] yourKey = generateKey("password");
    byte[] filesBytes = encodeFile(yourKey, yourByteArrayContainigDataToEncrypt);
    bos.write(fileBytes);
    bos.flush();
    bos.close();
    

    解码文件使用:

    byte[] yourKey = generateKey("password");
    byte[] decodedData = decodeFile(yourKey, bytesOfYourFile);
    

    将文件读入字节数组有不同的方法。一个例子:http://examples.javacodegeeks.com/core-java/io/fileinputstream/read-file-in-byte-array-with-fileinputstream/

    【讨论】:

    • 这不适用于现代版本的 Android。自 API 24 起,Google 已弃用来自 Crypto 提供程序的 SHA1PRNG 算法 SecureRandom。
    • 这不仅被弃用了。它还会根据您的 API 版本产生不同的密钥。那是因为“SHA1PRNG”的实现发生了变化。
    猜你喜欢
    • 2012-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-14
    • 1970-01-01
    • 2016-05-04
    • 1970-01-01
    相关资源
    最近更新 更多