osx macOS public key import to keychain duplicate - swift

I am trying to permanently import an RSA public key into the keychain
let params: [NSString: AnyObject] = [
kSecClass: kSecClassKey,
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrApplicationTag: tag as AnyObject,
kSecValueData: data as AnyObject,
kSecReturnPersistentRef: true as AnyObject
]
let persistKey = UnsafeMutablePointer<AnyObject?>(mutating: nil)
let status = SecItemAdd(params as CFDictionary, persistKey)
....
Unfortunately the key (regardless which) gets errSecDuplicateItem but if I select it with SecItemCopyMatching.... i get errSecItemNotFound
I tried multiple solutions like adding different attributes like account etc.. but most of this eventually resulted in unknown parameter errors.
I'm sure the key is correct cause SecKeyCreateWithData is fine with it.
edit:
Thats the way to collect the key:
let params: [NSString: AnyObject] = [
kSecClass: kSecClassKey,
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrApplicationTag: tag as AnyObject,
kSecReturnRef: true as AnyObject
]
var keyRef: AnyObject? = nil
status = SecItemCopyMatching(params, &keyRef)
The key is also not findable if you look it up manually in the keychain app

I had the same issue. The code worked fine for me on iOS but I could not get it to work for macOS either.
I then rewrote the code by using Apple's documentation https://developer.apple.com/documentation/security/certificate_key_and_trust_services/keys/storing_keys_as_data
So the code to get a SecKeyRef from a public key given as Data is just:
let options: [String: Any] = [kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
kSecAttrKeySizeInBits as String : 2048]
var error: Unmanaged<CFError>?
guard let key = SecKeyCreateWithData(data as CFData,
options as CFDictionary,
&error) else {
throw error!.takeRetainedValue() as Error
}

Related

Error when adding/updating keychain value with kSecClass

I'm trying to add/update a keychain value using the dictionary below:
public var itemDictionary: [String: Any] {
let dictionary: [CFString: Any?] = [
kSecAttrAccessGroup: accessGroup, // nil
kSecAttrService: service, // nil
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: account,
kSecAttrSynchronizable: synchronizable, // false
kSecAttrAccessible: access.queryValue,
kSecValueData: data
]
return dictionary.compactMapValues { $0 } as [String: Any]
}
However, both SecItemAdd and SecItemUpdate fail with an error of -25303 (errSecNoSuchAttr). After some debugging, I figured out that if I removed kSecClass from the dictionary above, that both SecItemAdd and SecItemUpdate work as expected. This doesn't seem right though as my understanding is that kSecClass is a required field. What am I doing wrong here?

Where does SecKeyCreateSignature get the key name for Keychain signing authorization dialog?

I have noticed a difference between certain keys in the Keychain with respect to how they appear in the Keychain signing dialog, and I cannot figure out why some are displayed a certain way while others are not.
Here is some test code to use identities in the Keychain to sign a sample bit of data.
func testCreateSignature() throws {
let query: [String: Any] = [kSecClass as String: kSecClassIdentity,
kSecMatchLimit as String: kSecMatchLimitAll,
kSecReturnAttributes as String: false,
kSecReturnRef as String: true,
kSecReturnData as String: true]
var resultsRef: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &resultsRef)
guard status == errSecSuccess else { throw SecurityError.unhandledError(status: status) }
guard let results = resultsRef as? [[String:Any]] else {
throw SecurityError.unexpectedCertificateData
}
let data = Data([0xDE, 0xAD, 0xBE, 0xEF])
var privateKey: SecKey!
for result in results {
let secIdentity = result[kSecValueRef as String] as! SecIdentity
try SecIdentityCopyPrivateKey(secIdentity, &privateKey).check()
var error: Unmanaged<CFError>?
let signature = SecKeyCreateSignature(privateKey, .rsaSignatureMessagePKCS1v15SHA1, data as CFData, &error)!
if let error = error {
throw error.takeRetainedValue()
}
print(signature)
}
}
When the code attempts to use one of the keys that Xcode installed for code signing, the resulting dialog looks like the following:
However, when the code attempts to use a key that I've installed, no matter what the label on the key in the Keychain is, it always looks like this:
When my app attempts to use a key to sign, I would like the user to see the name of the key the app wants to use, instead of just generic "privateKey", but I cannot find where this information might be stored on the key.
I have checked the kSecAttrLabel and kSecAttrApplicationLabel attributes of both identities and the private keys and cannot find the text that appears in the dialogs.
I found it. It is a property of the Access Control List of a Keychain item. See 'descriptor' param for SecAccessCreate.
If you do not specify a custom ACL when importing a key, it will default to "privateKey".
I was using SecPKCS12Import to import a .pfx file. I attempted to set the kSecImportExportAccess key in the options parameter to a custom SecAccess object, but it would always import with a default ACL.
I ended up refactoring the code to use SecItemImport instead to import the .pfx file and supplied a custom SecAccess instance:
static func importIdentity(contentsOf url: URL, password: String) throws {
let data = try Data.init(contentsOf: url)
var access: SecAccess!
try SecAccessCreate("License Key" as CFString, nil, &access).check()
var keychain: SecKeychain!
var outItems: CFArray?
let filename: CFString? = url.isFileURL ? url.lastPathComponent as CFString : nil
var inputFormat: SecExternalFormat = .formatPKCS12
var itemType: SecExternalItemType = .itemTypeAggregate
let unmanagedPassword = Unmanaged<AnyObject>.passRetained(password as AnyObject)
let unmanagedAccess = Unmanaged<SecAccess>.passRetained(access)
var params: SecItemImportExportKeyParameters = SecItemImportExportKeyParameters(version: UInt32(SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION),
flags: .importOnlyOne,
passphrase: unmanagedPassword,
alertTitle: nil,
alertPrompt: nil,
accessRef: unmanagedAccess,
keyUsage: nil,
keyAttributes: nil)
try SecKeychainCopyDefault(&keychain).check()
try SecItemImport(data as CFData, filename, &inputFormat, &itemType, [], &params, keychain, &outItems).check()
}
Importing the identity as above will result in "License Key" being shown in the signing dialog rather than "privateKey".

Error trying to generate a random key pair when unit testing on a real device iOS Swift

When i try to run my unit test on a real device it fails.
The returned error is
-25293 ("The user name or passphrase you entered is not correct.").
Here is my failing code:
let accessControl = SecAccessControlCreateWithFlags(
kCFAllocatorDefault,
kSecAttrAccessibleAfterFirstUnlock,
[.privateKeyUsage],
nil)
let privateKeyAttrs = [
kSecAttrIsPermanent as String : true,
kSecAttrApplicationTag as String : tag,
kSecAttrAccessControl as String : accessControl
] as [String : Any]
let generationQuery: [String: Any] = [
kSecAttrKeyType as String : kSecAttrKeyTypeRSA,
kSecAttrKeySizeInBits as String : 2048,
kSecPrivateKeyAttrs as String : privateKeyAttrs]
var error: Unmanaged<CFError>?
guard let privateKey = SecKeyCreateRandomKey(generationQuery as CFDictionary, &error) else {
throw <MyError>
}
The SecKeyCreateRandomKey fails even if I change my Accessible protection to kSecAttrAccessibleAlways or kSecAttrAccessibleWhenUnlocked and my test pass if I just remove kSecAttrAccessControl from my privateKeyAttrs.
More details: If I run my tests on a simulator it pass; I'm using
Xcode 11.6 and swift 5.
.privateKeyUsage is specifically for creating keypairs that are stored in the Secure Enclave:
An attempt to use this constraint while generating a key pair outside the Secure Enclave fails.
Drop that option. You don't need it (and can't use it) if you're just trying to create a keypair in the keychain.

Encrypt using RSA from Swift to C#

I am trying to encrypt username and password and send it to a website with RSA.
I've been searching the web and didn't find any thing.
I get the Public Key from a .Net WebServer in XML formal like this:
<RSAKeyValue>
<Modulus>vB/j1viHNHdSSnD1JwrZKu93GZtXO/oQAzp90w/QRQC7s7RO4PhTcW3ADOUVB1+BlmbaFsEreNUAOV5P4aZh+68T+InwmU1javFsGkjCcVoQO/uEpp2zjrM9Eh84OPaKH429GVmdfTgUj0YbmYVanM3HX4byMH25DKQD687b7x8=
</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
So I have to encrypt it with this key, I am with XCode 8 and Swift 3.
With .Net C· is very Easy.
RSACryptoServiceProvider encryptKey = new RSACryptoServiceProvider();
UnicodeEncoding encoding = new UnicodeEncoding();
encryptKey.FromXmlString(XML PublicKey);
byte[] usr = encryptKey.Encrypt(encoding.GetBytes(txtUser.Text),false);
byte[] pwd = encryptKey.Encrypt(encoding.GetBytes(txtPassword.Text),false);
¿Does any one can help me, please?
thanks
UPDATED
i try this code but with publicKey was generated in Mac OS work fine, but with public key i got from server is not working, i don't know why? #Charles Srstka
let pubKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDzZtW7ETOkJGTwN/4adYI5oQZ7U7EPzfDtpZTf+cQ9zAcmcC6g6uAC6KuovBSsigcUNzw3s2eNh0RvYBl6ipJ71hH1awTBwVEWo4fl7uIqdpBjwvO1wWXg9UifpvSsV3GPff9YqMvuggDznOGc20CvsXusQKt9dDx8ESxP6yjqiwIDAQAB"
let keyData = NSData(base64Encoded: pubKey, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters)
var dict: [NSString: AnyObject] = [
kSecClass: kSecClassKey,
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeySizeInBits: pubKey.utf8.count as AnyObject,
kSecAttrKeyClass: kSecAttrKeyClassPublic,
kSecAttrApplicationTag: "me.lhu" as AnyObject,
kSecValueData: keyData as AnyObject,
kSecReturnRef: true as AnyObject
];
SecItemDelete(dict as CFDictionary)
var err = SecItemAdd(dict as CFDictionary, nil);
if ((err != noErr)) {
print("error loading public key");
}
var resultData: SecKey? = nil
var result: AnyObject?
err = SecItemCopyMatching(dict as CFDictionary, &result)
if err == noErr, let keyRef as! SecKey {
let plaintext = tf_taikhoan.text
let plaintextLen = plaintext?.lengthOfBytes(using: String.Encoding.utf8)
let plaintextBytes = [UInt8](plaintext!.utf8)
var encryptedLen: Int = SecKeyGetBlockSize(keyRef)
var encryptedBytes = [UInt8](repeating: 0, count: encryptedLen)
err = SecKeyEncrypt(keyRef, SecPadding.PKCS1, plaintextBytes, plaintextLen!, &encryptedBytes, &encryptedLen);
if (err != noErr) {
print(encryptedBytes);
}
}
If you can require macOS 10.12 or iOS 10, use the SecKey APIs:
https://developer.apple.com/library/content/documentation/Security/Conceptual/CertKeyTrustProgGuide/Encryption.html
If you have to support older versions of macOS or iOS, you can do encryption and decryption on macOS/iOS using Security Transforms, for which you can find the documentation here:
https://developer.apple.com/library/content/documentation/Security/Conceptual/SecTransformPG/EncryptionandDecryption/EncryptionandDecryption.html#//apple_ref/doc/uid/TP40010801-CH3-SW1

Error adding Key item to macOS keychain

I have following code:
let keyData = UUID().uuidString.data(using: .utf8)!
var attributes: [NSString: Any] = [
kSecClass: kSecClassKey,
kSecAttrApplicationTag: keyData,
]
let st1 = SecItemDelete(attributes as CFDictionary)
attributes[kSecValueData] = keyData
let st2 = SecItemAdd(attributes as CFDictionary, nil)
I am trying to add item to the keychain with type kSecClassKey. For some reason this code works perfectly in iOS and doesn't work in macOS.
In macOS st1 is -25300 (which means The item cannot be found.) and st2 is -25299 (which means The item already exists.)
What can I do to make this code work?
The error errSecDuplicateItem (-25299) might also be returned if you miss a mandatory attribute, e.g., if you try to add a kSecClassGenericPassword key without the kSecAttrService set.
In your case I wonder why you try to store the UUID as a cryptographic key (kSecClassKey). Storing it as a generic password (kSecClassGenericPassword) instead would suffice.
let keyData = UUID().uuidString.data(using: .utf8)!
var attributes: [NSString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: "YourApp-UUID", // Determines the purpose/context of the used password/value
kSecAttrLabel: "YourApp (UUID)", // Name of the Keychain item
kSecValueData: keyData, // Actual value, that will be stored securely
]
let status = SecItemAdd(attributes as CFDictionary, nil)