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)

6 comments:

  1. Thank you for the tutorial Enoch!
    I'm guessing if the let message = " " is changed to let message = myTextField.tex, then you can encrypt the text from text field? And also, if print(decryptedString) is changed to outputTextLabel.text = decryptedString, the decrypted text will be displayed?
    I'm sorry for the dumb questions, but I'm not advanced with swift.

    And thanks again for the tutorial Enoch!

    ReplyDelete
  2. I forgot one more thing. When you convert the code to Swift 3 the last line: if !item.containsString("-----") { //Example: -----BEGIN PUBLIC KEY-----
    gives me an error. How should I change it so I don't get error?

    ReplyDelete
  3. thank you . very much ..for this very useful article

    ReplyDelete
  4. This comment has been removed by the author.

    ReplyDelete
  5. Why the below code is require?

    // 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"

    I tried put some random string as TAG_PUBLIC_KEY and it gave me the same encrypted string.
    Here is my repo https://github.com/anirudhamahale/RSA-Encryption

    and how do I tag the pem file in keychain Access?

    ReplyDelete
  6. Is there any option to use only public key for encryption and decryption?

    ReplyDelete