Created
October 31, 2024 09:31
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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] | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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