2

我正在加密在蓝牙服务中的 android 应用程序上创建的文件。稍后在另一堂课中,我想解密此文件并将其上传到服务器。

对于加密,我使用的是 AndroidX androidx.security:security-crypto:1.0.0-alpha02 库,它是 Tink 的包装器。我已经阅读了所有可以找到的有关 EncryptedFile、EncryptedFile.Builder 等的开发人员文档和教程。

我加密文件如下:

String keySetAlias = "BilboBaggins";
String keySetPref = "Hobbits";

EncryptedFile m_StudyChannelEncryptedFile = new EncryptedFile.Builder(
    filePath,
    getApplicationContext(),
    masterKeyAlias,
   EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB).setKeysetAlias(keySetAlias).setKeysetPrefName(keySetPref).build();
m_output = m_StudyChannelEncryptedFile.openFileOutput();

从这里我可以像普通的 FileOutputStream 一样写入文件,通过查看写入手机存储的数据,我可以确认它是加密的。

在上传之前,我尝试在另一个类中做同样的事情,然后解密它。

String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);
String keySetAlias = "BilboBaggins";
String keySetPref = "Hobbits";

EncryptedFile encryptedFile = new EncryptedFile.Builder(
  filePath,
  getApplicationContext(),
  masterKeyAlias,
  EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB).setKeysetAlias(keySetAlias).setKeysetPrefName(keySetPref).build();
// Read channel data file  
FileInputStream fChannel = encryptedFile.openFileInput();
m_Dat1Size = fChannel.available();

从这里开始,问题是我得到的文件的可用大小为零 - 就像它不存在一样。我可以确认写入的原始数据没有被覆盖,因为手机存储上的文件仍然有加密数据。

我相信,通过为其提供位置 keySetAlias、keySetPref,EncryptedFile 构建器应该能够初始化一个具有正确密钥的 EncryptedFile 实例。

我将不胜感激任何帮助或见解!

谢谢你,迈克尔

4

2 回答 2

0

例如,不能依靠InputStream返回值来返回可以读取的字节总数:available

返回可以从此输入流中读取(或跳过)的剩余字节数的估计值,而不会被下一次调用此输入流的方法阻塞。

对于同一系统上的文件,这总是“剩余”到文件末尾的字节数是有道理的。但是,对于其他流available,主要是关于您应该请求多少字节的提示。对于解密,available()可以简单地返回解密后已经可用的字节数。很可能密文仅在请求时才被解密,因此它返回 0。


对于特定密码,由于明文需要取消填充(ECB/CBC) 或验证和删除身份验证标签(GCM) ,确定是否到达流的末尾也可能很棘手。这可能会使 的计算复杂化available。API 实现可能只是决定简单地返回0always(这意味着调用read 可能总是阻塞)。

于 2020-02-20T17:19:57.387 回答
0

我创建了上述类来使用 AES 执行文件加密和解密,下面是代码。我已将文件转换为字节数组并对其执行加密并再次使用文件对其进行解密并从函数中返回字节。

class AESEncryptDecrypt private constructor() {
private val secretKey: SecretKey
private val iV: AlgorithmParameterSpec

init {
    this.secretKey = createAESKey(randomString()) as SecretKey
    this.iV = IvParameterSpec(AES_IV.toByteArray())
}

/**
 * Encrypt File
 * @param file : File to Encrypt
 */
fun encryptFile(file: File): ByteArray {
    var cipher: Cipher? = null
    try {
        cipher = Cipher.getInstance(AES_TRANSFORMATION)
        cipher!!.init(Cipher.ENCRYPT_MODE, secretKey, iV)
    } catch (e: NoSuchAlgorithmException) {
        e.printStackTrace()
    } catch (e: NoSuchPaddingException) {
        e.printStackTrace()
    } catch (e: InvalidAlgorithmParameterException) {
        e.printStackTrace()
    } catch (e: InvalidKeyException) {
        e.printStackTrace()
    }
    return cipher!!.doFinal(file.readBytes())
}

/**
 * Encrypt Bytes
 * @param byteArray : Plain Bytes to Encrypted Bytes
 */
fun encryptBytes(byteArray: ByteArray): ByteArray {
    var cipher: Cipher? = null
    try {
        cipher = Cipher.getInstance(AES_TRANSFORMATION)
        cipher!!.init(Cipher.ENCRYPT_MODE, secretKey, iV)
    } catch (e: NoSuchAlgorithmException) {
        e.printStackTrace()
    } catch (e: NoSuchPaddingException) {
        e.printStackTrace()
    } catch (e: InvalidAlgorithmParameterException) {
        e.printStackTrace()
    } catch (e: InvalidKeyException) {
        e.printStackTrace()
    }
    return cipher!!.doFinal(byteArray)
}

/**
 * Decrypt File
 * @param file : File to Decrypt
 */
fun decryptFile(file: File): ByteArray {
    var cipher: Cipher? = null
    try {
        cipher = Cipher.getInstance(AES_TRANSFORMATION)
        cipher!!.init(Cipher.DECRYPT_MODE, secretKey, iV)
    } catch (e: NoSuchAlgorithmException) {
        e.printStackTrace()
    } catch (e: NoSuchPaddingException) {
        e.printStackTrace()
    } catch (e: InvalidAlgorithmParameterException) {
        e.printStackTrace()
    } catch (e: InvalidKeyException) {
        e.printStackTrace()
    }
    return cipher!!.doFinal(file.readBytes())
}


companion object {

    private var INSTANCE: AESEncryptDecrypt? = null

    fun getInstance() =
        INSTANCE ?: synchronized(AESEncryptDecrypt::class.java) {
            INSTANCE ?: AESEncryptDecrypt()
                .also { INSTANCE = it }
        }

    private fun createAESKey(keyValue: String): Key {
        if (keyValue.length != AES_RANDOM_STRING_LENGTH)
            try {
                throw Exception("Key must be exactly 16 characters")
            } catch (e: Exception) {
                e.printStackTrace()
            }
        return SecretKeySpec(keyValue.toByteArray(), AES_TRANSFORMATION)
    }

    private fun randomString(): String {
        return "ABCDE12345ABCDE1"
    }
}
}

常量值如下所述

const val AES_IV = "abcaqwerabcaqwer"
const val AES_TRANSFORMATION = "AES/CBC/PKCS7Padding"
const val AES_RANDOM_STRING = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$"
const val AES_RANDOM_STRING_LENGTH = 16

我已经用于图像文件的加密和解密。该文件首先以未加密的方式存储,然后我使用了该功能

对于加密: FileUtils.writeByteArrayToFile(file, AESEncryptDecrypt.getInstance().encryptBytes(byteArray))

AESEncryptDecrypt.getInstance().encryptBytes(byteArray) 使我的文件第二次加密 FileUtils.writeByteArrayToFile我覆盖我的文件并使其加密的函数。

对于解密:

val file: File? = getFile()
FileUtils.writeByteArrayToFile(
                        file, AESEncryptDecrypt.getInstance().decryptFile(file)
                    )

注意: FileUtils.writeByteArrayToFile 是从下面提到的依赖中使用的:(其次,这个函数是从依赖中提到的。)

implementation 'org.apache.commons:commons-lang3:3.7'
于 2020-02-20T17:54:59.243 回答