Utility class to encrypt/decrypt data using RSA private/public key.
Objective-C version can be found at https://github.com/ideawu/Objective-C-RSA
Note: if you receive error status -34018 on Xcode8 / iOS 10, enable Keychain Sharing. See: http://stackoverflow.com/questions/38456471/secitemadd-always-returns-error-34018-in-xcode-8-in-ios-10-simulator/38543243#38543243
This library is to solve the following problem (actually mine):
- We already had a server that provides APIs for clients to call. Data has to be encrypted using RSA, and the server had already generated a pair of RSA public/private key for each client.
- We had to implement an iOS client for the server's APIs. It seemed to be a simple task at first, but it soon turned out not so:
- In Apple's Security framework all key management happens at the keychain level. It means existing key(s) must be imported to keychain in order to be usable. Using the key-pair generated by
SecKeyGeneratePair
was easy. Importing an existing key (in file, or as a string), however, turned out to be tricky. - There was a solution in Objective-C but we needed a pure-Swift one and there was no good pure-Swift implementation at the time.
- The amount of data that can be encrypted in one go is limitted by the key's length. If a large amount of data needs to be encrypted, it has to be processed chunk by chunk.
- In Apple's Security framework all key management happens at the keychain level. It means existing key(s) must be imported to keychain in order to be usable. Using the key-pair generated by
This small library addresses all 3 problems above.
Notes:
Public key: only
X509
public key is supported. On disk,X509
public key file begins with-----BEGIN PUBLIC KEY-----
and ends with-----END PUBLIC KEY-----
.Private key:
PKCS#1
andPKCS#8
private keys are supported. On disk,PKCS#8 private key
file begins with-----BEGIN PRIVATE KEY-----
and ends with-----END PRIVATE KEY-----
,PKCS#1 private key
file begins with-----BEGIN RSA PRIVATE KEY-----
and ends with-----END RSA PRIVATE KEY-----
.
Obtain PKCS#8 private key
and X509 public key
:
Usually you have the PKCS#1 private key
in a .pem
file (this file starts with -----BEGIN RSA PRIVATE KEY-----
and ends with -----END RSA PRIVATE KEY-----
). To convert to PKCS#8
format, use the following command:
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in privkey.pem -out privkey.pkcs8.pem
You can extract X509 public key
from a PEM private key by using the following command:
openssl rsa -pubout -in privkey.pem -out pubkey.x509.pem
or
openssl rsa -pubout -in privkey.pkcs8.pem -out pubkey.x509.pem
Example of a PKCS#1 private key
:
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDAF4nCtUoppj4taUW7T4+aGhf45lGR8mAvVnKHufTzPaNinYzl
mkLF7I7dk0Tgc3KoACJ6I2NC//mhEpNErX0MAqVlCMbAgWvUNSFvHByaqC/5o5hu
k3CwvERFho2IHmSMMTasMG6PoOhto3KPF9AxszkRCsY3xnS5JURE/4h4DQIDAQAB
AoGAcwOk8Hgr15Q6VmZZ4jVY/iPxho4g+QnunWldWfb1u06ErV84JaGfqXJieDjd
XKbDV+P8wLS/kjZB+TduBgj6fVTqTdL26sBsAhTiYTBK4UoXwRMg14dbXJrsnH5n
KUU9GFslFQefRaWn4mjvaMFkUqRsrLkM2nmgBXDnmJLdmwECQQDd40S4EuwoLFYW
xDiWpdCn2nA6n7SBp2xa26Az7bZog6KcDxGgNPY1Z1+PGnnOw6Lcgmx2LOZS4g4S
WwzjnuQ1AkEA3Z+cFFaGll9di5J2BWlQNKzv5UeSMZqvspxsd2VDJXmWs2PvqEHd
BzU4WW8WsHWyoycOY0R710t8JcWCrMGPeQJBAIl6rTNYFhZ2EgkdHurIZlX6FBte
pJrIv2w0NDi4ipKLLQ+Ajq0y43IHUL/76Yjg0mHKNaWrADJOeeWJoJzP8BkCQQCo
zHSYmP6RcblSIQ97N6c7N6zQOR8EYQkJRVLn7VyjImTB5ZAX23J5lvOASrhBVqPk
2E6BFRwt8vRv4GuTISohAkAImxaeC1+jiRGN+EtsNDSmgNjILg5Tyteor6t3guTR
uMv2Y8tJiCh7UTguUnWiDo/RLrDjuvoE4lLpbyAvariY
-----END RSA PRIVATE KEY-----
It's PKCS#8 private key
format:
-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMAXicK1SimmPi1p
RbtPj5oaF/jmUZHyYC9Wcoe59PM9o2KdjOWaQsXsjt2TROBzcqgAInojY0L/+aES
k0StfQwCpWUIxsCBa9Q1IW8cHJqoL/mjmG6TcLC8REWGjYgeZIwxNqwwbo+g6G2j
co8X0DGzOREKxjfGdLklRET/iHgNAgMBAAECgYBzA6TweCvXlDpWZlniNVj+I/GG
jiD5Ce6daV1Z9vW7ToStXzgloZ+pcmJ4ON1cpsNX4/zAtL+SNkH5N24GCPp9VOpN
0vbqwGwCFOJhMErhShfBEyDXh1tcmuycfmcpRT0YWyUVB59FpafiaO9owWRSpGys
uQzaeaAFcOeYkt2bAQJBAN3jRLgS7CgsVhbEOJal0KfacDqftIGnbFrboDPttmiD
opwPEaA09jVnX48aec7DotyCbHYs5lLiDhJbDOOe5DUCQQDdn5wUVoaWX12LknYF
aVA0rO/lR5Ixmq+ynGx3ZUMleZazY++oQd0HNThZbxawdbKjJw5jRHvXS3wlxYKs
wY95AkEAiXqtM1gWFnYSCR0e6shmVfoUG16kmsi/bDQ0OLiKkostD4COrTLjcgdQ
v/vpiODSYco1pasAMk555YmgnM/wGQJBAKjMdJiY/pFxuVIhD3s3pzs3rNA5HwRh
CQlFUuftXKMiZMHlkBfbcnmW84BKuEFWo+TYToEVHC3y9G/ga5MhKiECQAibFp4L
X6OJEY34S2w0NKaA2MguDlPK16ivq3eC5NG4y/Zjy0mIKHtROC5SdaIOj9EusOO6
+gTiUulvIC9quJg=
-----END PRIVATE KEY-----
And it's X509 public key
:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDAF4nCtUoppj4taUW7T4+aGhf4
5lGR8mAvVnKHufTzPaNinYzlmkLF7I7dk0Tgc3KoACJ6I2NC//mhEpNErX0MAqVl
CMbAgWvUNSFvHByaqC/5o5huk3CwvERFho2IHmSMMTasMG6PoOhto3KPF9AxszkR
CsY3xnS5JURE/4h4DQIDAQAB
-----END PUBLIC KEY-----
Functions:
Key management functions:
static func getRSAKeyFromKeychain(_ tagName: String) -> SecKey?
: Gets an existing RSA key specified by a tag from keychain.static func deleteRSAKeyFromKeychain(_ tagName: String)
: Deletes an existing RSA key specified by a tag from keychain.static func addRSAPrivateKey(_ privkeyBase64: String, tagName: String) throws -> SecKey?
: Adds a RSA private key to keychain and returns its SecKey reference.- The private key is in base-64 PKCS#1 or PKCS#8 format.
- Throws
RSAUtilsError
if the input key is not a valid PKCS#8 private key. -----BEGIN PRIVATE KEY-----
,-----BEGIN RSA PRIVATE KEY-----
,-----END PRIVATE KEY-----
and-----END RSA PRIVATE KEY-----
can be omitted.
static func addRSAPublicKey(_ pubkeyBase64: String, tagName: String) throws -> SecKey?
: Adds a RSA public key to keychain and returns its SecKey reference.- The public key is in base-64 X509 format.
- Throws
RSAUtilsError
if the input key is not a valid X509 public key. -----BEGIN PUBLIC KEY-----
and-----END PUBLIC KEY-----
can be omitted.
Encryption/Decryption functions:
static func encryptWithRSAKey(data: Data, tagName: String) -> Data?
: Encrypts data using a RSA key from keychain specified bytagName
.static func encryptWithRSAKey(str: String, tagName: String) -> Data?
: Encrypts a string using a RSA key from keychain specified bytagName
.static func decryptWithRSAKey(encryptedData: Data, tagName: String) -> Data?
: Decrypts an encrypted data using a RSA key from keychain specified bytagName
.static func encryptWithRSAPublicKey(data: Data, pubkeyBase64: String) throws -> Data?
: Encrypts data using RSA public key.- The public key is in base-64 X509 format.
- Throws
RSAUtilsError
if the input key is not a valid X509 public key. -----BEGIN PUBLIC KEY-----
and-----END PUBLIC KEY-----
can be omitted.
static func encryptWithRSAPublicKey(str: String, pubkeyBase64: String) throws -> Data?
: Encrypts a string using RSA public key.static func encryptWithRSAPublicKey(data: Data, pubkeyBase64: String, tagName: String) throws -> Data?
: Encrypts data using RSA public key. Also, the public key will be stored in keychain specified bytagName
.static func encryptWithRSAPublicKey(str: String, pubkeyBase64: String, tagName: String) throws -> Data?
: Encrypts a string using RSA public key. Also, the public key will be stored in keychain specified bytagName
.static func decryptWithRSAPrivateKey(encryptedData: Data, privkeyBase64: String) throws -> Data?
: Decrypts an encrypted data using a RSA private key.static func decryptWithRSAPrivateKey(encryptedData: Data, privkeyBase64: String, tagName: String) throws -> Data?
: Decrypts an encrypted data using a RSA private key. Also, the private key will be stored in keychain specified bytagName
.
The amount of data that can be encrypted in one go is key's size - 11
. For example, if the key's size is 256 bytes (2048 bits), maximum amount of data that can be encrypted is 245 bytes.
In order to encrypt a large amount of data, encryptXXX()
function splits the large data into chunks of size key's size - 11
, encrypts each chunk and concatenates encrypted chunks into the final result.
Decryption performs similarly: encrypted data is splitted into chunks of size key's size
, each chunk is then decrypted and concatenated into the final result.
- Since v1.2.1 - 2016-09-18: add support for RSA PKCS#1 private key.
- Since v1.2.0 - 2016-09-17: migrated from https://github.com/btnguyen2k/swift-rsautils.