Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generating CSR from SecKey using OpenSSL

Tags:

ios

openssl

swift

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?

like image 761
Rachit Agarwal Avatar asked Apr 19 '17 18:04

Rachit Agarwal


1 Answers

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.

like image 100
Antwan van Houdt Avatar answered Sep 27 '22 18:09

Antwan van Houdt