strtotime("$mm/$dd/$yy")) $db['status'] = "Expired"; $db['serial'] = $a[3]; $db['country'] = $b[1]; $db['province'] = $b[2]; $db['locality'] = $b[3]; $db['organization'] = $b[4]; $db['issuer'] = $b[5]; $db['unit'] = $b[6]; $db['common_name'] = $b[7]; $db['email'] = $b[8]; return $db; } // // Returns the date & time a specified certificate is revoked, // Returns FALSE if the certificate is not revoked. // function CAdb_is_revoked($serial) { global $config; $regexp = "^R\t.*\t.*\t$serial\t.*\t.*$"; $x = exec('egrep '.escshellarg($regexp).' '.$config['index']); if ($x) { list($j,$j,$revoke_date,$j,$j,$j) = explode("\t", $x); sscanf($revoke_date, "%2s%2s%2s",$yy,$mm,$dd); return strftime("%b %d, %Y", strtotime("$mm/$dd/$yy")); } else return false; } // // Returns TRUE if a certificate is valid, otherwise FALSE. // function CAdb_is_valid($serial) { global $config; $regexp = "^V\t.*\t.*\t$serial\t.*\t.*$"; if (exec('egrep '.escshellarg($regexp).' '.$config['index'])) return true; else return false; } // // Returns the long-form certificate description as output by // openssl x509 -in certificatefile -text -purpose // function CA_cert_text($serial) { global $config; $certfile = $config['new_certs_dir'] . '/' . $serial . '.pem'; return(shell_exec(X509.' -in '.escshellarg($certfile).' -text -purpose 2>&1')); } // // Returns the long-form text of the Certificate Revocation List // openssl crl -in crlfile -text // function CA_crl_text() { global $config; $crlfile = $config['cacrl_pem']; return(shell_exec(CRL.' -in '.escshellarg($crlfile).' -text 2>&1')); } // // Returns the subject of a certificate. // function CA_cert_subject($serial) { global $config; $certfile = $config['new_certs_dir'] . '/' . $serial . '.pem'; $x = exec(X509.' -in '.escshellarg($certfile).' -noout -subject 2>&1'); return(str_replace('subject=', '', $x)); } // // Returns the common name of a certificate. // function CA_cert_cname($serial) { global $config; return(ereg_replace('^.*/CN=(.*)/.*','\\1',CA_cert_subject($serial))); } // // Returns the email address of a certificate. // function CA_cert_email($serial) { global $config; $certfile = $config['new_certs_dir'] . '/' . $serial . '.pem'; $x = exec(X509.' -in '.escshellarg($certfile).' -noout -email 2>&1'); return($x); } // // Returns the effective date of a certificate. // function CA_cert_startdate($serial) { global $config; $certfile = $config['new_certs_dir'] . '/' . $serial . '.pem'; $x = exec(X509.' -in '.escshellarg($certfile).' -noout -startdate 2>&1'); return(str_replace('notBefore=','',$x)); } // // Returns the expiration date of a certificate. // function CA_cert_enddate($serial) { global $config; $certfile = $config['new_certs_dir'] . '/' . $serial . '.pem'; $x = exec(X509.' -in '.escshellarg($certfile).' -noout -enddate 2>&1'); return(str_replace('notAfter=','',$x)); } // // Revokes a specified certificate. // function CA_revoke_cert($serial) { global $config; $fd = fopen($config['index'],'a'); flock($fd, LOCK_EX); $certfile = "$config[new_certs_dir]/$serial.pem"; $cmd_output[] = 'Revoking the certificate.'; exec(CA." -config '$config[openssl_cnf]' -revoke ".escshellarg($certfile)." -passin pass:'$config[ca_pwd]' 2>&1", $cmd_output, $ret); if ($ret == 0) { unset($cmd_output); list($ret, $cmd_output[]) = CA_generate_crl(); } fclose($fd); return array(($ret == true || $ret == 0 ? true : false), implode('
',$cmd_output)); } // // Creates a new certificate request, and certificate in various formats // according to specified parameters. PKCS12 bundle files contain the // private key, certificate, and CA certificate. // // Returns an array containing the output of failed openssl commands. // function CA_create_cert($cert_type='email',$country,$province,$locality,$organization,$unit,$common_name,$email,$expiry,$passwd,$keysize=1024) { global $config; # Wait here if another user has the database locked. $fd = fopen($config['index'],"a"); flock($fd, LOCK_EX); # Get the next available serial number $serial = trim(implode('',file($config['serial']))); $userkey = $config['private_dir'].'/'.$serial.'-key.pem'; $userreq = $config['req_dir'].'/'.$serial.'-req.pem'; $usercert = $config['new_certs_dir'].'/'.$serial.'.pem'; $userder = $config['cert_dir'].'/'.$serial.'.der'; $userpfx = $config['pfx_dir'].'/'.$serial.'.pfx'; $expiry_days = round($expiry * 365.25, 0); $cnf_file = CA_create_cnf($country,$province,$locality,$organization,$unit,$common_name,$email,$keysize); # Escape certain dangerous characters in user input $email = escshellcmd($email); $_passwd = escshellarg($passwd); $friendly_name = escshellarg($common_name); $extensions = escshellarg($cert_type.'_ext'); # Create the certificate request unset($cmd_output); $cmd_output[] = 'Creating certifcate request.'; if ($passwd) { exec(REQ." -new -newkey rsa:$keysize -keyout '$userkey' -out '$userreq' -config '$cnf_file' -days '$expiry_days' -passout pass:$_passwd 2>&1", $cmd_output, $ret); } else { exec(REQ." -new -nodes -newkey rsa:$keysize -keyout '$userkey' -out '$userreq' -config '$cnf_file' -days '$expiry_days' 2>&1", $cmd_output, $ret); } # Sign the certificate request and create the certificate if ($ret == 0) { unset($cmd_output); $cmd_output[] = "Signing $cert_type certifcate request."; exec(CA." -config '$cnf_file' -in '$userreq' -out /dev/null -notext -days '$expiry_days' -passin pass:'$config[ca_pwd]' -batch -extensions $extensions 2>&1", $cmd_output, $ret); }; # Create DER format certificate if ($ret == 0) { unset($cmd_output); $cmd_output[] = "Creating DER format certifcate."; exec(X509." -in '$usercert' -out '$userder' -inform PEM -outform DER 2>&1", $cmd_output, $ret); }; # Create a PKCS12 certificate file for download to Windows if ($ret == 0) { unset($cmd_output); $cmd_output[] = "Creating PKCS12 format certifcate."; if ($passwd) { $cmd_output[] = "infile: $usercert keyfile: $userkey outfile: $userpfx pass: $_passwd"; exec(PKCS12." -export -in '$usercert' -inkey '$userkey' -certfile '$config[cacert_pem]' -caname '$config[organization]' -out '$userpfx' -name $friendly_name -rand '$config[random]' -passin pass:$_passwd -passout pass:$_passwd 2>&1", $cmd_output, $ret); } else { $cmd_output[] = "infile: $usercert keyfile: $userkey outfile: $userpfx"; exec(PKCS12." -export -in '$usercert' -inkey '$userkey' -certfile '$config[cacert_pem]' -caname '$config[organization]' -out '$userpfx' -name $friendly_name -nodes -passout pass: 2>&1", $cmd_output, $ret); } }; #Unlock the CA database fclose($fd); #Remove temporary openssl config file. if (file_exists($cnf_file)) unlink($cnf_file); if ($ret == 0) { # Successful! # Return status=true and serial number of issued certificate. return array(true, $serial); } else { # Not successful. :-( # Clean up our loose ends. # Return status=false and openssl output/errors for debug. CA_remove_cert($serial); $cmd_output[] = 'Click on the "Help" link above for information on how to report this problem.'; return array(false, implode("
",$cmd_output)); } } // // Renews a specified certificate, revoking any existing valid versions. // Uses old certificate request to Creates a new request, and certificate // in various formats. // // Returns an array containing the output of failed openssl commands. // // FIXME: Yes, I know... This functions contains much duplicative code // from CA_create_cert(). Bleh! // function CA_renew_cert($old_serial,$expiry,$passwd) { global $config; # Don't renew a revoked certificate if a valid one exists for this # URL. Find and renew the valid certificate instead. if (CAdb_is_revoked($old_serial)) { $ret = CAdb_in(CA_cert_email($old_serial),CA_cert_cname($old_serial)); if ($ret && $old_serial != $ret) $old_serial = $ret; } # Valid certificates must be revoked prior to renewal. if (CAdb_is_valid($old_serial)) { $ret = CA_revoke_cert($old_serial); if (! $ret[0]) return $ret; } $cert_type = CA_cert_type($old_serial); $extensions = $cert_type.'_ext'; # Get common_name from old certificate for use as the # "friendly name" of PKCS12 certificate. $rec = CAdb_get_entry($old_serial); $country = $rec['country']; $province = $rec['province']; $locality = $rec['locality']; $organization = $rec['organiztion']; $unit = $rec['unit']; $common_name = $rec['common_name']; $email = $rec['email']; # Wait here if another user has the database locked. $fd = fopen($config['index'],"a"); flock($fd, LOCK_EX); # Get the next available serial number $serial = trim(implode('',file($config['serial']))); $old_userkey = $config['private_dir'].'/'.$old_serial.'-key.pem'; $old_userreq = $config['req_dir'].'/'.$old_serial.'-req.pem'; $userkey = $config['private_dir'].'/'.$serial.'-key.pem'; $userreq = $config['req_dir'].'/'.$serial.'-req.pem'; $usercert = $config['new_certs_dir'].'/'.$serial.'.pem'; $userder = $config['cert_dir'].'/'.$serial.'.der'; $userpfx = $config['pfx_dir'].'/'.$serial.'.pfx'; $expiry_days = round($expiry * 365.25, 0); $cmd_output = array(); $ret = 0; # Create a new certificate request by copying the old request. if (! file_exists($old_userreq) || ! copy($old_userreq,$userreq)) { $cmd_output[] = 'Could not create new certificate request file.'; $ret = 1; } # Copy private key to new file. if ($ret == 0 && (! file_exists($old_userkey) || ! copy($old_userkey,$userkey))) { $cmd_output[] = "Could not update private key file."; $ret = 1; } $cnf_file = CA_create_cnf($country,$province,$locality,$organization,$unit,$common_name,$email); # "friendly name" of PKCS12 certificate. $friendly_name = escshellarg($rec['common_name']); # Escape dangerous characters in user input. $_passwd = escshellarg($passwd); # Sign the certificate request and create the certificate. if ($ret == 0) { unset($cmd_output); $cmd_output[] = "Signing the $cert_type certificate request."; exec(CA." -config '$cnf_file' -in '$userreq' -out /dev/null -notext -days '$expiry_days' -passin pass:'$config[ca_pwd]' -batch -extensions $extensions 2>&1", $cmd_output, $ret); }; # Create DER format certificate if ($ret == 0) { unset($cmd_output); $cmd_output[] = "Creating DER format certificate."; exec(X509." -in '$usercert' -out '$userder' -inform PEM -outform DER 2>&1", $cmd_output, $ret); }; # Create a PKCS12 certificate file for download to Windows if ($ret == 0) { unset($cmd_output); $cmd_output[] = "Creating PKCS12 format certificate."; if ($passwd) { $cmd_output[] = "infile: $usercert keyfile: $userkey outfile: $userpfx pass: $_passwd"; exec(PKCS12." -export -in '$usercert' -inkey '$userkey' -certfile '$config[cacert_pem]' -caname '$config[organization]' -out '$userpfx' -name $friendly_name -rand '$config[random]' -passin pass:$_passwd -passout pass:$_passwd 2>&1", $cmd_output, $ret); } else { $cmd_output[] = "infile: $usercert keyfile: $userkey outfile: $userpfx"; #exec(PKCS12." -export -in '$usercert' -inkey '$userkey' -certfile '$config[cacert_pem]' -caname '$config[organization]' -out '$userpfx' -name $friendly_name -passout pass: 2>&1", $cmd_output, $ret); exec(PKCS12." -export -in '$usercert' -inkey '$userkey' -certfile '$config[cacert_pem]' -caname '$config[organization]' -out '$userpfx' -name $friendly_name -nodes 2>&1", $cmd_output, $ret); } }; #Unlock the CA database fclose($fd); #Remove temporary openssl config file. if (file_exists($cnf_file)) unlink($cnf_file); if ($ret == 0) { return array(true, $serial); } else { # Not successful, so clean up before exiting. CA_remove_cert($serial); if (eregi_array('.*private key.*',$cmd_output)) $cmd_output[] = 'This was likely caused by entering the wrong certificate password.'; else $cmd_output[] = 'Click on the "Help" link above for information on how to report this problem.'; return array(false, implode('
',$cmd_output)); } } // // Creates a new Certificate Revocation List and copies it the the approriate // locations. Returns error messages from failed commands. // function CA_generate_crl() { global $config; $ret = 0; $cmd_output[] = "Generating Certificate Revocation List."; exec(CA. " -gencrl -config '$config[openssl_cnf]' -out '$config[cacrl_pem]' -passin pass:'$config[ca_pwd]' 2>&1", $cmd_output, $ret); if ($ret == 0) { unset($cmd_output); $cmd_output[] = "Creating DER format Certificate Revocation List."; exec(CRL." -in '$config[cacrl_pem]' -out '$config[cacrl_der]' -inform PEM -outform DER 2>&1", $cmd_output, $ret); } return array(($ret == 0 ? true : false), implode('
',$cmd_output)); } // // Removes a specified certificate from the certificate index, // and all traces of it from the file system. // function CA_remove_cert($serial) { global $config; $userreq = $config['req_dir'].'/'.$serial.'-req.pem'; $userkey = $config['private_dir'].'/'.$serial.'-key.pem'; $usercert = $config['new_certs_dir'].'/'.$serial.'.pem'; $userder = $config['cert_dir'].'/'.$serial.'.der'; $userpfx = $config['pfx_dir'].'/'.$serial.'.pfx'; # Wait here if another user has the database locked. $fd = fopen($config['index'],'a'); flock($fd, LOCK_EX); if( file_exists($userreq)) unlink($userreq); if( file_exists($userkey)) unlink($userkey); if( file_exists($usercert)) unlink($usercert); if( file_exists($userder)) unlink($userder); if( file_exists($userpfx)) unlink($userpfx); $tmpfile = $config['index'].'.tmp'; copy($config['index'], $tmpfile); $regexp = "^[VR]\t.*\t.*\t".$serial."\t.*\t.*$"; exec('egrep -v '.escshellarg($regexp)." $tmpfile > $config[index] 2>/dev/null"); unlink($tmpfile); fclose($fd); } // // Returns the likely intended use for a specified certificate // (email, server, vpn). // function CA_cert_type($serial) { $certtext = CA_cert_text($serial); if (ereg('OpenSSL.* (E.?mail|Personal) .*Certificate', $certtext) && ereg('Code Signing', $certtest)) { $cert_type = 'email_signing'; } if (ereg('OpenSSL.* (E.?mail|Personal) .*Certificate', $certtext)) { $cert_type = 'email'; } elseif (ereg('OpenSSL.* Server .*Certificate', $certtext)) { $cert_type = 'server'; } elseif (ereg('timeStamping|Time Stamping', $certtext)) { $cert_type = 'time_stamping'; } elseif (ereg('TLS Web Client Authentication', $certtext) && ereg('TLS Web Server Authentication', $certtext)) { $cert_type = 'vpn_client_server'; } elseif (ereg('TLS Web Client Authentication', $certtext)) { $cert_type = 'vpn_client'; } elseif (ereg('TLS Web Server Authentication', $certtext)) { $cert_type = 'vpn_server'; } else { $cert_type = 'vpn_client_server'; } return $cert_type; } function CA_get_root_pem() { global $config; return(file_get_contents($config['cacert_pem'])); } ?>