Swift 3 export SecKey to String - ios

I am developing an iOS app using swift 3.
I need to export an SecKey (which is the user RSA publickey reference) to a string (e.g base64) in order to share it through a generated QRCode.
It also has to work the other way since the other user that scans the QRCode, will be able to rebuild a SecKey reference from the string extracted from the QRCode.
I found few tutorials but I don't understand exactly what I need to extract from the SecKey reference, and I don't know how to convert it to a String.

Export Key (iOS 10 only)
var error:Unmanaged<CFError>?
if let cfdata = SecKeyCopyExternalRepresentation(publicKey!, &error) {
let data:Data = cfdata as Data
let b64Key = data.base64EncodedString()
}
See https://stackoverflow.com/a/30662270/5276890 and https://stackoverflow.com/a/27935528/5276890 for longer ways which probably support iOS < 10.
Reimport Key
guard let data2 = Data.init(base64Encoded: b64Key) else {
return
}
let keyDict:[NSObject:NSObject] = [
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeyClass: kSecAttrKeyClassPublic,
kSecAttrKeySizeInBits: NSNumber(value: 512),
kSecReturnPersistentRef: true as NSObject
]
guard let publicKey = SecKeyCreateWithData(data2 as CFData, keyDict as CFDictionary, nil) else {
return
}
Note: This generates a base64 key and not a certificate. A lot of code samples online deal with how to generate a public key from a certificate using SecCertificateCreateWithData
Also: 512 bit is fast to generate but worthless. Pick a longer and secure value once you're satisfied with the results.
I got valid results back when importing the key I generated and exported, so I assume it works, but I did not try to encrypt and decrypt with it.

Related

Why am I getting errSecParam (-50) back from trying to save a key to keychain?

I have the following code following this storing_keys_in_the_keychain.
func generateInitialKey() -> Data {
let key = AES256.randomKey()
let addQuery: Dictionary<String, Any> = [
kSecClass as String: kSecClassKey,
kSecAttrApplicationTag as String: applicationTag,
kSecValueRef as String: key
]
let status = SecItemAdd(addQuery as CFDictionary, nil)
print(errSecParam, status)
guard status == errSecSuccess else { fatalError("Can't save Key") }
return key
}
The function AES256.randomKey() generates Data of 64 bytes. The applicationTag is Data too:
let applicationTag = "example".data(using: .utf8)!
However, I do end up receiving the errSecParam(-50) error. Can someone help me out?
Read the documentation carefully. errSecParam(-50) means one or more parameters passed to the function were not valid. The link leads you to the site where you can see the description of the status.
At a minimum, you specify the type and size of keys to create using the kSecAttrKeyType and kSecAttrKeySizeInBits parameters, respectively.
This will result in you having the next problem: there is no kSecAttrKeyTypeAES. This is already discussed and answered on the Apple developer forums. The advice there is to use kSecClassGenericPassword.

Swift 3: Convert SHA256 hash string to SecCertificate

Alamofire allows pinning using certificates as well as public keys (though the function to get public keys from the bundle gets the keys from the certificates in the bundle).
I am able to make the pinning work when the public keys are extracted from the certificates, but the pinning fails when I supply a SHA256 String as a public key (I receive the key string from an api call and its is supposed to be used as a public key if the first pinning fails.) I use the code below to convert the string to a [SecKey]
//Create server trust policy
let serverTrustPolicies: [String: ServerTrustPolicy] = [
destinationURL!: .pinPublicKeys(
publicKeys:savePublicKeys(),
validateCertificateChain:true,
validateHost:true
)]
self.manager = SessionManager(
serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies))
//Get [SecKey]
func savePublicKeys() -> [SecKey]
{
var key:SecKey?
var publicKeys:[SecKey] = []
//Check and use if backup key is received from beacon call
if(KeychainService().checkIfKeyExists(tag: "backupURL"))
{
key = KeychainService().obtainKey(tag: backupURLKey)
publicKeys.append(key!)
}
return publicKeys
}
//Functions to insert and retrieve keychain data
func insertPublicKey(publicTag: String, data: Data) -> SecKey? {
let query: Dictionary<String, AnyObject> = [
String(kSecAttrKeyType): kSecAttrKeyClassPublic,
String(kSecClass): kSecClassKey as CFString,
String(kSecAttrApplicationTag): publicTag as CFString,
String(kSecValueData): data as CFData,
String(kSecReturnPersistentRef): true as CFBoolean]
var persistentRef: AnyObject?
let status = SecItemAdd(query as CFDictionary, &persistentRef)
if status != noErr && status != errSecDuplicateItem {
return nil
}
return obtainKey(tag: publicTag)
}
func obtainKey(tag: String) -> SecKey? {
var keyRef: AnyObject?
let query: Dictionary<String, AnyObject> = [
String(kSecAttrKeyType): kSecAttrKeyClassPublic,
String(kSecReturnRef): kCFBooleanTrue as CFBoolean,
String(kSecClass): kSecClassKey as CFString,
String(kSecAttrApplicationTag): tag as CFString,
String(kSecReturnPersistentRef): true as CFBoolean
]
let status = SecItemCopyMatching(query as CFDictionary, &keyRef)
switch status {
case noErr:
if let ref = keyRef {
return (ref as! SecKey)
}
default:
break
}
return nil
}
Where am I going wrong? From what I know, the String I use is a base64encoded one and works in the Android part.
This is for others who might stumble around trying to find the answer to the same or similar question.
Short Answer: Hashing is kind of a one way street. While theoretically you might be able to try different inputs to get the hash and thus get the certificate data as desired in the question, realistically it is very difficult to do so. Hashing algorithms have been written to prevent exactly the thing you want to achieve here. To get the desired input, you might have to spend humongous amount of time, space and computing power.
Long Answer Read up more on what hashing really does.
For example, for SHA256 in the question there are 22562256 possible hashes. There is a 50% chance if you tried 22552255 different inputs. Even if you tried one every microsecond, this would take you 10631063 years. That is one of the major reasons why this is realistically difficult to achieve.
Reversing a hash is like trying to guess two numbers from their sum (x+y = 234). There are a lot of possible combinations.
There are some great answers here

Can I get the modulus or exponent from a SecKeyRef object in Swift?

In Swift, I created a SecKeyRef object by calling SecTrustCopyPublicKey on some raw X509 certificate data. This is what this SecKeyRef object looks like.
Optional(<SecKeyRef algorithm id: 1,
key type: RSAPublicKey,
version: 3, block size: 2048 bits,
exponent: {hex: 10001, decimal: 65537},
modulus: <omitted a bunch of hex data>,
addr: 0xsomeaddresshere>)
Basically, this SecKeyRef object holds a whole bunch of information about the public key, but there seems to be no way to actually convert this SecKeyRef into a string, NSData, or anything else (this is my goal, is just to get a base64 public key).
However, I have a function that I can give a modulus and an exponent, and it will just calculate what the public key is. I've tested it by passing in the data that's logged from the above SecKeyRef.
But somehow I can't access those properties from the SecKeyRef object (I can only see the whole object in the console; for example, I cannot do SecKeyRef.modulus or anything of the sort, it seems).
My question: how can I access SecKeyRef.modulus, or alternatively, convert this SecKeyRef into NSData or something similar? Thanks
Edit
(for more information)
I am creating my SecKeyRef dynamically, through this function I have:
func bytesToPublicKey(certData: NSData) -> SecKeyRef? {
guard let certRef = SecCertificateCreateWithData(nil, certData) else { return nil }
var secTrust: SecTrustRef?
let secTrustStatus = SecTrustCreateWithCertificates(certRef, nil, &secTrust)
if secTrustStatus != errSecSuccess { return nil }
var resultType: SecTrustResultType = UInt32(0) // result will be ignored.
let evaluateStatus = SecTrustEvaluate(secTrust!, &resultType)
if evaluateStatus != errSecSuccess { return nil }
let publicKeyRef = SecTrustCopyPublicKey(secTrust!)
return publicKeyRef
}
What that does is takes the raw byte stream from a certificate (which can be broadcasted from, say, a piece of hardware using PKI), and then turns that into a SecKeyRef.
Edit 2
(comments on existing answers as of 7 January 2015)
This does not work:
let mirror = Mirror(reflecting: mySecKeyObject)
for case let (label?, value) in mirror.children {
print (label, value)
}
This results in this output in the console:
Some <Raw SecKeyRef object>
Not sure what the string "Some" means.
Additionally, mirror.descendant("exponent") (or "modulus") results in nil, even though when printing the raw object in the console, I can clearly see that those properties exist, and that they are in fact populated.
Also, if at all possible, I would like to avoid having to save to the keychain, reading as NSData, and then deleting from the keychain. As stated in the bounty description, if this is the only way possible, please cite an authoritative reference. Thank you for all answers provided so far.
It is indeed possible to extract modulus and exponent using neither keychains nor private API.
There is the (public but undocumented) function SecKeyCopyAttributes which extracts a CFDictionary from a SecKey.
A useful source for attribute keys is SecItemConstants.c
Inspecting the content of this dictionary, we find an entry "v_Data" : <binary>. Its content is DER-encoded ASN for
SEQUENCE {
modulus INTEGER,
publicExponent INTEGER
}
Be aware that integers are padded with a zero byte if they are positive and have a leading 1-bit (so as not to confuse them with a two-complement negative number), so you may find one byte more than you expect. If that happens, just cut it away.
You can implement a parser for this format or, knowing your key size, hard-code the extraction. For 2048 bit keys (and 3-byte exponent), the format turns out to be:
30|82010(a|0) # Sequence of length 0x010(a|0)
02|82010(1|0) # Integer of length 0x010(1|0)
(00)?<modulus>
02|03 # Integer of length 0x03
<exponent>
For a total of 10 + 1? + 256 + 3 = 269 or 270 bytes.
import Foundation
extension String: Error {}
func parsePublicSecKey(publicKey: SecKey) -> (mod: Data, exp: Data) {
let pubAttributes = SecKeyCopyAttributes(publicKey) as! [String: Any]
// Check that this is really an RSA key
guard Int(pubAttributes[kSecAttrKeyType as String] as! String)
== Int(kSecAttrKeyTypeRSA as String) else {
throw "Tried to parse non-RSA key as RSA key"
}
// Check that this is really a public key
guard Int(pubAttributes[kSecAttrKeyClass as String] as! String)
== Int(kSecAttrKeyClassPublic as String)
else {
throw "Tried to parse non-public key as public key"
}
let keySize = pubAttributes[kSecAttrKeySizeInBits as String] as! Int
// Extract values
let pubData = pubAttributes[kSecValueData as String] as! Data
var modulus = pubData.subdata(in: 8..<(pubData.count - 5))
let exponent = pubData.subdata(in: (pubData.count - 3)..<pubData.count)
if modulus.count > keySize / 8 { // --> 257 bytes
modulus.removeFirst(1)
}
return (mod: modulus, exp: exponent)
}
(I ended up writing a full ASN parser, so this code is not tested, beware!)
Note that you can extract details of private keys in very much the same way. Using DER terminology, this is the format of v_Data:
PrivateKey ::= SEQUENCE {
version INTEGER,
modulus INTEGER, -- n
publicExponent INTEGER, -- e
privateExponent INTEGER, -- d
prime1 INTEGER, -- p
prime2 INTEGER, -- q
exponent1 INTEGER, -- d mod (p-1) (dmp1)
exponent2 INTEGER, -- d mod (q-1) (dmq1)
coefficient INTEGER, -- (inverse of q) mod p (coeff)
otherPrimeInfos OtherPrimeInfos OPTIONAL
}
Parsing this by hand is probably ill-advised since any of the integers may have been padded.
Nota bene: The format of the public key is different if the key has been generated on macOS; the structure given above is wrapped like so:
SEQUENCE {
id OBJECTID,
PublicKey BITSTRING
}
The bit-string is DER-encoded ASN of the form above.
Update The answer below might result in your app being rejected due to usage of non-public API's.
The answer lies in the SecRSAKey.h file from Apple's opensource website (Security is part of the code that Apple opensourced). The file is not big, and among other stuff it declares the following two important functions:
CFDataRef SecKeyCopyModulus(SecKeyRef rsaPublicKey);
CFDataRef SecKeyCopyExponent(SecKeyRef rsaPublicKey);
You can add those functions to your bridging header to be able to call them from Swift, also while doing this you can switch from CFDataRef to NSData* as the two types toll-free bridged:
NSData* SecKeyCopyModulus(SecKeyRef rsaPublicKey);
NSData* SecKeyCopyExponent(SecKeyRef rsaPublicKey);
Demo Swift usage:
let key = bytesToPublicKey(keyData)
let modulus = SecKeyCopyModulus(key)
let exponent = SecKeyCopyExponent(key)
print(modulus, exponent)
This is a private API though, and there might be a chance it will no longer be available at some point, however I looked over the versions of Security made public (http://www.opensource.apple.com/source/Security), and looks like the two functions are present in all of them. More, since Security is a critical component of the OS, it's unlikely Apple will do major changes over it.
Tested on iOS 8.1, iOS 9.2, and OSX 10.10.5, and the code works on all three platforms.
I've been down the same path trying to do SSL Public Key Pinning. The API's are pretty much non-existent, and the solution I found was to put it in the Keychain which you can then retrieve as NSData (which can then be Base64 Encoded). It's horrible but the only thing I could find after a day or so of research (without resorting to bundling OpenSSL with my app).
I ported some of my code over to Swift, but I haven't tested it very much so I'm not 100% sure that it works: https://gist.github.com/chedabob/64a4cdc4a1194d815814
It's based off this Obj-C code (which I'm confident works as it's in a production app): https://gist.github.com/chedabob/49eed109a3dfcad4bd41
I found how to get data for a SecKey.
let publicKey: SecKey = ...
let data = SecKeyCopyExternalRepresentation(publicKey, nil)
This seems to work well and I have been able to successfully compare public keys.
This is in Swift 3 (Xcode 8 beta 3)
SecKeyRefis a struct so there is a chance that it can be reflected with Mirror() to retrieve the wanted values.
struct myStruct {
let firstString = "FirstValue"
let secondString = "SecondValue"}
let testStruct = myStruct()
let mirror = Mirror(reflecting: testStruct)
for case let (label?, value) in mirror.children {
print (label, value)
}
/**
Prints:
firstString FirstValue
secondString SecondValue
*/
I've found a single Obj-c re-implementation of the ASN.1 parser in an abandoned project, that appears to work. Problem is, it uses a great deal of pointer tricks that I don't know how to translate into Swift (not even sure some of it is possible). It should be possible to create a swift friendly wrapper around it, since the only input it takes is the NSData.
Everything on the net is using the store and retrieve in the Keychain trick to get to the pub key data, even really popular libs like TrustKit. I found reference in the Apple docs on SecKeyRef to the root cause (I think):
A SecKeyRef object for a key that is stored in a keychain can be
safely cast to a SecKeychainItemRef for manipulation as a keychain
item. On the other hand, if the SecKeyRef is not stored in a keychain,
casting the object to a SecKeychainItemRef and passing it to Keychain
Services functions returns errors.
Since SecCertificateCopyValues isn't available on iOS at this time, you're limited to either parsing the certificate data, or doing the Keychain Item shuffle.
Did you think about using SecCertificateCopyData()? The resulting CFData is toll-Free bridged, I think.
Refer to https://developer.apple.com/library/ios/documentation/Security/Reference/certifkeytrustservices/ to see the relevant documentation of the API.
From How do I encode an unmanaged<SecKey> to base64 to send to another server? :
func convertSecKeyToBase64(inputKey: SecKey) ->String? {
// Add to keychain
let tempTag = "net.example." + NSUUID().UUIDString
let addParameters :[String:AnyObject] = [
String(kSecClass): kSecClassKey,
String(kSecAttrApplicationTag): tempTag,
String(kSecAttrKeyType): kSecAttrKeyTypeRSA,
String(kSecValueRef): inputKey,
String(kSecReturnData):kCFBooleanTrue
]
var result: String?
var keyPtr: AnyObject?
if (SecItemAdd(addParameters, &keyPtr) == noErr) {
let data = keyPtr! as! NSData
result = data.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
}
// Remove from Keychain:
SecItemDelete(addParameters)
return result
}
But if you want to avoid adding to keychain, you can use Mirror:
let mirrorKey = Mirror(reflecting: secKey)
let exponent = mirrorKey.descendant("exponent")
let modulus = mirrorKey.descendant("modulus");
[edit: Mirror not working according to Josh]
I wrote this one base on some other's answer in stackoverflow. Currently I am using it in my production but I am happy to use another solution that doesn't require to write into keychain.
- (NSData *)getPublicKeyBitsFromKey:(SecKeyRef)givenKey host:(NSString*)host {
NSString *tag = [NSString stringWithFormat:#"%#.%#",[[NSBundle mainBundle] bundleIdentifier], host];
const char* publicKeyIdentifier = [tag cStringUsingEncoding:NSUTF8StringEncoding];
NSData *publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:strlen(publicKeyIdentifier) * sizeof(char)];
OSStatus sanityCheck = noErr;
// NSData * publicKeyBits = nil;
CFTypeRef publicKeyBits;
NSMutableDictionary * queryPublicKey = [[NSMutableDictionary alloc] init];
// Set the public key query dictionary.
[queryPublicKey setObject:(id)kSecClassKey forKey:(id)kSecClass];
[queryPublicKey setObject:publicTag forKey:(id)kSecAttrApplicationTag];
[queryPublicKey setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
[queryPublicKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnData];
[queryPublicKey setObject:(__bridge id)givenKey forKey:(__bridge id)kSecValueRef];
// Get the key bits.
NSData *data = nil;
sanityCheck = SecItemCopyMatching((CFDictionaryRef)queryPublicKey, &publicKeyBits);
if (sanityCheck == errSecSuccess) {
data = CFBridgingRelease(publicKeyBits);
//I don't want to leak this information
(void)SecItemDelete((__bridge CFDictionaryRef) queryPublicKey);
}else {
sanityCheck = SecItemAdd((CFDictionaryRef)queryPublicKey, &publicKeyBits);
if (sanityCheck == errSecSuccess)
{
data = CFBridgingRelease(publicKeyBits);
(void)SecItemDelete((__bridge CFDictionaryRef) queryPublicKey);
}
}
return data;
}

SWIFT - Realm db encryption not working

Im trying to encrypt the data stored in the realm database. I followed the Sample Code mentioned on Realm's Swift page. I want to encrypt the data NOT the database file. Below is the code I'm using:
var error: NSError? = nil
let configuration = Realm.Configuration(encryptionKey: EncryptionManager().getKey())
if let realmE = Realm(configuration: configuration, error: &error) {
// Add an object
realmE.write {
realmE.add(objects, update: T.primaryKey() != nil)
}
}
Where objects is a list of objects i need to insert in the database. Below is the code fore getKey() func also picked from the sample code:
func getKey() -> NSData {
// Identifier for our keychain entry - should be unique for your application
let keychainIdentifier = "io.Realm.test"
let keychainIdentifierData = keychainIdentifier.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
// First check in the keychain for an existing key
var query: [NSString: AnyObject] = [
kSecClass: kSecClassKey,
kSecAttrApplicationTag: keychainIdentifierData,
kSecAttrKeySizeInBits: 512,
kSecReturnData: true
]
// To avoid Swift optimization bug, should use withUnsafeMutablePointer() function to retrieve the keychain item
// See also: http://stackoverflow.com/questions/24145838/querying-ios-keychain-using-swift/27721328#27721328
var dataTypeRef: AnyObject?
var status = withUnsafeMutablePointer(&dataTypeRef) { SecItemCopyMatching(query, UnsafeMutablePointer($0)) }
if status == errSecSuccess {
return dataTypeRef as! NSData
}
// No pre-existing key from this application, so generate a new one
let keyData = NSMutableData(length: 64)!
let result = SecRandomCopyBytes(kSecRandomDefault, 64, UnsafeMutablePointer<UInt8>(keyData.mutableBytes))
// Store the key in the keychain
query = [
kSecClass: kSecClassKey,
kSecAttrApplicationTag: keychainIdentifierData,
kSecAttrKeySizeInBits: 512,
kSecValueData: keyData
]
status = SecItemAdd(query, nil)
assert(status == errSecSuccess, "Failed to insert the new key in the keychain")
return keyData
}
The problem is this that the code is not getting encrypted. After inserting data when i open the realm file using Realm Browser the code is NOT encrypted.
Tested on both simulator and device. Using Swift 1.2, Xcode 6.4, Realm 0.95.
Any ideas?
Realm's encryption feature applies only to the ability to encrypt whole .realm files. There's no feature to encrypt discrete objects within the .realm file and leave the rest as-is.
If you do want to go about doing this, I'm afraid you would need to roll the encryption system yourself.
If I was going to do this, I'd do it this way:
Create a Realm Object subclass with an NSData property called
encryptedData.
Serialize any objects you wanted to encrypt to NSData using the
NSCoding protocol. (Saving custom SWIFT class with NSCoding to UserDefaults)
Encrypt that resulting NSData object using an encryption method of your choice (AES Encryption for an NSString on the iPhone)
Take the resulting encrypted NSData object and save it to the encryptedData property in your Realm object.
Reverse the process when you want to retrieve that data.
While this process would work, as you can see, it's a non-trivial amount of extra work, and you would also lose all of the speed and memory-saving benefits of Realm in the process.
I would encourage you to rethink your app's design, and see if it is feasible to use Realm's own encryption feature after all. Good luck!

How to generate an RSA asymmetric Key Pair in Swift for IOS?

I need a way to generate an RSA asymmetrical key pair in Swift. I don't need to store it in the keychain or anything. I just need to generate a key pair and shove both keys into String variables.
The keys do need to be compatible with PHP on the other end. I will use symmetrical encryption to secure the private key and store it on the phone. I'll be sending the public key to a web service that is implemented in PHP, and the web service will store the public key in a database.
That public key will be used later by the web service to encrypt values like one-time passwords and other sensitive values destined for the IOS app. I'll implement a similar scheme for small pieces of data flowing from the IOS app to the web service.
The only documented API declaration I could find for generating a key pair in Swift is shown on developer.apple.com:
func SecKeyGeneratePairAsync(_ parameters: CFDictionary!,
_ deliveryQueue: dispatch_queue_t!,
_ result: SecKeyGeneratePairBlock!)
I tried to figure out how to use this, but XCode doesn't like the underscores, and I'm not sure what I am actually supposed to do with this or how to use it.
Even if XCode would accept it, I am not sure how I would call the function and what values to pass it, etc. I included "import Security" at the top, so that's not the problem.
It's ridiculous that this should be so hard. All I want to do is generate an asymmetric key pair.
It's a piece of cake to do this in PHP or .NET or Java or any other language, but I can't find any clear documentation on it for Swift. I have to use Swift for this app. I don't want to use OpenSSL because it's deprecated.
I am using Swift because I bloody hate objective C. Swift is the reason I am finally jumping into IOS development.
I don't know how to integrate an Objective C class with Swift, and I'd really rather have a pure Swift solution if there is one. If not, I'd appreciate some pointers on how to integrate an Objective C solution and make it work.
The above snippet is the only function call Apple provides, and naturally it's incomplete, doesn't make sense, and doesn't work.
There is a good example of how to do this in Swift in the CertificateSigningRequestSwift_Test project in GitHub. Using a single call to SecKeyCreateRandomKey() one can generate the public/private key pair both at the same time and they will be saved in the keychain.
let tagPublic = "com.example.public"
let tagPrivate = "com.example.private"
let publicKeyParameters: [String: AnyObject] = [
String(kSecAttrIsPermanent): kCFBooleanTrue,
String(kSecAttrApplicationTag): tagPublic as AnyObject,
String(kSecAttrAccessible): kSecAttrAccessibleAlways
]
var privateKeyParameters: [String: AnyObject] = [
String(kSecAttrIsPermanent): kCFBooleanTrue,
String(kSecAttrApplicationTag): tagPrivate as AnyObject,
String(kSecAttrAccessible): kSecAttrAccessibleAlways
]
//Define what type of keys to be generated here
var parameters: [String: AnyObject] = [
String(kSecAttrKeyType): kSecAttrKeyTypeRSA,
String(kSecAttrKeySizeInBits): 2048,
String(kSecReturnRef): kCFBooleanTrue,
kSecPublicKeyAttrs as String: publicKeyParameters as AnyObject,
kSecPrivateKeyAttrs as String: privateKeyParameters as AnyObject,
]
//Use Apple Security Framework to generate keys, save them to application keychain
var error: Unmanaged<CFError>?
let privateKey = SecKeyCreateRandomKey(parameters as CFDictionary, &error)
if privateKey == nil{
print("Error creating keys occurred: \(error!.takeRetainedValue() as Error), keys weren't created")
return
}
//Get generated public key
let query: [String: AnyObject] = [
String(kSecClass): kSecClassKey,
String(kSecAttrKeyType): kSecAttrKeyTypeRSA,
String(kSecAttrApplicationTag): tagPublic as AnyObject,
String(kSecReturnRef): kCFBooleanTrue
]
var publicKeyReturn:AnyObject?
let result = SecItemCopyMatching(query as CFDictionary, &publicKeyReturn)
if result != errSecSuccess{
print("Error getting publicKey from keychain occurred: \(result)")
return
}
let publicKey = publicKeyReturn as! SecKey?
//Set block size
let keyBlockSize = SecKeyGetBlockSize(self.publicKey!)
//Ask keychain to provide the publicKey in bits
let query: [String: AnyObject] = [
String(kSecClass): kSecClassKey,
String(kSecAttrKeyType): keyAlgorithm.secKeyAttrType,
String(kSecAttrApplicationTag): tagPublic as AnyObject,
String(kSecReturnData): kCFBooleanTrue
]
var tempPublicKeyBits:AnyObject?
_ = SecItemCopyMatching(query as CFDictionary, &tempPublicKeyBits)
guard let publicKeyBits = tempPublicKeyBits as? Data else {
return
}
Heimdall seems to be what you're looking for. It's easy to use, can create RSA keypairs, encrypt, decrypt, sign and verify.
It uses the iOS/OS X keychain for storing the keys, so the keys are stored in a secure way.
From the GitHub Readme:
if let heimdall = Heimdall(tagPrefix: "com.example") {
let testString = "This is a test string"
// Encryption/Decryption
if let encryptedString = heimdall.encrypt(testString) {
println(encryptedString) // "cQzaQCQLhAWqkDyPoHnPrpsVh..."
if let decryptedString = heimdall.decrypt(encryptedString) {
println(decryptedString) // "This is a test string"
}
}
// Signatures/Verification
if let signature = heimdall.sign(testString) {
println(signature) // "fMVOFj6SQ7h+cZTEXZxkpgaDsMrki..."
var verified = heimdall.verify(testString, signatureBase64: signature)
println(verified) // True
// If someone meddles with the message and the signature becomes invalid
verified = heimdall.verify(testString + "injected false message",
signatureBase64: signature)
println(verified) // False
}
}

Resources