"No Status (default)", OCSP_REVOKED_STATUS_UNSPECIFIED => "Unspecified", OCSP_REVOKED_STATUS_KEYCOMPROMISE => "Key Compromise", OCSP_REVOKED_STATUS_CACOMPROMISE => "CA Compromise", OCSP_REVOKED_STATUS_AFFILIATIONCHANGED => "Affiliation Changed", OCSP_REVOKED_STATUS_SUPERSEDED => "Superseded", OCSP_REVOKED_STATUS_CESSATIONOFOPERATION => "Cessation of Operation", OCSP_REVOKED_STATUS_CERTIFICATEHOLD => "Certificate Hold" ); function & lookup_ca($refid) { global $config; if (is_array($config['ca'])) { foreach ($config['ca'] as & $ca) { if ($ca['refid'] == $refid) { return $ca; } } } return false; } function & lookup_ca_by_subject($subject) { global $config; if (is_array($config['ca'])) { foreach ($config['ca'] as & $ca) { $ca_subject = cert_get_subject($ca['crt']); if ($ca_subject == $subject) { return $ca; } } } return false; } function & lookup_cert($refid) { global $config; if (is_array($config['cert'])) { foreach ($config['cert'] as & $cert) { if ($cert['refid'] == $refid) { return $cert; } } } return false; } function & lookup_cert_by_name($name) { global $config; if (is_array($config['cert'])) { foreach ($config['cert'] as & $cert) { if ($cert['descr'] == $name) { return $cert; } } } } function & lookup_crl($refid) { global $config; if (is_array($config['crl'])) { foreach ($config['crl'] as & $crl) { if ($crl['refid'] == $refid) { return $crl; } } } return false; } function ca_chain_array(& $cert) { if ($cert['caref']) { $chain = array(); $crt = lookup_ca($cert['caref']); $chain[] = $crt; while ($crt) { $caref = $crt['caref']; if ($caref) { $crt = lookup_ca($caref); } else { $crt = false; } if ($crt) { $chain[] = $crt; } } return $chain; } return false; } function ca_chain(& $cert) { if ($cert['caref']) { $ca = ""; $cas = ca_chain_array($cert); if (is_array($cas)) { foreach ($cas as & $ca_cert) { $ca .= base64_decode($ca_cert['crt']); $ca .= "\n"; } } return $ca; } return ""; } function ca_import(& $ca, $str, $key = "", $serial = "") { global $config; $ca['crt'] = base64_encode($str); if (!empty($key)) { $ca['prv'] = base64_encode($key); } if (empty($serial)) { $ca['serial'] = 0; } else { $ca['serial'] = $serial; } $subject = cert_get_subject($str, false); $issuer = cert_get_issuer($str, false); // Find my issuer unless self-signed if ($issuer <> $subject) { $issuer_crt =& lookup_ca_by_subject($issuer); if ($issuer_crt) { $ca['caref'] = $issuer_crt['refid']; } } /* Correct if child certificate was loaded first */ if (is_array($config['ca'])) { foreach ($config['ca'] as & $oca) { $issuer = cert_get_issuer($oca['crt']); if ($ca['refid'] <> $oca['refid'] && $issuer == $subject) { $oca['caref'] = $ca['refid']; } } } if (is_array($config['cert'])) { foreach ($config['cert'] as & $cert) { $issuer = cert_get_issuer($cert['crt']); if ($issuer == $subject) { $cert['caref'] = $ca['refid']; } } } return true; } function ca_create(& $ca, $keylen, $lifetime, $dn, $digest_alg = "sha256") { $args = array( "x509_extensions" => "v3_ca", "digest_alg" => $digest_alg, "private_key_bits" => (int)$keylen, "private_key_type" => OPENSSL_KEYTYPE_RSA, "encrypt_key" => false); // generate a new key pair $res_key = openssl_pkey_new($args); if (!$res_key) { return false; } // generate a certificate signing request $res_csr = openssl_csr_new($dn, $res_key, $args); if (!$res_csr) { return false; } // self sign the certificate $res_crt = openssl_csr_sign($res_csr, null, $res_key, $lifetime, $args); if (!$res_crt) { return false; } // export our certificate data if (!openssl_pkey_export($res_key, $str_key) || !openssl_x509_export($res_crt, $str_crt)) { return false; } // return our ca information $ca['crt'] = base64_encode($str_crt); $ca['prv'] = base64_encode($str_key); $ca['serial'] = 0; return true; } function ca_inter_create(& $ca, $keylen, $lifetime, $dn, $caref, $digest_alg = "sha256") { // Create Intermediate Certificate Authority $signing_ca =& lookup_ca($caref); if (!$signing_ca) { return false; } $signing_ca_res_crt = openssl_x509_read(base64_decode($signing_ca['crt'])); $signing_ca_res_key = openssl_pkey_get_private(array(0 => base64_decode($signing_ca['prv']) , 1 => "")); if (!$signing_ca_res_crt || !$signing_ca_res_key) { return false; } $signing_ca_serial = ++$signing_ca['serial']; $args = array( "x509_extensions" => "v3_ca", "digest_alg" => $digest_alg, "private_key_bits" => (int)$keylen, "private_key_type" => OPENSSL_KEYTYPE_RSA, "encrypt_key" => false); // generate a new key pair $res_key = openssl_pkey_new($args); if (!$res_key) { return false; } // generate a certificate signing request $res_csr = openssl_csr_new($dn, $res_key, $args); if (!$res_csr) { return false; } // Sign the certificate $res_crt = openssl_csr_sign($res_csr, $signing_ca_res_crt, $signing_ca_res_key, $lifetime, $args, $signing_ca_serial); if (!$res_crt) { return false; } // export our certificate data if (!openssl_pkey_export($res_key, $str_key) || !openssl_x509_export($res_crt, $str_crt)) { return false; } // return our ca information $ca['crt'] = base64_encode($str_crt); $ca['prv'] = base64_encode($str_key); $ca['serial'] = 0; $ca['caref'] = $caref; return true; } function cert_import(& $cert, $crt_str, $key_str) { $cert['crt'] = base64_encode($crt_str); $cert['prv'] = base64_encode($key_str); $subject = cert_get_subject($crt_str, false); $issuer = cert_get_issuer($crt_str, false); // Find my issuer unless self-signed if ($issuer <> $subject) { $issuer_crt =& lookup_ca_by_subject($issuer); if ($issuer_crt) { $cert['caref'] = $issuer_crt['refid']; } } return true; } function cert_create(& $cert, $caref, $keylen, $lifetime, $dn, $type = "user", $digest_alg = "sha256") { $cert['type'] = $type; if ($type != "self-signed") { $cert['caref'] = $caref; $ca =& lookup_ca($caref); if (!$ca) { return false; } $ca_str_crt = base64_decode($ca['crt']); $ca_str_key = base64_decode($ca['prv']); $ca_res_crt = openssl_x509_read($ca_str_crt); $ca_res_key = openssl_pkey_get_private(array(0 => $ca_str_key, 1 => "")); if (!$ca_res_key) { return false; } if (empty($ca['serial'])) { $ca['serial'] = 0; } $ca_serial = ++$ca['serial']; } switch ($type) { case "ca": $cert_type = "v3_ca"; break; case "server": case "self-signed": $cert_type = "server"; break; default: $cert_type = "usr_cert"; break; } // in case of using Subject Alternative Names use other sections (with postfix '_san') // pass subjectAltName over environment variable 'SAN' if ($dn['subjectAltName']) { putenv("SAN={$dn['subjectAltName']}"); // subjectAltName can be set _only_ via configuration file $cert_type .= '_san'; unset($dn['subjectAltName']); } $args = array( "x509_extensions" => $cert_type, "digest_alg" => $digest_alg, "private_key_bits" => (int)$keylen, "private_key_type" => OPENSSL_KEYTYPE_RSA, "encrypt_key" => false); // generate a new key pair $res_key = openssl_pkey_new($args); if (!$res_key) { return false; } // If this is a self-signed cert, blank out the CA and sign with the cert's key if ($type == "self-signed") { $ca = null; $ca_res_crt = null; $ca_res_key = $res_key; $ca_serial = 0; $cert['type'] = "server"; } // generate a certificate signing request $res_csr = openssl_csr_new($dn, $res_key, $args); if (!$res_csr) { return false; } // sign the certificate using an internal CA $res_crt = openssl_csr_sign($res_csr, $ca_res_crt, $ca_res_key, $lifetime, $args, $ca_serial); if (!$res_crt) { return false; } // export our certificate data if (!openssl_pkey_export($res_key, $str_key) || !openssl_x509_export($res_crt, $str_crt)) { return false; } // return our certificate information $cert['crt'] = base64_encode($str_crt); $cert['prv'] = base64_encode($str_key); return true; } function csr_generate(& $cert, $keylen, $dn, $digest_alg = "sha256") { $args = array( "x509_extensions" => "v3_req", "digest_alg" => $digest_alg, "private_key_bits" => (int)$keylen, "private_key_type" => OPENSSL_KEYTYPE_RSA, "encrypt_key" => false); // generate a new key pair $res_key = openssl_pkey_new($args); if (!$res_key) { return false; } // generate a certificate signing request $res_csr = openssl_csr_new($dn, $res_key, $args); if (!$res_csr) { return false; } // export our request data if (!openssl_pkey_export($res_key, $str_key) || !openssl_csr_export($res_csr, $str_csr)) { return false; } // return our request information $cert['csr'] = base64_encode($str_csr); $cert['prv'] = base64_encode($str_key); return true; } function csr_complete(& $cert, $str_crt) { $str_key = base64_decode($cert['prv']); cert_import($cert, $str_crt, $str_key); unset($cert['csr']); return true; } function csr_get_subject($str_crt, $decode = true) { if ($decode) { $str_crt = base64_decode($str_crt); } $components = openssl_csr_get_subject($str_crt); if (empty($components) || !is_array($components)) { return "unknown"; } ksort($components); foreach ($components as $a => $v) { if (!strlen($subject)) { $subject = "{$a}={$v}"; } else { $subject = "{$a}={$v}, {$subject}"; } } return $subject; } function cert_get_subject($str_crt, $decode = true) { if ($decode) { $str_crt = base64_decode($str_crt); } $inf_crt = openssl_x509_parse($str_crt); $components = $inf_crt['subject']; if (empty($components) || !is_array($components)) { return "unknown"; } ksort($components); foreach ($components as $a => $v) { if (is_array($v)) { ksort($v); foreach ($v as $w) { $asubject = "{$a}={$w}"; $subject = (strlen($subject)) ? "{$asubject}, {$subject}" : $asubject; } } else { $asubject = "{$a}={$v}"; $subject = (strlen($subject)) ? "{$asubject}, {$subject}" : $asubject; } } return $subject; } function cert_get_subject_array($crt) { $str_crt = base64_decode($crt); $inf_crt = openssl_x509_parse($str_crt); $components = $inf_crt['subject']; if (!is_array($components)) { return; } $subject_array = array(); foreach ($components as $a => $v) { $subject_array[] = array('a' => $a, 'v' => $v); } return $subject_array; } function cert_get_subject_hash($crt) { $str_crt = base64_decode($crt); $inf_crt = openssl_x509_parse($str_crt); return $inf_crt['subject']; } function cert_get_sans($str_crt, $decode = true) { if ($decode) { $str_crt = base64_decode($str_crt); } $sans = array(); $crt_details = openssl_x509_parse($str_crt); if (!empty($crt_details['extensions']['subjectAltName'])) { $sans = explode(',', $crt_details['extensions']['subjectAltName']); } return $sans; } function cert_get_issuer($str_crt, $decode = true) { if ($decode) { $str_crt = base64_decode($str_crt); } $inf_crt = openssl_x509_parse($str_crt); $components = $inf_crt['issuer']; if (empty($components) || !is_array($components)) { return "unknown"; } ksort($components); foreach ($components as $a => $v) { if (is_array($v)) { ksort($v); foreach ($v as $w) { $aissuer = "{$a}={$w}"; $issuer = (strlen($issuer)) ? "{$aissuer}, {$issuer}" : $aissuer; } } else { $aissuer = "{$a}={$v}"; $issuer = (strlen($issuer)) ? "{$aissuer}, {$issuer}" : $aissuer; } } return $issuer; } /* Works for both RSA and ECC (crt) and key (prv) */ function cert_get_publickey($str_crt, $decode = true, $type = "crt") { if ($decode) { $str_crt = base64_decode($str_crt); } switch ($type) { case 'prv': exec("echo \"{$str_crt}\" | openssl pkey -pubout", $out); break; case 'crt': exec("echo \"{$str_crt}\" | openssl x509 -inform pem -noout -pubkey", $out); break; case 'csr': exec("echo \"{$str_crt}\" | openssl req -inform pem -noout -pubkey", $out); break; default: $out = array(); break; } return implode("\n", $out); } function cert_get_purpose($str_crt, $decode = true) { $extended_oids = array( "1.3.6.1.5.5.8.2.2" => "IP Security IKE Intermediate", ); if ($decode) { $str_crt = base64_decode($str_crt); } $crt_details = openssl_x509_parse($str_crt); $purpose = array(); if (!empty($crt_details['extensions']['keyUsage'])) { $purpose['ku'] = explode(',', $crt_details['extensions']['keyUsage']); foreach ($purpose['ku'] as & $ku) { $ku = trim($ku); if (array_key_exists($ku, $extended_oids)) { $ku = $extended_oids[$ku]; } } } else { $purpose['ku'] = array(); } if (!empty($crt_details['extensions']['extendedKeyUsage'])) { $purpose['eku'] = explode(',', $crt_details['extensions']['extendedKeyUsage']); foreach ($purpose['eku'] as & $eku) { $eku = trim($eku); if (array_key_exists($eku, $extended_oids)) { $eku = $extended_oids[$eku]; } } } else { $purpose['eku'] = array(); } $purpose['ca'] = (stristr($crt_details['extensions']['basicConstraints'], 'CA:TRUE') === false) ? 'No': 'Yes'; $purpose['server'] = (in_array('TLS Web Server Authentication', $purpose['eku'])) ? 'Yes': 'No'; return $purpose; } function cert_get_dates($str_crt, $decode = true) { if ($decode) { $str_crt = base64_decode($str_crt); } $crt_details = openssl_x509_parse($str_crt); if ($crt_details['validFrom_time_t'] > 0) { $start = date('r', $crt_details['validFrom_time_t']); } if ($crt_details['validTo_time_t'] > 0) { $end = date('r', $crt_details['validTo_time_t']); } return array($start, $end); } function cert_get_serial($str_crt, $decode = true) { if ($decode) { $str_crt = base64_decode($str_crt); } $crt_details = openssl_x509_parse($str_crt); if (isset($crt_details['serialNumber']) && !empty($crt_details['serialNumber'])) { return $crt_details['serialNumber']; } else { return NULL; } } function is_openvpn_server_ca($caref) { global $config; if (!is_array($config['openvpn']['openvpn-server'])) { return; } foreach ($config['openvpn']['openvpn-server'] as $ovpns) { if ($ovpns['caref'] == $caref) { return true; } } return false; } function is_openvpn_client_ca($caref) { global $config; if (!is_array($config['openvpn']['openvpn-client'])) { return; } foreach ($config['openvpn']['openvpn-client'] as $ovpnc) { if ($ovpnc['caref'] == $caref) { return true; } } return false; } function is_ipsec_peer_ca($caref) { global $config; if (!is_array($config['ipsec']['phase1'])) { return; } foreach ($config['ipsec']['phase1'] as $ipsec) { if ($ipsec['caref'] == $caref) { return true; } } return false; } function is_ldap_peer_ca($caref) { global $config; if (!is_array($config['system']['authserver'])) { return; } foreach ($config['system']['authserver'] as $authserver) { if ($authserver['ldap_caref'] == $caref) { return true; } } return false; } function ca_in_use($caref) { return (is_openvpn_server_ca($caref) || is_openvpn_client_ca($caref) || is_ipsec_peer_ca($caref) || is_ldap_peer_ca($caref)); } function is_user_cert($certref) { global $config; if (!is_array($config['system']['user'])) { return; } foreach ($config['system']['user'] as $user) { if (!is_array($user['cert'])) { continue; } foreach ($user['cert'] as $cert) { if ($certref == $cert) { return true; } } } return false; } function is_openvpn_server_cert($certref) { global $config; if (!is_array($config['openvpn']['openvpn-server'])) { return; } foreach ($config['openvpn']['openvpn-server'] as $ovpns) { if ($ovpns['certref'] == $certref) { return true; } } return false; } function is_openvpn_client_cert($certref) { global $config; if (!is_array($config['openvpn']['openvpn-client'])) { return; } foreach ($config['openvpn']['openvpn-client'] as $ovpnc) { if ($ovpnc['certref'] == $certref) { return true; } } return false; } function is_ipsec_cert($certref) { global $config; if (!is_array($config['ipsec']['phase1'])) { return; } foreach ($config['ipsec']['phase1'] as $ipsec) { if ($ipsec['certref'] == $certref) { return true; } } return false; } function is_webgui_cert($certref) { global $config; if (($config['system']['webgui']['ssl-certref'] == $certref) && ($config['system']['webgui']['protocol'] != "http")) { return true; } } function is_package_cert($certref) { $pluginparams = array(); $pluginparams['type'] = 'certificates'; $pluginparams['event'] = 'used_certificates'; $certificates_used_by_packages = pkg_call_plugins('plugin_certificates', $pluginparams); /* Check if any package is using certificate */ foreach ($certificates_used_by_packages as $name => $package) { if (is_array($package['certificatelist'][$certref]) && isset($package['certificatelist'][$certref]) > 0) { return true; } } } function is_captiveportal_cert($certref) { global $config; if (!is_array($config['captiveportal'])) { return; } foreach ($config['captiveportal'] as $portal) { if (isset($portal['enable']) && isset($portal['httpslogin']) && ($portal['certref'] == $certref)) { return true; } } return false; } function cert_in_use($certref) { return (is_webgui_cert($certref) || is_user_cert($certref) || is_openvpn_server_cert($certref) || is_openvpn_client_cert($certref) || is_ipsec_cert($certref) || is_captiveportal_cert($certref) || is_package_cert($certref)); } function cert_usedby_description($refid, $certificates_used_by_packages) { $result = ""; if (is_array($certificates_used_by_packages)) { foreach ($certificates_used_by_packages as $name => $package) { if (isset($package['certificatelist'][$refid])) { $hint = "" ; if (is_array($package['certificatelist'][$refid])) { foreach ($package['certificatelist'][$refid] as $cert_used) { $hint = $hint . $cert_used['usedby']."\n"; } } $count = count($package['certificatelist'][$refid]); $result .= "