Files
phpki-ng/include/openssl_functions.php

977 lines
32 KiB
PHP
Raw Normal View History

2012-02-28 08:23:39 +00:00
<?php
//
// Creates a temporary openssl config file specific to given parameters.
// File name is placed in ./tmp with a random name. It lingers unless
// removed manually.
//
function CA_create_cnf($country = '', $province = '', $locality = '', $organization = '', $unit = '', $common_name = '', $email = '', $keysize = 2048, $dns_names = '', $ip_addr = '', $serial = '')
{
global $config, $PHPki_user;
$issuer = $PHPki_user;
$count_dns = 0;
$count_ip = 0;
$alt_names = "";
2021-03-07 18:57:17 +01:00
if (! $dns_names == '') {
$dns_n=explode("\n", $dns_names);
$count_dns = $count_dns + 1;
$alt_names .= "DNS.$count_dns = $common_name\n";
foreach ($dns_n as $value) {
if (! $value == '') {
$count_dns = $count_dns + 1;
$alt_names .= "DNS.$count_dns = ".trim($value)."\n";
}
}
}
2021-03-07 18:57:17 +01:00
if (! $ip_addr == '') {
$ip_ar=explode("\n", $ip_addr);
foreach ($ip_ar as $value) {
if (! $value == '') {
$count_dns = $count_dns + 1;
$count_ip = $count_ip + 1;
# reetp IP should not be added to a DNS entry
#$alt_names .= "DNS.$count_dns = ".trim($value)."\n";
$alt_names .= "IP.$count_ip = ".trim($value)."\n";
}
}
}
if (($count_dns > 0) || ($count_ip > 0)) {
$server_altnames = "@alt_names";
} else {
$server_altnames = "DNS:$common_name,email:copy";
}
2021-03-07 18:57:17 +01:00
$configHOME = $config['home_dir'];
$configRANDFILE = $config['random'];
$configCa_dir = $config['ca_dir'];
$configCert_dir = $config['cert_dir'];
$configCrl_dir = $config['crl_dir'];
$configDatabase = $config['index'];
$configNew_certs_dir = $config['new_certs_dir'];
$configPrivate_dir = $config['private_dir'];
$configSerial = $config['serial'];
$configCacert_pem = $config['cacert_pem'];
$configCacrl_pem = $config['cacrl_pem'];
$configCakey = $config['cakey'];
$configDefault_md = $config['default_md'];
$configBase_url = $config['base_url'];
$configCrl_dist = $config['crl_distrib'];
$configComment_root = $config['comment_root'];
$configPolicy_url = $config['policy_url'];
$configRevoke_url = $config['revoke_url'];
$configComment_email = $config['comment_email'];
$configComment_sign = $config['comment_sign'];
$configComment_srv = $config['comment_srv'];
2021-03-07 18:57:17 +01:00
$cnf_contents = "
2021-03-07 18:57:17 +01:00
HOME = $configHOME
2021-03-02 13:05:19 +01:00
RANDFILE = $configRANDFILE
dir = $configCa_dir
2021-03-02 13:05:19 +01:00
certs = $configCert_dir
crl_dir = $configCrl_dir
2021-03-02 13:05:19 +01:00
database = $configDatabase
new_certs_dir = $configNew_certs_dir
private_dir = $configPrivate_dir
serial = $configSerial
certificate = $configCacert_pem
crl = $configCacrl_pem
private_key = $configCakey
crl_extentions = crl_ext
2012-02-28 08:23:39 +00:00
default_days = 365
default_crl_days = 30
preserve = no
2021-03-02 13:05:19 +01:00
default_md = $configDefault_md
2012-02-28 08:23:39 +00:00
[ req ]
default_bits = $keysize
string_mask = nombstr
prompt = no
distinguished_name = req_name
req_extensions = req_ext
[ req_name]
C=$country
ST=$province
L=$locality
0.O=$organization
1.O='$issuer'
OU=$unit
CN=$common_name
emailAddress=$email
[ ca ]
default_ca = email_cert
[ root_cert ]
x509_extensions = root_ext
default_days = 3650
policy = policy_supplied
[ email_cert ]
x509_extensions = email_ext
default_days = 365
policy = policy_supplied
[ email_signing_cert ]
x509_extensions = email_signing_ext
default_days = 365
policy = policy_supplied
[ server_cert ]
x509_extensions = server_ext
default_days = 365
policy = policy_supplied
[ vpn_cert ]
x509_extensions = vpn_client_server_ext
default_days = 365
policy = policy_supplied
2021-03-07 18:57:17 +01:00
2012-02-28 08:23:39 +00:00
[ time_stamping_cert ]
x509_extensions = time_stamping_ext
default_days = 365
policy = policy_supplied
[ policy_supplied ]
countryName = supplied
stateOrProvinceName = supplied
localityName = supplied
organizationName = supplied
organizationalUnitName = supplied
commonName = supplied
emailAddress = supplied
[ req_ext]
basicConstraints = CA:false
[ crl_ext ]
issuerAltName=issuer:copy
authorityKeyIdentifier=keyid:always,issuer:always
[ root_ext ]
basicConstraints = CA:true
keyUsage = cRLSign, keyCertSign
nsCertType = sslCA, emailCA, objCA
subjectKeyIdentifier = hash
subjectAltName = email:copy
2021-03-02 13:05:19 +01:00
crlDistributionPoints = URI:$configBase_url$configCrl_dist
nsComment = $configComment_root
#nsCaRevocationUrl =
2021-03-02 13:05:19 +01:00
nsCaPolicyUrl = $configBase_url$configPolicy_url
2012-02-28 08:23:39 +00:00
[ email_ext ]
basicConstraints = critical, CA:false
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = critical, emailProtection, clientAuth
nsCertType = critical, client, email
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always, issuer:always
subjectAltName = email:copy
issuerAltName = issuer:copy
2021-03-02 13:05:19 +01:00
crlDistributionPoints = URI:$configBase_url$configCrl_dist
nsComment = $configComment_email
nsBaseUrl = $configBase_url
nsRevocationUrl = $configBase_url$configRevoke_url$serial
nsCaPolicyUrl = $configBase_url$configPolicy_url
2012-02-28 08:23:39 +00:00
[ email_signing_ext ]
basicConstraints = critical, CA:false
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = critical, emailProtection, clientAuth, codeSigning
nsCertType = critical, client, email
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always, issuer:always
subjectAltName = email:copy
issuerAltName = issuer:copy
2021-03-02 13:05:19 +01:00
crlDistributionPoints = URI:$configBase_url$configCrl_dist
nsComment = $configComment_sign
nsBaseUrl = $configBase_url
nsRevocationUrl = $configBase_url$configRevoke_url$serial
nsCaPolicyUrl = $configBase_url$configPolicy_url
2012-02-28 08:23:39 +00:00
[ server_ext ]
basicConstraints = critical, CA:false
keyUsage = critical, digitalSignature, keyEncipherment
nsCertType = server
2012-02-28 08:23:39 +00:00
extendedKeyUsage = critical, serverAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always, issuer:always
2013-07-21 11:31:44 +02:00
subjectAltName = $server_altnames
2012-02-28 08:23:39 +00:00
issuerAltName = issuer:copy
2021-03-02 13:05:19 +01:00
crlDistributionPoints = URI:$configBase_url$configCrl_dist
nsComment = $configComment_srv
nsBaseUrl = $configBase_url
nsRevocationUrl = $configBase_url$configRevoke_url$serial
nsCaPolicyUrl = $configBase_url$configPolicy_url
2012-02-28 08:23:39 +00:00
[ time_stamping_ext ]
basicConstraints = CA:false
keyUsage = critical, nonRepudiation, digitalSignature
extendedKeyUsage = timeStamping
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always, issuer:always
subjectAltName = DNS:$common_name,email:copy
issuerAltName = issuer:copy
2021-03-02 13:05:19 +01:00
crlDistributionPoints = URI:$configBase_url$configCrl_dist
nsComment = $config[comment_stamp]
2021-03-02 13:05:19 +01:00
nsBaseUrl = $configBase_url
nsRevocationUrl = $configBase_url$configRevoke_url$serial
2012-02-28 08:23:39 +00:00
[ vpn_client_ext ]
basicConstraints = critical, CA:false
keyUsage = critical, digitalSignature
extendedKeyUsage = critical, clientAuth
nsCertType = critical, client
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always, issuer:always
subjectAltName = DNS:$common_name,email:copy
[ vpn_server_ext ]
basicConstraints = critical, CA:false
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = critical, serverAuth
nsCertType = critical, server
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always, issuer:always
subjectAltName = DNS:$common_name,email:copy
[ vpn_client_server_ext ]
basicConstraints = critical, CA:false
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = critical, serverAuth, clientAuth
nsCertType = critical, server, client
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always, issuer:always
subjectAltName = DNS:$common_name,email:copy
2013-07-21 11:31:44 +02:00
[alt_names]
$alt_names
2012-02-28 08:23:39 +00:00
";
2013-07-21 11:31:44 +02:00
# Write out the config file.
$cnf_file = tempnam('../../tmp', 'cnf-'); // Why is this not in the phpki dir ? why ../../ ?
$handle = fopen($cnf_file, "w");
fwrite($handle, $cnf_contents);
fclose($handle);
2021-03-07 18:57:17 +01:00
return($cnf_file);
2012-02-28 08:23:39 +00:00
}
//
// Search the certificate index and return resulting
// records in array[cert_serial_number][field_name].
// Fields: serial, country, province, locality, organization,
// issuer, unit, common_name, email
//
function CAdb_to_array($search = '.*')
{
global $config;
# Prepend a default status to search string if missing.
#if (! ereg('^\^\[.*\]', $search)) $search = '^[VRE].*'.$search;
if (! preg_match("/^\^\[.*\]/", $search)) {
$search = '^[VRE].*'.$search;
}
# Include valid certs?
#if (ereg('^\^\[.*V.*\]',$search)) $inclval = true;
if (preg_match('/^\^\[.*V.*\]/', $search)) {
$inclval = true;
}
# Include revoked certs?
#if (ereg('^\^\[.*R.*\]',$search)) $inclrev = true;
if (preg_match('/^\^\[.*R.*\]/', $search)) {
$inclrev = true;
}
# Include expired certs?
#if (ereg('^\^\[.*E.*\]',$search)) $inclexp = true;
if (preg_match('/^\^\[.*E.*\]/', $search)) {
$inclexp = true;
}
# There isn't really a status of 'E' in the openssl index.
# Change (E)xpired to (V)alid within the search string.
#$search = ereg_replace('^(\^\[.*)E(.*\])','\\1V\\2',$search);
$search = preg_replace('/^(\^\[.*)E(.*\])/', '${1}V${2}', $search);
$db = array();
exec('egrep -i '.escshellarg($search).' '.$config['index'], $x);
foreach ($x as $y) {
$i = CAdb_explode_entry($y);
if (($i['status'] == "Valid" && $inclval) || ($i['status'] == "Revoked" && $inclrev) || ($i['status'] == "Expired" && $inclexp)) {
$db[$i['serial']] = $i;
}
}
return($db);
2012-02-28 08:23:39 +00:00
}
//
// Returns an array containing the index record for
// certificate $serial.
//
function CAdb_get_entry($serial)
{
global $config;
$regexp = "^[VR]\t.*\t.*\t$serial\t.*\t.*$";
2021-03-07 18:57:17 +01:00
$x = exec('egrep '.escshellarg($regexp).' '.$config['index']);
if ($x) {
return CAdb_explode_entry($x);
} else {
return false;
}
2012-02-28 08:23:39 +00:00
}
//
// Returns the serial number of a VALID certificate matching
2012-02-28 08:23:39 +00:00
// $email and/or $name. Returns FALSE if no match is found.
//
function CAdb_in($email = "", $name = "")
{
global $config;
$email = escshellcmd($email);
$name = escshellcmd($name);
$regexp = "^[V].*CN=$name/(Email|emailAddress)=$email";
2021-03-07 18:57:17 +01:00
$x = exec('egrep '.escshellarg($regexp).' '.$config['index']);
2012-02-28 08:23:39 +00:00
if ($x) {
list($j,$j,$j,$serial,$j,$j) = explode("\t", $x);
return "$serial";
} else {
return false;
}
2012-02-28 08:23:39 +00:00
}
//
// Alias for CAdb_in()
//
function CAdb_serial($email, $name = '')
{
return CAdb_in($email, $name = '');
2012-02-28 08:23:39 +00:00
}
//
// Alias for CAdb_in()
//
function CAdb_exists($email, $name = '')
{
return CAdb_in($email, $name = '');
2012-02-28 08:23:39 +00:00
}
//
// Returns the certificate 'issuer'
//
function CAdb_issuer($serial)
{
global $config;
$rec = CAdb_get_entry($serial);
return $rec['issuer'];
2012-02-28 08:23:39 +00:00
}
//
// Returns an array containing the respective fields given a
// a raw line ($dbentry) from the certificate index.
// Fields: serial, country, province locality, organization,
2012-02-28 08:23:39 +00:00
// issuer, unit, common_name, email
//
function CAdb_explode_entry($dbentry)
{
$a = explode("\t", $dbentry);
$b = preg_split('/\/([A-Z]|[a-z])+=/', $a[5]);
switch ($a[0]) {
case "V":
$db['status'] = "Valid";
break;
case "R":
$db['status'] = "Revoked";
break;
}
// CA_cert_start/enddate
// A date will be returned in this format
// Feb 27 16:00:09 2020 GMT
// Add a 'digital' sort key for digital date sorting later
sscanf(CA_cert_startdate($a[3]), "%s%s%s%s", $mm, $dd, $tt, $yy);
$db['issued'] = strftime("%Y-%b-%d", strtotime("$yy-$mm-$dd"));
$db['issuedSort'] = strftime("%Y-%m-%d", strtotime("$yy-$mm-$dd"));
sscanf(CA_cert_enddate($a[3]), "%s%s%s%s", $mm, $dd, $tt, $yy);
$db['expires'] = strftime("%Y-%b-%d", strtotime("$yy-$mm-$dd"));
$db['expiresSort'] = strftime("%Y-%m-%d", strtotime("$yy-$mm-$dd"));
2021-03-07 18:57:17 +01:00
if (time() > strtotime("$yy-$mm-$dd")) {
$db['status'] = "Expired";
}
// Compatibility with migrated certs from openvpn-bridge
if (count($b) == 7) {
2021-03-07 18:57:17 +01:00
$db['serial'] = $a[3];
$db['country'] = $b[1];
$db['province'] = $b[2];
$db['locality'] = '';
$db['organization'] = $b[3];
$db['issuer'] = '';
$db['unit'] = $b[4];
$db['common_name'] = $b[5];
$db['email'] = $b[6];
} // Compatibility with renewed certs from openvpn-bridge
elseif (count($b) == 8) {
2021-03-07 18:57:17 +01:00
$db['serial'] = $a[3];
$db['country'] = $b[1];
$db['province'] = $b[2];
$db['locality'] = $b[3];
$db['organization'] = $b[4];
$db['issuer'] = '';
$db['unit'] = $b[5];
$db['common_name'] = $b[6];
$db['email'] = $b[7];
} // Else, it's a certificate created with phpki
else {
$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;
2012-02-28 08:23:39 +00:00
}
//
// 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.*$";
2021-03-07 18:57:17 +01:00
$x = exec('egrep '.escshellarg($regexp).' '.$config['index']);
2012-02-28 08:23:39 +00:00
if ($x) {
list($j,$j,$revoke_date,$j,$j,$j) = explode("\t", $x);
// Revoke date = 'R' + start date and is in this format
// 200227162209Z
sscanf($revoke_date, "%2s%2s%2s", $yy, $mm, $dd);
return strftime("%b %d, %Y", strtotime("$yy-$mm-$dd"));
} else {
return false;
}
2012-02-28 08:23:39 +00:00
}
//
// Returns TRUE if a certificate is valid, otherwise FALSE.
//
function CAdb_is_valid($serial)
{
global $config;
$regexp = "^V\t.*\t.*\t$serial\t.*\t.*$";
2012-02-28 08:23:39 +00:00
if (exec('egrep '.escshellarg($regexp).' '.$config['index'])) {
return true;
} else {
return false;
}
2012-02-28 08:23:39 +00:00
}
//
// 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'));
2012-02-28 08:23:39 +00:00
}
//
// Returns the long-form text of the Certificate Revocation List
// openssl crl -in crlfile -text
2012-02-28 08:23:39 +00:00
//
function CA_crl_text()
{
global $config;
$crlfile = $config['cacrl_pem'];
return(shell_exec(CRL.' -in '.escshellarg($crlfile).' -text 2>&1'));
2012-02-28 08:23:39 +00:00
}
2020-02-27 16:16:31 +01:00
// Returns the static takey.pem file
function ta_key_text()
{
2020-02-27 16:16:31 +01:00
global $config;
return(shell_exec('cat '.escshellarg($config['private_dir']).'/takey.pem 2>&1'));
}
// Returns the dhparam file
function dhparam_text()
{
2020-02-27 16:16:31 +01:00
global $config;
2020-03-19 01:05:16 +01:00
return(shell_exec('cat '.escshellarg($config['private_dir']).'/dhparam2048.pem 2>&1'));
2020-02-27 16:16:31 +01:00
}
// Returns the root CA certificate file (PEM Encoded)
function root_pem_text()
{
2020-02-27 16:16:31 +01:00
global $config;
return(shell_exec('cat '.escshellarg($config['cacert_pem']).' 2>&1'));
}
2012-02-28 08:23:39 +00:00
//
// 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));
2012-02-28 08:23:39 +00:00
}
//
// Returns the common name of a certificate.
//
function CA_cert_cname($serial)
{
global $config;
#return(ereg_replace('^.*/CN=(.*)/.*','\\1',CA_cert_subject($serial)));
return(preg_replace('/^.*\/CN=(.*)\/.*/', '${1}', CA_cert_subject($serial)));
2012-02-28 08:23:39 +00:00
}
//
// 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);
2012-02-28 08:23:39 +00:00
}
//
// 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));
2012-02-28 08:23:39 +00:00
}
//
// 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));
2012-02-28 08:23:39 +00:00
}
//
// Revokes a specified certificate.
//
function CA_revoke_cert($serial)
{
global $config;
2012-02-28 08:23:39 +00:00
$fd = fopen($config['index'], 'a');
flock($fd, LOCK_EX);
2012-02-28 08:23:39 +00:00
$certfile = $config['new_certs_dir'] . "/$serial.pem";
$cmd_output[] = 'Revoking the certificate.';
$configCa_pwd = $config['ca_pwd'];
$configOpenssl_cnf = $config['openssl_cnf'];
2021-03-08 13:23:58 +01:00
exec(CA." -config $configOpenssl_cnf -revoke ".escshellarg($certfile)." -passin pass:$configCa_pwd 2>&1", $cmd_output, $ret);
2012-02-28 08:23:39 +00:00
if ($ret == 0) {
unset($cmd_output);
list($ret, $cmd_output[]) = CA_generate_crl();
}
2021-03-07 18:57:17 +01:00
fclose($fd);
2012-02-28 08:23:39 +00:00
return array(($ret == true || $ret == 0 ? true : false), implode('<br>', $cmd_output));
2012-02-28 08:23:39 +00:00
}
//
// Creates a new certificate request, and certificate in various formats
// according to specified parameters. PKCS12 bundle files contain the
2012-02-28 08:23:39 +00:00
// 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 = 2048, $dns_names, $ip_addr)
{
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, $dns_names, $ip_addr, $serial);
# Escape certain dangerous characters in user input
$email = escshellcmd($email);
$_passwd = escshellarg($passwd);
$friendly_name = escshellarg($common_name);
$extensions = escshellarg($cert_type.'_ext');
2021-03-07 18:57:17 +01:00
# Create the certificate request
unset($cmd_output);
$cmd_output[] = 'Creating certificate request.';
if (($_passwd) && ($_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 -newkey rsa:$keysize -keyout '$userkey' -out '$userreq' -config '$cnf_file' -days '$expiry_days' -nodes 2>&1", $cmd_output, $ret);
}
2021-03-07 18:57:17 +01:00
# Sign the certificate request and create the certificate
if ($ret == 0) {
unset($cmd_output);
$cmd_output[] = "Signing $cert_type certificate request.";
$configCa_pwd = $config['ca_pwd'];
exec(CA." -config '$cnf_file' -in '$userreq' -out /dev/null -notext -days '$expiry_days' -passin pass:'$configCa_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.";
$configCacert_pem = $config['cacert_pem'];
$configOrganization = $config['organization'];
$configRandom = $config['random'];
2021-03-07 18:57:17 +01:00
if (($_passwd) && ($_passwd != "''")) {
$cmd_output[] = "infile: $usercert keyfile: $userkey outfile: $userpfx pass: $_passwd";
exec(PKCS12." -export -in '$usercert' -inkey '$userkey' -certfile '$configCacert_pem' -caname '$configOrganization' -out '$userpfx' -name $friendly_name -rand '$configRandom' -passin pass:$_passwd -passout pass:$_passwd 2>&1", $cmd_output, $ret);
} else {
$cmd_output[] = "infile: $usercert keyfile: $userkey outfile: $userpfx";
// reetp - this needs looking at
exec(PKCS12." -export -in '$usercert' -inkey '$userkey' -certfile '$configCacert_pem' -caname '$configOrganization' -out '$userpfx' -name $friendly_name -nodes -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) {
# 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("<br>", $cmd_output));
}
2012-02-28 08:23:39 +00:00
}
//
// Renews a specified certificate, revoking any existing valid versions.
// Uses old certificate request to Creates a new request, and certificate
2012-02-28 08:23:39 +00:00
// in various formats.
//
// Returns an array containing the output of failed openssl commands.
//
// FIXME: Yes, I know... This functions contains much duplicative code
2012-02-28 08:23:39 +00:00
// from CA_create_cert(). Bleh!
//
function CA_renew_cert($old_serial, $expiry, $passwd)
{
global $config;
# Do not 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['organization'];
$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;
}
2021-03-07 18:57:17 +01:00
$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);
$configCa_pwd = $config['ca_pwd'];
$configCacert_pem = $config['cacert_pem'];
$configOrganization = $config['organization'];
$configRandom = $config['random'];
2021-03-07 18:57:17 +01:00
# 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:'$configCa_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) && ($_passwd != "''")) {
$cmd_output[] = "infile: $usercert keyfile: $userkey outfile: $userpfx pass: $_passwd";
exec(PKCS12." -export -in '$usercert' -inkey '$userkey' -certfile '$configCacert_pem' -caname '$configOrganization' -out '$userpfx' -name $friendly_name -rand '$configRandom' -passin pass:$_passwd -passout pass:$_passwd 2>&1", $cmd_output, $ret);
} else {
$cmd_output[] = "infile: $usercert keyfile: $userkey outfile: $userpfx";
2020-03-04 13:16:57 +01:00
// reetp - this needs looking at
exec(PKCS12." -export -in '$usercert' -inkey '$userkey' -certfile '$configCacert_pem' -caname '$configOrganization' -out '$userpfx' -name $friendly_name -nodes -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);
}
};
2021-03-07 18:57:17 +01:00
#Unlock the CA database
fclose($fd);
// Why is this here?
2021-03-08 13:23:58 +01:00
//# https://github.com/radicand/phpki/issues/14 - but ereg is deprecated
if (preg_match('/E-mail Protection/', $certtext)) {
$cert_type = 'email';
}
2021-03-08 13:23:58 +01:00
if (preg_match('/E-mail Protection/', $certtext) && preg_match('/Code Signing/', $certtext)) {
$cert_type = 'email_signing';
}
2021-03-07 18:57:17 +01:00
#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);
2021-03-08 13:23:58 +01:00
if (preg_match_array('.*private key.*', $cmd_output)) {
$cmd_output[] = '<strong>This was likely caused by entering the wrong certificate password.</strong>';
} else {
$cmd_output[] = '<strong>Click on the "Help" link above for information on how to report this problem.</strong>';
}
return array(false, implode('<br>', $cmd_output));
}
2012-02-28 08:23:39 +00:00
}
//
// Creates a new Certificate Revocation List and copies it the the approriate
2012-02-28 08:23:39 +00:00
// locations. Returns error messages from failed commands.
//
function CA_generate_crl()
{
global $config;
2012-02-28 08:23:39 +00:00
$configOpenssl_cnf = $config['openssl_cnf'];
$configCacrl_pem = $config['cacrl_pem'];
$configCa_pwd = $config['ca_pwd'];
$configCacrl_der = $config['cacrl_der'];
$ret = 0;
2012-02-28 08:23:39 +00:00
$cmd_output[] = "Generating Certificate Revocation List.";
exec(CA. " -gencrl -config '$configOpenssl_cnf' -out '$configCacrl_pem' -passin pass:'$configCa_pwd' 2>&1", $cmd_output, $ret);
2012-02-28 08:23:39 +00:00
if ($ret == 0) {
unset($cmd_output);
$cmd_output[] = "Creating DER format Certificate Revocation List.";
2021-03-08 18:34:51 +01:00
exec(CRL." -in '$configCacrl_pem' -out '$configCacrl_der' -inform PEM -outform DER 2>&1", $cmd_output, $ret);
}
2012-02-28 08:23:39 +00:00
return array(($ret == 0 ? true : false), implode('<br>', $cmd_output));
2012-02-28 08:23:39 +00:00
}
//
// 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";
2021-03-07 18:57:17 +01:00
$configIndex = $config['index'];
# Wait here if another user has the database locked.
$fd = fopen($configIndex, '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 = $configIndex .'.tmp';
copy($configIndex, $tmpfile);
$regexp = "^[VR]\t.*\t.*\t".$serial."\t.*\t.*$";
exec('egrep -v '.escshellarg($regexp)." $tmpfile > $configIndex 2>/dev/null");
unlink($tmpfile);
fclose($fd);
2012-02-28 08:23:39 +00:00
}
//
// Returns the likely intended use for a specified certificate
2012-02-28 08:23:39 +00:00
// (email, server, vpn).
//
function CA_cert_type($serial)
{
$certtext = CA_cert_text($serial);
2021-03-08 13:23:58 +01:00
if (preg_match('~OpenSSL.* (E.?mail|Personal) .*Certificate~', $certtext) && preg_match('~Code Signing~', $certtext)) {
$cert_type = 'email_signing'; // Was 'codesigning' but can't see that anywhere
} elseif (preg_match('~OpenSSL.* (E.?mail|Personal) .*Certificate~', $certtext)) {
$cert_type = 'email';
2021-03-08 13:23:58 +01:00
} elseif (preg_match('~OpenSSL.* Server .*Certificate~', $certtext)) {
$cert_type = 'server';
2021-03-08 13:23:58 +01:00
} elseif (preg_match('~timeStamping|Time Stamping~', $certtext)) {
$cert_type = 'time_stamping';
2021-03-08 13:23:58 +01:00
} elseif (preg_match('~TLS Web Client Authentication~', $certtext) && preg_match('~TLS Web Server Authentication~', $certtext)) {
$cert_type = 'vpn_client_server';
2021-03-08 13:23:58 +01:00
} elseif (preg_match('~TLS Web Client Authentication~', $certtext)) {
$cert_type = 'vpn_client';
2021-03-08 13:23:58 +01:00
} elseif (preg_match('~TLS Web Server Authentication~', $certtext)) {
$cert_type = 'vpn_server';
} else {
$cert_type = 'vpn_client_server';
}
return $cert_type;
2012-02-28 08:23:39 +00:00
}
function CA_get_root_pem()
{
global $config;
return(file_get_contents($config['cacert_pem']));
2012-02-28 08:23:39 +00:00
}