Monday, February 22, 2016

Conversion between String, NSString, NSData and [UInt8] array in Swift

Update - May 19, 2017 Swift 3.1 (Xcode 8.3.2)

The code below shows how to convert a string to a UInt8 array and then back to string. The code includes:

1. Step-by-step conversion between the String, NSString, NSData, and [UInt8] in this procedure:

String -> NSString -> NSData -> [UInt8] -> NSData -> NSString -> String

2. Direct conversion between String and [UInt8]:

String -> [UInt8] -> String

Swift Code:

Update - May 19, 2017 Swift 3.1 (Xcode 8.3.2)

let myString = "This is my string."


//1.Conversion between the String, NSString, NSData, and [UInt8]
//Convert String to NSString
let myNSString = myString as NSString

//Convert NSString to NSData
let myNSData = myNSString.data(using: String.Encoding.utf8.rawValue)!

//Convert NSData to [UInt8] array
let myArray = [UInt8](myNSData)

//Convert [UInt8] to NSData
let resultNSData = NSData(bytes: myArray, length: myArray.count)

//Convert NSData to NSString
let resultNSString = NSString(data: resultNSData as Data, encoding: String.Encoding.utf8.rawValue)!

//Convert NSString to String

let resultString = resultNSString as String

print(resultString)

//2.Direct conversion between the String and [UInt8]
//Directly convert string to [UInt8]
let directArray : [UInt8] = Array(myString.utf8)

//Directly convert [UInt8] to String
let directResultString = NSString(bytes: directArray, length: 
    directArray.count, encoding: String.Encoding.utf8.rawValue)! as String
print(directResultString)

Original Post: Feb. 22, 2016 with Swift 2.1



let myString = "This is my string."

//1.Conversion between the String, NSString, NSData, and [UInt8]
//Convert String to NSString
let myNSString = myString as NSString
        
//Convert NSString to NSData
let myNSData = myNSString.dataUsingEncoding(NSUTF8StringEncoding)!
        
//Get length of [UInt8]
let length = myNSData.length
        
//Convert NSData to [UInt8] array
var myArray = [UInt8](count: length, repeatedValue: 0)
myNSData.getBytes(&myArray, length: length)
        
//Convert [UInt8] to NSData
let resultNSData = NSData(bytes: &myArray, length: length)
        
//Convert NSData to NSString
let resultNSString = NSString(data: resultNSData, encoding: NSUTF8StringEncoding)!
        
//Convert NSString to String

let resultString = resultNSString as String

print(resultString)

//2.Direct conversion between the String and [UInt8]
//Directly convert string to [UInt8]
let directArray : [UInt8] = Array(myString.utf8)
        
//Directly convert [UInt8] to String
let directResultString = NSString(bytes: directArray, length: 
directArray.count, encoding: NSUTF8StringEncoding) as! String

Friday, February 19, 2016

NSSearchPathForDirectoriesInDomains - Read/Write a file in an iOS app

This example shows how to write a string to a storage file in an iOS app and then print the file content by reading. The app is built with Xcode 7.2.1 (Swift 2.1.1) and tested with the iPhone simulator. The generated text file is checked with Mac terminal commands.

1. The code:

let text = "The quick brown fox jumps over the lazy dog."
        
let file = "storage.txt"
        
if let paths : [String] = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true) {
    let path = paths[0].stringByAppendingString("/\(file)") #'/' character is required to locate in folder
            
    //Write
    try! text.writeToFile(path, atomically: false, encoding: NSUTF8StringEncoding)
            
    //Read
    let result = try! String(contentsOfFile: path, encoding: NSUTF8StringEncoding)
            
    print(path)    //Print the file directory
    print(result)  //Print the string in storage.txt
} else {
    print("Error")

}

2. Run the code with the simulator. The debug console result is:


/Users/xxx/Library/Developer/CoreSimulator/Devices/06....5A/data/Containers/Data/Application/D7....D8/Documents/storage.txt

The quick brown fox jumps over the lazy dog.

3. Open terminal and go to the directory shown in the debug console.

cd /Users/xxx/Library/Developer/CoreSimulator/Devices/06....5A/data/Containers/Data/Application/D7....D8/Documents/

4. Check the file with the nano command:

nano storage.txt


Related information:

Read/Write a file in Python

iOS: Store data in plist with NSFileManager

Thursday, February 18, 2016

Encrypt/decrypt a string with public/private keys imported from PEM files

To encrypt/decrypt a string in iOS using external public/private keys in PEM file format is not easy. I have spent several days on this topic. Finally I found that Swift-RSAUtils is a useful tool. Based on the RSAUtils.swift file in it, I have worked out my solution with the following steps:

1. Use OpenSSL in terminal to generate a RSA private and public key pair in PEM format:

Generate a 1024-bit private key:

openssl genrsa -out private_key.pem 1024

Obtain a public key from the private key:

openssl rsa -in private_key.pem -pubout -out public_key.pem

Convert the private key to PKCS#8 format:


openssl pkcs8 -topk8 -inform PEM -in private_key.pem -outform PEM -out private_key_pkcs8.pem -nocrypt

This format conversion is required because RSAUtils.swift works with PKCS#8 format, but not the traditional PKCS#1 format.

Header and footer of the traditional PKCS#1 format:


-----BEGIN RSA PRIVATE KEY-----

-----END RSA PRIVATE KEY-----

Header and footer of the PKCS#8 format:


-----BEGIN PRIVATE KEY-----

-----END PRIVATE KEY-----

2. Download Swift-RSAUtils. Find out the RSAUtils.swift file in the downloaded folder.

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

4. Add RSAUtils.swift and the two private/public PEM files ( private_key_pkcs8.pem and public_key.pem ) to the project.

5. Modify ViewController.swift as below:


import UIKit

class ViewController: UIViewController {
    
    // tag name to access the stored private key stored in keychain
    let TAG_PRIVATE_KEY = "com.mycompany.tag_private"
    
    // tag name to access the stored public key in keychain
    let TAG_PUBLIC_KEY = "com.mycompany.tag_public"

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let PUBLIC_KEY = getKeyStringFromPEM("public_key")
        let PRIVATE_KEY = getKeyStringFromPEM("private_key_pkcs8")

        let message = "This is my message."
        
        let data = (message as NSString).dataUsingEncoding(NSUTF8StringEncoding)!
        
        let encryptedData = RSAUtils.encryptWithRSAPublicKey(data, pubkeyBase64: PUBLIC_KEY, keychainTag: TAG_PUBLIC_KEY)!
        
        if let decryptedData = RSAUtils.decryptWithRSAPrivateKey(encryptedData, privkeyBase64: PRIVATE_KEY, keychainTag: TAG_PRIVATE_KEY) {
            
            let decryptedString = NSString(data: decryptedData, encoding: NSUTF8StringEncoding)!
            
            print(decryptedString)
        } else {
            print("Unable to decrypt.")
        }

    }

    func getKeyStringFromPEM(name: String) -> String {
        let bundle = NSBundle.mainBundle()

        let keyPath = bundle.pathForResource(name, ofType: "pem")!
        let keyString = try! NSString(contentsOfFile: keyPath, encoding: NSUTF8StringEncoding)
        let keyArray = keyString.componentsSeparatedByString("\n") //Remove new line characters
        
        var keyOutput : String = ""
        
        for item in keyArray {
            if !item.containsString("-----") { //Example: -----BEGIN PUBLIC KEY-----
                keyOutput += item //Join elements of the text array together as a single string
            }
        }
        return keyOutput
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

}

References:
Encrypt/decrypt a string with RSA public/private PEM files using Python
what is the differences between “BEGIN RSA PRIVATE KEY” and “BEGIN PRIVATE KEY”
Swift how to import rsa key
Encrypt/decrypt a string with code-generated RSA public/private keys in Swift
OpenSSL RSA commands to encrypt/decrypt a message in terminal (Mac)

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