在PHP中使用Openssl验证SSL证书
我正在做以下工作:在PHP中使用Openssl验证SSL证书,php,ssl,openssl,certificate,Php,Ssl,Openssl,Certificate,我正在做以下工作: Generate CSR(Certificate Signing Request) Upload SSL Certificates 要生成SSL证书,我将使用以下方法: $privkey = openssl_pkey_new(); $csr = openssl_csr_new($dn, $privkey); $sscert = openssl_csr_sign($csr, null, $privkey, $days);
Generate CSR(Certificate Signing Request)
Upload SSL Certificates
要生成SSL证书,我将使用以下方法:
$privkey = openssl_pkey_new();
$csr = openssl_csr_new($dn, $privkey);
$sscert = openssl_csr_sign($csr, null, $privkey, $days);
openssl_csr_export($csr, $csrout);
openssl_pkey_export($privkey, $pkeyout, $_POST['password']);
openssl_pkey_export_to_file($privkey, "<path/to/store/server.key>");
openssl_csr_export_to_file($csr, "/tmp/".<domain-name>.".csr");
$privkey=openssl\u pkey\u new();
$csr=openssl\u csr\u new($dn,$privkey);
$sscert=openssl\u csr\u符号($csr,null,$privkey,$days);
openssl_csr_导出($csr,$csrout);
openssl_pkey_导出($privkey,$pkeyout,$_POST['password']);
openssl_pkey_导出到_文件($privkey,“”);
openssl_csr_导出到_文件($csr,“/tmp/”。.csr”);
现在使用CSR请求,我能够生成(domain name.cer),(DigitalCert.cer)
现在,一旦我上传了这个(.cer)证书,我需要验证这些证书
原因:有人在“a.com”上生成了这些证书,并试图在“b.com”上上载。这不应该发生,所以我想验证上传的SSL证书
在PHP中,我们有
$ok=openssl\u验证($data、$signature、$publikeyid)
但是,根据上述证书生成过程,我无法获得将被视为$data、$signature和$pubkeyid的内容。我就是这样做的
system('openssl x509 -noout -modulus -in '.$crt.' | openssl md5', $crt_md5);
system('openssl rsa -noout -modulus -in '.$key.' | openssl md5', $key_md5);
if($crt_md5 != $key_md5){
echo 'BAD';
}
这对我有用
$crt_md5=exec('openssl x509 -noout -modulus -in /path/to/domain.crt/ | openssl md5 | sed "s/^.* //"');
$key_md5=exec('openssl rsa -noout -modulus -in /path/to/server.key | openssl md5 | sed "s/^.* //"');
if($crt_md5 != $key_md5){
echo 'BAD';
}
else{
echo "GOOD";
}
sed“s/^.*/”-将从输出中删除(stdin)=东西,以便
你得到了精确的md5字符串
看看这个:
尝试openssl\u x509\u check\u private\u key($crt,$key)它返回布尔值
ref警告:openssl\u x509\u check\u private\u密钥在某些情况下不起作用
例如:
SSL证书如下所示:
此证书不以----end certificate-----结尾,但它仍然可以通过此函数的检查。它将返回true,告诉您它是正确的,但实际上不是。如果将此证书上载到应用程序(如Nginx),Nginx将告诉您一个错误
这似乎不是一个只出现在PHP中的错误。如果在命令行上使用openssl函数进行检查,它将告诉您相同的结果
所以我认为最好的办法是你需要检查证书的段落是否完整
确认格式正确后,使用此功能验证证书和私钥。感谢@dagon的快速回复。。但是我怎样才能拿到$key。。它是公钥还是私钥。。$key是实际的密钥或它的引用,谢谢..我尝试了上面的代码片段,并假设它是私钥(内容不是引用)。。它总是给我一根火柴。我从一个域生成了一个证书,并尝试为不同的域上载和验证上述代码。理想情况下,它应该给出“坏”。但它给了我“好”的其他条件。我重复了$crt_md5和$key_md5,两者都等于“1”,因此给了我“结果良好”。我使用了1个域的私钥和不同域的证书。我也尝试过CSR MD5,但仍然有一个匹配。为什么会这样?@VirenH.Ajmera,私钥和公钥将具有相同的模数。您是如何生成域证书的?您是否使用相同的私钥生成CSR/证书?如果是这样,那么您将始终获得与此支票匹配的支票。私钥的模与公钥中的模相同,它被放入CSR并最终放入证书中。因此,如果您对多个证书使用相同的私钥,它们都将具有相同的公钥,并且都具有相同的模数。@gtrig,谢谢您提供的信息。。我已经更新了我的代码库,它工作了。。但是如何验证给定域证书的CA证书。。我不能使用相同的代码,即openssl md5,因为它可能有不同的md5。。那么我如何验证CA证书。。我想这就是你想要做的吗?在上面的代码中,我不确定你为什么要导出CSR而不是$sscert。您是否正在使用输出CSR创建另一个证书?openssl_verify()方法通常不用于验证证书签名。谢谢@gtrig,我正在使用输出CSR创建另一个证书。如果您使用同一私钥创建多个证书,那么它们都将具有相同的公钥和模数。请参阅下面答案中的我的评论。
<?php
$server = "smtp.gmail.com"; // Who I connect to
$myself = "my_server.example.com"; // Who I am
$cabundle = '/etc/ssl/cacert.pem'; // Where my root certificates are
// Verify server. There's not much we can do, if we suppose that an attacker
// has taken control of the DNS. The most we can hope for is that there will
// be discrepancies between the expected responses to the following code and
// the answers from the subverted DNS server.
// To detect these discrepancies though, implies we knew the proper response
// and saved it in the code. At that point we might as well save the IP, and
// decouple from the DNS altogether.
$match1 = false;
$addrs = gethostbynamel($server);
foreach($addrs as $addr)
{
$name = gethostbyaddr($addr);
if ($name == $server)
{
$match1 = true;
break;
}
}
// Here we must decide what to do if $match1 is false.
// Which may happen often and for legitimate reasons.
print "Test 1: " . ($match1 ? "PASSED" : "FAILED") . "\n";
$match2 = false;
$domain = explode('.', $server);
array_shift($domain);
$domain = implode('.', $domain);
getmxrr($domain, $mxhosts);
foreach($mxhosts as $mxhost)
{
$tests = gethostbynamel($mxhost);
if (0 != count(array_intersect($addrs, $tests)))
{
// One of the instances of $server is a MX for its domain
$match2 = true;
break;
}
}
// Again here we must decide what to do if $match2 is false.
// Most small ISP pass test 2; very large ISPs and Google fail.
print "Test 2: " . ($match2 ? "PASSED" : "FAILED") . "\n";
// On the other hand, if you have a PASS on a server you use,
// it's unlikely to become a FAIL anytime soon.
// End of maybe-they-help-maybe-they-don't checks.
// Establish the connection
$smtp = fsockopen( "tcp://$server", 25, $errno, $errstr );
fread( $smtp, 512 );
// Here you can check the usual banner from $server (or in general,
// check whether it contains $server's domain name, or whether the
// domain it advertises has $server among its MX's.
// But yet again, Google fails both these tests.
fwrite($smtp,"HELO $myself\r\n");
fread($smtp, 512);
// Switch to TLS
fwrite($smtp,"STARTTLS\r\n");
fread($smtp, 512);
stream_set_blocking($smtp, true);
stream_context_set_option($smtp, 'ssl', 'verify_peer', true);
stream_context_set_option($smtp, 'ssl', 'allow_self_signed', false);
stream_context_set_option($smtp, 'ssl', 'capture_peer_cert', true);
stream_context_set_option($smtp, 'ssl', 'cafile', $cabundle);
$secure = stream_socket_enable_crypto($smtp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
stream_set_blocking($smtp, false);
$opts = stream_context_get_options($smtp);
if (!isset($opts["ssl"]["peer_certificate"]))
$secure = false;
else
{
$cert = openssl_x509_parse($opts["ssl"]["peer_certificate"]);
$names = '';
if ('' != $cert)
{
if (isset($cert['extensions']))
$names = $cert['extensions']['subjectAltName'];
elseif (isset($cert['subject']))
{
if (isset($cert['subject']['CN']))
$names = 'DNS:' . $cert['subject']['CN'];
else
$secure = false; // No exts, subject without CN
}
else
$secure = false; // No exts, no subject
}
$checks = explode(',', $names);
// At least one $check must match $server
$tmp = explode('.', $server);
$fles = array_reverse($tmp);
$okay = false;
foreach($checks as $check)
{
$tmp = explode(':', $check);
if ('DNS' != $tmp[0]) continue; // candidates must start with DNS:
if (!isset($tmp[1])) continue; // and have something afterwards
$tmp = explode('.', $tmp[1]);
if (count($tmp) < 3) continue; // "*.com" is not a valid match
$cand = array_reverse($tmp);
$okay = true;
foreach($cand as $i => $item)
{
if (!isset($fles[$i]))
{
// We connected to www.example.com and certificate is for *.www.example.com -- bad.
$okay = false;
break;
}
if ($fles[$i] == $item)
continue;
if ($item == '*')
break;
}
if ($okay)
break;
}
if (!$okay)
$secure = false; // No hosts matched our server.
}
if (!$secure)
die("failed to connect securely\n");
print "Success!\n";
// Continue with connection...
?>
-----BEGIN CERTIFICATE-----
xxxx
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
xxxx
xxxx