Showing posts with label security. Show all posts
Showing posts with label security. Show all posts

Friday, March 4, 2016

Communication between iOS device (Client) and Raspberry Pi (Server)

To learn the client-server communication, a practical approach is to setup the connection between an iOS device (Client) and a Raspberry Pi (Server):

Client: iOS device with app developed in Swift
Server: Raspberry Pi with socket server programmed in Python

Requirements

1. Setup a Raspberry Pi

2. Mac + Xcode + iPhone/iPad

===============

The three sections below will explain how to:
1. Setup the basic client-server socket communication architecture.
2. Encrypt/decrypt a message with RSA public/private keys.
3. Combine sections 1 and 2 to setup the secure socket communication.

1. Basic Socket Communication

The basic form of client-server communication between an iPhone/iPad and a Python socket server built on a Raspberry Pi is shown in this tutorial:

Connect an iPhone to a Simple Python Socket Server (Raspberry Pi Part) (iOS Part)

The above tutorial must be fully understood, because it is the basis for the tutorials in the 3. Secure Socket Communication section.

2. RSA

The data transmitted between the client and the server in the above example are not encrypted and hence are insecure. Therefore, using RSA (Rivest-Shamir-Adleman) cryptosystem for secure communications is a good topic to be studied for understanding asymmetric public-private key cryptosystem.

All examples below follow this procedure:

1. Generate or obtain an RSA private and public key pair.
2. Encrypt a message with the public key.
3. Decrypt the encrypted message with the private key.

2.1 Basic OpenSSL commands

In order to understand how to use RSA keys for encryption/decryption, try the basic OpenSSL RSA commands: 

OpenSSL RSA commands to encrypt/decrypt a message in terminal (Raspberry Pi and Mac)

You should notice that RSA uses different keys of the same key pair for encrypting and decrypting a message. This is known as asymmetric since different keys are used.

Now let's start programming with RSA keys!!

2.2 Importing External RSA keys

Here are Python (Raspberry Pi) and Swift (iOS) examples showing how to import external OpenSSL-generated RSA keys in PEM file format:

Encrypt/decrypt a string with external public/private keys (Raspberry Pi Part) (iOS Part)

2.3 Using Code-generatd RSA keys

External RSA key files are fixed. If different key pairs are required while the program is running, try to generate keys in Python/Swift code:

Encrypt/decrypt a string with code-generated public/private keys (Raspberry Pi Part) (iOS Part)

Now let's move on to implement code-generated RSA keys in the client-server architecture.

3. Secure Socket Communication

Remember that:

Client: iOS device with app developed in Swift
Server: Raspberry Pi with socket server programmed in Python

There are two tutorials in this section. Both of them will explain how to transmit an encrypted message between a client and a server. The difference between the two tutorials is the opposite direction of data transmission. The principle of sending encrypted message is as below:

1. The receiving device generates an RSA private-public key pair and sends the public key to the transmitting device.
2. The transmitting device encrypts a message using the public key provided by the receiving device and sends the encrypted message to the receiving device.
3. The receiving device decrypts the encrypted message from the transmitting device using the private key.

The two tutorials with different directions of transmitting encrypted data are as below:

1. From iOS device to Python socket server (Raspberry Pi Part) (iOS Part)
2. From Python socket server to iOS device (Raspberry Pi Part) (iOS Part)

Or in terms of encrypt/decrypt in Python/Swift programming languages:

1. Encrypt in Swift and decrypt in Python (Raspberry Pi Part) (iOS Part)
2. Encrypt in Python and decrypt in Swift (Raspberry Pi Part) (iOS Part)

Note:

For Tutorial 2. Encrypt in Python and decrypt in Swift (iOS Part), you need to understand how to manage the iOS keychain in this tutorial:

Store/Retrieve/Delete RSA public/private keys with keychain (iOS Swift)

More Information:

Crypto 101 ebook (pdf)

Sending RSA encrypted message - From Python socket server to iOS device (iOS Part)

Secure Data Transmission: Raspberry Pi -> iOS device

Secure communication between a mobile device client and a server is important. This tutorial selects an iPhone as the server and uses a Raspberry Pi as the client. The communication procedure is as below:

1. iPhone generates an RSA private / public key pair and sends the public key to server.
2. Server encrypts a message using the public key provided by iPhone and sends the encrypted message to iPhone
3. iPhone decrypts the encrypted message from server 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 Python socket server to iOS device (Raspberry Pi Part)

iOS Part

The Xcode version used is 7.2.1 (Swift 2.1.1).

1. Download CryptoExportImportManager. Find out the CryptoExportImportManager.swift file in the downloaded folder.

2. Create a new Single View Application project in Xcode.

3. Add CryptoExportImportManager.swift to the project just created.

4. Modify ViewController.swift as below:

import UIKit

class ViewController: UIViewController, NSStreamDelegate {
    
    //Button
    var buttonConnect    : UIButton!
    var buttonSendKey     : UIButton!
    var buttonDecryptMsg : 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: 200, repeatedValue: 0)
    
    var inStreamLength : Int!
    
    //Key variables and constants
    var publicKey, privateKey: SecKey?
    var publicKeyString = ""
    let keySize = 1024
    let tagPrivate = "com.mycompany.tagPrivate"
    let tagPublic  = "com.mycompany.tagPublic"
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        ButtonSetup()
        
        LabelSetup()
        
        KeySetup()
    }
    
    //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)
        
        buttonSendKey = UIButton(frame: CGRectMake(20, 100, 300, 30))
        buttonSendKey.setTitle("Send public key to server", forState: UIControlState.Normal)
        buttonSendKey.setTitleColor(UIColor.blueColor(), forState: UIControlState.Normal)
        buttonSendKey.setTitleColor(UIColor.cyanColor(), forState: UIControlState.Highlighted)
        buttonSendKey.addTarget(self, action: "btnSendKey:", forControlEvents: UIControlEvents.TouchUpInside)
        buttonSendKey.alpha = 0.3
        buttonSendKey.enabled = false
        view.addSubview(buttonSendKey)
        
        buttonDecryptMsg = UIButton(frame: CGRectMake(20, 150, 300, 30))
        buttonDecryptMsg.setTitle("Decrypt message", forState: UIControlState.Normal)
        buttonDecryptMsg.setTitleColor(UIColor.blueColor(), forState: UIControlState.Normal)
        buttonDecryptMsg.setTitleColor(UIColor.cyanColor(), forState: UIControlState.Highlighted)
        buttonDecryptMsg.addTarget(self, action: "btnDecryptMsg:", forControlEvents: UIControlEvents.TouchUpInside)
        buttonDecryptMsg.alpha = 0.3
        buttonDecryptMsg.enabled = false
        view.addSubview(buttonDecryptMsg)
        
        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 btnSendKey(sender: UIButton) {
        let publicKeyTypeStr = "public_key=\(publicKeyString)"
        let data : NSData = publicKeyTypeStr.dataUsingEncoding(NSUTF8StringEncoding)!
        outStream?.write(UnsafePointer<UInt8>(data.bytes), maxLength: data.length)
    }
    func btnDecryptMsg(sender: UIButton) {

        let dataEncryptedSize : Int = inStreamLength

        var arrayDecrypted = [UInt8](count: dataEncryptedSize, repeatedValue: 0)
        var arrayDecryptedSize = dataEncryptedSize

        let status = SecKeyDecrypt(privateKey!, SecPadding.PKCS1, &buffer, dataEncryptedSize, &arrayDecrypted, &arrayDecryptedSize)

        if status == errSSLCrypto {
            print("errSSLCrypto") //-9809 error
            return
        } else if status != noErr {
            print("Decryption Error! \(status.description)")
            return
        }
        let messageStr = NSString(bytes: &arrayDecrypted, length: arrayDecryptedSize, encoding: NSUTF8StringEncoding)!

        label.text = "Server sent:\n\(messageStr)"
        print("Decrypt successfully:\n\(messageStr)")
        
        buttonDecryptMsg.alpha = 0.3
        buttonDecryptMsg.enabled = false
        buttonQuit.alpha = 1
        buttonQuit.enabled = true
    }
    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)
    }
    
    //Get keys from keychain or generate new keys
    func KeySetup() {
        
        //Reset keychain
        //deleteAllKeysInKeyChain()
        
        if (GetKeysFromKeychain()) {
            print("Key pair retrieved successfully!")
        } else {
            print("No valid key pair")
            deleteAllKeysInKeyChain()
            GenerateKeyPair()
        }
        GetPublicKeyStr()
    }

    //Other key functions - Check Keychain and get keys

    func GetKeysFromKeychain() -> Bool {
        privateKey = GetKeyTypeInKeyChain(tagPrivate)
        publicKey = GetKeyTypeInKeyChain(tagPublic)
        return ((privateKey != nil)&&(publicKey != nil))
    }
    
    func GetKeyTypeInKeyChain(tag : String) -> SecKey? {
        let query: [String: AnyObject] = [
            String(kSecClass)             : kSecClassKey,
            String(kSecAttrKeyType)       : kSecAttrKeyTypeRSA,
            String(kSecAttrApplicationTag): tag,
            String(kSecReturnRef)         : true
        ]
        
        var result : AnyObject?
        
        let status = SecItemCopyMatching(query, &result)
        
        if status == errSecSuccess {
            print("\(keyTypeStr(tag)) Key existed!")
            return result as! SecKey?
        }
        print("no \(keyTypeStr(tag)) key")
        
        return nil
    }
    
    //Get public key string from keychain
    func GetPublicKeyStr() {
        
        var unsafeData : AnyObject?
        
        let parameters = [
            kSecClass as String: kSecClassKey,
            kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
            kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
            kSecReturnData as String: true
        ]
        
        //Get public key string
        let status = SecItemCopyMatching(parameters, &unsafeData)
        
        if status != noErr {
            print("Get Key Error!")
            return
        }
        
        //Need CryptoExportImportManager.swift from GitHub.
        let manager = CryptoExportImportManager()
        publicKeyString = manager.exportRSAPublicKeyToPEM(unsafeData as! NSData, keyType: kSecAttrKeyTypeRSA as String, keySize: keySize)
        //print(publicKeyString) //Check this with server
    }
    
    //Generate private and public keys
    func GenerateKeyPair() {
        let privateKeyAttr: [NSString: AnyObject] = [
            kSecAttrIsPermanent: true,
            kSecAttrApplicationTag: tagPrivate
        ]
        let publicKeyAttr: [NSString: AnyObject] = [
            kSecAttrIsPermanent: true,
            kSecAttrApplicationTag: tagPublic
        ]
        
        let parameters: [String: AnyObject] = [
            kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
            kSecAttrKeySizeInBits as String: keySize,
            kSecPrivateKeyAttrs as String: privateKeyAttr,
            kSecPublicKeyAttrs as String: publicKeyAttr
        ]
        
        //Generate new keys and store them in keychain
        let status = SecKeyGeneratePair(parameters, &publicKey, &privateKey)
        
        if status != noErr {
            print("SecKeyGeneratePair Error! \(status.description)")
            return
        }
        
        print("Key pair generated successfully")
    }
    
    //key type string
    func keyTypeStr(tag: String) -> String {
        return tag.stringByReplacingOccurrencesOfString("com.mycompany.tag", withString: "")
    }
    
    //Delete keys when required.
    func deleteAllKeysInKeyChain() {
        
        let query : [String: AnyObject] = [
            String(kSecClass)             : kSecClassKey
        ]
        let status = SecItemDelete(query)
        
        switch status {
        case errSecItemNotFound:
            print("No key in keychain")
        case noErr:
            print("All Keys Deleted!")
        default:
            print("SecItemDelete error! \(status.description)")
        }
    }
    
    //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: 200, 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)

        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)
                
                //If data received
                if inStreamLength > 0 {
                    label.text = "Encrypted message received!"
                
                    buttonSendKey.alpha = 0.3
                    buttonSendKey.enabled = false
                    buttonDecryptMsg.alpha = 1.0
                    buttonDecryptMsg.enabled = true
                }
            }
            
        case NSStreamEvent.HasSpaceAvailable:
            print("HasSpaceAvailable")
        case NSStreamEvent.None:
            print("None")
        case NSStreamEvent.OpenCompleted:
            print("OpenCompleted")
            labelConnection.text = "Connected to server"
            buttonSendKey.alpha = 1.0
            buttonSendKey.enabled = true
        default:
            print("Unknown")
        }
    }
    
    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 message "Secret message from RPi server" sent by Raspberry Pi should be displayed on the iOS app and Xcode debug console:


Reference:

CryptoExportImportManager on GitHub.

Go back to Communication between iOS device (Client) and Raspberry Pi (Server)