如何编程加密解密设备内部存储Download目录下的docFolder文件夹
Hey there! Let's break down how to encrypt that docFolder in your Download directory programmatically, and then decrypt it when you need to use the files. I'll assume you're working on Android since you mentioned internal storage—this approach uses AES-GCM (a secure, authenticated encryption standard) which is perfect for this scenario.
Core Concept
We'll use symmetric encryption (same key for encrypting and decrypting) with AES-GCM, which adds a built-in integrity check to make sure files haven't been tampered with. We'll recursively traverse every file in the folder, encrypt each one individually, and store the necessary metadata (IV and authentication tag) alongside the encrypted content. For decryption, we'll reverse the process: read the metadata, decrypt the content, and restore the original files.
Prerequisites
- Secure Key Storage: Don't hardcode keys! Use Android's Keystore system to generate and store your AES key—this keeps the key safe from being extracted from your app's memory or storage.
- File Stream Handling: Process files in chunks to avoid loading large files entirely into memory (which can cause out-of-memory crashes).
Encryption Implementation
Step 1: Generate a Secure AES Key
First, create a function to generate or retrieve an AES key from Android Keystore. This key will be used for both encryption and decryption.
import android.security.keystore.KeyGenParameterSpec import android.security.keystore.KeyProperties import java.security.KeyStore import javax.crypto.KeyGenerator import javax.crypto.SecretKey fun getOrGenerateAESKey(keyAlias: String): SecretKey { val keyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) } // Check if the key already exists in Keystore return if (keyStore.containsAlias(keyAlias)) { keyStore.getKey(keyAlias, null) as SecretKey } else { // Generate a new 256-bit AES key for GCM mode val keyGenerator = KeyGenerator.getInstance( KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore" ) val keySpec = KeyGenParameterSpec.Builder( keyAlias, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT ) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .setKeySize(256) .build() keyGenerator.init(keySpec) keyGenerator.generateKey() } }
Step 2: Encrypt a Single File
This function takes an input file, encrypts its content using the AES key, and writes the encrypted data (plus IV and authentication tag) to an output file. We'll add a .encrypted suffix to distinguish encrypted files.
import javax.crypto.Cipher import javax.crypto.spec.GCMParameterSpec import java.io.File import java.io.FileInputStream import java.io.FileOutputStream import java.security.SecureRandom private const val IV_SIZE = 12 // GCM recommends 12-byte IV for better security private const val TAG_SIZE = 128 // 128-bit authentication tag fun encryptSingleFile(inputFile: File, outputFile: File, secretKey: SecretKey) { // Generate a random IV (Initialization Vector) for each file val iv = ByteArray(IV_SIZE).apply { SecureRandom().nextBytes(this) } val cipher = Cipher.getInstance("AES/GCM/NoPadding").apply { init(Cipher.ENCRYPT_MODE, secretKey, GCMParameterSpec(TAG_SIZE, iv)) } // Read the original file and write encrypted content FileInputStream(inputFile).use { inputStream -> FileOutputStream(outputFile).use { outputStream -> // Write IV first (we'll need this for decryption) outputStream.write(iv) // Process file in 8KB chunks to save memory val buffer = ByteArray(8192) var bytesRead: Int while (inputStream.read(buffer).also { bytesRead = it } != -1) { val encryptedChunk = cipher.update(buffer, 0, bytesRead) encryptedChunk?.let { outputStream.write(it) } } // Write the final authentication tag val finalChunk = cipher.doFinal() finalChunk?.let { outputStream.write(it) } } } }
Step 3: Recursively Encrypt the Entire Folder
This function traverses the folder (including subfolders) and encrypts every file it finds.
fun encryptFolder(targetFolder: File, secretKey: SecretKey) { if (!targetFolder.isDirectory) return targetFolder.listFiles()?.forEach { file -> if (file.isDirectory) { // Recursively encrypt subfolders encryptFolder(file, secretKey) } else { // Create encrypted file with .encrypted suffix val encryptedFile = File(file.parentFile, "${file.name}.encrypted") encryptSingleFile(file, encryptedFile, secretKey) // Optional: Delete the original file (only do this after verifying encryption works!) // file.delete() } } }
Decryption Implementation
Step 1: Retrieve the AES Key
Use the same getOrGenerateAESKey function from the encryption section to get your key (make sure to use the same keyAlias!).
Step 2: Decrypt a Single File
This function reads the encrypted file, extracts the IV, decrypts the content, and writes the original file back (removing the .encrypted suffix).
fun decryptSingleFile(inputFile: File, outputFile: File, secretKey: SecretKey) { FileInputStream(inputFile).use { inputStream -> // Read the IV from the start of the file val iv = ByteArray(IV_SIZE) inputStream.read(iv) val cipher = Cipher.getInstance("AES/GCM/NoPadding").apply { init(Cipher.DECRYPT_MODE, secretKey, GCMParameterSpec(TAG_SIZE, iv)) } // Write decrypted content to output file FileOutputStream(outputFile).use { outputStream -> val buffer = ByteArray(8192) var bytesRead: Int while (inputStream.read(buffer).also { bytesRead = it } != -1) { val decryptedChunk = cipher.update(buffer, 0, bytesRead) decryptedChunk?.let { outputStream.write(it) } } // Finalize decryption and verify authentication tag val finalChunk = cipher.doFinal() finalChunk?.let { outputStream.write(it) } } } }
Step 3: Recursively Decrypt the Entire Folder
Traverse the encrypted folder and decrypt all .encrypted files back to their original form.
fun decryptFolder(targetFolder: File, secretKey: SecretKey) { if (!targetFolder.isDirectory) return targetFolder.listFiles()?.forEach { file -> if (file.isDirectory) { // Recursively decrypt subfolders decryptFolder(file, secretKey) } else if (file.name.endsWith(".encrypted")) { // Remove .encrypted suffix to get original filename val originalFileName = file.name.substringBeforeLast(".encrypted") val decryptedFile = File(file.parentFile, originalFileName) decryptSingleFile(file, decryptedFile, secretKey) // Optional: Delete the encrypted file after successful decryption // file.delete() } } }
Key Things to Keep in Mind
- Key Security: Never hardcode keys or store them in plaintext. Android Keystore is designed to keep keys secure—use it!
- Error Handling: Add try-catch blocks around file operations and encryption/decryption to handle exceptions like missing files, invalid keys, or tampered encrypted data.
- Backup: Always backup the original folder before encrypting—if something goes wrong during encryption, you don't want to lose your files.
- Performance: Processing files in chunks (like 8KB) prevents memory issues with large files (e.g., high-res images or videos).
- Tamper Protection: AES-GCM automatically checks if the encrypted file has been modified—if the authentication tag doesn't match, decryption will fail, alerting you to tampering.
内容的提问来源于stack exchange,提问作者Jean-Pascal MEWENEMESSE

