Create a Secure Random number in iOS using Swift? - swift

public func createSecureRandomKey(numberOfBits: Int) -> Any {
let attributes: [String: Any] =
[kSecAttrKeyType as String:CFString.self,
kSecAttrKeySizeInBits as String:numberOfBits]
var error: Unmanaged<CFError>?
guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
return ""
}
return privateKey
}
I am trying to create Secure random number like above way, but returning nothing, Could any one please help me. Thanks.

It looks like you are using the wrong function. With your function you are generating a new key. But as your title says you want to generate secure random numbers.
For this there is a function called: SecRandomCopyBytes(::_:)
Here is a code snippet taken from the official apple documentation how to use it:
var bytes = [Int8](repeating: 0, count: 10)
let status = SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes)
if status == errSecSuccess { // Always test the status.
print(bytes)
// Prints something different every time you run.
}
Source: Apple doc

Related

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".

How to properly use CFStringGetCString in swift?

I am looking at the docs for
CFStringGetCString
and AXUIElementCopyAttributeValue.
CFStringGetCString takes the param buffer: UnsafeMutablePointer<Int8>!
AXUIElementCopyAttributeValue takes the param value: UnsafeMutablePointer<CFTypeRef?>
For the latter, I can do a call like this:
var value: CFTypeRef?
let err = AXUIElementCopyAttributeValue(element, attribute as CFString, &value);
This satisfies the doc asking for an UnsafeMutablePointer of type CFTypeRef.
However I can't get the same logic to apply by doing
let buffer: Int8!
CFStringGetCString(attribute as! CFString, &buffer, 2048, CFStringBuiltInEncodings.UTF8.rawValue)
I also tried
let buffer: Int8?
CFStringGetCString(attribute as! CFString, &buffer!, 2048, CFStringBuiltInEncodings.UTF8.rawValue)
Either way, it complains about using buffer before it's initialized, even though it never complained about value in the working method with similar param requirements.
All the working examples I've seen for CFStringGetCString are using objective-c like syntax with *. Not sure what the proper swift way is here.
I also tried this way to get the value I wanted:
let app = AXUIElementCreateSystemWide();
var valueString = "";
var value: CFTypeRef?
// An exception on execution happens here when passing app
// Passing in the element I clicked instead of app
// yields error -25205 (attributeunsupported)
let err = AXUIElementCopyAttributeValue(app, "AXFocusedApplication" as CFString, &value);
if (err == AXError.success) {
valueString = value! as! NSString as String;
} else {
print("ERROR!");
print(err.rawValue);
}
return valueString;
Why are you torturing yourself with CFStringGetCString? If you have a CFString in Swift, you can cast it to a String and get a C string from that:
let cString: [Int8] = (cfString as String).cString(using: .utf8)!
Note also that the value of the kAXFocusedApplicationAttribute is not a CFString. It is an AXUIElement.
Here's my playground test:
import Foundation
import CoreFoundation
let axSystem = AXUIElementCreateSystemWide()
var cfValue: CFTypeRef?
AXUIElementCopyAttributeValue(axSystem, kAXFocusedApplicationAttribute as CFString, &cfValue)
if let cfValue = cfValue, CFGetTypeID(cfValue) == AXUIElementGetTypeID() {
let axFocusedApplication = cfValue
print(axFocusedApplication)
}
The first time I executed this playground, I got a system dialog box telling me that I need to give Xcode permission to control my computer. I went to System Preferences > Security & Privacy > Privacy > Accessibility, found Xcode at the bottom of the list, and turned on its checkbox.
Here's the output of the playground:
<AXUIElement Application 0x7fb2d60001c0> {pid=30253}
I assume you're on macOS since the AXUI API is only available on macOS. If you just want the name of the front application as a string, you can do this:
if let frontAppName = NSWorkspace.shared.frontmostApplication?.localizedName {
print(frontAppName)
}

Encryption is not working in Swift4.2 using CommonCrypto. Throwing error 4301

I have been trying to implement encryption using CommonCrypto library in swift 4.2. But no luck, ending up with some unknown error.
Somebody please look at this code and help me.
func encrypty(data value: String) -> EncryptionResult {
guard var messageData = value.data(using: .utf8), var key = getSecretkey()?.data(using: .utf8) else {
return EncryptionResult.failure
}
//iv ata
guard let ivData = generateRandomBytes(of: Int32(SecurityConstants.blockSize))?.data(using: .utf8) else {
return EncryptionResult.failure
}
//output
var outputData = Data(count: (messageData.count + SecurityConstants.blockSize + ivData.count))
var localOutput = outputData
//output length
var outputLength: size_t = 0
//encyrption
let status = key.withUnsafeBytes { keyBytes in
messageData.withUnsafeBytes { messageBytes in
localOutput.withUnsafeMutableBytes { mutableOutput in
ivData.withUnsafeBytes { ivDataBytes in
CCCrypt( CCOperation(kCCEncrypt),
CCAlgorithm(kCCAlgorithmAES128),
CCOptions(kCCOptionPKCS7Padding),
keyBytes,
key.count,
ivDataBytes,
messageBytes,
messageData.count,
mutableOutput,
outputData.count,
&outputLength)
}
}
}
}
guard status == Int32(kCCSuccess) else {
logError("Error in encryption")
return EncryptionResult.failure
}
outputData.count = outputLength
return EncryptionResult.success(value: outputData.base64EncodedString())
}
Error -4310 is kCCKeySizeError (see CommonCryptoError.h). That means your key is not the right size.
Looking at this code, this in particular is very suspicious:
getSecretkey()?.data(using: .utf8)
If a key is decodable as UTF-8, it's not a proper key. You seem to have the same problem with your IV. I suspect that generateRandomBytes() does not quite do what it says it does. It's also not going to be possible to decrypt this data because you throw away the random IV (which the decryptor will require). You create room for it in the output (which is good), but you never write it.

How to get the realy fixed Device-ID in swift?

I use the below code since long time. I have thought it is unique
But I have deleted my app and reinstalled it, I get new different Device-ID.
if let uuid = UIDevice.current.identifierForVendor?.uuidString {
print(uuid)
}
every new reinstall, I get a new ID.
How do I get something which stays the same?
Since the value returned from identifierForVendor can be cleared when deleting the app or reset if the user resets it in the Settings app, you have to manage persisting it yourself.
There are a few ways to accomplish this. You can setup a server that assigns a uuid which is then persisted and fetched server side via user login, or you can create and store it locally in the keychain.
Items stored in the keychain will not be deleted when the app is deleted. This allows you to check if a uuid was previously stored, if so you can retrieve it, if not you can generate a new uuid and persist it.
Here's a way you could do it locally:
/// Creates a new unique user identifier or retrieves the last one created
func getUUID() -> String? {
// create a keychain helper instance
let keychain = KeychainAccess()
// this is the key we'll use to store the uuid in the keychain
let uuidKey = "com.myorg.myappid.unique_uuid"
// check if we already have a uuid stored, if so return it
if let uuid = try? keychain.queryKeychainData(itemKey: uuidKey), uuid != nil {
return uuid
}
// generate a new id
guard let newId = UIDevice.current.identifierForVendor?.uuidString else {
return nil
}
// store new identifier in keychain
try? keychain.addKeychainData(itemKey: uuidKey, itemValue: newId)
// return new id
return newId
}
And here's the class for storing/retrieving from the keychain:
import Foundation
class KeychainAccess {
func addKeychainData(itemKey: String, itemValue: String) throws {
guard let valueData = itemValue.data(using: .utf8) else {
print("Keychain: Unable to store data, invalid input - key: \(itemKey), value: \(itemValue)")
return
}
//delete old value if stored first
do {
try deleteKeychainData(itemKey: itemKey)
} catch {
print("Keychain: nothing to delete...")
}
let queryAdd: [String: AnyObject] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: itemKey as AnyObject,
kSecValueData as String: valueData as AnyObject,
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked
]
let resultCode: OSStatus = SecItemAdd(queryAdd as CFDictionary, nil)
if resultCode != 0 {
print("Keychain: value not added - Error: \(resultCode)")
} else {
print("Keychain: value added successfully")
}
}
func deleteKeychainData(itemKey: String) throws {
let queryDelete: [String: AnyObject] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: itemKey as AnyObject
]
let resultCodeDelete = SecItemDelete(queryDelete as CFDictionary)
if resultCodeDelete != 0 {
print("Keychain: unable to delete from keychain: \(resultCodeDelete)")
} else {
print("Keychain: successfully deleted item")
}
}
func queryKeychainData (itemKey: String) throws -> String? {
let queryLoad: [String: AnyObject] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: itemKey as AnyObject,
kSecReturnData as String: kCFBooleanTrue,
kSecMatchLimit as String: kSecMatchLimitOne
]
var result: AnyObject?
let resultCodeLoad = withUnsafeMutablePointer(to: &result) {
SecItemCopyMatching(queryLoad as CFDictionary, UnsafeMutablePointer($0))
}
if resultCodeLoad != 0 {
print("Keychain: unable to load data - \(resultCodeLoad)")
return nil
}
guard let resultVal = result as? NSData, let keyValue = NSString(data: resultVal as Data, encoding: String.Encoding.utf8.rawValue) as String? else {
print("Keychain: error parsing keychain result - \(resultCodeLoad)")
return nil
}
return keyValue
}
}
Then you can just have a user class where you get the identifier:
let uuid = getUUID()
print("UUID: \(uuid)")
If you put this in a test app in viewDidLoad, launch the app and note the uuid printed in the console, delete the app and relaunch and you'll have the same uuid.
You can also create your own completely custom uuid in the app if you like by doing something like this:
// convenience extension for creating an MD5 hash from a string
extension String {
func MD5() -> Data? {
guard let messageData = data(using: .utf8) else { return nil }
var digestData = Data(count: Int(CC_MD5_DIGEST_LENGTH))
_ = digestData.withUnsafeMutableBytes { digestBytes in
messageData.withUnsafeBytes { messageBytes in
CC_MD5(messageBytes, CC_LONG(messageData.count), digestBytes)
}
}
return digestData
}
}
// extension on UUID to generate your own custom UUID
extension UUID {
static func custom() -> String? {
guard let bundleID = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String else {
return nil
}
let unique = bundleID + NSUUID().uuidString
let hashData = unique.MD5()
let md5String = hashData?.map { String(format: "%02hhx", $0) }.joined()
return md5String
}
}
Note that to use the MD5 function you will have to add the following import to an Objective-C bridging header in your app: (if you're building with Xcode < 10. In Xcode 10+ CommonCrypto is included so you can skip this step)
#import <CommonCrypto/CommonCrypto.h>
If your app does not have a bridging header, add one to your project and make sure to set it in build settings:
Once it's setup you can generate your own custom uuid like this:
let otherUuid = UUID.custom()
print("Other: \(otherUuid)")
Running the app and logging both outputs generates uuids something like this:
// uuid from first example
UUID: Optional("8A2496F0-EFD0-4723-8C6D-8E18431A49D2")
// uuid from second custom example
Other: Optional("63674d91f08ec3aaa710f3448dd87818")
Unique Id in iPhone is UDID, which is not accessible in current version of OS, because it can be misused. So Apple has given the other option for the unique key but it change every time you install the app.
--Cannot access UDID
But there is another way to implement this feature.
First you have to generate the Unique ID :
func createUniqueID() -> String {
let uuid: CFUUID = CFUUIDCreate(nil)
let cfStr: CFString = CFUUIDCreateString(nil, uuid)
let swiftString: String = cfStr as String
return swiftString
}
After getting the this which is unique but changes after the app install and reinstall.
Save this id to the Key-Chain on any key let say "uniqueID".
To save the key in keyChain :
func getDataFromKeyChainFunction() {
let uniqueID = KeyChain.createUniqueID()
let data = uniqueID.data(using: String.Encoding.utf8)
let status = KeyChain.save(key: "uniqueID", data: data!)
if let udid = KeyChain.load(key: "uniqueID") {
let uniqueID = String(data: udid, encoding: String.Encoding.utf8)
print(uniqueID!)
}
}
func save(key: String, data: Data) -> OSStatus {
let query = [
kSecClass as String : kSecClassGenericPassword as String,
kSecAttrAccount as String : key,
kSecValueData as String : data ] as [String : Any]
SecItemDelete(query as CFDictionary)
return SecItemAdd(query as CFDictionary, nil)
}
Next when you required to perform any task based on uniqueID ,first check if any data is saved in Key-Chain on key "uniqueID" .
Even if you uninstall the app , the key-chain data is still persisted, it will delete by OS.
func checkUniqueID() {
if let udid = KeyChain.load(key: "uniqueID") {
let uniqueID = String(data: udid, encoding: String.Encoding.utf8)
print(uniqueID!)
} else {
let uniqueID = KeyChain.createUniqueID()
let data = uniqueID.data(using: String.Encoding.utf8)
let status = KeyChain.save(key: "uniqueID", data: data!)
print("status: ", status)
}
}
In this way you can generate the uniqueID once and use this id every time.
NOTE: But when you upload next version of your app, upload it with the same Provisioning Profile otherwise you cannot access the key-chain store of your last installed app.
Key-Chain Store is associated with the Provisioning profile.
Check Here
Access to the unique device id (UDID) has been disallowed for ages now. identifierForVendor is its replacement, and its behaviour has always been documented.

How do I encode an unmanaged<SecKey> to base64 to send to another server?

I'm trying to use key pair encryption to validate identity between my app and my PHP server. To do this I need to send the public key over to the server after I generate it in my app.
if let pubKey = NSData(base64EncodedData: publicKey, options: NSDataBase64DecodingOptions.allZeros)! {
println(pubKey)
}
publicKey is of type Unmanaged<SecKey>.
The error I'm getting in the above code is: Extra argument 'base64EncodedData' in call
How would I do this? Is there a better way?
Edit: This is how the keypair is generated:
var publicKeyPtr, privateKeyPtr: Unmanaged<SecKey>?
let parameters = [
String(kSecAttrKeyType): kSecAttrKeyTypeRSA,
String(kSecAttrKeySizeInBits): 2048
]
let result = SecKeyGeneratePair(parameters, &publicKeyPtr, &privateKeyPtr)
let publicKey = publicKeyPtr!.takeRetainedValue()
let privateKey = privateKeyPtr!.takeRetainedValue()
let blockSize = SecKeyGetBlockSize(publicKey)
Edit 2: So the issue is that SecKey is not NSData, so my question here should be: How do I convert a publicKey:SecKey to NSData?
It seems that you can temporary store the key to keychain and then get it back and convert it to data:
func convertSecKeyToBase64(inputKey: SecKey) ->String? {
// First Temp add to keychain
let tempTag = "de.a-bundle-id.temp"
let addParameters :[String:AnyObject] = [
String(kSecClass): kSecClassKey,
String(kSecAttrApplicationTag): tempTag,
String(kSecAttrKeyType): kSecAttrKeyTypeRSA,
String(kSecValueRef): inputKey,
String(kSecReturnData):kCFBooleanTrue
]
var keyPtr: Unmanaged<AnyObject>?
let result = SecItemAdd(addParameters, &keyPtr)
switch result {
case noErr:
let data = keyPtr!.takeRetainedValue() as! NSData
// Remove from Keychain again:
SecItemDelete(addParameters)
let encodingParameter = NSDataBase64EncodingOptions(rawValue: 0)
return data.base64EncodedStringWithOptions(encodingParameter)
case errSecDuplicateItem:
println("Duplicate Item")
SecItemDelete(addParameters)
return nil
case errSecItemNotFound:
println("Not found!")
return nil
default:
println("Error: \(result)")
return nil
}
}
While the fact is barely documented, you can pull out everything you need (that is, modulus and exponent) from the SecKey using SecKeyCopyAttributes.
See here for the details.
Swift 4 method to get base64 string from SecKey, publicKey :)
guard let publicKeyData = SecKeyCopyExternalRepresentation(publicKey!, nil) else {
NSLog("\tError obtaining export of public key.")
return ""
}
let publicKeyNSData = NSData(data: publicKeyData as Data)
let publicKeyBase64Str = publicKeyNSData.base64EncodedString()