SSL PHP Server Key and Certificate generation
Server key and certificate generation revisited.
Uniform Server keeps increasing in size partly due to core component increases and duplication.
Nothing can be done regarding core components hence this write-up addresses duplication. Server key and certificate generation comes into this category it currently uses components from the OpenSSL distribution. As a side note using the CA plugin introduces yet more duplication.
The OpenSSL distribution is primarily command line driven and suitable for batch files as originally implemented on Uniform Server. However control architecture moved over to a PHP CLI implementation with Server key and certificate generation reaming batch file driven. This tutorial covers converting this batch files implementation to PHP CLI solution.
PHP ships with extension php_openssl.dll and associated support libraries, these provide a vast number of openssl functions of which only four are required to generate a server certificate and key.
If you wish to follow this tutorial or experiment with other openssl functions download any Uniform Server 5.6-Nano version.
In folder UniServer\unicon create a new folder named z_cert
In this folder create two files Run.bat and cert.php with the following content.
TITLE UNIFORM SERVER - Certificate and Key generator COLOR B0 @echo off cls ..\..\usr\local\php\php.exe -c ..\..\usr\local\php\php-cli.ini gen.php pause
<?php print "\ntest\n"; ?>
Double click Run.bat runs script cert.php it produces nothing spectacular other than to display test. It proves you have a working set-up for this tutorial.
- ..\..\usr\local\php\php.exe - Runs PHP in CLI mode
- -c ..\..\usr\local\php\php-cli.ini - Informs PHP CLI to use configuration file php-cli.ini
- gen.php - Name of script to be run by PHP CLI
If you have read any of my tutorials you will know I hate just providing working examples this tutorial is no exception.
PHP Configuration file
Add the following lines as shown on right to cert.php
Fatal error: Call to undefined function openssl_pkey_new() in C:\Nano_5_6_6\UniServer\unicon\z_cert\gen.php on line 4 Press any key to continue . . .
<?php print "\ntest\n"; //=== Generate a new private (and public) key pair $privkey = openssl_pkey_new(); ?>
Function openssl_pkey_new() is defined in the openssl library. Problem is extension php_openssl.dll is not being loaded because its not configured in configuration file php-cli.ini
Edit file UniServer\usr\local\php\php-cli.ini
Add the following line:
Run (Run.bat) script again this time there will be no errors.
[PHP] extension=php_curl.dll extension=php_mysql.dll extension=php_openssl.dll extension_dir = "./extensions" error_reporting = E_ALL | E_STRICT date.timezone = "Europe/London"
The above line creates private and public keys used in other function.
Before looking at these in detail add the following code as shown on the right to file cert.php
Run the new script you will receive a warning
Warning: openssl_csr_sign(): cannot get CSR from parameter 1 in C:\Nano_5_6_6\UniServer\unicon\z_cert\gen.php on line 21
Although it gives the impression it’s a warning and can be ignored in reality it is fatal.
Parameter 1 ($csr) is a resource that was never created hence the above error.
Reading the manual you will find
Note: You need to have a valid openssl.cnf installed for this function to operate correctly.
Most functions use this file; trouble is it cannot be found. Path assumed to be either defined by OPENSSL_CONF or SSLEAY_CONF environmental variables or on the default path c:\usr\local\ssl.
<?php print "\ntest\n"; //=== Generate a new private (and public) key pair $privkey = openssl_pkey_new(); //=== Create data array for certificate information $dn = array( "countryName" => "UK", "stateOrProvinceName" => "Cambridge", "localityName" => "Cambs", "organizationName" => "UniServer", "organizationalUnitName" => "Demo", "commonName" => "localhost", "emailAddress" => "email@example.com" ); //=== Generate a certificate signing request $csr = openssl_csr_new($dn, $privkey); //== Create a self-signed certificate valid for 365 days $sscert = openssl_csr_sign($csr, "my secret", $privkey, 365); ?>
Uniform Server is portable hence the above default path is not applicable. Using environmental variables is not always a predictable solution.
Alternative every function that requires access to the openssl.conf file has an input array named $configargs allowing a path to be specified by setting its config key. This is reliable and preferred method covered later.
Open SSL configuration file
Uniform Server already has a pre-configured configuration file copy this to our test folder.
- Copy file UniServer\unicon\key_cert_gen\openssl.cnf
- To folder UniServer\unicon\z_cert
Alternatively create a new file named openssl.cnf with the following content:
####################################################################### # File name: openssl.cnf # Created By: The Uniform Server Development Team # Edited Last By: Mike Gleaves (ric) # V 1.0 7-1-2009 # Uses OpenSSL 0.9.8i ######################################################################## # # OpenSSL configuration file. # # Establish working directory. dir = . [ req ] default_bits = 1024 default_md = sha1 default_keyfile = privkey.pem distinguished_name = req_distinguished_name x509_extensions = v3_ca string_mask = nombstr [ req_distinguished_name ] countryName = Country Name (2 letter code) countryName_min = 2 countryName_max = 2 stateOrProvinceName = State or Province Name (full name) localityName = Locality Name (eg, city) 0.organizationName = Organization Name (eg, company) organizationalUnitName = Organizational Unit Name (eg, section) commonName = Common Name (eg, YOUR fqdn) commonName_max = 64 emailAddress = Email Address emailAddress_max = 64 [ ssl_server ] basicConstraints = CA:FALSE nsCertType = server keyUsage = digitalSignature, keyEncipherment extendedKeyUsage = serverAuth, nsSGC, msSGC nsComment = "OpenSSL Certificate for SSL Web Server" [ v3_req ] basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment [ v3_ca ] basicConstraints = critical, CA:true, pathlen:0 nsCertType = sslCA keyUsage = cRLSign, keyCertSign extendedKeyUsage = serverAuth, clientAuth nsComment = "OpenSSL CA Certificate"
Functions and arrays required
This section looks at functions and arrays required to implement a server certificate and key.
You can use this array instead of a configuration file and set its keys accordingly details on this page
We are interested only in defining a path to our configuration file. First absolute path is calculated and assigned to key.
//== Determine path $ssl_path = getcwd(); $ssl_path = preg_replace('/\\\/','/', $ssl_path); // Replace \ with / //== Create a configuration array containing path to openssl.cnf $config = array( "config" => "$ssl_path/openssl.cnf" );
Data array dn
Next we create a data array (dn) containing our certificate details.
Change appropriate keys to meet your own requirements:
//=== Create data array for certificate information $dn = array( "countryName" => "UK", "stateOrProvinceName" => "Cambridge", "localityName" => "Cambs", "organizationName" => "UniServer", "organizationalUnitName" => "Demo", "commonName" => "localhost", "emailAddress" => "firstname.lastname@example.org" );
Note: Common name for a real signed certificate would be what a user would type into a browser e.g www.fred.com
Function openssl_pkey_new() generates a new private and public key pair.
resource openssl_pkey_new ([ array $configargs ] )
Function openssl_csr_new() generates a new CSR (Certificate Signing Request) based on the information provided by dn,
mixed openssl_csr_new (array $dn, resource &$privkey [,array $configargs [,array $extraattribs ]] )
Function openssl_csr_sign() generates an x509 certificate resource from the given CSR.
resource openssl_csr_sign(mixed $csr, mixed $cacert, mixed $priv_key, int $days[,array $configargs[,int $serial = 0 ]])
Essentially that completes certificate and key generation! They are currently resources these require extracting to appropriate files. Following function perform this task:
Function openssl_pkey_export_to_file()saves an ascii PEM encoded verion of key into the file named by outfilename.
bool openssl_pkey_export_to_file(mixed $key,string $outfilename[,string $passphrase[,array $configargs]])
This function is a quick way to kill Apache stone dead! To prevent this ensure you use NULL for $passphrase.
Function openssl_x509_export_to_file() exports a certificate to file
bool openssl_x509_export_to_file(mixed $x509, string $outfilename [,bool $notext ])
The optional parameter notext if it is FALSE, additional human-readable information is included in the output.
The default value of notext is TRUE.
Function openssl_csr_export_to_file()exports a CSR to a file
bool openssl_csr_export_to_file(resource $csr, string $outfilename[, bool $notext = true ])
Edit file gen.php to contain the folloing code:
Run the script, you can manually copy key and certificate to the server.
The above provides a starting point; with a few minor additions you can automatically copy key and certificate to appropriate server locations. This was introduced to Uniform Server 5.6.7-Nano see folder UniServer\unicon\key_cert_gen
Was it effective in reducing size?
With reference to today’s hungry applications not that significant but worth having.
When run as a portable application it’s very easy to fall into a trap and proliferate components that are not being picked up. This applies to Uniform Server however there were historical reasons for this that I believe no longer applies.
Taking a step back how are dlls picked-up by Windows? General sequence of events are as follows:
Note 1 and 5 are similar and leads to confusion. From Apache’s point of view it meets criteria 1. When run as a server it forces PHP to pick-up extra dlls it requires from the Apache folder.
However when PHP is run as CLI it picks-up the extra dlls from its own folder.
It’s a viscous circle that I resolved when using libmysql.dll, same technique is applicable to openssl libraries.
Make PHP folder prime source of any duplicated files. Apache is amenable to this because it has the following directive:
Yep! Probably not what you expected, however it is simple and effective, ideally any additional files should be loaded before any modules.
For example this extract from Nano_5_6_7 httpd.conf
# Example: # LoadModule foo_module modules/mod_foo.so # Loadfile "C:/Nano_5_6_7/UniServer/usr/local/php/ssleay32.dll" Loadfile "C:/Nano_5_6_7/UniServer/usr/local/php/libeay32.dll" Loadfile "C:/Nano_5_6_7/UniServer/usr/local/php/libmysql.dll" LoadModule actions_module modules/mod_actions.so LoadModule alias_module modules/mod_alias.so
It gives a saving of 1.51 MB
That concludes this short tutorial. You now have a reasonable understanding how to use some of the opensll functions.
Probably what is also import is to avoid duplication in Uniform Server.
I have shown how to save around 3.2M