copensslx509csr

Is there a way to sign a complete CSR in the OpenSSL C API to get a certificate?


With the OpenSSL command line tools the standard pattern for creating a valid certificate is to generate a Certificate Signing Request with openssl req and then sign it with an issuer to create a certificate with openssl x509. Like so:

openssl req -new -key privatekey.pem -subj '/CN=examplehost.exampledomain.com' -out examplehost.csr
openssl x509 -req -in examplehost.csr -days 365 -sha256 -CA issuer.pem -CAkey issuer_priv.key -CAserial serial.file -out examplehost.pem

In the examples I've found for the C API the pattern appears to be to use X509_new() to instantiate a blank X509 object, then use various methods to fill out the metadata and key manually within the X509 object. After that X509_sign() is used to sign it with the the issuer private key.

I haven't found any examples of using an X509_REQ as an input to creating a certificate even being possible with the C API. Is this a correct read? Or is there some call in this API that links Certs to CSRs by more than just manually copying fields over?


Solution

  • If you look at the source code of the OpenSSL C client, it will copy the fields manually (the corresponding locations in the current OpeNSSL source code are linked) using the following logic:

    1. It loads the CSR (implementation of the format-agnostic loader) and extracts the public key via X509_REQ_get0_pubkey.
    2. It then copies the extensions of the CSR into a new certificate (implementation of the extension copy routine). (Note that extensions include subject alternative names, etc.)
    3. It copies over the subject name and the public key of the CSR into the certificate.
    4. If a CA is being used it loads a new certificate serial number (implementation of the file loader routine), and then applies that serial number to the certificate.
    5. It sets the issuer name to that of the CA (or to itself if self-signed).
    6. It sets the not before / not after dates of the certificate. (Subroutine implementation)
    7. It initializes the X509v3 context of the certificate.
    8. It signs the certificate (implementation of the subroutine).
    9. Finally it writes out the certificate + displays some information to the user.

    There is no official single routine that does this kind of operation directly in the API. However, if you're doing this to implement a CA-like functionality it is considered best practice to actually manually copy values over so that you can fully control what the resulting certificate contains. For example, you probably want to restrict the generation of certificates that are themselves CAs and only do that in cases where you explicitly want a sub-CA. (The X509v3 extension can be used to indicate this.)

    That said, I hope the links to the source code of the command line utility are helpful in telling you what kind of operations you'd need to perform yourself to sign a CSR using OpenSSL's C API.

    Finally: there's an undocumented function called X509_REQ_to_X509() that automatically signs a CSR. DO NOT USE IT, as it doesn't support most modern certificate features (it only copies some things over) and it still uses the broken MD5 (!) algorithm for signing. (Remember that SHA-1 isn't accepted universally anymore?) Plus since the function is not documented, so there's no guarantee how long that routine will remain part of OpenSSL in the future. I'm only mentioning it for completeness sake and to warn against its usage.