Secure communication between a mobile device client and a server is important. This tutorial selects a Raspberry Pi as the server and uses an iPhone simulator as the client. The communication procedure is as below:
1. Server generates an RSA private / public key pair and sends the public key to iPhone
2. iPhone encrypts a message using the public key provided by server and sends the encrypted message to server
3. Server decrypts the encrypted message from iPhone using the private key
This tutorial is derived from the basic communication between an iPhone and a Python socket server built on a Raspberry Pi without encryption:
Connect an iPhone to a Simple Python Socket Server (Raspberry Pi Part) (iOS Part)
For an overview on client-server communications, see this:
Communication between iOS device (Client) and Raspberry Pi (Server)
Raspberry Pi Part
Setup a socket server in Python:
Sending RSA encrypted message - From iOS device to Python socket server (Raspberry Pi Part)
iOS Part
The Xcode version used is 7.2.1 (Swift 2.1.1).
Using external public key to encrypt a string in iOS is not easy. I have spent several days on this topic. Finally I found that Swift-RSAUtils is a useful tool. Here are the steps:
2. Create a new Single View Application project in Xcode.
3. Add RSAUtils.swift to the project just created.
4. Modify ViewController.swift as below:
import UIKit
class ViewController: UIViewController, NSStreamDelegate {
//Button
var buttonConnect : UIButton!
var buttonGetKey : UIButton!
var buttonSendMsg : UIButton!
var buttonQuit : UIButton!
//Label
var label : UILabel!
var labelConnection : UILabel!
//Socket server
let addr = "192.168.xx.xx"
let port = xxxx
//Network variables
var inStream : NSInputStream?
var outStream: NSOutputStream?
//Data received
var buffer = [UInt8](count: 2000, repeatedValue: 0)
var inStreamLength : Int!
var keyString = ""
override func viewDidLoad() {
super.viewDidLoad()
ButtonSetup()
LabelSetup()
}
//Button Functions
func ButtonSetup() {
buttonConnect = UIButton(frame: CGRectMake(20, 50, 300, 30))
buttonConnect.setTitle("Connect to server", forState: UIControlState.Normal)
buttonConnect.setTitleColor(UIColor.blueColor(), forState: UIControlState.Normal)
buttonConnect.setTitleColor(UIColor.cyanColor(), forState: UIControlState.Highlighted)
buttonConnect.addTarget(self, action: "btnConnectPressed:", forControlEvents: UIControlEvents.TouchUpInside)
view.addSubview(buttonConnect)
buttonGetKey = UIButton(frame: CGRectMake(20, 100, 300, 30))
buttonGetKey.setTitle("Get server's public key", forState: UIControlState.Normal)
buttonGetKey.setTitleColor(UIColor.blueColor(), forState: UIControlState.Normal)
buttonGetKey.setTitleColor(UIColor.cyanColor(), forState: UIControlState.Highlighted)
buttonGetKey.addTarget(self, action: "btnGetKey:", forControlEvents: UIControlEvents.TouchUpInside)
buttonGetKey.alpha = 0.3
buttonGetKey.enabled = false
view.addSubview(buttonGetKey)
buttonSendMsg = UIButton(frame: CGRectMake(20, 150, 300, 30))
buttonSendMsg.setTitle("Send encrypted message", forState: UIControlState.Normal)
buttonSendMsg.setTitleColor(UIColor.blueColor(), forState: UIControlState.Normal)
buttonSendMsg.setTitleColor(UIColor.cyanColor(), forState: UIControlState.Highlighted)
buttonSendMsg.addTarget(self, action: "btnSendMsg:", forControlEvents: UIControlEvents.TouchUpInside)
buttonSendMsg.alpha = 0.3
buttonSendMsg.enabled = false
view.addSubview(buttonSendMsg)
buttonQuit = UIButton(frame: CGRectMake(20, 200, 300, 30))
buttonQuit.setTitle("Send \"Quit\"", forState: UIControlState.Normal)
buttonQuit.setTitleColor(UIColor.blueColor(), forState: UIControlState.Normal)
buttonQuit.setTitleColor(UIColor.cyanColor(), forState: UIControlState.Highlighted)
buttonQuit.addTarget(self, action: "btnQuitPressed:", forControlEvents: UIControlEvents.TouchUpInside)
buttonQuit.alpha = 0.3
buttonQuit.enabled = false
view.addSubview(buttonQuit)
}
func btnConnectPressed(sender: UIButton) {
NetworkEnable()
buttonConnect.alpha = 0.3
buttonConnect.enabled = false
}
func btnGetKey(sender: UIButton) {
let data : NSData = "Client: OK".dataUsingEncoding(NSUTF8StringEncoding)!
outStream?.write(UnsafePointer<UInt8>(data.bytes), maxLength: data.length)
}
func btnSendMsg(sender: UIButton) {
let message = "Secret message from iPhone!!"
let data = (message as NSString).dataUsingEncoding(NSUTF8StringEncoding)!
// tag name to access the stored public key in keychain
let TAG_PUBLIC_KEY = "com.mycompany.tag_public"
let encryptStr = "encrypted_message="
let encryptStrData = encryptStr.dataUsingEncoding(NSUTF8StringEncoding)!
let encryptedData = RSAUtils.encryptWithRSAPublicKey(data, pubkeyBase64: keyString, keychainTag: TAG_PUBLIC_KEY)!
let length = encryptStrData.length + encryptedData.length
var array = [UInt8](count: length, repeatedValue: 0)
encryptStrData.getBytes(&array, length: encryptStrData.length)
encryptedData.getBytes(&array+encryptStrData.length, length: encryptedData.length)
outStream?.write(UnsafePointer<UInt8>(array), maxLength: length)
buttonSendMsg.alpha = 0.3
buttonSendMsg.enabled = false
buttonQuit.alpha = 1.0
buttonQuit.enabled = true
label.text = "Encrypted message sent"
}
func btnQuitPressed(sender: UIButton) {
let data : NSData = "Quit".dataUsingEncoding(NSUTF8StringEncoding)!
outStream?.write(UnsafePointer<UInt8>(data.bytes), maxLength: data.length)
buttonQuit.alpha = 0.3
buttonQuit.enabled = false
}
//Label setup function
func LabelSetup() {
label = UILabel(frame: CGRectMake(0,0,300,150))
label.center = CGPointMake(view.center.x, view.center.y+100)
label.textAlignment = NSTextAlignment.Center
label.numberOfLines = 0 //Multi-lines
label.font = UIFont(name: "Helvetica-Bold", size: 20)
view.addSubview(label)
labelConnection = UILabel(frame: CGRectMake(0,0,300,30))
labelConnection.center = view.center
labelConnection.textAlignment = NSTextAlignment.Center
labelConnection.text = "Please connect to server"
view.addSubview(labelConnection)
}
//Network functions
func NetworkEnable() {
print("NetworkEnable")
NSStream.getStreamsToHostWithName(addr, port: port, inputStream: &inStream, outputStream: &outStream)
inStream?.delegate = self
outStream?.delegate = self
inStream?.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
outStream?.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
inStream?.open()
outStream?.open()
buffer = [UInt8](count: 2000, repeatedValue: 0)
}
func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) {
switch eventCode {
case NSStreamEvent.EndEncountered:
print("EndEncountered")
labelConnection.text = "Connection stopped by server"
label.text = ""
inStream?.close()
inStream?.removeFromRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
outStream?.close()
print("Stop outStream currentRunLoop")
outStream?.removeFromRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
buttonConnect.alpha = 1
buttonConnect.enabled = true
buffer.removeAll(keepCapacity: true)
keyString = ""
case NSStreamEvent.ErrorOccurred:
print("ErrorOccurred")
inStream?.close()
inStream?.removeFromRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
outStream?.close()
outStream?.removeFromRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
labelConnection.text = "Failed to connect to server"
buttonConnect.alpha = 1
buttonConnect.enabled = true
label.text = ""
case NSStreamEvent.HasBytesAvailable:
print("HasBytesAvailable")
if aStream == inStream {
inStreamLength = inStream!.read(&buffer, maxLength: buffer.count)
let bufferStr = NSString(bytes: &buffer, length: inStreamLength, encoding: NSUTF8StringEncoding) as! String
if keyString == "" {
label.text = "Public key received"
keyString = getKeyStringFromPEMString(bufferStr)
buttonGetKey.alpha = 0.3
buttonGetKey.enabled = false
buttonSendMsg.alpha = 1.0
buttonSendMsg.enabled = true
}
else {
print(bufferStr)
}
}
case NSStreamEvent.HasSpaceAvailable:
print("HasSpaceAvailable")
case NSStreamEvent.None:
print("None")
case NSStreamEvent.OpenCompleted:
print("OpenCompleted")
labelConnection.text = "Connected to server"
buttonGetKey.alpha = 1.0
buttonGetKey.enabled = true
default:
print("Unknown")
}
}
//Key function - remove header and footer
func getKeyStringFromPEMString(PEMString: String) -> String {
let keyArray = PEMString.componentsSeparatedByString("\n") //Remove new line characters
var keyOutput : String = ""
for item in keyArray {
if !item.containsString("-----") { //Example: -----BEGIN PUBLIC KEY-----
keyOutput += item //Join the text together as a single string
}
}
return keyOutput
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Result
Run both the socket server on Raspberry Pi and iOS simulator on Mac. Press the four app buttons from top to bottom. The terminal of Raspberry Pi should display the "Secret message from iPhone!!" message:
For more information about importing external keys into iOS, see this:
Reference:
Swift-RSAUtils on GitHub.
Go back to Communication between iOS device (Client) and Raspberry Pi (Server)
No comments:
Post a Comment