Skip to content

Instantly share code, notes, and snippets.

@lanserxt
Created October 31, 2024 09:31
Show Gist options
  • Save lanserxt/5ef48e97adf23dfda3ff4ff0091d4378 to your computer and use it in GitHub Desktop.
Save lanserxt/5ef48e97adf23dfda3ff4ff0091d4378 to your computer and use it in GitHub Desktop.
Secured Credentials Manager to load API keys, login credentials and other from local secured file
import Foundation
import CryptoKit
struct AuthDataManager {
//MARK: - Encrypt/Decrypt Logic
/// Generate a symmetric key from a password
/// - Parameter password: password to use
/// - Returns: key
private func generateSymmetricKey(from password: String) -> SymmetricKey {
let keyData = password.data(using: .utf8)!
let hash = SHA256.hash(data: keyData)
return SymmetricKey(data: hash)
}
/// Encrypt the FirebaseAuthData
/// - Parameters:
/// - authData: data to encrypt
/// - password: symmetric key password to use
/// - Returns: encrypted data
func encryptAuthData(authData: any Codable, withPassword password: String) -> Data? {
let key = generateSymmetricKey(from: password)
do {
let jsonData = try JSONEncoder().encode(authData)
let sealedBox = try AES.GCM.seal(jsonData, using: key)
return sealedBox.combined
} catch {
print("Error encrypting data: \(error)")
return nil
}
}
/// Decrypt the FirebaseAuthData
/// - Parameters:
/// - encryptedData: data to decrypt
/// - password: symmetric key password to use
/// - Returns: <#description#>
func decryptAuthData<T: Codable>(encryptedData: Data, withPassword password: String) -> T? {
let key = generateSymmetricKey(from: password)
do {
let sealedBox = try AES.GCM.SealedBox(combined: encryptedData)
let decryptedData = try AES.GCM.open(sealedBox, using: key)
let authData = try JSONDecoder().decode(T.self, from: decryptedData)
return authData
} catch {
print("Error decrypting data: \(error)")
return nil
}
}
//MARK: - Storing/Loading From Documents
/// Load the unencrypted JSON from file
/// - Parameter fileName: file to load
/// - Returns: plain data of file
func loadAuthData<T: Codable>(fromFile fileName: String) -> T? {
let url = getDocumentsDirectory().appendingPathComponent(fileName)
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
let authData = try decoder.decode(T.self, from: data)
return authData
} catch {
print("Error loading JSON: \(error)")
return nil
}
}
/// Save the unencrypted JSON to a local file
/// - Parameters:
/// - authData: credentials container
/// - fileName: file to save
func saveAuthData(authData: any Codable, toFile fileName: String, keyPassword: String) {
do {
if let encryptedData = encryptAuthData(authData: authData, withPassword: keyPassword) {
let url = getDocumentsDirectory().appendingPathComponent(fileName)
try encryptedData.write(to: url)
print("File saved to: \(url)")
}
} catch {
print("Error saving JSON: \(error)")
}
}
/// Load encrypted file from Main Bundle
/// - Parameters:
/// - fileName: filename of encrypted creds
/// - password: password for creds
/// - Returns: Decrypted credentials data
func loadEncryptedAuthData<T: Codable>(fileName: String, withPassword password: String) -> T? {
guard let url = Bundle.main.url(forResource: fileName, withExtension: "json") else {
print("Encrypted file not found in bundle")
return nil
}
do {
let encryptedData = try Data(contentsOf: url)
return decryptAuthData(encryptedData: encryptedData, withPassword: password)
} catch {
print("Error loading encrypted data: \(error)")
return nil
}
}
/// Helper function to get the documents directory
/// - Returns: Documents folder URL
private func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return paths[0]
}
}
//Declaring a struct to hold credentials
struct FirebaseAuthData: Codable {
let email: String
let password: String
}
//Storing it to local file
let authManager = AuthDataManager
//Create data to store
let authData = FirebaseAuthData(email: "[email protected]", password: "plain_pass")
//Saving it to local file
authManager.saveAuthData(authData: authData, toFile: "encrypted.json", keyPassword: "sym_key")
//Now we can take it from Documents and embed in Bundle
//And read from it in
let authDataFromBundle = authManager.loadEncryptedAuthData(fileName: "encrypted.json", withPassword: "sym_key")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment