Cannot get symmetric key from keychain - swift

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.

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.

EXC_BAD_ACCESS code=257 at SecKeyIsAlgorithmSupported

I have a KeyChain class where I sign a string.
I got Thread 8: EXC_BAD_ACCESS (code=257, address=0x3fd574bc6a7ef9db) error at SecKeyIsAlgorithmSupported function. I could not figure out why this error pops up.
When I use the getquery variable which is commented it all works fine except on iPhone 13 pro max devices. So I wanted to try different queries hoping that can work on all devices. But in that case SecKeyIsAlgorithmSupported function crashes giving this error EXC_BAD_ACCESS. Here is the function I use.
func signString(clearString:String) -> Bool {
/*let getquery: [String: Any] = [kSecClass as String: kSecClassKey,
kSecAttrApplicationTag as String: serviceName,
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecReturnRef as String: true]*/
let getquery: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: serviceName,
kSecReturnAttributes as String: kCFBooleanTrue!,
kSecMatchLimit as String: kSecMatchLimitAll]
var item: CFTypeRef?
let status = SecItemCopyMatching(getquery as CFDictionary, &item)
print("status = ",status)
if (status != errSecSuccess) {
print("No key found")
return false
}
else {
let key = item as! SecKey
self.privateKey = key
let data = clearString.data(using: .utf8)! as CFData
let algorithm: SecKeyAlgorithm = .ecdsaSignatureMessageX962SHA256
if (self.privateKey != nil) {
guard SecKeyIsAlgorithmSupported(self.privateKey!, .sign, algorithm) else {
print("Algorithm Not Supported")
return false
}
var error: Unmanaged<CFError>?
guard let signature = SecKeyCreateSignature(self.privateKey!,algorithm, data, &error) as Data? else {
print("signature error")
return false
}
self.signedString = signature.base64EncodedString()
return true
}
else {
print("Private Key is null")
return false
}
}
}
I wish there would be a way to avoid this crash. I searched about it but I could not find a way to fix that.
Any help will be appreciated. Thanks in advance.
Your get query states kSecMatchLimitAll, which will result in a CFArray object as a result. You can easily fix that by changing it to kSecMatchLimitOne, or you can loop the list, by casting it to an array.
let keys = item as! [SecKey]
for key in keys {
SecKeyIsAlgorithmSupported(key, .sign, . ecdsaSignatureMessageX962SHA256)
}
Do note that not all generic items, or likely none, are valid SecKey objects. It appears you're using ECC keys, which can be stored using the kSecClass: kSecClassKey attribute. I would highly recommend storing it as what it is, instead of storing it as a generic password (kSecClassGenericPassword) as you're doing right now

How to unwrap an callback in swift?

I'm using FB Login for my app in Swift and when I make a graph request, it returns the following result:
Optional({
email = "arjun.ramjams#gmail.com";
id = 10218497873670001;
name = "Arjun Ram";
})
Now, how should I read, each individual value(i.e: email, id and name)?
func fetchProfile() {
let parameters = ["fields": "id,email, first_name, last_name"]
GraphRequest(graphPath: "me",parameters: parameters).start{(connection, user, Err) in
if Err != nil {
print(Err!)
return
}
let dic = user as! NSDictionary
let userID = dic["id"] as! String
let Email = dic["email"] as! String
let fname = dic["first_name"] as! String
let lname = dic["last_name"] as! String
}
}
The result is a Dictionary of type [String:Any]?.
let result:[String:Any]? = [
"email":"arjun.ramjams#gmail.com",
"id" : 10218497873670001,
"name":"Arjun Ram"
]
You can fetch the fields from result like,
let email = result?["email"] as? String
let id = result?["id"] as? Int
let name = result?["name"] as? String
You need to use if let or guard let for unwraping an object or variable
e.g.:
if let result = result as? [String: Any] {
print(result[“email”])
}
or
guard let result = result as? [String: Any] else {
// return something
}

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.

Cannot save Security Item to Keychain

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
}
}