Encrypt a single file and save the result to the file system, then decrypt and recreate the original file from the archive file using Apple Encrypted Archive.
This sample code project implements the Apple Encrypted Archive library to compress and encrypt the contents of a single file using a SymmetricKey
. The sample saves the encrypted file to the user’s temporary directory and then calls a second function that decrypts the contents of the archive and recreates the original file.
Before running the sample code project in Xcode, ensure you have a file in your temporary directory (see: NSTemporaryDirectory()
) named file.txt
.
The sample code project defines FilePath
structures that represent the locations of the source file, the encrypted version of the source file, and the recreated, unencrypted version of the source file.
// The sample defines a `FilePath` structure that represents the path of
// the source file. Before running the same, you must have a file in
// your temporary directory named `file.txt`.
let sourceFilePath = FilePath(NSTemporaryDirectory() + "file.txt")
// The sample writes the encrypted source file to the path defined by
// the `encryptedFilePath` file path structure.
let encryptedFilePath = FilePath(NSTemporaryDirectory() + "file.encrypted")
// The sample defines a `FilePath` structure that represents the path of
// the decrypted recreation of the original file.
let decryptedFilePath = FilePath(NSTemporaryDirectory() + "file.decrypted.txt")
The sample imports the CryptoKit framework to generate the symmetric cryptographic key.
let key = SymmetricKey(size: SymmetricKeySize.bits256)
The sample uses the same key for encryption and decryption.
An ArchiveEncryptionContext
object contains the parameters, keys, and other data that the Apple Encrypted Archive library requires to open an encrypted archive for encryption and decryption streams. The sample initializes the context with a profile and compression algorithm, and its symmetric key set for encryption.
let context = ArchiveEncryptionContext(profile: .hkdf_sha256_aesctr_hmac__symmetric__none,
compressionAlgorithm: .lzfse)
try context.setSymmetricKey(key)
The sample creates a readOnly
file stream to read the source file, and a writeOnly
file stream to write the encrypted file to the file system.
guard let sourceFileStream = ArchiveByteStream.fileStream(
path: sourceFilePath,
mode: .readOnly,
options: [ ],
permissions: FilePermissions(rawValue: 0o644)),
let destinationFileStream = ArchiveByteStream.fileStream(
path: destinationFilePath,
mode: .writeOnly,
options: [ .create, .truncate ],
permissions: FilePermissions(rawValue: 0o644)) else {
throw Error.unableToCreateFileStream
}
The encryption stream uses the encryption context and the destination file stream to write the encrypted string to the file system.
guard let encryptionStream = ArchiveByteStream.encryptionStream(
writingTo: destinationFileStream,
encryptionContext: context) else {
throw Error.unableToCreateEncryptionStream
}
The process(readingFrom:writingTo:)
function sends the output of the file-reading stream to the encryption stream. In turn, the compression stream sends its output to the file-writing stream and writes the encrypted file to the file system.
_ = try ArchiveByteStream.process(readingFrom: sourceFileStream,
writingTo: encryptionStream)
The sample creates a source file stream to open the encrypted file.
guard let sourceFileStream = ArchiveByteStream.fileStream(
path: sourceFilePath,
mode: .readOnly,
options: [ ],
permissions: FilePermissions(rawValue: 0o644)) else {
throw Error.unableToCreateFileStream
}
The ArchiveEncryptionContext
object for decryption derives its parameters, keys, and other data from the encrypted source file, and the sample sets the decryption context with the same symmetric key that was used for encryption.
guard let decryptionContext = ArchiveEncryptionContext(from: sourceFileStream) else {
throw Error.unableToCreateDecryptionContext
}
// Set the key on the context.
try decryptionContext.setSymmetricKey(key)
The decryption stream uses the encryption context and the source file stream to read the encrypted string from the file system.
guard let decryptionStream = ArchiveByteStream.decryptionStream(
readingFrom: sourceFileStream,
encryptionContext: decryptionContext) else {
throw Error.unableToCreateFileStream
}
The destination file stream writes the encrypted file to the file system. In this case, the file stream's mode is writeOnly
. The options specify that the stream creates the file if it doesn't exist, and that if the file does exist, it should be truncated to zero bytes before the stream performs any operations.
guard let decryptedFileStream = ArchiveByteStream.fileStream(
path: destinationFilePath,
mode: .writeOnly,
options: [ .create, .truncate ],
permissions: FilePermissions(rawValue: 0o644)) else {
throw Error.unableToCreateFileStream
}
The process(readingFrom:writingTo:)
writes the output of the decryption stream to the file-writing stream
_ = try ArchiveByteStream.process(readingFrom: decryptionStream,
writingTo: decryptedFileStream)
On return, file.decrypted.txt
exists in NSTemporaryDirectory()
and contains the decrypted contents of file.encrypted
.