// JWSmythe SSL Certificate Generator
//
// v2.6 - 10 15-2022. Added source links.
// Changed OpenSSL path to variable, rather than hard coded in each command.
// v2.5 - 12-02-2020. Cleaned up poorly quoted arrays. These are fatal errors in PHP 8.0
// Example: PHP Fatal error: Uncaught Error: Undefined constant "private_key"
// Modified to fix undefined array key. These are new errors in PHP 8.0
// Example: PHP Warning: Undefined array key "OU"
// v2.4 - 12-22-2015. Added Modulus MD5 check.
// Added ability to check user provided key, csr, and/or cert.
// any can be provided. The key is only required if the user
// wants the checksum to validate.
// v2.3 - 12-07-2015. Changed layout to horizontal, to show more element details
// Added L ST and C defaults. OU was already done.
//
// v2.2 - 09-21-2015. Changed flag in genrsa to really switch from SHA1 to SHA256.
// Increased key size from 2048 to 4096.
// v2.1 - 11-18-2014. Changed key to AES256. Changed signing hash SHA1 to SHA256.
// This is demonstration of how to generate your SSL certificate.
// You should not use anything generated on *my* server. You are trusting
// a 3rd party with confidential data.
//
// Reference the openssl commands below for the whole generation process.
//
// Notice that I do not do housekeeping for the temp files. This is so *you*
// can use this code to learn from, and examine the files used after the process is complete.
//
// If you should choose to use this code, use it on your own machine.
// Ideally, an air-gapped machine.
// Your own security decisions are your own problem.
//
// You may use this code with attribution.
// Unattributed use will be pointed out and taunted.
?>
// This turns on debugging output. It will just show at the top of the page.
// you can add more logic here, or in the function show_debug().
$GLOBALS['debug'] = "off";
// OpenSSL is absolutely required. It should already be present on all
// Linux or Unix like machines. Change this setting if yours is located
// in a different place.
$openssl_cmd = "/usr/bin/openssl";
// Generate as much as we can here.
// Auto-generate as much as we can here, to help guide the user through.
// We'll populate all the details below.
// Use $pid for the timestamp. It's grown from the initial version,
// and I don't want to change the variable.
// The $randkey section is from http://jwsmythe.com/password/passgen.php .
$chars = "abcdefghijklmnopqrstuvwxyz1234567890";
$numchar = rand(32,63);
$charlist = preg_split("//u", $chars, -1, PREG_SPLIT_NO_EMPTY);
$disp_size = count($charlist);
$list_size = $disp_size - 1;
// print "
" . print_r($charlist, TRUE) . "
";
$randkey = '';
for ($i=0; $i<$numchar; $i++) {
$thischar = intval(rand(0,$list_size));
$randkey .= $charlist[$thischar];
};
$pid = "sslgen_" . $randkey;
show_debug("Generated PID: $pid");
// print "$disp_size char pool, $numchar char string ";
// print "PID: $pid";
// If the user didn't provide anything, fill in these defaults.
#if (($_POST['OU']) == ''){
if (empty($_POST['OU'])){
$_POST['OU'] = "Security Services";
show_debug("Setting DEFAULT OU as " . $_POST['OU']);
};
if (empty($_POST['L'])){
$_POST['L'] = "New York";
show_debug("Setting DEFAULT L as " . $_POST['L']);
};
if (empty($_POST['ST'])){
$_POST['ST'] = "NY";
show_debug("Setting DEFAULT ST as " . $_POST['ST']);
};
if (empty($_POST['C'])){
$_POST['C'] = "US";
show_debug("Setting DEFAULT C as " . $_POST['C']);
};
if (empty($_POST['private_key'])){
// No private key was received. Generate one. A password is required. We'll clear it below.
show_debug("No private key was received. Generate one.");
$c = "$openssl_cmd genrsa -aes256 -passout pass:secret -out /tmp/$pid.key 4096";
#$c = "$openssl_cmd genrsa -passout pass:secret -out /tmp/$pid.key 4096";
system ($c);
// Remove password, so the web server can restart without user interaction.
$c = "$openssl_cmd rsa -passin pass:secret -in /tmp/$pid.key -out /tmp/$pid.key.insecure";
system($c);
$_POST['private_key'] = file_get_contents("/tmp/$pid.key.insecure");
// Cleanup. We aren't sure if we're continuing or not yet.
unlink("/tmp/$pid.key.insecure");
unlink("/tmp/$pid.key");
$debug_showkey = substr($_POST['private_key'], 0, 48) . "...";
show_debug("Setting generated Private_Key as $debug_showkey");
};
/*
if (
(($_POST['private_key']) != '') &&
(($_POST['C']) != '') &&
(($_POST['ST']) != '') &&
(($_POST['L']) != '') &&
(($_POST['O']) != '') &&
(($_POST['OU']) != '') &&
(($_POST['CN']) != '')
){
*/
if (
(!empty($_POST['private_key'])) &&
(!empty($_POST['C'])) &&
(!empty($_POST['ST'])) &&
(!empty($_POST['L'])) &&
(!empty($_POST['O'])) &&
(!empty($_POST['OU'])) &&
(!empty($_POST['CN']))
){
show_debug("Completed form provided. Generate CSR and self-cert.");
$conf_data = "
[ req ]
default_bits = 4096
default_days = 3650
default_keyfile = /tmp/$pid.key
distinguished_name = req_distinguished_name
prompt = no
[ req_distinguished_name ]
C = " . $_POST['C'] . "
ST = " . $_POST['ST'] . "
L = " . $_POST['L'] . "
O = " . $_POST['O'] . "
OU = " . $_POST['OU'] . "
CN = " . $_POST['CN'] . "
";
file_put_contents("/tmp/$pid.key", $_POST['private_key']);
file_put_contents("/tmp/$pid.conf", $conf_data);
#$c = "$openssl_cmd req -new -key /tmp/$pid.key -out /tmp/$pid.csr -config /tmp/$pid.conf";
$c = "$openssl_cmd req -sha256 -new -key /tmp/$pid.key -out /tmp/$pid.csr -config /tmp/$pid.conf";
#print "$c";
system($c);
$_POST['csr'] = file_get_contents("/tmp/$pid.csr");
$c = "$openssl_cmd x509 -sha256 -req -days 3650 -in /tmp/$pid.csr -signkey /tmp/$pid.key -out /tmp/$pid.selfcert";
#print "$c ";
system($c);
$_POST['selfcert'] = file_get_contents("/tmp/$pid.selfcert");
};
// The following tests are for if someone was validating data through the form.
// We support putting the key, csr, or cert in the file row,
// and seeing the result in the validation row.
if (
(($_POST['private_key']) != '') &&
(!file_exists("/tmp/$pid.key"))
){
show_debug("Writing PK file");
// We have a key sent, but no key file present. Probably a validation. Save the file.
file_put_contents("/tmp/$pid.key", $_POST['private_key']);
};
if (
(!empty($_POST['csr'])) &&
(!file_exists("/tmp/$pid.csr"))
){
show_debug("Writing CSR file");
// We have a csr sent, but no csr file present. Probably a validation. Save the file.
file_put_contents("/tmp/$pid.csr", $_POST['csr']);
};
// Note: Throughout we reference the cert as "selfcert". It is assumed
// that this script is generating the temporary (self-signed) certificate.
// If the user is submitting a real certificate for validation, it will
// still process fine. It uses the same OpenSSL command.
// We may sign with our own CA later.
if (
(!empty($_POST['selfcert'])) &&
(!file_exists("/tmp/$pid.selfcert"))
){
show_debug("Writing self-cert file");
// We have a csr sent, but no csr file present. Probably a validation. Save the file.
file_put_contents("/tmp/$pid.selfcert", $_POST['selfcert']);
};
if (file_exists("/tmp/$pid.key")){
$_POST['check_key'] = `$openssl_cmd rsa -check -noout -text -in /tmp/$pid.key`;
$_POST['mod_key'] = `$openssl_cmd rsa -modulus -noout -in /tmp/$pid.key`;
$_POST['mod_key'] = md5($_POST['mod_key']);
}else{
$_POST['check_key'] = '';
$_POST['mod_key'] = '';
};
if (file_exists("/tmp/$pid.csr")){
$_POST['check_csr'] = `$openssl_cmd req -verify -noout -text -in /tmp/$pid.csr`;
$_POST['mod_csr'] = `$openssl_cmd req -modulus -noout -in /tmp/$pid.csr`;
$_POST['mod_csr'] = md5($_POST['mod_csr']);
}else{
$_POST['check_csr'] = '';
$_POST['mod_csr'] = '';
};
if (file_exists("/tmp/$pid.selfcert")){
$_POST['check_selfcert'] = `$openssl_cmd x509 -noout -text -in /tmp/$pid.selfcert`;
$_POST['mod_selfcert'] = `$openssl_cmd x509 -modulus -noout -in /tmp/$pid.selfcert`;
$_POST['mod_selfcert'] = md5($_POST['mod_selfcert']);
show_debug("mod_selfcert check " . $_POST['mod_selfcert']);
}else{
$_POST['check_selfcert'] = '';
$_POST['mod_selfcert'] = '';
};
// Determine if the modulus md5 matches.
if (!empty($_POST['mod_key'])){
show_debug("mod key found good");
$mod_class_key='modulus_good';
}else{
show_debug("mod key found bad");
$mod_class_key='modulus_bad';
};
if ($_POST['mod_key'] == $_POST['mod_csr']){
show_debug("mod csr found good");
$mod_class_csr='modulus_good';
}else{
show_debug("mod csr found bad");
$mod_class_csr='modulus_bad';
};
if ($_POST['mod_key'] == $_POST['mod_selfcert']){
show_debug("mod cert found good");
$mod_class_cert='modulus_good';
}else{
show_debug("mod cert found bad");
$mod_class_cert='modulus_bad';
};
// IMPORTANT!!! The cleanup section is disabled on the testing site.
// Make sure it's enabled on the production site, or we'll have a ton of junk files.
// Clean up all temp files. We don't want to retain anything for liability reasons.
if(file_exists("/tmp/$pid.key")){ unlink("/tmp/$pid.key"); };
if(file_exists("/tmp/$pid.conf")){ unlink("/tmp/$pid.conf"); };
if(file_exists("/tmp/$pid.csr")){ unlink("/tmp/$pid.csr"); };
if(file_exists("/tmp/$pid.selfcert")){ unlink("/tmp/$pid.selfcert"); };
function show_debug($in){
$debug = $GLOBALS['debug'];
if ($debug == 'on'){
if(is_array($in)){
$out = "