in source/code/shared/tools/scx_ssl_config/scxsslcert.cpp [340:589]
void SCXSSLCertificate::DoGenerate()
{
try
{
int newKeyLength = m_bits;
// Arguments from the command line.
string outfile(StrToMultibyte(m_CertPath));
string keyout(StrToMultibyte(m_KeyPath));
#if OPENSSL_VERSION_NUMBER <= 0x100fffffL // SSL 1.0.x or lower?
ManagedResource res1(ERR_load_crypto_strings, ERR_free_strings);
ManagedResource res2(SSL_OpenSSL_add_all_algorithms, EVP_cleanup);
ManagedResource res3(ENGINE_load_builtin_engines, ENGINE_cleanup);
#else
ManagedResource res1(ENGINE_load_builtin_engines, NoOp_Destructor);
#endif
// Serial number is always set to "1".
// This is a self-signed certificate. Serial number is unimportant.
char one[] = "1";
ManagedValueResource<ASN1_INTEGER> serial(LoadASN1(NULL, one)(), ASN1_INTEGER_free);
if (0 == serial.Get())
{
throw SCXNULLPointerException(L"Error generating serial number", SCXSRCLOCATION);
}
ManagedValueResource<BIO> out(BIO_new(BIO_s_file()), BIO_free_all);
if (0 == out.Get())
{
throw SCXSSLException(L"Failed to open out file", SCXSRCLOCATION);
}
// Allocate an empty private key structure.
ManagedValueResource<EVP_PKEY> pkey(EVP_PKEY_new(), EVP_PKEY_free);
if (pkey.Get() == 0)
{
throw SCXNULLPointerException(L"Unable to allocate empty private key structure.",
SCXSRCLOCATION);
}
{
int ret = 1;
#if OPENSSL_VERSION_NUMBER < 0x0090800fL // SSL version lower than 0.9.8? It is needed for Solaris-10.
RSA * rsa = RSA_generate_key(newKeyLength, 0x10001, 0, 0);
if ( ! rsa )
{
throw SCXCoreLib::SCXNULLPointerException(L"Error allocating RSA structure.",
SCXSRCLOCATION);
}
#else
BIGNUM *bne = BN_new();
ret = BN_set_word(bne,RSA_F4);
if(ret !=1){
throw SCXNULLPointerException(L"Unable to set empty private key structure.",
SCXSRCLOCATION);
}
RSA * rsa = RSA_new();
if ( ! rsa )
{
throw SCXCoreLib::SCXNULLPointerException(L"Error allocating RSA structure.",
SCXSRCLOCATION);
}
ret = RSA_generate_key_ex(rsa, newKeyLength, bne, NULL);
#endif
if ( ret != 1 || ! EVP_PKEY_assign_RSA(pkey.Get(), rsa))
{
// Free rsa if the assign was unsuccessful. (If it was successful, then rsa
// is owned by pkey.)
RSA_free(rsa);
throw SCXSSLException(L"Error generating RSA key pair..", SCXSRCLOCATION);
}
}
if (BIO_write_filename(out.Get(),const_cast<char*>(keyout.c_str())) <= 0)
{
int e = errno;
std::wstring errText = SCXCoreLib::StrFromUTF8(SCXCoreLib::strerror(e));
std::wostringstream ss;
ss << keyout.c_str() << L": ";
if ( ! errText.empty() )
{
ss << errText;
}
else
{
ss << L"errno=" << e;
}
throw SCXSSLException(ss.str(), SCXSRCLOCATION);
}
if ( ! PEM_write_bio_PrivateKey(out.Get(),pkey.Get(),NULL,NULL,0,NULL,NULL))
{
throw SCXSSLException(L"Error writing private key file", SCXSRCLOCATION);
}
// Allocate a new X509_REQ structure
ManagedValueResource<X509_REQ> req(X509_REQ_new(), X509_REQ_free);
if (0 == req.Get())
{
throw SCXNULLPointerException(L"Unable to allocate memory for an X509_REQ struct.",
SCXSRCLOCATION);
}
// Set the properties in the req structure from the private key.
SetX509Properties(req.Get(),pkey.Get());
ManagedValueResource<X509> x509ss(X509_new(), X509_free);
if (0 == x509ss.Get())
{
throw SCXNULLPointerException(L"Error allocating X509 structure x509ss.",
SCXSRCLOCATION);
}
if (!X509_set_serialNumber(x509ss.Get(), serial.Get()))
{
throw SCXSSLException(L"Unable to set certificate serial nubmer.", SCXSRCLOCATION);
}
// Copy the issuer name from the request.
if (!X509_set_issuer_name(x509ss.Get(), X509_REQ_get_subject_name(req.Get())))
{
throw SCXSSLException(L"Unable to set issuer name.", SCXSRCLOCATION);
}
// Ensure the time is not before the certificate time.
if (!X509_gmtime_adj(X509_get_notBefore(x509ss.Get()),(long)60*60*24*m_startDays))
{
throw SCXSSLException(L"Invalid time range.", SCXSRCLOCATION);
}
// Ensure the time is not after the certificate time.
if (!X509_gmtime_adj(X509_get_notAfter(x509ss.Get()), (long)60*60*24*m_endDays))
{
throw SCXSSLException(L"Invalid time range", SCXSRCLOCATION);
}
// Copy the subject name from the request.
if (!X509_set_subject_name(x509ss.Get(), X509_REQ_get_subject_name(req.Get())))
{
throw SCXSSLException(L"Unable to set subject name.", SCXSRCLOCATION);
}
{
// Get the public key from the request, and set it in our cert.
ManagedValueResource<EVP_PKEY> tmppkey(X509_REQ_get_pubkey(req.Get()), EVP_PKEY_free);
if (!tmppkey.Get() || !X509_set_pubkey(x509ss.Get(),tmppkey.Get()))
{
throw SCXSSLException(L"Unable to set the public key in the certificate", SCXSRCLOCATION);
}
}
/* Set up V3 context struct */
X509V3_CTX ext_ctx;
X509V3_set_ctx(&ext_ctx, x509ss.Get(), x509ss.Get(), NULL, NULL, 0);
// Add serverAuth extension ... this magic OID is defined in RFC 3280,
// section 4.2.1.13 "Extended Key Usage", as "1.3.6.1.5.5.7.3.1"
// We will access it the right way ...
// There is no need to free the pointer returned here, no memory is allocated
ASN1_OBJECT * authOBJ = NULL;
if (m_clientCert)
{
authOBJ = OBJ_nid2obj(NID_client_auth);
}
else
{
authOBJ = OBJ_nid2obj(NID_server_auth);
}
if(authOBJ == NULL)
{
throw SCXSSLException(L"Unable to get serverAuth/clientAuth ASN1_OBJECT pointer", SCXSRCLOCATION);
}
// The oid is of known length, 17 bytes ... pad it a little ...
char authOIDBuf[24] = {0};
// The flag 1 denotes that the numeric form of the answer (not long or short name) will be used
// The return is (apparently) the string length of the converted string (this is undocumented) ..
if(OBJ_obj2txt(authOIDBuf, static_cast<int> (sizeof(authOIDBuf)/sizeof(*authOIDBuf)), authOBJ, 1) <= 0)
{
throw SCXSSLException(L"Not able to convert OBJ_server_auth/OBJ_client_auth to text", SCXSRCLOCATION);
}
X509_EXTENSION * ext = X509V3_EXT_conf_nid(NULL, &ext_ctx, (int)NID_ext_key_usage, authOIDBuf);
if(!ext)
{
throw SCXSSLException(L"Unable to get extension pointer for serverAuth/clientAuth extension", SCXSRCLOCATION);
}
int ext_OK = X509_add_ext(x509ss.Get(), ext, -1);
X509_EXTENSION_free(ext);
if(!ext_OK)
{
throw SCXSSLException(L"Unable to add serverAuth/clientAuth extension", SCXSRCLOCATION);
}
// Sign the certificate
const EVP_MD * digest = EVP_sha256();
int i = X509_sign(x509ss.Get(),pkey.Get(),digest);
if (! i)
{
throw SCXSSLException(L"Error signing certificate.", SCXSRCLOCATION);
}
// Write the new certificate to a file.
if ( ! BIO_write_filename(out.Get(),const_cast<char*>(outfile.c_str())))
{
throw SCXCoreLib::SCXInternalErrorException(L"Unable to open the cert file for writing.", SCXSRCLOCATION);
}
if ( ! PEM_write_bio_X509(out.Get(),x509ss.Get()))
{
throw SCXCoreLib::SCXInternalErrorException(L"Error writing the cert file.", SCXSRCLOCATION);
}
// Cleanup the rest of the resources that may have been allocated internally.
#if OPENSSL_VERSION_NUMBER <= 0x100fffffL // SSL 1.0.x or lower?
OBJ_cleanup();
#endif
CONF_modules_unload(1);
#if OPENSSL_VERSION_NUMBER <= 0x100fffffL // SSL 1.0.x or lower?
CRYPTO_cleanup_all_ex_data();
ERR_remove_state(0);
#endif
}
catch (SCXCoreLib::SCXException & e)
{
// Blunt force resource release functions.
#if OPENSSL_VERSION_NUMBER <= 0x100fffffL // SSL 1.0.x or lower?
OBJ_cleanup();
CONF_modules_free();
CRYPTO_cleanup_all_ex_data();
ERR_remove_state(0);
#endif
throw;
}
}