I have created a public/private key pair (Elliptic Curve) with SecKeyGeneratePair
.
How can I use the SecKey
instances to generate a CSR using OpenSSL in Swift?
As far as I can tell Apple's own Security framework does not have an API currently exposed f or generating CSR's. It is technically OpenSSL wrapped; so in case you do have OpenSSL ( I personally prefer LibreSSL, libtls makes life easy ).
As an alternative you can also use Commoncrypto, which my answer won't cover but there are many many examples out there
So lets go through the steps of finding out how to do this. I found that the easiest way to work with OpenSSL
is to ignore the documentation completely and go straight to reading source.
To achieve what we need, we need to 'replicate' the following 2 commands:
openssl ecparam -out server.key -name prime256v1 -genkey
openssl req -new -key server.key -out server.csr
Important Note: Be sure to pick a secure curve. Use openssl ecparam -list_curves to get a list of supported curves by your version of openssl. Read more
Step 1 Generating the key
ecparam
tells us we have to look in sslsource/apps/openssl/ecparam.c first.
The source gives us 3 steps: Set up BIO for writing, setting up group parameters and finally generating the key.
Step 2 Generating the CSR
Following the same principles but this time looking in req.c
and some code in apps.c
which contains some boilerplate code used in req.c
I used this method to create a crude but working proof of concept that runs in swift, you can check it out on github here: CertificateTool The code will run on any system you can compile the swift4+ toolchain on and some version of openSSL.
Edit:
Generating the EC Secret key:
//
// ECKey.swift
// CertificateToolPackageDescription
//
// Created by Antwan van Houdt on 10/01/2018.
//
import CLibreSSL
public class ECKey {
internal let secretKey: OpaquePointer
private let group: OpaquePointer
public init() {
group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)
EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE)
EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_COMPRESSED)
secretKey = EC_KEY_new()
EC_KEY_set_group(secretKey, group)
EC_KEY_generate_key(secretKey)
}
deinit {
EC_KEY_free(secretKey)
EC_GROUP_free(group)
}
}
Creating the signing request:
//
// CertificateRequest.swift
// CertificateToolPackageDescription
//
// Created by Antwan van Houdt on 10/01/2018.
//
import CLibreSSL
public enum NIDType: String {
case email = "emailAddress"
case hostName = "CN"
case organizationalUnit = "OU"
case organization = "O"
case city = "L"
case state = "ST"
case countryCode = "C"
}
public class CertificateSigningRequest {
private let request: UnsafeMutablePointer<X509_REQ>
private let key: ECKey
private let name: UnsafeMutablePointer<X509_NAME>
public init(key: ECKey, email: String, hostName: String, organizationalUnit: String, organization: String, countryCode: String, state: String, city: String) {
request = X509_REQ_new()
self.key = key
name = X509_NAME_new()
X509_REQ_set_version(request, 2)
self.add(name: email, type: .email)
self.add(name: hostName, type: .hostName)
self.add(name: organizationalUnit, type: .organizationalUnit)
self.add(name: organization, type: .organization)
self.add(name: countryCode, type: .countryCode)
self.add(name: city, type: .city)
self.add(name: state, type: .state)
X509_REQ_set_subject_name(request, name)
self.setPublicKey()
}
deinit {
X509_REQ_free(request)
X509_NAME_free(name)
}
private func add(name: String, type: NIDType) {
var buff = Array(name.utf8)
X509_NAME_add_entry_by_NID(self.name, OBJ_txt2nid(type.rawValue), MBSTRING_UTF8, &buff, Int32(buff.count), 0, 0)
}
private func setPublicKey() {
let certKey = EVP_PKEY_new()
EVP_PKEY_set1_EC_KEY(certKey, key.secretKey)
X509_REQ_set_pubkey(request, certKey)
X509_REQ_sign(request, certKey, EVP_sha256())
EVP_PKEY_free(certKey)
}
}
Note This code does not contain any BIO related functions ( yet ) to write out the PEM data to a file or to a data buffer, but they are really easy to add.
Disclaimer: I am not a professional cryptographer nor do I know all the ins and outs of the OpenSSL API. I cannot guarantee that the code I provided is a 100% correct implementation. Always be wary of code, especially crypto code, downloaded from the web.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With