Using iOS Notifications, Cryptography and iCloud to build your own Chat App.

I should start this article with a disclaimer, it based on iOS 13, Swift 5 and Xcode 11.x. If you reading this and those numbers look dated, be forewarned.

I should also warn you that notifications and iCloud code, involve Apple’s infrastructure which means you will need an Apple Developers account to use them.

Finally I need say I already cover iOS notifications in another series, for which the I link in here. I’ll be using the code developed with it hence the mention.

On to the plan. One of the central pillars mentioned in iOS notification is the generation of the 64 character string that is linked to your idevice, a string that is used to identify it.

It’s a string I need to share with another users to start a notifcation conversation, indeed a meeting :). We can upload it to the public icloud directory of the app, but it feels a little too loosey goosey. No I need a tad more security. Not to mention the fact that typing in a 64 character address isn’t going to be too user friendly.

So lets create a simple directory, within it we’ll store a nickname and a public key linked to it. We’ll also have a second database that we’ll store the name again and a private key within. The former we’ll keep in the public cloud database, the later in the private cloud database. We’ll have one more database that we’ll use to mediate the conversations. A database that will contain the link the name and that 64 character device address.

This is how it will work.

If Jude wants to talk to Josh, firstly she’ll need to encrypt her 64 character address with Josh’s public key as well as her name to keep things confidential. Then she’ll need to save it to a mediating container that they’re both watching with Josh as the addressee.

Now when he get a notifcation that something has changed on the database he’ll need to check who it is for initially, see that is him and fetch the encypted sender and device.

Knowing it is for him decrypt them both the sender and device details with his private key. On doing so he’ll get Jude’s 64 character address and her name as the sender. He can than look up her name and use her public key to encrypt his device details and name.

She’ll get notified of the change, see it is for her and decrypt the details.

Once they both have 64 character keys they can start a conversation using notifications.

To boot. We will start with cryptography, since is central to our theme. Load the posting application we wrote, the one called “Noob”. Create a Cocoa touch class and call it RSA. Now cut and paste this code into your new Keys class. It is rather long I am sorry.

import UIKit
import Security
class RSA: NSObject {private var publicKey : SecKey?
private var privateKey : SecKey?
func generateKeyPair(keySize: UInt, privateTag: String, publicTag: String) -> Bool {let publicKeyParameters: [NSString: AnyObject] = [kSecAttrIsPermanent: true as AnyObject,kSecAttrApplicationTag: publicTag as AnyObject]let privateKeyParameters: [NSString: AnyObject] =[kSecAttrIsPermanent: true as AnyObject,
kSecAttrApplicationTag: publicTag as AnyObject]
let parameters: [String: AnyObject] = [kSecAttrKeyType as String: kSecAttrKeyTypeRSA,kSecAttrKeySizeInBits as String: keySize as AnyObject,kSecPrivateKeyAttrs as String: privateKeyParameters as AnyObject,kSecPublicKeyAttrs as String: publicKeyParameters as AnyObject]let status : OSStatus = SecKeyGeneratePair(parameters as CFDictionary, &(self.publicKey), &(self.privateKey))return (status == errSecSuccess && self.publicKey != nil && self.privateKey != nil)
}
func encrypt(text: String) -> [UInt8] {
let plainBuffer = [UInt8](text.utf8)
var cipherBufferSize : Int = Int(SecKeyGetBlockSize((self.publicKey)!))
var cipherBuffer = [UInt8](repeating:0, count:Int(cipherBufferSize))
// Encrypto should less than key lengthlet status = SecKeyEncrypt((self.publicKey)!, SecPadding.PKCS1, plainBuffer, plainBuffer.count, &cipherBuffer, &cipherBufferSize)if (status != errSecSuccess) {
print("Failed Encryption")
}
return cipherBuffer
}
func decprypt(encrpted: [UInt8]) -> String? {
var plaintextBufferSize = Int(SecKeyGetBlockSize((self.privateKey)!))
var plaintextBuffer = [UInt8](repeating:0, count:Int(plaintextBufferSize))
let status = SecKeyDecrypt((self.privateKey)!, SecPadding.PKCS1, encrpted, plaintextBufferSize, &plaintextBuffer, &plaintextBufferSize)if (status != errSecSuccess) {
print("Failed Decrypt")
return nil
}
return NSString(bytes: &plaintextBuffer, length: plaintextBufferSize, encoding: String.Encoding.utf8.rawValue)! as String
}
func encryptBase64(text: String) -> String {
let plainBuffer = [UInt8](text.utf8)
var cipherBufferSize : Int = Int(SecKeyGetBlockSize((self.publicKey)!))
var cipherBuffer = [UInt8](repeating:0, count:Int(cipherBufferSize))
// Encrypto should less than key lengthlet status = SecKeyEncrypt((self.publicKey)!, SecPadding.PKCS1, plainBuffer, plainBuffer.count, &cipherBuffer, &cipherBufferSize)
if (status != errSecSuccess) {
print("Failed Encryption")
}
let mudata = NSData(bytes: &cipherBuffer, length: cipherBufferSize)
return mudata.base64EncodedString(options: NSData.Base64EncodingOptions.lineLength64Characters)
}
func decpryptBase64(encrpted: String) -> String? {let data : NSData = NSData(base64Encoded: encrpted, options: .ignoreUnknownCharacters)!
let count = data.length / MemoryLayout<UInt8>.size
var array = [UInt8](repeating: 0, count: count)
data.getBytes(&array, length:count * MemoryLayout<UInt8>.size)
var plaintextBufferSize = Int(SecKeyGetBlockSize((self.privateKey)!))
var plaintextBuffer = [UInt8](repeating:0, count:Int(plaintextBufferSize))
let status = SecKeyDecrypt((self.privateKey)!, SecPadding.PKCS1, array, plaintextBufferSize, &plaintextBuffer, &plaintextBufferSize)if (status != errSecSuccess) {
print("Failed Decrypt")
return nil
}
return NSString(bytes: &plaintextBuffer, length: plaintextBufferSize, encoding: String.Encoding.utf8.rawValue)! as String}func getPublicKey() -> SecKey? {
return self.publicKey
}
func getPrivateKey() -> SecKey? {
return self.privateKey
}
}

I must confess a little abstruse too, which is the reason I included the entire block. Rest assure it simply does the public/private key dance for you.

Now switch over to your ContentView.swift file and add an additional button to the interface. Just above it create a Keys object and then add code to call it within your new button.

import SwiftUI
import AVFoundation
...let rsa = RSA()...Button(action: {
let success : Bool = (rsa.generateKeyPair(keySize: 2048, privateTag: "ch.cqd.noob", publicTag: "ch.cqd.noob"))
if (!success) {
print("Failed")
return
}
let test : String = poster.token
let encryption = rsa.encryptBase64(text: test)
print(encryption)
let decription = rsa.decpryptBase64(encrpted: encryption)
print(decription)
}) {
Text("keys")
}

OK, you should be ready to compile and run it. Press the button and you’ll see the device token, then an encypted version, then a decrypted version. This is very much a POC piece of code, we will restructure it.

Ok, although I was tempted to start the next article here I thought better of it, since most of what has already been said is a monster code block. Lets start the iCloud implementation. Go back to “Noob” click on the targets and add a new capability. We’re after iCloud. Tick the box that says iCloudKit.

Click the plus sign and use the bundle ID as the name of the database you want to create.

It may take a moment or two to work. Click on the CloudKit Dashboard which will open a browser window and you should find yourself looking at something like this.

Be warned, this dash board has changed quite dramatically every release, so depending on how old his article is your’s will not look exactly the same.

Select the Schema and add a new record type. Lets call it directory. Add the fields and indexes you see here. There are more than you need, but we added then to help us/you debug your app. Don’t forget to add the indexes.

And here is a screenshot of our mediator record.

I am going end this article here to give you a break, obviously we’re only halfway thru. Read on after you got a coffee.

Written by

Coding for 35+ years, enjoying using and learning Swift/iOS development. Writer @ Better Programming, @The StartUp, @Mac O’Clock, Level Up Coding & More

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store