如何在Android中加密和解密文件?

Posted

技术标签:

【中文标题】如何在Android中加密和解密文件?【英文标题】:How to encrypt and decrypt file in Android? 【发布时间】:2011-05-15 14:08:14 【问题描述】:

我想加密文件并将其存储在 SD 卡中。我想解密该加密文件并将其再次存储在 SD 卡中。我试图通过将文件作为文件流打开并加密来加密文件,但它不起作用。我想知道如何做到这一点。

【问题讨论】:

【参考方案1】:

使用CipherOutputStreamCipherInputStreamCipher 和您的FileInputStream / FileOutputStream

我建议使用 Cipher.getInstance("AES/CBC/PKCS5Padding") 之类的东西来创建 Cipher 类。 CBC 模式是安全的,没有vulnerabilities of ECB mode for non-random plaintexts。它应该存在于任何通用加密库中,以确保高度兼容性。

如果您想使用相同的密钥加密多个文件,请不要忘记使用由secure random generator 生成的Initialization Vector (IV)。您可以在密文的开头添加纯 IV 前缀。它的大小总是正好是一个块(16 字节)。

如果您想使用密码,请确保您确实使用了良好的密钥派生机制(查找基于密码的加密或基于密码的密钥派生)。 PBKDF2 是最常用的基于密码的密钥派生方案,它是present in most Java runtimes,包括android。请注意,SHA-1 是一个有点过时的哈希函数,但它在 PBKDF2 中应该没问题,并且目前确实提供了最兼容的选项。

在编码/解码字符串时总是指定字符编码,否则当平台编码与前一个不同时,你会遇到麻烦。换句话说,不要使用String.getBytes(),而是使用String.getBytes(StandardCharsets.UTF_8)

为使其更安全,请通过在密文和 IV 上添加安全校验和(MAC 或 HMAC)来添加加密完整性和真实性,最好使用不同的密钥。如果没有身份验证标签,密文可能会以无法检测到的方式更改。

请注意CipherInputStream 可能 不报告BadPaddingException,这包括为经过身份验证的密码生成的BadPaddingException,例如作为 GCM。这将使流对于这些经过身份验证的密码不兼容和不安全。

【讨论】:

+1 表示“在编码/解码字符串时始终指定字符编码”:) @abhy 从 Java 7 开始,您可以使用 StandardCharsets 功能为您提供方便。显然,StandardCharsets 中的 static import 也可能有用。 感谢更新。我真的很感激。 我假设“IV”代表“初始化向量”,对于那些不知道的人,你应该至少说一次,也许是第一次提到它。此外,最好有链接解释为什么需要 Cipher 的特定配置。 @Hamid 好的,稍微修改了一下文字,我没想到这个相当笼统的建议会如此受欢迎:)【参考方案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 设备加密文件,我无法从我的计算机解密文件。似乎生成的密钥不一致。你知道解决办法吗? 这是 Android sn-ps 的副本,它使用相同的不正确方法来获取密钥。使用它,您可能会丢失数据。 @MaartenBodewes,请论证您的说法,为什么您认为获取密钥的方法不正确?如何改进? 这使用随机数生成器来派生密钥。这个 RNG 不需要只依赖种子,也没有指定确切的算法。而是查找正确使用 PBKDF2 或类似的方法。 加密货币现已在 android N 中被贬值 - android-developers.googleblog.com/2016/06/…【参考方案3】:

您可以使用java-aes-crypto 或Facebook's Conceal

java-aes-crypto

引用回购

一个用于加密和解密字符串的简单 Android 类,旨在 避免大多数此类课程遭受的经典错误。

Facebook 的隐藏

引用回购

Conceal 提供简单的 Android API 来执行快速加密和 数据认证

【讨论】:

以上是关于如何在Android中加密和解密文件?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用Base64进行加密和解密

.NET RSACryptoServiceProvider 使用 4096 私钥加密,如何在 Android 上解密

如何从 php 为 Android 和 Ios 集成视频加密和解密?

如何给加密文件解密

如何对数据库进行加密和解密

为啥windows下aes解密android上的加密文件失败