Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to programmatically create a Certificate Signing Request (CSR)?

Tags:

c

ssl

openssl

pki

csr

How to implement it in C?

openssl req -new -key cert.key -out cert.csr

openssl x509 -req -in cert.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out cert.crt -days 5000
like image 399
Andrey Egorov Avatar asked Aug 15 '16 05:08

Andrey Egorov


People also ask

How do I issue a CSR certificate?

In the Server Manager dashboard, in the top right corner, choose Tools, Certification Authority. In the Certification Authority window, choose your computer name. From the Action menu, choose All Tasks, Submit new request. Select your CSR file, and then choose Open.

Is CSR required for code signing certificate?

Code Signing Certificates must be purchased from a Certificate Authority (CA) before you can request a Certificate Signing Request (CSR). The CSR contains encoded information on the requestor (you), including name, email address, and public key.


1 Answers

I hope it is not too late and my answer will help :)

I'm providing my solution, which I implemented ~three years ago after researching openssl sources. The generated certs are very simple - review which fields you need and adjust the functions.

openssl req -new -key cert.key -out cert.csr

is implemented the following way:

X509_REQ *generate_cert_req(const char *p_path) {
    FILE *p_file = NULL;
    EVP_PKEY *p_key = NULL;
    X509_REQ *p_x509_req = NULL;

    if (NULL == (p_file = fopen(p_path, "r"))) {
        printf("failed to open the private key file\n");
        goto CLEANUP;
    }

    if (NULL == (p_key = PEM_read_PrivateKey(p_file, NULL, NULL, NULL))) {
        printf("failed to read the private key file\n");
        goto CLEANUP;
    }

    if (NULL == (p_x509_req = X509_REQ_new())) {
        printf("failed to create a new X509 REQ\n");
        goto CLEANUP;
    }

    if (0 > X509_REQ_set_pubkey(p_x509_req, p_key)) {
        printf("failed to set pub key\n");
        X509_REQ_free(p_x509_req);
        p_x509_req = NULL;
        goto CLEANUP;
    }

    if (0 > X509_REQ_sign(p_x509_req, p_key, EVP_sha256())) {
        printf("failed to sign the certificate\n");
        X509_REQ_free(p_x509_req);
        p_x509_req = NULL;
        goto CLEANUP;
    }

    CLEANUP:
    fclose(p_file);
    EVP_PKEY_free(p_key);

    return p_x509_req;
}

the function has only one argument which is a path to a key file (cert.key in the openssl command snippet above) and as its result returns a pointer to a generated certificate request.

openssl x509 -req -in cert.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out cert.crt -days 5000

is implemented as the "generate_cert" function

int randSerial(ASN1_INTEGER *ai) {
    BIGNUM *p_bignum = NULL;
    int ret = -1;

    if (NULL == (p_bignum = BN_new())) {
        goto CLEANUP;
    }

    if (!BN_pseudo_rand(p_bignum, 64, 0, 0)) {
        goto CLEANUP;
    }

    if (ai && !BN_to_ASN1_INTEGER(p_bignum, ai)) {
        goto CLEANUP;
    }

    ret = 1;

    CLEANUP:
    BN_free(p_bignum);

    return ret;
}

X509 *generate_cert(X509_REQ *pCertReq, const char *p_ca_path, const char *p_ca_key_path) {
    FILE *p_ca_file = NULL;
    X509 *p_ca_cert = NULL;
    EVP_PKEY *p_ca_pkey = NULL;
    FILE *p_ca_key_file = NULL;
    EVP_PKEY *p_ca_key_pkey = NULL;
    X509 *p_generated_cert = NULL;
    ASN1_INTEGER *p_serial_number = NULL;
    EVP_PKEY *p_cert_req_pkey = NULL;

    if (NULL == (p_ca_file = fopen(p_ca_path, "r"))) {
        printf("failed to open the ca file\n");
        goto CLEANUP;
    }

    if (NULL == (p_ca_cert = PEM_read_X509(p_ca_file, NULL, 0, NULL))) {
        printf("failed to read X509 CA certificate\n");
        goto CLEANUP;
    }

    if (NULL == (p_ca_pkey = X509_get_pubkey(p_ca_cert))) {
        printf("failed to get X509 CA pkey\n");
        goto CLEANUP;
    }

    if (NULL == (p_ca_key_file = fopen(p_ca_key_path, "r"))) {
        printf("failed to open the private key file\n");
        goto CLEANUP;
    }

    if (NULL == (p_ca_key_pkey = PEM_read_PrivateKey(p_ca_key_file, NULL, NULL, NULL))) {
        printf("failed to read the private key file\n");
        goto CLEANUP;
    }

    if (NULL == (p_generated_cert = X509_new())) {
        printf("failed to allocate a new X509\n");
        goto CLEANUP;
    }

    p_serial_number = ASN1_INTEGER_new();
    randSerial(p_serial_number);
    X509_set_serialNumber(p_generated_cert, p_serial_number);

    X509_set_issuer_name(p_generated_cert, X509_REQ_get_subject_name(pCertReq));
    X509_set_subject_name(p_generated_cert, X509_REQ_get_subject_name(pCertReq));

    X509_gmtime_adj(X509_get_notBefore(p_generated_cert), 0L);
    X509_gmtime_adj(X509_get_notAfter(p_generated_cert), 31536000L);

    if (NULL == (p_cert_req_pkey = X509_REQ_get_pubkey(pCertReq))) {
        printf("failed to get certificate req pkey\n");
        X509_free(p_generated_cert);
        p_generated_cert = NULL;
        goto CLEANUP;
    }

    if (0 > X509_set_pubkey(p_generated_cert, p_cert_req_pkey)) {
        printf("failed to set pkey\n");
        X509_free(p_generated_cert);
        p_generated_cert = NULL;
        goto CLEANUP;
    }

    if (0 > EVP_PKEY_copy_parameters(p_ca_pkey, p_ca_key_pkey)) {
        printf("failed to copy parameters\n");
        X509_free(p_generated_cert);
        p_generated_cert = NULL;
        goto CLEANUP;
    }

    X509_set_issuer_name(p_generated_cert, X509_get_subject_name(p_ca_cert));

    if (0 > X509_sign(p_generated_cert, p_ca_key_pkey, EVP_sha256())) {
        printf("failed to sign the certificate\n");
        X509_free(p_generated_cert);
        p_generated_cert = NULL;
        goto CLEANUP;
    }

    CLEANUP:
    fclose(p_ca_file);
    X509_free(p_ca_cert);
    EVP_PKEY_free(p_ca_pkey);
    fclose(p_ca_key_file);
    EVP_PKEY_free(p_ca_key_pkey);
    ASN1_INTEGER_free(p_serial_number);
    EVP_PKEY_free(p_cert_req_pkey);

    return p_generated_cert;
}

the function has three arguments:

1: is a point to a certificate request (generated in the first step)

2: a path to ca (-CA rootCA.crt in the command snippet above)

3: a path to ca key (-CAkey rootCA.key)

the functions can be used the following way:

int save_cert_req(X509_REQ *p_cert_req, const char *path) {
    FILE *p_file = NULL;
    if (NULL == (p_file = fopen(path, "w"))) {
        printf("failed to open file for saving csr\n");
        return -1;
    }

    PEM_write_X509_REQ(p_file, p_cert_req);
    fclose(p_file);
    return 0;
}

int save_cert(X509 *p_generated_cert, const char *path) {
    FILE *p_file = NULL;
    if (NULL == (p_file = fopen(path, "w"))) {
        printf("failed to open file for saving csr\n");
        return -1;
    }

    PEM_write_X509(p_file, p_generated_cert);
    fclose(p_file);
    return 0;
}

int main() {
    int ret = 0;
    X509_REQ *p_cert_req = NULL;
    X509 *p_generated_cert = NULL;

    p_cert_req = generate_cert_req(CERT_REQUEST_KEY_PATH);
    if (NULL == p_cert_req) {
        printf("failed to generate cert req\n");
        ret = -1;
        goto CLEANUP;
    }

    if (save_cert_req(p_cert_req, GENERATED_CERT_REQUEST_SAVE_PATH)) {
        printf("failed to save generated cert request\n");
        ret = -1;
        goto CLEANUP;
    }

    p_generated_cert = generate_cert(p_cert_req, CERT_CA_PATH, CERT_CA_KEY_PATH);
    if (NULL == p_generated_cert) {
        printf("failed to generate cert\n");
        ret = -1;
        goto CLEANUP;
    }

    if (save_cert(p_generated_cert, GENERATED_CERT_SAVE_PATH)) {
        printf("failed to save generated cert\n");
        ret = -1;
        goto CLEANUP;
    }

    printf("the certificates have been generated.");

    CLEANUP:
    X509_REQ_free(p_cert_req);
    X509_free(p_generated_cert);

    return ret;
}

You can download the whole solution that you can compile and test from github repository: https://github.com/egorovandreyrm/openssl_cert_req

like image 85
Andrey Egorov Avatar answered Sep 20 '22 09:09

Andrey Egorov