void SCXSSLCertificate::DoGenerate()

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;
    }
}