As we know, iOS has already secure with sandboxing the application where data cannot be transferred from one app to another but if any attacker or intruder wants to hack or steal data on-fly then it would be very difficult to stop hacking our system. There are many ways to secure our data in iOS and kept in private mode to achieve this target.
There are many ways in iOS to protect data So, the developer has to ensure during the iPhone application development phase which data need to be protected from hackers and then they need to follow the same steps for that. Protection and security required more on banking domain apart from other domain because if any breached happen then it would be very difficult to tackle that situation.
Encryption and Decryption technique to secure Private data
Common Cryptographic technique is used to secure data in iOS from hacker’s, reverse engineering, intruders and spoofing. Encryption of data when users are not accessing data on the device and Decrypt the data when users are accessing the data. Hashing technique is used wherever it is required to secure the data for day to day life in web/mobile application. Using Encryption techniques data can be encrypted and converted into cipher format and then using Decryption techniques data can be converted again into plain text. The developer needs to add a cryptographic library and need to write the below code for converting encryption and decryption.
protocol Cryptographic{ func encryption(_ string: String) throws -> Data func decryptionTech(_ data: Data) throws -> String } struct AESHashing { private let key: Data private let ivSize: Int = kCCBlockSizeAES128 private let options: CCOptions = CCOptions(kCCOptionPKCS7Padding) init(keyString: String) throws { guard keyString.count == kCCKeySizeAES256 else { throw Error.invalidKeySize } self.key = Data(keyString.utf8) } } extension AESEncrypt { enum Error: Swift.Error { case invalidKeySize case generateRandomIVFailed case encryptionFailed case decryptionFailed case dataToStringFailed } } private extension AESEncrypt { func generateRandomIV(for data: inout Data) throws { try data.withUnsafeMutableBytes { dataBytes in guard let dataBytesBaseAddress = dataBytes.baseAddress else { throw Error.generateRandomIVFailed } let status: Int32 = SecRandomCopyBytes( kSecRandomDefault, kCCBlockSizeAES128, dataBytesBaseAddress ) guard status == 0 else { throw Error.generateRandomIVFailed }}}} extension AESEncrypt: Cryptographic{ func encrypt(_ string: String) throws -> Data { let dataToEncrypt = Data(string.utf8) let bufferSize: Int = ivSize + dataToEncrypt.count + kCCBlockSizeAES128 var buffer = Data(count: bufferSize) try generateRandomIV(for: &buffer) var numberBytesEncrypted: Int = 0 do { try key.withUnsafeBytes { keyBytes in try dataToEncrypt.withUnsafeBytes { dataToEncryptBytes in try buffer.withUnsafeMutableBytes { bufferBytes in guard let keyBytesBaseAddress = keyBytes.baseAddress, let dataToEncryptBytesBaseAddress = dataToEncryptBytes.baseAddress, let bufferBytesBaseAddress = bufferBytes.baseAddress else { throw Error.encryptionFailed } let cryptStatus: CCCryptorStatus = CCCrypt( // Stateless, one-shot encrypt operation CCOperation(kCCEncrypt), // op: CCOperation CCAlgorithm(kCCAlgorithmAES), // alg: CCAlgorithm options, // options: CCOptions keyBytesBaseAddress, // key: the "password" key.count, // keyLength: the "password" size bufferBytesBaseAddress, // iv: Initialization Vector dataToEncryptBytesBaseAddress, // dataIn: Data to encrypt bytes dataToEncryptBytes.count, // dataInLength: Data to encrypt size bufferBytesBaseAddress + ivSize, // dataOut: encrypted Data buffer bufferSize, // dataOutAvailable: encrypted Data buffer size &numberBytesEncrypted // dataOutMoved: the number of bytes written ) guard cryptStatus == CCCryptorStatus(kCCSuccess) else { throw Error.encryptionFailed }}}} } catch { throw Error.encryptionFailed } let encryptedData: Data = buffer[..<(numberBytesEncrypted + ivSize)] return encryptedData } func decryptionTech(_ data: Data) throws -> String { let bufferSize: Int = data.count - ivSize var buffer = Data(count: bufferSize) var numberBytesDecrypted: Int = 0 do { try key.withUnsafeBytes { keyBytes in try data.withUnsafeBytes { dataToDecryptBytes in try buffer.withUnsafeMutableBytes { bufferBytes in guard let keyBytesBaseAddress = keyBytes.baseAddress, let dataToDecryptBytesBaseAddress = dataToDecryptBytes.baseAddress, let bufferBytesBaseAddress = bufferBytes.baseAddress else { throw Error.encryptionFailed } let cryptStatus: CCCryptorStatus = CCCrypt( // Stateless, one-shot encrypt operation CCOperation(kCCDecrypt), // op: CCOperation CCAlgorithm(kCCAlgorithmAES128), // alg: CCAlgorithm options, // options: CCOptions keyBytesBaseAddress, // key: the "password" key.count, // keyLength: the "password" size dataToDecryptBytesBaseAddress, // iv: Initialization Vector dataToDecryptBytesBaseAddress + ivSize, // dataIn: Data to decrypt bytes bufferSize, // dataInLength: Data to decrypt size bufferBytesBaseAddress, // dataOut: decrypted Data buffer bufferSize, // dataOutAvailable: decrypted Data buffer size &numberBytesDecrypted // dataOutMoved: the number of bytes written ) guard cryptStatus == CCCryptorStatus(kCCSuccess) else { throw Error.decryptionFailed } } } } } catch { throw Error.encryptionFailed } let decryptedData: Data = buffer[..] guard let decryptedString = String(data: decryptedData, encoding: .utf8) else { throw Error.dataToStringFailed } return decryptedString } } do { let aes = try AESHashing (keyString: "KighsteADhJKiutReatswnhgGJjLOpPnBvCx") let stringToEncrypt: String = "Encrypt data" print("String to encrypt:\t\t\t\(stringToEncrypt)") let encryptedData: Data = try aes.encrypt(stringToEncrypt) print("String encrypted (base64):\t\(encryptedData.base64EncodedString())") let decryptedData: String = try aes.decryptionTech(encryptedData) print("String decrypted:\t\t\t\(decryptedData)") } catch { print("Something went wrong: \(error)") } }
Data Protection
A developer can switch on data protection from the capability but he has to ensure that once he enabled capability then need to set a passcode in device security also. The file is completely encrypted in the disk until the device is a reboot or unlocked.
For encrypting the file in the disk using Data Protection Capability, the developer needs to write some piece of code related to secure file/data in the disk. There are many levels of protection for reading and write which can be set by the developer in entitlement or developer need to write a program for Protection level. If the developer wants to set the protection level in the particular file level then he needs to write some piece of code to protect it.
try data.write(to: fileURL, options: .completeFileProtection) try FileManager.default.setAttributes([.protectionKey: FileProtectionType.complete], ofItemAtPath: fileURL.path) try (fileURL as NSURL).setResourceValue(URLFileProtection.complete, forKey: .fileProtectionKey) try persistentStoreCoordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: [NSPersistentStoreFileProtectionKey: FileProtectionType.complete]) try FileManager.default.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: [.protectionKey: FileProtectionType.complete]) guard let directoryLevelProtection= FileManager.default.enumerator(at: directoryURL, includingPropertiesForKeys: [], options: [], errorHandler: { url, error -> Bool in print(error) return true }) else { print("not created directory Level at \(directoryURL.path)") return } for urlDirectory in directoryLevelProtection { do { try (urlDirectory as! NSURL).setResourceValue(URLFileProtection.complete, forKey: .fileProtectionKey) } catch { print(error) } }
KeyChainis another way of Protecting Confidential data
KeyChain is used to protect some limited confidential data in the app. By default, KeyChain is encrypted when the device is locked and when users unlocked the device then keychain will be accessible. Keychain not accessible by one to another application in the device and apple always recommends utilizing these default features for the app.
let saveKeychain: Bool = KeychainWrapper.standard.set("Storing Value", forKey: "Storing Key") let retrievedKeychain: String? = customKeychainWrapperInstance.string(forKey: "Storing Key") let removeKeychain: Bool = customKeychainWrapperInstance.removeObject(forKey: "Storing Key")
SSL pinning Security and Protecting data from Hacker’s
SSL pinning is used to secure from hackers or intruders or reverse-engineering when the network is connected wirelessly. SSL pinning is used to trust the right server all the time and if any other server tries to connect the system then it won’t be connected because of SSL pinning security. So the developer needs to implement the SSL pinning logic to secure applications from a third-party attacker or intruder.
SSL implementation should be done by frontend where the developer can add the local certificate in the app. It will match with domain name and server certificate. Once it gets trusted from the server with an SSL certificate then users can access the app otherwise it will block the users at a particular moment. The developer needs to create the URL session where it can accept or reject the certificate from the server.
fun urlSession1(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust, let serverTrust = challenge.protectionSpace.serverTrust, SecTrustEvaluate(serverTrust, nil) == errSecSuccess, let serverCert = SecTrustGetCertificateAtIndex(serverTrust, 0) else { rejectCertificate (with: completionHandler) return } let serverCertData = SecCertificateCopyData(serverCert) as Data guard let localCertPath = Bundle.main.path(forResource: "google.com", ofType: "cer"), let localCertData = NSData(contentsOfFile: localCertPath) as Data?, localCertData == serverCertData else { rejectCertificate(with: completionHandler) return } acceptCertificate(with: serverTrust, completionHandler) } func rejectCertificate(with completionHandler: ((URLSession.AuthChallengeDisposition, URLCredential?) -> Void)) { completionHandler(.cancelAuthenticationChallenge, nil) } func acceptCertificate(with serverTrust: SecTrust, _ completionHandler: ((URLSession.AuthChallengeDisposition, URLCredential?) -> Void)) { completionHandler(.useCredential, URLCredential(trust: serverTrust)) }