29

I would like to be able to read the SSL certificate information with CURL. From the Linux console I get this response header:

GET https://www.google.com/ -ed
Cache-Control: private, max-age=0
Connection: close
Date: Sun, 20 Jun 2010 21:34:12 GMT
Server: gws
Content-Type: text/html; charset=ISO-8859-1
Expires: -1
Client-Date: Sun, 20 Jun 2010 21:34:18 GMT
Client-Peer: 66.102.13.106:443
Client-Response-Num: 1
Client-SSL-Cert-Issuer: /C=ZA/O=Thawte Consulting (Pty) Ltd./CN=Thawte SGC CA
Client-SSL-Cert-Subject: /C=US/ST=California/L=Mountain View/O=Google Inc/CN=www.google.com
Client-SSL-Cipher: RC4-SHA
Client-SSL-Warning: Peer certificate not verified
Set-Cookie: PREF=ID=4d56960f6e3ad831:TM=1277069652:LM=1277069652:S=GF-w8Yc-_61NBzzJ; expires=Tue, 19-Jun-2012 21:34:12 GMT; path=/; domain=.google.com
Title: Google
X-XSS-Protection: 1; mode=block

But with CURL the header is much shorter:

HTTP/1.1 200 OK
Date: Sun, 20 Jun 2010 21:39:07 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=UTF-8
Set-Cookie: PREF=ID=2d4fb1c933eebd09:TM=1277069947:LM=1277069947:S=6_TgGKzD0rM4IWms; expires=Tue, 19-Jun-2012 21:39:07 GMT; path=/; domain=.google.com
Server: gws
X-XSS-Protection: 1; mode=block
Transfer-Encoding: chunked

Is there any possibility to get these information, the full header with CURL or with some other PHP function?

Radek Suski
  • 1,352
  • 1
  • 13
  • 23

5 Answers5

46

You will get the certificate as a resource using stream_context_get_params. Plug that resource into $certinfo = openssl_x509_parse($cert['options']['ssl']['peer_certificate']); to get more certificate information.

$url = "http://www.google.com";
$orignal_parse = parse_url($url, PHP_URL_HOST);
$get = stream_context_create(array("ssl" => array("capture_peer_cert" => TRUE)));
$read = stream_socket_client("ssl://".$orignal_parse.":443", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $get);
$cert = stream_context_get_params($read);
$certinfo = openssl_x509_parse($cert['options']['ssl']['peer_certificate']);
print_r($certinfo);

Example result

Array
(
    [name] => /C=US/ST=California/L=Mountain View/O=Google Inc/CN=www.google.com
    [subject] => Array
        (
            [C] => US
            [ST] => California
            [L] => Mountain View
            [O] => Google Inc
            [CN] => www.google.com
        )

    [hash] => dcdd9741
    [issuer] => Array
        (
            [C] => US
            [O] => Google Inc
            [CN] => Google Internet Authority G2
        )

    [version] => 2
    [serialNumber] => 3007864570594926146
    [validFrom] => 150408141631Z
    [validTo] => 150707000000Z
    [validFrom_time_t] => 1428498991
    [validTo_time_t] => 1436223600
    [purposes] => Array
        (
            [1] => Array
                (
                    [0] => 1
                    [1] => 
                    [2] => sslclient
                )

            [2] => Array
                (
                    [0] => 1
                    [1] => 
                    [2] => sslserver
                )

            [3] => Array
                (
                    [0] => 1
                    [1] => 
                    [2] => nssslserver
                )

            [4] => Array
                (
                    [0] => 
                    [1] => 
                    [2] => smimesign
                )

            [5] => Array
                (
                    [0] => 
                    [1] => 
                    [2] => smimeencrypt
                )

            [6] => Array
                (
                    [0] => 1
                    [1] => 
                    [2] => crlsign
                )

            [7] => Array
                (
                    [0] => 1
                    [1] => 1
                    [2] => any
                )

            [8] => Array
                (
                    [0] => 1
                    [1] => 
                    [2] => ocsphelper
                )

        )

    [extensions] => Array
        (
            [extendedKeyUsage] => TLS Web Server Authentication, TLS Web Client Authentication
            [subjectAltName] => DNS:www.google.com
            [authorityInfoAccess] => CA Issuers - URI:http://pki.google.com/GIAG2.crt
OCSP - URI:http://clients1.google.com/ocsp

            [subjectKeyIdentifier] => FD:1B:28:50:FD:58:F2:8C:12:26:D7:80:E4:94:E7:CD:BA:A2:6A:45
            [basicConstraints] => CA:FALSE
            [authorityKeyIdentifier] => keyid:4A:DD:06:16:1B:BC:F6:68:B5:76:F5:81:B6:BB:62:1A:BA:5A:81:2F

            [certificatePolicies] => Policy: 1.3.6.1.4.1.11129.2.5.1

            [crlDistributionPoints] => URI:http://pki.google.com/GIAG2.crl

        )

)
starbeamrainbowlabs
  • 5,692
  • 8
  • 42
  • 73
Intekhab Khan
  • 1,775
  • 4
  • 18
  • 28
  • 4
    This is a better answer than the accepted answer was missing the key line `$certinfo = openssl_x509_parse($cert['options']['ssl']['peer_certificate']);` which I needed to check different details about my cert – greg_diesel Aug 13 '15 at 14:14
  • 1
    This is the solution I needed, but with a small change. I will be using this to check the cert validity period on localhost (so the cert is not valid for this "domain"), so I needed to change: `$get = stream_context_create(array("ssl" => array("capture_peer_cert" => TRUE)));` to `$get = stream_context_create(array("ssl" => array("capture_peer_cert" => TRUE, "verify_peer_name" => FALSE)));` – Whatts Jan 24 '17 at 15:53
  • When you need whole chain, use `capture_peer_cert_chain` and `peer_certificate_chain`. – Martin Prikryl Jun 18 '20 at 08:30
  • Even tho it's not recommended, I had to set (for few hosts, on which similar code wasn't working) ssl option `"verify_peer" => false`, otherwise I wasn't able to get the certificate data at all. – The Vojtisek Aug 25 '20 at 10:07
  • I've been using this code for a while now. But as of the October 1st 2021 LetsEncrypt root expiration, php is no longer able to make connections to domains that use LetsEncrypt certificates. Now, I have manually added their root cert to my OS and now browsers and curl work properly but PHP still does not. Does PHP keeps it's own store of root certs, ignoring the operating system's roots? How does that work? – l008com Oct 13 '21 at 12:07
25

No. EDIT: A CURLINFO_CERTINFO option has been added to PHP 5.3.2. See http://bugs.php.net/49253

Apparently, that information is being given to you by your proxy in the response headers. If you want to rely on that, you can use curl's CURLOPT_HEADER option to trueto include the headers in the output.

However, to retrieve the certificate without relying on some proxy, you must do

<?php
$g = stream_context_create (array("ssl" => array("capture_peer_cert" => true)));
$r = fopen("https://www.google.com/", "rb", false, $g);
$cont = stream_context_get_params($r);
var_dump($cont["options"]["ssl"]["peer_certificate"]);

You can manipulate the value of $cont["options"]["ssl"]["peer_certificate"] with the OpenSSL extension.

EDIT: This option is better since it doesn't actually make the HTTP request and does not require allow_url_fopen:

<?php
$g = stream_context_create (array("ssl" => array("capture_peer_cert" => true)));
$r = stream_socket_client("ssl://www.google.com:443", $errno, $errstr, 30,
    STREAM_CLIENT_CONNECT, $g);
$cont = stream_context_get_params($r);
var_dump($cont["options"]["ssl"]["peer_certificate"]);
Charles
  • 50,943
  • 13
  • 104
  • 142
Artefacto
  • 96,375
  • 17
  • 202
  • 225
  • So I will need the directive "allow_url_fopen" to get the certificate information? – Radek Suski Jun 20 '10 at 22:05
  • BTW: Why do you think that this connection is going through a proxy server? The CURLOPT_HEADER has been set. But the header seems not to be complete – Radek Suski Jun 20 '10 at 22:08
  • @Radek Suski Because google does not send such `Client-*` headers. – Artefacto Jun 20 '10 at 23:00
  • 1
    @Rad I've also included an option that doesn't require `allow_url_fopen`. – Artefacto Jun 20 '10 at 23:11
  • I find it hard to believe that CURL can't do this. CURL is the de facto low-level connection library which I found as an answer to problems even in FTP. I'll do my own tests to get this working, if possible. – Christian Jun 20 '10 at 23:26
  • @Chris You don't have to believe. You can see here http://svn.php.net/viewvc/php/php-src/trunk/ext/curl/interface.c?revision=299365&view=markup that peer certificate verification is done in an opaque manner by the curl library. The certificate never reaches the php extension. Maybe the curl library has some option to obtain the peer certificate (probably it does), but the extension does not seem to expose such functionality. – Artefacto Jun 20 '10 at 23:52
  • 1
    @Artefacto: Thank you very much. It works great and with openssl_x509_parse I was able to parse the certificate. @Chris: I have tried also to get certificate of my server and it does not work either. Funny thing is that the function curl_getinfo returns also an array called "certinfo" which is always empty. – Radek Suski Jun 21 '10 at 07:05
  • @Rad You're right, my bad, there's a certinfo array that's defined in another file. If it's not filled and `LIBCURL_VERSION_NUM > 0x071301`, that's probably a bug. – Artefacto Jun 21 '10 at 14:49
  • Even tho it's not recommended, I had to set (for few hosts, on which similar code wasn't working) ssl option `"verify_peer" => false`, otherwise I wasn't able to get the certificate data at all. – The Vojtisek Aug 25 '20 at 10:06
19

To do this in php and curl:

<?php
if($fp = tmpfile())
{
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL,"https://www.digicert.com/");
    curl_setopt($ch, CURLOPT_STDERR, $fp);
    curl_setopt($ch, CURLOPT_CERTINFO, 1);
    curl_setopt($ch, CURLOPT_VERBOSE, 1);
    curl_setopt($ch, CURLOPT_HEADER, 1);
    curl_setopt($ch, CURLOPT_NOBODY, 1);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST,  2);
    $result = curl_exec($ch);
    curl_errno($ch)==0 or die("Error:".curl_errno($ch)." ".curl_error($ch));
    fseek($fp, 0);//rewind
    $str='';
    while(strlen($str.=fread($fp,8192))==8192);
    echo $str;
    fclose($fp);
}
?>
velcrow
  • 6,336
  • 4
  • 29
  • 21
  • 2
    My example requires php 5.3.2, and the SSL cert information is available in $str, the HTTP headers are available in $result. – velcrow Oct 25 '10 at 22:44
  • This is the solution I needed to check not only if the SSL certificate exists but also if it has any errors or not. Thanks! – armandbertea Nov 27 '15 at 18:03
  • Just to add that $info = curl_getinfo($ch) will set the certificate information under in $info['certinfo']. – Juan Oct 24 '18 at 15:03
  • Even tho it's not recommended, I had to set (for few hosts, on which similar code wasn't working) ssl option `"verify_peer" => false`, otherwise I wasn't able to get the certificate data at all. – The Vojtisek Aug 25 '20 at 10:07
7

This code snippet isn't using curl specifically, but it retrieves and prints the remote certificate text (can be manipulated to return whatever detail you want using the various openssl_ functions)

$g = stream_context_create (array("ssl" => array("capture_peer_cert" => true)));
$r = fopen("https://somesite/my/path/", "rb", false, $g);
$cont = stream_context_get_params($r);
openssl_x509_export($cont["options"]["ssl"]["peer_certificate"],$cert);
print $cert;

outputs:

-----BEGIN CERTIFICATE-----
...certificate content...
-----END CERTIFICATE-----
Pancho
  • 2,043
  • 24
  • 39
-2

This could done the trick

[mulhasan@sshgateway-01 ~]$ curl --insecure -v https://yourdomain.com 2>&1 | awk 'BEGIN { cert=0 } /^\* SSL connection/ { cert=1 } /^\*/ { if (cert) print }'
Mansur Ul Hasan
  • 2,898
  • 27
  • 24