BlueRSA
Swift cross-platform RSA wrapper library for RSA encryption and signing. Works on supported Apple platforms (using Security framework). Linux (using OpenSSL) is working but is still somewhat of a work in progress.
Test jazzy docs build 2. – do generate docs.
Test jazzy docs build 3. – pr request merge comment.
Contents
- CryptorRSA: Utility functions for RSA encryption and signing. Pure Swift
Prerequisites
Swift
- Swift Open Source
swift-5.1.3-RELEASEtoolchain (Minimum REQUIRED for latest release) - Swift toolchain included in Xcode Version 11.3 or higher.
macOS
- Xcode Version 11.3 or higher using the included toolchain (Recommended).
iOS
- iOS 10.3 or higher
Linux
- Ubuntu tested on 18.04 but 16.04 and 20.04 should also be compatible.
- Swift Open Source
swift-5.1.3-RELEASEtoolchain (Minimum REQUIRED for latest release) - OpenSSL is provided by the distribution. Note: 1.0.x, 1.1.x and later releases of OpenSSL are supported.
- The appropriate libssl-dev package is required to be installed when building.
Build
To build CryptorRSA from the command line:
% cd <path-to-clone>
% swift build
Testing
To run the supplied unit tests for CryptorRSA from the command line:
% cd <path-to-clone>
% swift build
% swift test
Using CryptorRSA
Including in your project
Swift Package Manager
To include BlueRSA into a Swift Package Manager package, add it to the dependencies attribute defined in your Package.swift file. You can select the version using the majorVersion and minor parameters. For example:
dependencies: [
.Package(url: "https://github.com/Kitura-Next/BlueRSA", majorVersion: <majorVersion>, minor: <minor>)
]
Carthage
To include BlueRSA in a project using Carthage, add a line to your Cartfile with the GitHub organization and project names and version. For example:
github "Kitura-Next/BlueRSA" ~> <majorVersion>.<minor>
Before starting
The first you need to do is import the CryptorRSA framework. This is done by the following:
import CryptorRSA
Data Types
BlueRSA supports the following major data types:
Key Handling
CryptorRSA.PublicKey- Represents an RSA Public Key.CryptorRSA.PrivateKey- Represents an RSA Private Key.
Data Handling
CryptorRSA.EncryptedData- Represents encrypted data.CryptorRSA.PlaintextData- Represents plaintext or decrypted data.CryptorRSA.SignedData- Represents signed data.
Key Handling
BlueRSA provides seven (7) functions each for creating public and private keys from data. They are as follows (where createXXXX is either createPublicKey or createPrivateKey depending on what you’re trying to create):
CryptorRSA.createXXXX(with data: Data) throws- This creates either a private or public key containing the data provided. It is assumed that the data being provided is in the proper format.CryptorRSA.createXXXX(withBase64 base64String: String) throws- This creates either a private or public key using theBase64 encoded Stringprovided.CryptorRSA.createXXXX(withPEM pemString: String) throws- This creates either a private or public key using thePEM encoded Stringprovided.CryptorRSA.createXXXX(withPEMNamed pemName: String, onPath path: String) throws- This creates either a private or public key using thePEM encoded filepointed at by thepemNameand located on the path specified bypathprovided.CryptorRSA.createXXXX(withDERNamed derName: String, onPath path: String) throws- This creates either a private or public key using theDER encoded filepointed at by thederNameand located on the path specified bypathprovided.CryptorRSA.createXXXX(withPEMNamed pemName: String, in bundle: Bundle = Bundle.main) throws- This creates either a private or public key using thePEM encoded filepointed at by thepemNameand located in theBundlespecified bybundleprovided. By default this API will look in themainbundle. Note: Apple Platforms OnlyCryptorRSA.createXXXX(withDERNamed derName: String, in bundle: Bundle = Bundle.main) throws- This creates either a private or public key using theDER encoded filepointed at by thederNameand located in theBundlespecified bybundleprovided. By default this API will look in themainbundle. Note: Apple Platforms Only
Additionally, there are three APIs for creating a public key by extracting the key from a PEM formatted certificate: They are:
CryptorRSA.createPublicKey(extractingFrom data: Data) throws- This creates either a public key by extracting from thePEM encoded certificatepointed at by thedata.CryptorRSA.createPublicKey(extractingFrom certName: String, onPath path: String) throws- This creates a public key by extracting from thePEM encoded certificatepointed at by thecertNameand located on the path specified bypathprovided.CryptorRSA.createPublicKey(extractingFrom certName: String, in bundle: Bundle = Bundle.main) throws- This creates a public key using thePEM encoded certificatepointed at by thederNameand located in theBundlespecified bybundleprovided. By default this API will look in themainbundle. Note: Apple Platforms Only
Example
The following example illustrates creating a public key given PEM encoded file located on a certain path. *Note: Exception handling omitted for brevity.
import Foundation
import CryptorRSA
...
let keyName = ...
let keyPath = ...
let publicKey = try CryptorRSA.createPublicKey(withPEMNamed: keyName, onPath: keyPath)
...
<Do something with the key...>
Data Encryption and Decryption Handling
BlueRSA provides functions for the creation of each of the three (3) data handling types:
Plaintext Data Handling and Signing
There are two class level functions for creating a PlaintextData object. These are:
CryptorRSA.createPlaintext(with data: Data) -> PlaintextData- This function creates aPlaintextDatacontaining the specifieddata.CryptorRSA.createPlaintext(with string: String, using encoding: String.Encoding) throws -> PlaintextData- This function creates aPlaintextDataobject using thestringencoded with the specifiedencodingas the data.
Once the PlaintextData object is created, there are two instance functions that can be used to manipulate the contained data. These are:
encrypted(with key: PublicKey, algorithm: Data.Algorithm) throws -> EncryptedData?- This function allows you to encrypt containing data using the publickeyandalgorithmspecified. This function returns an optionalEncryptedDataobject containing the encryped data.signed(with key: PrivateKey, algorithm: Data.Algorithm) throws -> SignedData?- This function allows you to sign the contained data using the privatekeyandalgorithmspecified. This function returns an optionalSignedDataobject containing the signature of the signed data.
Example
- Encryption: Note: Exception handling omitted for brevity.
import Foundation
import CryptorRSA
...
let keyName = ...
let keyPath = ...
let myData: Data = <... Data to be encrypted ...>
let publicKey = try CryptorRSA.createPublicKey(withPEMNamed: keyName, onPath: keyPath)
let myPlaintext = CryptorRSA.createPlaintext(with: myData)
let encryptedData = try myPlaintext.encrypt(with: publicKey, algorithm: .sha1)
...
< Do something with the encrypted data...>
- Signing: Note: Exception handling omitted for brevity.
import Foundation
import CryptorRSA
...
let keyName = ...
let keyPath = ...
let myData: Data = <... Data to be signed ...>
let privateKey = try CryptorRSA.createPrivateKey(withPEMNamed: keyName, onPath: keyPath)
let myPlaintext = CryptorRSA.createPlaintext(with: myData)
let signedData = try myPlaintext.signed(with: privateKey, algorithm: .sha1)
...
< Do something with the signed data...>
Encrypted Data Handling
There are two class level functions for creating a EncryptedData object. These are:
CryptorRSA.createEncrypted(with data: Data) -> EncryptedData- This function creates aEncryptedDatacontaining the specified encrypteddata.CryptorRSA.createEncrypted(with base64String: String) throws -> EncryptedData- This function creates aEncrpytedDatausing the Base64 representation of already encrypted data.
Once the EncryptedData object is created, there is an instance function that can be used to decrypt the enclosed data:
decrypted(with key: PrivateKey, algorithm: Data.Algorithm) throws -> DecryptedData?- This function allows you to decrypt containing data using the publickeyandalgorithmspecified. This function returns an optionalDecryptedDataobject containing the encryped data.
BlueRSA currently supports OAEP padding, which is the recommended padding algorithm.
Example
- Decryption: Note: Exception handling omitted for brevity.
import Foundation
import CryptorRSA
...
let keyName = ...
let keyPath = ...
let publicKey = try CryptorRSA.createPublicKey(withPEMNamed: keyName, onPath: keyPath)
let pkeyName = ...
let pkeyPath = ...
let privateKey = try CryptorRSA.createPrivateKey(withPEMNamed: pkeyName, onPath: pkeyPath)
let myData: Data = <... Data to be encrypted ...>
let myPlaintext = CryptorRSA.createPlaintext(with: myData)
let encryptedData = try myPlaintext.encrypt(with: publicKey, algorithm: .sha1)
let decryptedData = try encryptedData.decrypt(with: privateKey, algorithm: .sha1)
...
< Do something with the decrypted data...>
Signature Verification Handling
There is a single class level function that can be used to create a SignedData object. It is:
CryptorRSA.createSigned(with data: Data) -> SignedData- This function creates aSignedDatacontaining the specified signeddata.
Once created or obtained PlaintextData and SignedData, there is an instance function which can be used to verify the signature contained therein:
verify(with key: PublicKey, signature: SignedData, algorithm: Data.Algorithm) throws -> Bool- This function is used to verify, using the publickeyandalgorithm, thesignature. Returns true if the signature is valid, false otherwise.Verifying: Note: Exception handling omitted for brevity.
import Foundation
import CryptorRSA
...
let keyName = ...
let keyPath = ...
let publicKey = try CryptorRSA.createPublicKey(withPEMNamed: keyName, onPath: keyPath)
let pkeyName = ...
let pkeyPath = ...
let privateKey = try CryptorRSA.createPrivateKey(withPEMNamed: pkeyName, onPath: pkeyPath)
let myData: Data = <... Data to be signed ...>
let myPlaintext = CryptorRSA.createPlaintext(with: myData)
let signedData = try myPlaintext.signed(with: privateKey, algorithm: .sha1)
if try myPlaintext.verify(with: publicKey, signature: signedData, algorithm: .sha1) {
print("Signature verified")
} else {
print("Signature Verification Failed")
}
Data Type Utility Functions
All three of the data handling types have two common utility instance functions. These are:
digest(using algorithm: Data.Algorithm) throws -> Data- This function returns aDataobject containing a digest constructed using the specifiedalgorithm.string(using encoding: String.Encoding) throws -> String- This functions returns aStringrepresentation of the data using the specifiedencoding.
Community
We love to talk server-side Swift and Kitura. Join our Slack to meet the team!
License
This library is licensed under Apache 2.0. Full license text is available in LICENSE.
CryptorRSA Reference