I have a C/C++ application and I need to create a X509 pem certificate containing both a public and private key. The certificate can be self signed, or unsigned, doesn\'t m
Nathan Osman explained it greatly and fully, had the same problem to be solved in C++ so here is my small addition, cpp-style rewritten concept with a couple of caveats taken into account:
bool generateX509(const std::string& certFileName, const std::string& keyFileName, long daysValid)
{
bool result = false;
std::unique_ptr certFile { BIO_new_file(certFileName.data(), "wb"), BIO_free_all };
std::unique_ptr keyFile { BIO_new_file(keyFileName.data(), "wb"), BIO_free_all };
if (certFile && keyFile)
{
std::unique_ptr rsa { RSA_new(), RSA_free };
std::unique_ptr bn { BN_new(), BN_free };
BN_set_word(bn.get(), RSA_F4);
int rsa_ok = RSA_generate_key_ex(rsa.get(), RSA_KEY_LENGTH, bn.get(), nullptr);
if (rsa_ok == 1)
{
// --- cert generation ---
std::unique_ptr cert { X509_new(), X509_free };
std::unique_ptr pkey { EVP_PKEY_new(), EVP_PKEY_free};
// The RSA structure will be automatically freed when the EVP_PKEY structure is freed.
EVP_PKEY_assign(pkey.get(), EVP_PKEY_RSA, reinterpret_cast(rsa.release()));
ASN1_INTEGER_set(X509_get_serialNumber(cert.get()), 1); // serial number
X509_gmtime_adj(X509_get_notBefore(cert), 0); // now
X509_gmtime_adj(X509_get_notAfter(cert), daysValid * 24 * 3600); // accepts secs
X509_set_pubkey(cert.get(), pkey.get());
// 1 -- X509_NAME may disambig with wincrypt.h
// 2 -- DO NO FREE the name internal pointer
X509_name_st* name = X509_get_subject_name(cert.get());
const uchar country[] = "RU";
const uchar company[] = "MyCompany, PLC";
const uchar common_name[] = "localhost";
X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, country, -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, company, -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, common_name, -1, -1, 0);
X509_set_issuer_name(cert.get(), name);
X509_sign(cert.get(), pkey.get(), EVP_sha256()); // some hash type here
int ret = PEM_write_bio_PrivateKey(keyFile.get(), pkey.get(), nullptr, nullptr, 0, nullptr, nullptr);
int ret2 = PEM_write_bio_X509(certFile.get(), cert.get());
result = (ret == 1) && (ret2 == 1); // OpenSSL return codes
}
}
return result;
}
Of course, there should be more checks of function's return values, actually all of them should be checked but that would make a sample too "branchy" and is pretty easy to improve anyway.