Cannot save Security Item to Keychain - swift

I am trying to save SecIdentity item into the keychain using Swift 3. After saving status of operation is alway 0 (Successful), but when I try to retrieve the saved one, status is -25300 (which means that object does not exist) What I'm doing wrong?
func saveIdentity(identity: SecIdentity) -> Data? {
let str = "identity"
let saveQuery = [
kSecClass as String : kSecClassIdentity,
kSecValueRef as String : identity,
kSecAttrLabel as String : "identity",
kSecAttrAccessible as String : kSecAttrAccessibleAlways
] as [String : Any]
var item: CFTypeRef? = nil
var status: OSStatus = SecItemAdd(saveQuery as CFDictionary, nil)
// Status = 0
let loadQuery = [
kSecClass as String : kSecClassIdentity,
kSecAttrLabel as String : "identity",
kSecReturnRef as String : kCFBooleanTrue,
kSecMatchLimit as String : kSecMatchLimitAll
] as [String : Any]
status = SecItemCopyMatching(loadQuery as CFDictionary, &item)
// Status = -25300
if status == noErr {
return item as! Data?
} else {
return nil
}
}

Related

Update dictionary values in nested dictionary

The nested dictionary that I am using is build like this
var fDict : [String : Any] = [:]
var errors : [String : Any] = [:]
var error : [String : Any] = [:]
let base : [String : Any] = ["key": "123"]
error["error"] = base
errors["errors"] = error
fDict["profile-response"] = errors
The dictionary is looking like :
{
“profile-response“ : {
“errors” : {
“error” : {
“key“ = “123”
}
}
}
}
I have written code to update the value of key to "abc"
Code :
func replaceErrorWithCustomError( data : inout [String: Any]) {
for (key,value) in data {
if key == "key" {
data.updateValue("abc", forKey: key)
break
} else if var value = value as? [String: Any] {
replaceErrorWithCustomError(data: &value)
}
}
}
The result before update and after update remains same. Please suggest how to make changes in the current dictionary without taking another dictionary.
You can try this -
func replaceErrorWithCustomError(data: inout [String: Any]) {
func updateError(dict: inout [String: Any]) -> [String: Any] {
for (key, value) in dict {
if key == "key" {
dict.updateValue("abc", forKey: key)
break
} else if var value = value as? [String: Any] {
// This is the key change
// Result must be updated back into parent
dict[key] = updateError(dict: &value)
}
}
return dict
}
updateError(dict: &data)
}

Cannot get symmetric key from keychain

Try to create and retreive a symmetric key from keychain :
Add the key
let key = Data(repeating: 0xee, count: 32)
let name = "test"
let attributes = [
kSecAttrKeyType: kSecAttrKeyTypeAES,
kSecAttrKeySizeInBits: NSNumber(value: 256)
] as CFDictionary
var error: Unmanaged<CFError>?
let secKey = SecKeyCreateFromData(attributes, key as CFData, &error)
let addquery = [
kSecClass: kSecClassKey,
kSecAttrKeyClass: kSecAttrKeyClassSymmetric,
kSecAttrLabel: name,
kSecValueRef: secKey!
] as CFDictionary
let status = SecItemAdd(addquery as CFDictionary, nil)
if status != errSecSuccess {
print(SecCopyErrorMessageString(status, nil)!)
}
The keychain item is created
Get the key
let name = "test"
let getquery = [
kSecClass: kSecClassKey,
kSecAttrKeyClass: kSecAttrKeyClassSymmetric,
kSecAttrLabel: name
] as [CFString : Any]
var secKey: CFTypeRef?
let status = SecItemCopyMatching(getquery as CFDictionary, &secKey)
if status == errSecSuccess {
if let dic = SecKeyCopyAttributes(secKey as! SecKey) as? [CFString: Any] {
if let key = dic[kSecValueData] {
print("Ok")
} else {
print("Cannot get the key")
}
} else {
print("Error retrieving dictionnary")
}
} else {
print(SecCopyErrorMessageString(status, nil)!)
}
If the key is added and retrieve in the same run it works. The number of elements in the dic is 21.
But if i only try to get the key stored in keychain i get the dictionary but not the key. The number of elements in the dic is 20 (kSecValueData is missing).
What parameters are missing to get the key ?
Thank you
In order to retrieve your key from the KeyChain, you should also specify the kSecAttrAccessible option:
let attributes = [
kSecAttrKeyType: kSecAttrKeyTypeAES,
kSecAttrKeySizeInBits: NSNumber(value: 256),
kSecAttrAccessible: kSecAttrAccessibleWhenUnlocked
] as CFDictionary
This one can be queried and retrieved when the Mac is unlocked.
You can then use the code you already provided
let getquery = [
kSecClass: kSecClassKey,
kSecAttrKeyClass: kSecAttrKeyClassSymmetric,
kSecAttrLabel: name,
kSecMatchLimit: kSecMatchLimitAll
] as [CFString : Any]
var secKeys: CFTypeRef?
let status = SecItemCopyMatching(getquery as CFDictionary, &secKeys)
if status == errSecSuccess {
for symmetricKey in (secKeys as! [SecKey]) {
guard let keyAttributes = SecKeyCopyAttributes(symmetricKey) as? [CFString: Any] else {
fatalError("No key attributes for symmetric key")
}
guard let keyData = keyAttributes[kSecValueData] as? Data else {
fatalError("No key data for symmetric key")
}
print("Key data retrieved: \(keyData.base64EncodedString())")
}
}
Now, you might encounter the old keys you've added, which still will not return any data (as their accessibility flag is set incorrectly). Remove those using the Keychain Access application on your Mac. After that, you should be able to add an AES key and retrieve an AES key.

How to get an array field from Firestore and write in struct field in Swift?

The problem that I faced is, I can reach every field in Firestore and write in structs except array&map field.
My firestore data is something like:
let data : [String : Any] = [
"name" : "House A",
"price" : 2000,
"contents" : [
"water" : true,
"internet" : false
]
]
Here is getDocument function:
let docRef = db.collection("example").document("example")
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let data = Houses(Doc: document)
...
...
...
} else {
print(error, "Item not found")
}
}
Here is my structs :
struct Houses {
var name: String?
var price: Int
var contents : Contents
init(Doc: DocumentSnapshot){
self.name = Doc.get("name") as? String ?? ""
self.price = Doc.get("price") as! Int
self.contents = Doc.get("contents") as! Contents
}
}
struct Contents {
var water: Bool
var internet : Bool
init?(data: [String: Any]) {
guard let water = data["water"] as? Bool,
let internet = data["internet"] as? Bool else {
return nil
}
self.water = water
self.internet = internet
}
}
The other version of Contents :
struct Contents {
var water: Bool
var internet : Bool
init(Doc: DocumentSnapshot){
self.water = Doc.get("water") as! Bool
self.internet = Doc.get("internet") as! Bool
}
}
UPDATED
The problem solved with changing this line:
self.contents = Doc.get("contents") as! Contents
to;
self.contents = Contents(data: Doc.get("contents") as! [String : Any])
name and price returns what I expected but contents always return nil. I tried to configure Contents but results are same. I think, I have to configure struct named Contents.
Any help would be appreciated.

Read item from keychain right after creating

I'm working with custom keychain in my application and I have problems with reading keychain item right after adding new one. This is code example:
let path = "/tmp/testkeychain_48"
let password = "password"
let serviceName = "service"
let account = "account"
let data = "data".data(using: .utf8)
var keychain: SecKeychain? = nil
var status = SecKeychainCreate(path, UInt32(password.characters.count), password, false, nil, &keychain)
assert(status == errSecSuccess)
var query: [String: AnyObject] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: serviceName as AnyObject,
kSecAttrAccount as String: account as AnyObject,
kSecValueData as String: data as AnyObject,
kSecUseKeychain as String: keychain as AnyObject,
]
var item: CFTypeRef? = nil
status = SecItemAdd(query as CFDictionary, &item)
assert(status == errSecSuccess)
assert(item != nil)
query = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: serviceName as AnyObject,
kSecAttrAccount as String: account as AnyObject,
kSecMatchLimit as String: kSecMatchLimitAll as AnyObject,
kSecUseKeychain as String: keychain as AnyObject,
]
status = SecItemCopyMatching(query as CFDictionary, &item)
assert(status == errSecSuccess)
assert(item != nil)
try? FileManager.default.removeItem(atPath: path)
SecItemAdd(_:_) returns success but SecItemCopyMatching(_:_) fails with error The specified item could not be found in the keychain. With default keychain all works correctly.

swift nil form dictionary

I am using the linkedIn SDK in my app to create a linkedIn login.
I am attempting to get the company name from the API but keep getting nil on the line:
print("Company: (companyString!)")
The dict is as follows:
["publicProfileUrl": https://www.linkedin.com/in/joebloggs, "formattedName": Joe Bloggs, "id": Zazobgtf1Q, "pictureUrls": {
"_total" = 1;
values = (
"https://media.licdn.com/mpr/mprx/0_xBXVf6v56zJf42DuUQJy70N69gfspmi8VYJMYZq_Q6X8SCm_a-4jUmz6FF4wOai_xjJMpPN_qiNQ7xaiUpvv4jq5_iN67xx8apv4S6HL9JLf7HadaZ5JIuNzlJ"
);
}, "pictureUrl": https://media.licdn.com/mpr/mprx/0_OzOPtLSS9GH8gFDYUA6-xtfSKPkgY5YmNkEPBAGSc5ypRvS_AzXjVcuSNssssXO_qkE1Mi_DxXcyZT2mBQR7sAa3VXcjZTdGlQRKlrR2ALGltB-YjcSOAkzjslDDATY14Lxx9mjGA2G, "lastName": Bloggs, "emailAddress": joe#gmail.com, "positions": {
"_total" = 1;
values = (
{
company = {
id = 9433004;
industry = "Marketing & Advertising";
name = "Company Name";
size = "2-10";
type = "Privately Held";
};
id = 865817330;
isCurrent = 1;
location = {
country = {
code = gb;
name = "United Kingdom";
};
name = "Leeds, United Kingdom";
};
startDate = {
month = 9;
year = 2016;
};
title = "Staff";
}
);
}, "firstName": Joe]
I am using the code below:
func linkedInLogin(){
LISDKSessionManager.createSession(withAuth: [LISDK_BASIC_PROFILE_PERMISSION, LISDK_EMAILADDRESS_PERMISSION], state: nil, showGoToAppStoreDialog: true, successBlock: { (returnState) -> Void in
print("success called!")
let session = LISDKSessionManager.sharedInstance().session
//let url = "https://api.linkedin.com/v1/people/~"
let url = "https://api.linkedin.com/v1/people/~:(id,summary,positions,email-address,first-name,last-name,public-profile-url,formatted-name,picture-url,picture-urls::(original))?format=json"
if LISDKSessionManager.hasValidSession() {
LISDKAPIHelper.sharedInstance().getRequest(url, success: { (response) -> Void in
// print(response!.data!)
let str = response!.data!
let dict = self.convertToDictionary(text: str)
print(dict!)
let firstName : String? = dict!["firstName"] as! String?
let lastName : NSString? = dict?["lastName"] as? NSString
let email : NSString? = dict?["emailAddress"] as? NSString
let userName : NSString? = dict?["formattedName"] as? NSString
let linkedInID : NSString? = dict?["id"] as? NSString
let link : NSString? = dict?["publicProfileUrl"] as? NSString
let liid : NSString? = dict?["id"] as? NSString
let picurl : NSString? = dict?["pictureUrl"] as? NSString
// let summary : NSString? = dict?["summary"] as? NSString
let positions : NSString? = dict?["positions"] as? NSString
var companyString:String!
// let type = (self.data[indexPath.row] as? [String : String])?["Type"]
if let company = (dict?["company"] as? [String : String])?["name"]{
companyString = company
}
print("FIRSTNAME: \(firstName!)")
print("LASTNAME: \(lastName!)")
if email != nil {print("email: \(email!)")}
print("userName: \(userName!)")
print("linkedinid: \(linkedInID!)")
print("Link: \(link!)")
print("Liid: \(liid!)")
print("pic url: \(picurl!)")
// print("summary: \(summary!)")
print("positions: \(positions!)")
print("Company: \(companyString!)")
}, error: { (error) -> Void in
print(error!)
})
}
}) { (error) -> Void in
print("Error: \(error)")
}
}
func convertToDictionary(text: String) -> [String: Any]? {
if let data = text.data(using: .utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
} catch {
print(error.localizedDescription)
}
}
return nil
}
The value for company is [String:Any] because id is Int
if let dict = self.convertToDictionary(text: str) {
...
if let company = dict["company"] as? [String : Any],
let companyName = company["name"] as? String {
print(companyName)
}
...
}
Side notes:
Don't annotate types the compiler can infer.
Don't use NSString in Swift.
There are too many exclamation and question marks. Use optional bindings to get safely unwrapped non-optional types.