\n"; if ($this_hash == $hash) { $aryr[] = $a; } if ($code != 0) return false; } else { $out = array(); exec(OPENSSL_EXEC.' x509 -subject_hash -noout -in '.escapeshellarg($a), $out, $code); if ($code != 0) return false; $this_hash = trim($out[0]); unset($out); # echo "CERT $a : $this_hash == $hash
\n"; if ($this_hash == $hash) { $ary[] = $a; } } } $found = false; # echo "Searching issuer for $infile... (Hash = $hash)
\n"; foreach ($ary as &$a) { if (in_array($a, $aryr)) continue; # echo "Check $a...
\n"; if (x_509_matching_issuer($infile, $a)) { # echo "Found! New file is $a
\n"; $found = true; $infile = $a; if (in_array($a, $chain)) { # echo "Finished.\n"; return $chain; } $chain[] = $a; break; } } if (!$found) { # echo "No issuer found!\n"; return false; } } } function x_509_get_ocsp_uris($infile) { exec(OPENSSL_EXEC.' x509 -ocsp_uri -in '.escapeshellarg($infile).' -noout', $out, $code); if ($code != 0) return false; return $out; } // TODO: Needs caching, otherwise the page is too slow function x_509_ocsp_check_chain($infile, $CApath) { $x = x_509_chain($infile, $CApath); if ($x === false) { return 'Error: Could not complete chain!'; } # echo 'Chain: '; # print_r($x); $found_ocsp = false; $diag_nonce_err = false; $diag_verify_err = false; $diag_revoked = false; $diag_unknown = false; foreach ($x as $n => &$y) { if (isset($x[$n+1])) { $issuer = $x[$n+1]; } else { $issuer = $y; // Root } $uris = x_509_get_ocsp_uris($y); foreach ($uris as &$uri) { $found_ocsp = true; $out = array(); $xx = parse_url($uri); $host = $xx['host']; # $cmd = OPENSSL_EXEC." ocsp -issuer ".escapeshellarg($issuer)." -cert ".escapeshellarg($y)." -url ".escapeshellarg($uri)." -CApath ".escapeshellarg($CApath)." -VAfile ".escapeshellarg($issuer)." -nonce -header 'HOST' ".escapeshellarg($host)." -header 'User-Agent' 'Mozilla/5.0 (Windows NT 6.1; rv23.0) Gecko/20100101 Firefox/23.0' 2>&1" /* -text */; # TODO: trusted.pem nicht hartcoden $cmd = OPENSSL_EXEC." ocsp -issuer ".escapeshellarg($issuer)." -cert ".escapeshellarg($y)." -url ".escapeshellarg($uri)." -CAfile ".escapeshellarg($CApath.'/../trusted.pem')." -VAfile ".escapeshellarg($issuer)." -nonce -header 'HOST' ".escapeshellarg($host)." -header 'User-Agent' 'Mozilla/5.0 (Windows NT 6.1; rv23.0) Gecko/20100101 Firefox/23.0' 2>&1" /* -text */; #echo $cmd; exec($cmd, $out, $code); if ($code != 0) { if (($out[0] == 'Error querying OCSP responsder') || ($out[0] == 'Error querying OCSP responder')) { # TODO: openssl has a typo 'Error querying OCSP responsder' # TODO: why does this error occour for comodo CA? return "Error querying OCSP responder (Code $code)"; } # print_r($out); return 'Error: OpenSSL-Exec failure ('.$code.')!'; } $outc = implode("\n", $out); if (strpos($outc, "Response verify OK") === false) $diag_verify_err = true; if (strpos($outc, "WARNING: no nonce in response") !== false) $diag_nonce_err = true; # We are currently not watching for other warnings (ToDo) if (strpos($outc, "$y: unknown") !== false) { $diag_unknown = true; } else if (strpos($outc, "$y: revoked") !== false) { $diag_revoked = true; } else if (strpos($outc, "$y: good") === false) { #echo "C = $outc
\n"; # TODO: # COMODO sagt # C = Responder Error: unauthorized # STARTCOM sagt # C = Responder Error: malformedrequest return "Error: Unexpected OCSP state! ($outc)"; } # print_r($out); unset($out); } } # echo "Found OCSP = ".($found_ocsp ? 1 : 0)."\n"; # echo "Diag Nonce Error = ".($diag_nonce_err ? 1 : 0)."\n"; # echo "Diag Verify Error = ".($diag_verify_err ? 1 : 0)."\n"; # echo "Diag Revoked Error = ".($diag_revoked ? 1 : 0)."\n"; # echo "Diag Unknown Error = ".($diag_unknown ? 1 : 0)."\n"; if (!$found_ocsp) { return 'No OCSP responders found in chain.'; } if ($diag_verify_err) { return 'Error: OCSP Verification failure!'; } if ($diag_revoked) { return 'Error: Some certs are revoked!'; } if ($diag_unknown) { return 'Warning: Some certs have unknown state!'; } if ($diag_nonce_err) { return 'OK, but NONCE missing'; } return 'OK'; } function _opensslVerify($cert, $mode = 0, $crl_mode = 0) { # mode # 0 = cert is a file # 1 = cert is pem string # crl_mode # 0 = no crl check # 1 = 1 crl check # 2 = all crl check $params = ''; if ($crl_mode == 0) { $params = ''; } else if ($crl_mode == 1) { $params = '-crl_check '; } else if ($crl_mode == 2) { $params = '-crl_check_all '; } else { return false; } if ($mode == 0) { # $cmd = OPENSSL_EXEC.' verify '.$params.' -CApath '.escapeshellarg(__DIR__.'/../ca/trusted/').' '.escapeshellarg($cert); $cmd = OPENSSL_EXEC.' verify '.$params.' -CAfile '.escapeshellarg(__DIR__.'/../ca/trusted.pem').' '.escapeshellarg($cert); } else if ($mode == 1) { # $cmd = 'echo '.escapeshellarg($cert).' | '.OPENSSL_EXEC.' verify '.$params.' -CApath '.escapeshellarg(__DIR__.'/../ca/trusted/'); $cmd = 'echo '.escapeshellarg($cert).' | '.OPENSSL_EXEC.' verify '.$params.' -CAfile '.escapeshellarg(__DIR__.'/../ca/trusted.pem'); } else { return false; } $out = array(); exec($cmd, $out, $code); if ($code != 0) return false; return $out; } function opensslVerify($cert, $mode = 0) { # 0 = cert is a file # 1 = cert is pem string $out = _opensslVerify($cert, $mode, 0); if ($out === false) return 'Internal error'; $outtext = implode("\n", $out); $out_crl = _opensslVerify($cert, $mode, 2); if ($out_crl === false) return 'Internal error'; $outtext_crl = implode("\n", $out_crl); if (strpos($outtext, "unable to get local issuer certificate") !== false) { return 'CA unknown'; } else if (strpos($outtext, "certificate signature failure") !== false) { return 'Fraudulent!'; } $stat_expired = (strpos($outtext, "certificate has expired") !== false); $stat_revoked = (strpos($outtext_crl, "certificate revoked") !== false); # (ToDo) We are currently not looking for warnings # $stat_crl_expired = (strpos($outtext_crl, "CRL has expired") !== false); if ($stat_expired && $stat_revoked) { return 'Expired & Revoked'; } else if ($stat_revoked) { return 'Revoked'; } else if ($stat_expired) { return 'Expired'; } if (strpos($out[0], ': OK') !== false) { return 'Verified'; } return 'Unknown error'; } function getTextdump($cert, $mode = 0, $format = 0) { # mode # 0 = cert is a file # 1 = cert is pem string # format # 0 = normal # 1 = nameopt if ($format == 0) { $params = ''; } else if ($format == 1) { $params = ' -nameopt "esc_ctrl, esc_msb, sep_multiline, space_eq, lname"'; } else { return false; } if ($mode == 0) { exec(OPENSSL_EXEC.' x509 -noout -text'.$params.' -in '.escapeshellarg($cert), $out, $code); } else if ($mode == 1) { exec('echo '.escapeshellarg($cert).' | '.OPENSSL_EXEC.' x509 -noout -text'.$params, $out, $code); } else { return false; } if ($code != 0) return false; $text = implode("\n", $out); $text = str_replace("\n\n", "\n", $text); # TODO: repeat until no \n\n exist anymore return $text; } function getAttributes($cert, $mode = 0, $issuer = false, $longnames = false) { # mode # 0 = cert is a file # 1 = cert is pem string if ($longnames) { $params = ' -nameopt "esc_ctrl, esc_msb, sep_multiline, space_eq, lname"'; } else { $params = ' -nameopt "esc_ctrl, esc_msb, sep_multiline, space_eq"'; } if ($issuer) { $params .= ' -issuer'; } else { $params .= ' -subject'; } if ($mode == 0) { exec(OPENSSL_EXEC.' x509 -noout'.$params.' -in '.escapeshellarg($cert), $out, $code); } else if ($mode == 1) { exec('echo '.escapeshellarg($cert).' | '.OPENSSL_EXEC.' x509 -noout'.$params, $out, $code); } else { return false; } $attributes = array(); foreach ($out as $n => &$o) { if ($n == 0) continue; preg_match("| (.*) = (.*)$|ismU", $o, $m); if (!isset($attributes[$m[1]])) $attributes[$m[1]] = array(); $attributes[$m[1]][] = $m[2]; } return $attributes; } function openssl_get_sig_base64($cert, $mode = 0) { # mode # 0 = cert is a file # 1 = cert is pem string $params = ''; $out = array(); if ($mode == 0) { exec(OPENSSL_EXEC.' x509 -noout'.$params.' -in '.escapeshellarg($cert), $out, $code); } else if ($mode == 1) { exec('echo '.escapeshellarg($cert).' | '.OPENSSL_EXEC.' x509 -noout'.$params, $out, $code); } else { return false; } $dump = implode("\n", $out); /* Signature Algorithm: sha1WithRSAEncryption 65:f0:6f:f0:1d:66:a4:fe:d1:38:85:6f:5e:06:7b:f3:a7:08: ... 1a:13:37 */ $regex = "@\n {4}Signature Algorithm: (\S+)\n(( {8}([a-f0-9][a-f0-9]:){18}\n)* {8}([a-f0-9][a-f0-9](:[a-f0-9][a-f0-9]){0,17}\n))@sm"; preg_match_all($regex, "$dump\n", $m); if (!isset($m[2][0])) return false; $x = preg_replace("@[^a-z0-9]@", "", $m[2][0]); $x = hex2bin($x); return base64_encode($x); }