doubi7306
doubi7306
2016-02-09 14:08

在PHP cURL中使用Windows AD Cert Auth证书

已采纳

I am running PHP 5.6 on a Windows Server 2008R2 server. I am trying to get TLS v1.2 communications working between this server and another internal server (running Windows Server 2003x64). We use internal Windows AD Root and Issuing Cert Authorities so our certificate chain is Root CA - Issuing CA - Server Cert. When I try and connect, I get the following error:

*   Trying 192.168.1.10...
* Connected to targetserver.example.com (192.168.1.10) port 443 (#0)
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: e:\php\cacert.pem
  CApath: none
* SSL certificate problem: unable to get local issuer certificate
* Closing connection 0

I have converted both the root CA cert and the issuing CA cert to PEM format (using openssl) and added them to the end of the cacert.pem file.

<?php
  function nxs_cURLTest($url, $msg, $testText){  
    $ch = curl_init(); 
    curl_setopt($ch, CURLOPT_URL, $url); 
    curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.73 Safari/537.36"); 
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 
    curl_setopt($ch, CURLOPT_TIMEOUT, 10); 
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
    curl_setopt($ch, CURLOPT_VERBOSE, true);
    curl_setopt($ch, CURLOPT_CAINFO, "e:\\php\\cacert.pem");

    $verbose = fopen('php://temp', 'w+');
    curl_setopt($ch, CURLOPT_STDERR, $verbose);    

    $response = curl_exec($ch); 
    $errmsg = curl_error($ch); 
    $cInfo = curl_getinfo($ch); 
    curl_close($ch); 
    echo "<br />Testing ... ".$url." - ".$cInfo['url']."<br />";
    if (stripos($response, $testText)!==false) 
      echo "....<b style='color:green;'>".$msg." - OK</b><br />"; 
    else 
    { 
      echo "....<b style='color:red;'>".$msg." - Problem</b><br /><pre>"; 
      print_r($errmsg); 
      print_r($cInfo); 
      print_r(strlen($response) . " bytes received."); 
      print_r(htmlentities($response)); 

      rewind($verbose);
      $verboseLog = stream_get_contents($verbose);
      echo "<br />Verbose output:</br />";
      echo "<pre>", htmlspecialchars($verboseLog), "</pre>";    

      echo "</pre>There is a problem with cURL. You need to contact your server admin or hosting provider.<br />";
    }
  }

  nxs_cURLTest("https://targetserver.example.com/curl/", "HTTPS to TargetServer", "Document contents");
?>

If I try to just use cURL from the command line, then I get a different error (EDIT: forced tls 1.0 as Win Serv 2003 doesn't support anything higher):

E:\openssl>curl -Iv --tlsv1.0 "https://targetserver.example.com"
* Rebuilt URL to: https://targetserver.example.com/
*   Trying 192.168.1.10...
* Connected to targetserver.example.com (192.168.1.10) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: E:\openssl\curl-ca-bundle.crt
  CApath: none
* TLSv1.0 (OUT), TLS header, Certificate Status (22):
* TLSv1.0 (OUT), TLS handshake, Client hello (1):
* TLSv1.0 (IN), TLS handshake, Server hello (2):
* TLSv1.0 (IN), TLS handshake, Certificate (11):
* TLSv1.0 (OUT), TLS alert, Server hello (2):
* SSL certificate problem: unable to get local issuer certificate
* Closing connection 0
* TLSv1.0 (OUT), TLS alert, Client hello (1):
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.html

curl performs SSL certificate verification by default, using a "bundle"
 of Certificate Authority (CA) public keys (CA certs). If the default
 bundle file isn't adequate, you can specify an alternate file
 using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
 the bundle, the certificate verification probably failed due to a
 problem with the certificate (it might be expired, or the name might
 not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
 the -k (or --insecure) option.

(note: ca-bundle.crt is a renamed copy of cacert.pem from above)

If I connect to a different server that runs Server 2008R2 but that also has its certificate issued from our internal CA I get the following:

E:\openssl>curl -Iv --tlsv1.0 "https://int-winsrv2008.example.com"
* Rebuilt URL to: https://int-winsrv2008.example.com/
*   Trying 192.168.1.6...
* Connected to int-winsrv2008.example.com (192.168.1.6) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: E:\openssl\curl-ca-bundle.crt
  CApath: none
* TLSv1.0 (OUT), TLS header, Certificate Status (22):
* TLSv1.0 (OUT), TLS handshake, Client hello (1):
* TLSv1.0 (IN), TLS handshake, Server hello (2):
* TLSv1.0 (IN), TLS handshake, Certificate (11):
* TLSv1.0 (IN), TLS handshake, Server key exchange (12):
* TLSv1.0 (IN), TLS handshake, Server finished (14):
* TLSv1.0 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.0 (OUT), TLS change cipher, Client hello (1):
* TLSv1.0 (OUT), TLS handshake, Finished (20):
* TLSv1.0 (IN), TLS change cipher, Client hello (1):
* TLSv1.0 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.0 / ECDHE-RSA-AES256-SHA
* ALPN, server did not agree to a protocol
* Server certificate:
*        subject: C=KY; ST=Grand Cayman; L=George Town; O=Port Authority of the Cayman Islands; OU=IT Department; CN=int-winsrv2008.example.com
*        start date: Aug 10 15:26:14 2015 GMT
*        expire date: Aug  9 15:26:14 2017 GMT
*        common name: int-winsrv2008.example.com (matched)
*        issuer: DC=com; DC=example; CN=example-Issuing-CA
*        SSL certificate verify ok.
> HEAD / HTTP/1.1
> Host: int-winsrv2008.example.com
> User-Agent: curl/7.47.1
> Accept: */*
>
* TLSv1.0 (IN), TLS handshake, Hello request (0):
* TLSv1.0 (OUT), TLS handshake, Client hello (1):
* TLSv1.0 (IN), TLS handshake, Server hello (2):
* TLSv1.0 (IN), TLS handshake, Certificate (11):
* TLSv1.0 (IN), TLS handshake, Server key exchange (12):
* TLSv1.0 (IN), TLS handshake, Request CERT (13):
* TLSv1.0 (IN), TLS handshake, Server finished (14):
* TLSv1.0 (OUT), TLS handshake, Certificate (11):
* TLSv1.0 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.0 (OUT), TLS change cipher, Client hello (1):
* TLSv1.0 (OUT), TLS handshake, Finished (20):
* TLSv1.0 (IN), TLS change cipher, Client hello (1):
* TLSv1.0 (IN), TLS handshake, Finished (20):
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Content-Length: 689
Content-Length: 689
< Content-Type: text/html
Content-Type: text/html
< Last-Modified: Thu, 18 Jul 2013 15:46:23 GMT
Last-Modified: Thu, 18 Jul 2013 15:46:23 GMT
< Accept-Ranges: bytes
Accept-Ranges: bytes
< ETag: "f12a46f4cd83ce1:0"
ETag: "f12a46f4cd83ce1:0"
< Server: Microsoft-IIS/7.5
Server: Microsoft-IIS/7.5
< X-Powered-By: ASP.NET
X-Powered-By: ASP.NET
< Date: Tue, 09 Feb 2016 15:18:51 GMT
Date: Tue, 09 Feb 2016 15:18:51 GMT

<
* Connection #0 to host int-winsrv2008.example.com left intact

So it seems it's something to do with Win Server 2003. What am I missing?

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

1条回答

  • duanjian9148 duanjian9148 5年前

    For completeness sake, I thought I would answer this with what I found out.

    There was a few different errors occurring here. First, I was trying to use TLS1.2 which is not supported by Windows Server 2003. Second, I am using host headers which are not evaluated at the point of the certificate handshake (unless you use SNI which is only supported by IIS 8/Win Serv 2012) so I was not getting our internal certificate but our default wildcard cert issued by Comodo. Finally, there seems to be an issue with the Comodo certificate chain on our server (seems to be missing one of the root or intermediate certs).

    点赞 评论 复制链接分享