Import a pkcs12 in mac osx Swift - swift

I would need to import a pkcs12 into the osx keychain and make the private key non-extractable
Is this possible?
this is my example code
let keyString = "MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgQCVXkvZifORfS8VVEp+BQTrnXu2a2+KL+Rw5FPHeSAOXjrS5DoC0GxK29jTKPGkJrg2WOiL/ZSbKvTq8wBUZzoUGaJQ+kzBJ40ShVtbJYGNFixubuKrSjUNQB149t25lxHnJia0i9i1sLfzrPnqPJ4ABf7lnhkTbNt8g/KriwoqmQICAQE="
let keyData = NSData(base64Encoded: keyString, options: .ignoreUnknownCharacters)!
var hostError: Unmanaged<CFError>?
guard let hostPrivateKey = SecKeyCreateRandomKey(optionDict as CFDictionary, &hostError) else {
fatalError("😡 Could not generate the private key.")
}
let passphrase = "Password" as CFString
let keyDict1 = SecKeyCopyAttributes(hostPrivateKey)
var keyParams = SecItemImportExportKeyParameters()
keyParams.version = UInt32(SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION)
keyParams.passphrase = Unmanaged.passUnretained(passphrase)
/*
keyParams.keyAttributes=?? <---- ????
*/
var inputFormat :SecExternalFormat = .formatPKCS12
var itemType :SecExternalItemType = .itemTypeUnknown
var itemsCFArray :CFArray? = nil
let error = SecItemImport(hostPrivateKey as! CFData , nil, &inputFormat, &itemType, SecItemImportExportFlags(rawValue: UInt32(0)), &keyParams, "login" as! SecKeychain, &itemsCFArray)

Related

Import and user ".cert" certificate in swift

I am very new to networking, so I have a ".cert" mTls certificate and password to it and I need to use it to access API, for now, every way that I tried resulted in failure.
I tried to extract it like this:
func extractIdentity(certData: NSData, certPassword: String) -> IdentityAndTrust? {
var securityError: OSStatus = errSecSuccess
var items: CFArray?
let certOptions = [ kSecImportExportPassphrase as String: certPassword ] as CFDictionary
// import certificate to read its entries
securityError = SecPKCS12Import(certData, certOptions, &items)
guard securityError == errSecSuccess,
let certItems = items,
let dict: AnyObject = (certItems as Array).first,
let certEntry: Dictionary = dict as? Dictionary<String, AnyObject> else {
return nil
}
// grab the identity
let identityPointer: AnyObject? = certEntry["identity"]
let secIdentityRef: SecIdentity = identityPointer as! SecIdentity;
// grab the trust
let trustPointer:AnyObject? = certEntry["trust"]
let trustRef: SecTrust = trustPointer as! SecTrust
// grab the certificate chain
var certRef: SecCertificate?
SecIdentityCopyCertificate(secIdentityRef, &certRef)
let certArray: NSMutableArray = NSMutableArray()
certArray.add(certRef! as SecCertificate)
return IdentityAndTrust(identityRef: secIdentityRef, trust: trustRef, certArray: certArray)
}
But it returning -26275(errSecDecode), and SecCertificateCreateWithData returning nil as well, any ideas? Should I try to convert it to the other format? If yes to which and how, I couldn't find the appropriate openssl command.

Derive An AWS Signing Key For Signature Version 4

I am attempting to generate a signing key for AWS according to AWS's documentation here for an iOS application. The documentation is pretty good however, it doesn't provide an example using Swift. Apple provides CryptoKit which should be the right framework, but I haven't been able to puzzle it out.
Ruby Example
def getSignatureKey key, dateStamp, regionName, serviceName
kDate = OpenSSL::HMAC.digest('sha256', "AWS4" + key, dateStamp)
kRegion = OpenSSL::HMAC.digest('sha256', kDate, regionName)
kService = OpenSSL::HMAC.digest('sha256', kRegion, serviceName)
kSigning = OpenSSL::HMAC.digest('sha256', kService, "aws4_request")
kSigning
end
Sample Inputs from AWS Docs
key = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY'
dateStamp = '20120215'
regionName = 'us-east-1'
serviceName = 'iam'
Should output
kSecret = '41575334774a616c725855746e46454d492f4b374d44454e472b62507852666943594558414d504c454b4559'
kDate = '969fbb94feb542b71ede6f87fe4d5fa29c789342b0f407474670f0c2489e0a0d'
kRegion = '69daa0209cd9c5ff5c8ced464a696fd4252e981430b10e3d3fd8e2f197d7a70c'
kService = 'f72cfd46f26bc4643f06a11eabb6c0ba18780c19a8da0c31ace671265e3c87fa'
kSigning = 'f4780e2d9f65fa895f9c67b32ce1baf0b0d8a43505a000a1a9e090d414db404d'
My attempt (note you have to append AWS to the key according to the docs)
import Foundation
import CryptoKit
let key = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"
let dateStamp = "20120215"
let regionName = "us-east-1"
let serviceName = "iam"
let keyData = Data("AWS\(key)".utf8)
let symmetricKey = SymmetricKey(data: keyData)
let dateStampData = Data(dateStamp.utf8)
let signature = HMAC<SHA256>.authenticationCode(for: dateStampData, using: symmetricKey)
let skeyString = keyData.map { String(format: "%02hhx", $0) }.joined()
print("kSecret \t= \(skeyString)")
let kDateString = Data(signature).map { String(format: "%02hhx", $0) }.joined()
print("kDate \t\t= \(kDateString)")
The first one is correct, so it seems I have the initial key correct, but when trying to apply it to dateStamp it doesn't match.
Outputs
kSecret = 415753774a616c725855746e46454d492f4b374d44454e472b62507852666943594558414d504c454b4559
kDate = 2226579f8b317a03ec325a8c8b3d27cf465ce52787455e1880039824b4ba0e25
Of course the moment I post the question I find the issue. The original issue was I was appending AWS instead of AWS4 the string appeared to be correct for kSecret because I was looking at the first set and last set of digits. Here is the solution for anyone looking to do the same.
import Foundation
import CryptoKit
let key = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"
let dateStamp = "20120215"
let regionName = "us-east-1"
let serviceName = "iam"
let keyData = Data("AWS4\(key)".utf8)
let dateStampData = Data(dateStamp.utf8)
let regionNameData = Data(regionName.utf8)
let serviceNameData = Data(serviceName.utf8)
let signingData = Data("aws4_request".utf8)
var symmetricKey = SymmetricKey(data: keyData)
let dateSHA256 = HMAC<SHA256>.authenticationCode(for: dateStampData, using: symmetricKey)
symmetricKey = SymmetricKey(data: Data(dateSHA256))
let regionSHA256 = HMAC<SHA256>.authenticationCode(for: regionNameData, using: symmetricKey)
symmetricKey = SymmetricKey(data: Data(regionSHA256))
let serviceNameSHA256 = HMAC<SHA256>.authenticationCode(for: serviceNameData, using: symmetricKey)
symmetricKey = SymmetricKey(data: Data(serviceNameSHA256))
let signingSHA256 = HMAC<SHA256>.authenticationCode(for: signingData, using: symmetricKey)
let skeyString = keyData.map { String(format: "%02hhx", $0) }.joined()
print("kSecret \t= \(skeyString)")
let kDateString = Data(dateSHA256).map { String(format: "%02hhx", $0) }.joined()
print("kDate \t\t= \(kDateString)")
let kRegionString = Data(regionSHA256).map { String(format: "%02hhx", $0) }.joined()
print("kRegion \t= \(kRegionString)")
let kServiceString = Data(serviceNameSHA256).map { String(format: "%02hhx", $0) }.joined()
print("kService \t= \(kServiceString)")
let kSigningString = Data(signingSHA256).map { String(format: "%02hhx", $0) }.joined()
print("kSigning \t= \(kSigningString)")
Outputs
kSecret = 41575334774a616c725855746e46454d492f4b374d44454e472b62507852666943594558414d504c454b4559
kDate = 969fbb94feb542b71ede6f87fe4d5fa29c789342b0f407474670f0c2489e0a0d
kRegion = 69daa0209cd9c5ff5c8ced464a696fd4252e981430b10e3d3fd8e2f197d7a70c
kService = f72cfd46f26bc4643f06a11eabb6c0ba18780c19a8da0c31ace671265e3c87fa
kSigning = f4780e2d9f65fa895f9c67b32ce1baf0b0d8a43505a000a1a9e090d414db404d

Decrypt data using AES.GCM.SealedBox in Swift

I am trying to decrypt data using AES.GCM.The encrypted data works fine but when I try to decrypt the data using the same key it gives authentication error.
Below is the code to decrypt
func decryptData(decryptToData: Data, key: SymmetricKey) -> String {
var decryptedString: String!
let combinedData = decryptToData // Previous sealed bo
let sealedBoxToOpen = try! AES.GCM.SealedBox(combined: decryptToData)
if let decryptedData = try? AES.GCM.open(sealedBoxToOpen, using: key) {
decryptedString = String(data: decryptedData, encoding: .utf8)!
print(decryptedString)
} else {
print("error", CryptoKitError.self)
// Ouch, doSomething() threw an error.
}
return decryptedString
}
The following is my encryption code
let iv = AES.GCM.Nonce()
var encryptedData: Data!
let key = SymmetricKey(size: .bits128)
func encryptData(encryptString: String, key: SymmetricKey) -> Data {
var encryptedData: Data?
do {
let datatoEncrypt1 = encryptString.data(using: .utf8)!
let mySealedBox = try AES.GCM.seal(datatoEncrypt1, using: key, nonce: iv)
encryptedData = mySealedBox.combined
} catch {
print("Error")
}
return encryptedData
}
import XCTest
import CryptoKit
import Foundation
class CryptoKitUnitTest: XCTestCase {
func testEncryptandDecrypt(){
let secret = "my-256-bit-secret-my-secret-my-s"
let key = SymmetricKey(data: secret.data(using: .utf8)!)
let plain = "Say hello to my little friend!"
let nonce = try! AES.GCM.Nonce(data: Data(base64Encoded: "fv1nixTVoYpSvpdA")!)
// Encrypt
let sealedBox = try! AES.GCM.seal(plain.data(using: .utf8)!, using: key, nonce: nonce)
let ciphertext = sealedBox.ciphertext.base64EncodedString()
print("ciphertext: \(ciphertext)") // bWtTZkPAu7oXpQ3QpHvoTvc4NQgDTIycXHFJWvjk
let sealedBoxToDecrypt = try! AES.GCM.SealedBox(nonce: nonce,
ciphertext: Data(base64Encoded: ciphertext)!,
tag: sealedBox.tag)
let decrypted = try! AES.GCM.open(sealedBoxToDecrypt, using: key)
print(String(decoding: decrypted, as: UTF8.self))
}
func testEncryptandDecryptFirstWay() {
let keyStr = "d5a423f64b607ea7c65b311d855dc48f36114b227bd0c7a3d403f6158a9e4412"
let key = SymmetricKey(data: Data(hex:keyStr))
let nonceData = Data(hex: "131348c0987c7eece60fc0bc")
let nonce: AES.GCM.Nonce = try! AES.GCM.Nonce(data: nonceData)
let plain = "This is first cypto graphy method"
var decyptedStr = ""
if let encyptedData = plain.asData.encrypt(nonce: nonce, key: key) {
decyptedStr = encyptedData.decrypt(nonce: nonce, key: key)
}
XCTAssertEqual(plain, decyptedStr)
}
}
extension Data {
func encrypt(nonce: AES.GCM.Nonce, key: SymmetricKey) ->Data?{
// Encrypt
do {
let sealedBox = try AES.GCM.seal(self, using: key, nonce: nonce)
let cipherText = sealedBox.ciphertext.base64EncodedString()
let tag = sealedBox.tag
let tagPlusCipherText = tag + cipherText.asData
return tagPlusCipherText
}
catch let exceptionInfo {
debugPrint("Encrypt exception Info: \(exceptionInfo)")
}
return nil
}
func decrypt(nonce: AES.GCM.Nonce, key: SymmetricKey) -> String{
let tag = self.subtract(0, 16)
let cipherTextData = self.subtract(tag.count, self.count - tag.count)
let cipherText = cipherTextData.asString
// Decrypt
var decodeStr: String = ""
do {
let sealedBoxToDecrypt = try AES.GCM.SealedBox(nonce: nonce,
ciphertext: Data(base64Encoded: cipherText)!,
tag: tag)
let decrypted = try AES.GCM.open(sealedBoxToDecrypt, using: key)
decodeStr = String(decoding: decrypted, as: UTF8.self)
} catch let exceptionInfo {
debugPrint("Decrypt exception info: \(exceptionInfo)")
}
return decodeStr
}
public func subtract(_ start: Int, _ length: Int) ->Data {
precondition(self.count >= start + length,
"Invalid data range range. trying to find out of bound data")
let allBytes = Array(Data(bytes: self.bytes, count: self.count))
let partBytes = Array(allBytes[start..<start + length])
let dataPart = Data(bytes: partBytes, count: partBytes.count)
return dataPart
}
var asString: String {
let str = String(decoding: self, as: UTF8.self)
return str
}
}
extension String {
var asData: Data {
return self.data(using: .utf8) ?? Data()
}
}

How to encode SecKey to base64 string using swift

I want to encode keys to base64 string. But i really can't understand how to do it. I tried to find some information but i find nothing. Thats why im asking for help here. I will be very grateful for any help.
For now i have this:
var error: Unmanaged<CFError>? = nil
var statusCode: OSStatus = 0
var publicKey: SecKey?
var privateKey: SecKey?
let publicKeyAttribute: [NSObject : NSObject] = [kSecAttrIsPermanent: true as NSObject, kSecAttrApplicationTag: "dove.apppublic".data(using: String.Encoding.utf8)! as NSObject]
let privateKeyAtrribute: [NSObject: NSObject] = [kSecAttrIsPermanent: true as NSObject, kSecAttrApplicationTag: "dove.appprivate".data(using: String.Encoding.utf8)! as NSObject]
var keyPairAttr = [NSObject: Any]()
and
func keysGeneration() {
rsaKeyGenerator.keyPairAttr[kSecAttrType] = kSecAttrKeyTypeRSA
rsaKeyGenerator.keyPairAttr[kSecAttrKeySizeInBits] = 2048
rsaKeyGenerator.keyPairAttr[kSecReturnData] = true
rsaKeyGenerator.keyPairAttr[kSecPublicKeyAttrs] = rsaKeyGenerator.publicKeyAttribute
rsaKeyGenerator.keyPairAttr[kSecPrivateKeyAttrs] = rsaKeyGenerator.privateKeyAtrribute
rsaKeyGenerator.statusCode = SecKeyGeneratePair(rsaKeyGenerator.keyPairAttr as CFDictionary, &rsaKeyGenerator.publicKey, &rsaKeyGenerator.privateKey)
if #available(iOS 10.0, *) {
let publicKey = SecKeyCopyExternalRepresentation(rsaKeyGenerator.publicKey!, &rsaKeyGenerator.error)
let privateKey = SecKeyCopyExternalRepresentation(rsaKeyGenerator.privateKey!, &rsaKeyGenerator.error)
// print("MY PUBLIC KEY = \(publicKey)")
// print("MY PRIVATE KEY = \(privateKey)")
}
To convert a string to its base64 representation, you need to take a trip through Data.
let s = "string to encode as base64"
let d = s.data(using: .utf8)
let b64 = base64EncodedString()

Trying to use KeychainItemWrapper by Apple "translated" to Swift

Sigh, I have been working on this the whole afternoon... here is my nightmare:
I am trying to use the KeychainItemWrapper made by Apple. But I "translated" its Objective-C codes to Swift:
import Foundation
import Security
class MyKeychainItemWrapper: NSObject {
var keychainItemData: NSMutableDictionary?
var genericPasswordQuery: NSMutableDictionary = NSMutableDictionary()
init(identifier: String, accessGroup: String?) {
super.init()
// Begin Keychain search setup. The genericPasswordQuery leverages the special user
// defined attribute kSecAttrGeneric to distinguish itself between other generic Keychain
// items which may be included by the same application.
genericPasswordQuery.setObject(kSecClassGenericPassword, forKey: kSecClass)
genericPasswordQuery.setObject(identifier, forKey: kSecAttrGeneric)
// The keychain access group attribute determines if this item can be shared
// amongst multiple apps whose code signing entitlements contain the same keychain access group.
println(accessGroup)
if (!(accessGroup == nil)) {
genericPasswordQuery.setObject(accessGroup!, forKey: kSecAttrAccessGroup)
}
// Use the proper search constants, return only the attributes of the first match.
genericPasswordQuery.setObject(kSecMatchLimitOne, forKey: kSecMatchLimit)
genericPasswordQuery.setObject(kCFBooleanTrue, forKey: kSecReturnAttributes)
var tempQuery: NSDictionary = NSDictionary(dictionary: genericPasswordQuery)
var outDictionary: Unmanaged<AnyObject>? = nil
var status: OSStatus = SecItemCopyMatching(tempQuery as CFDictionaryRef, &outDictionary)
println(status == noErr)
if (status == noErr) {
// Stick these default values into keychain item if nothing found.
resetKeychainItem()
// Add the generic attribute and the keychain access group.
keychainItemData!.setObject(identifier, forKey: kSecAttrGeneric)
if (!(accessGroup == nil)) {
keychainItemData!.setObject(accessGroup!, forKey: kSecAttrAccessGroup)
}
} else {
// load the saved data from Keychain.
keychainItemData = secItemFormatToDictionary(outDictionary?.takeRetainedValue() as NSDictionary)
}
}
Then in my app's AppDelegate.swift, I am trying to use it by:
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var passwordItem: MyKeychainItemWrapper = MyKeychainItemWrapper(identifier: "Password", accessGroup: nil)
...
So, the initializer is called, but but but somehow, I ALWAYS, ALWAYS get
Thread 1: EXC_BREAKPOINT (code=EXC_ARM_BREAKPOINT, subcode=0xe7ffdefe)
I have tried commenting out the problem lines and then I get this error at another if():
I even tried:
var mmm: Bool = (accessGroup == nil)
if (!mmm) {
genericPasswordQuery.setObject(accessGroup!, forKey: kSecAttrAccessGroup)
}
But same error at the same place, i.e. if(..)
I am now so confused. Did I miss something here or?
Environment: Xcode6-beta6, iOS 8 beta 5 on a non-jailbroken iPhone 5.
Swift 3
import UIKit
import Security
let kSecClassGenericPasswordValue = String(format: kSecClassGenericPassword as String)
let kSecClassValue = String(format: kSecClass as String)
let kSecAttrServiceValue = String(format: kSecAttrService as String)
let kSecValueDataValue = String(format: kSecValueData as String)
let kSecMatchLimitValue = String(format: kSecMatchLimit as String)
let kSecReturnDataValue = String(format: kSecReturnData as String)
let kSecMatchLimitOneValue = String(format: kSecMatchLimitOne as String)
let kSecAttrAccountValue = String(format: kSecAttrAccount as String)
struct KeychainAccess {
func setPasscode(identifier: String, passcode: String) {
if let dataFromString = passcode.data(using: String.Encoding.utf8) {
let keychainQuery = [
kSecClassValue: kSecClassGenericPasswordValue,
kSecAttrServiceValue: identifier,
kSecValueDataValue: dataFromString
] as CFDictionary
SecItemDelete(keychainQuery)
print(SecItemAdd(keychainQuery, nil))
}
}
func getPasscode(identifier: String) -> String? {
let keychainQuery = [
kSecClassValue: kSecClassGenericPasswordValue,
kSecAttrServiceValue: identifier,
kSecReturnDataValue: kCFBooleanTrue,
kSecMatchLimitValue: kSecMatchLimitOneValue
] as CFDictionary
var dataTypeRef: AnyObject?
let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef)
var passcode: String?
if (status == errSecSuccess) {
if let retrievedData = dataTypeRef as? Data,
let result = String(data: retrievedData, encoding: String.Encoding.utf8) {
passcode = result as String
}
}
else {
print("Nothing was retrieved from the keychain. Status code \(status)")
}
return passcode
}
}
Swift 2
import UIKit;
import Security;
let kSecClassGenericPasswordValue = NSString(format: kSecClassGenericPassword);
let kSecClassValue = NSString(format: kSecClass);
let kSecAttrServiceValue = NSString(format: kSecAttrService);
let kSecValueDataValue = NSString(format: kSecValueData);
let kSecMatchLimitValue = NSString(format: kSecMatchLimit);
let kSecReturnDataValue = NSString(format: kSecReturnData);
let kSecMatchLimitOneValue = NSString(format: kSecMatchLimitOne);
let kSecAttrAccountValue = NSString(format: kSecAttrAccount);
class KeychainAccess: NSObject {
func setPasscode(identifier: String, passcode: String) {
let dataFromString: NSData = passcode.dataUsingEncoding(NSUTF8StringEncoding)!;
let keychainQuery = NSDictionary(
objects: [kSecClassGenericPasswordValue, identifier, dataFromString],
forKeys: [kSecClassValue, kSecAttrServiceValue, kSecValueDataValue]);
SecItemDelete(keychainQuery as CFDictionaryRef);
let status: OSStatus = SecItemAdd(keychainQuery as CFDictionaryRef, nil);
}
func getPasscode(identifier: String) -> NSString? {
let keychainQuery = NSDictionary(
objects: [kSecClassGenericPasswordValue, identifier, kCFBooleanTrue, kSecMatchLimitOneValue],
forKeys: [kSecClassValue, kSecAttrServiceValue, kSecReturnDataValue, kSecMatchLimitValue]);
var dataTypeRef: AnyObject?
let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef)
var passcode: NSString?;
if (status == errSecSuccess) {
let retrievedData: NSData? = dataTypeRef as? NSData
if let result = NSString(data: retrievedData!, encoding: NSUTF8StringEncoding) {
passcode = result as String
}
}
else {
print("Nothing was retrieved from the keychain. Status code \(status)")
}
return passcode;
}
}
Then from anywhere simply call:
func setPasscode(passcode: String) {
let keychainAccess = KeychainAccess();
keychainAccess.setPasscode("YourAppIdentifier", passcode:passcode);
}
func getPasscode() -> NSString {
let keychainAccess = KeychainAccess();
return keychainAccess.getPasscode("YourAppIdentifier")!;
}
func deletePasscode() {
let keychainAccess = KeychainAccess();
keychainAccess.setPasscode("YourAppIdentifier", passcode:"");
}
official is GenericKeychain
existing several swift version, the best one is:
jrendel/SwiftKeychainWrapper · GitHub
how to use it:
download file: KeychainWrapper.swift
write code to set/get/delete:
let StrUsernameKey:String = "username"
let StrPasswordKey:String = "password"
let saveSuccessful: Bool = KeychainWrapper.setString(usernameTextField.text!, forKey: StrUsernameKey)
print("saveSuccessful=\(saveSuccessful)") //saveSuccessful=true
let retrievedString: String? = KeychainWrapper.stringForKey(StrUsernameKey)
print("retrievedString=\(retrievedString)") //retrievedString=Optional("yourLastStoredUsernameString")
let removeSuccessful: Bool = KeychainWrapper.removeObjectForKey(StrUsernameKey)
print("removeSuccessful=\(removeSuccessful)") //removeSuccessful=true
let retrievedStringAfterDelete: String? = KeychainWrapper.stringForKey(StrUsernameKey)
print("retrievedStringAfterDelete=\(retrievedStringAfterDelete)") //retrievedStringAfterDelete=nil
Updates for Swift 2.
Here is an example implementation that may help.:
import Security
class ZLKeychainService: NSObject {
var service = "Service"
var keychainQuery :[NSString: AnyObject]! = nil
func save(name name: NSString, value: NSString) -> OSStatus? {
let statusAdd :OSStatus?
guard let dataFromString: NSData = value.dataUsingEncoding(NSUTF8StringEncoding) else {
return nil
}
keychainQuery = [
kSecClass : kSecClassGenericPassword,
kSecAttrService : service,
kSecAttrAccount : name,
kSecValueData : dataFromString]
if keychainQuery == nil {
return nil
}
SecItemDelete(keychainQuery as CFDictionaryRef)
statusAdd = SecItemAdd(keychainQuery! as CFDictionaryRef, nil)
return statusAdd;
}
func load(name name: NSString) -> String? {
var contentsOfKeychain :String?
keychainQuery = [
kSecClass : kSecClassGenericPassword,
kSecAttrService : service,
kSecAttrAccount : name,
kSecReturnData : kCFBooleanTrue,
kSecMatchLimit : kSecMatchLimitOne]
if keychainQuery == nil {
return nil
}
var dataTypeRef: AnyObject?
let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef)
if (status == errSecSuccess) {
let retrievedData: NSData? = dataTypeRef as? NSData
if let result = NSString(data: retrievedData!, encoding: NSUTF8StringEncoding) {
contentsOfKeychain = result as String
}
}
else {
print("Nothing was retrieved from the keychain. Status code \(status)")
}
return contentsOfKeychain
}
}
//Test:
let userName = "TestUser"
let userValue: NSString = "TestValue"
print("userName: '\(userName)'")
print("userValue: '\(userValue)'")
let kcs = ZLKeychainService()
kcs.save(name:userName, value: userValue)
print("Keychain Query \(kcs.keychainQuery)")
if let recoveredToken = kcs.load(name:userName) {
print("Recovered Value: '\(recoveredToken)'")
}
Output:
userName: 'TestUser'
userValue: 'TestValue'
Keychain Query [acct: TestUser, v_Data: <54657374 56616c75 65>, svce: Service, class: genp]
Recovered Value: 'TestValue'
My solution seems working:
init(identifier: String) {
super.init()
genericPasswordQuery.setObject(kSecClassGenericPassword, forKey: kSecClass as String)
genericPasswordQuery.setObject(identifier, forKey: kSecAttrGeneric as String)
// Use the proper search constants, return only the attributes of the first match.
genericPasswordQuery.setObject(kSecMatchLimitOne, forKey: kSecMatchLimit as String)
genericPasswordQuery.setObject(kCFBooleanTrue, forKey: kSecReturnAttributes as String)
var tempQuery: NSDictionary = NSDictionary(dictionary: genericPasswordQuery)
var outDictionary: Unmanaged<AnyObject>? = nil
let status: OSStatus = SecItemCopyMatching(tempQuery as CFDictionaryRef, &outDictionary)
var result: NSDictionary? = outDictionary?.takeRetainedValue() as NSDictionary?
if (result == nil) {
// Stick these default values into keychain item if nothing found.
resetKeychainItem()
// Add the generic attribute and the keychain access group.
keychainItemData!.setObject(identifier, forKey: kSecAttrGeneric as String)
} else {
// load the saved data from Keychain.
keychainItemData = secItemFormatToDictionary(result!)
}
}
The only thing I did is to unwrap the outDictionary immediately after getting it.