3

I am working on below things:

Generate CSR(Certificate Signing Request)
Upload SSL Certificates

To generate SSL certificate I am using something like:

        $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");

Now using that CSR request, I am able to generate(domain-name.cer),(DigitalCert.cer).

Now once I upload this(.cer) certificates, I need to verify those certificates.

Reason: Someone generated these certificates on say "a.com" and tries to upload on "b.com". this should not happen, so I want to validate the uploaded SSL certificates.

In PHP, we have

$ok = openssl_verify($data, $signature, $pubkeyid);

but i am not able to get what things would be treated as $data, $signature and $pubkeyid based on the above certificate generation process.

tshepang
  • 12,111
  • 21
  • 91
  • 136
Viren H. Ajmera
  • 31
  • 1
  • 1
  • 4
  • 1
    http://stackoverflow.com/q/19955922/569976 I think that does what you're trying to do? – neubert Nov 27 '13 at 16:03
  • In your code above, I'm not sure why you are exporting the CSR instead of $sscert. Are you using the output CSR to create another certificate? The openssl_verify() method is not typically used for verifying certificate signatures. – gtrig Nov 28 '13 at 00:49
  • thanks @gtrig, I am using the output CSR to create another certificate.. – Viren H. Ajmera Nov 28 '13 at 07:56
  • If you are creating multiple certificates from the same private key, then they will all have the same public key and modulus. See my comment in the answer below. – gtrig Nov 28 '13 at 22:27

5 Answers5

3

Check this out: Verify SMTP in PHP

<?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...
?>
Community
  • 1
  • 1
authcate
  • 1,125
  • 6
  • 13
0

this is how i do it...

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';
}
Olie
  • 24,597
  • 18
  • 99
  • 131
  • Thanks @dagon for quick reply.. but how can I get $key.. is it a public key or private key.. and $key is actual key or its reference Thanks.. – Viren H. Ajmera Nov 26 '13 at 06:03
  • I tried with above snippet and assumed it is private key(content not reference).. it always gave me a match. I generated a certificate from one domain and tried to upload and validate the above code for different domain. Ideally it should give 'BAD'. But it gave me "GOOD" in else condition. I echoed the $crt_md5 and $key_md5, both equals to "1", hence gave me "Good as a result." I have used private key of 1 domain and cert of different domain. I have also tried with CSR MD5, still got a match. Why is this so? – Viren H. Ajmera Nov 26 '13 at 08:48
  • @VirenH.Ajmera, the private key and public key will have the same modulus. How are you generating the domain certificates? Are you using the same private key to generate the CSRs/Certificates? If so, then you will always get a match with this check. The modulus of the private key is the same as the modulus in the public key, which gets put into the CSR and eventually the certificate. So if you are using the same private key for multiple certificates, they will all have the same public key and will all have the same modulus. – gtrig Nov 28 '13 at 00:53
  • @gtrig, thanks for the info.. I have updated my codebase and it worked.. But how can i verify the CA certificate for the given domain certificate.. and I cannot use the same code i.e. openssl md5, as it may have different md5.. so how can i validate CA certificate.. – Viren H. Ajmera Nov 29 '13 at 03:19
0

This works for me

$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/^.* //" - will remove (stdin)= thing from the output, so that you get exact md5 string

Viren H. Ajmera
  • 31
  • 1
  • 1
  • 4
0

Try openssl_x509_check_private_key( $crt, $key ) it returns boolean

ref http://php.net/manual/en/function.openssl-x509-check-private-key.php

Surjit Sidhu
  • 377
  • 4
  • 14
0

WARNING: openssl_x509_check_private_key will not work for some case.

Example:

SSL certificate like this:

-----BEGIN CERTIFICATE-----
xxxx
-----END CERTIFICATE-----

-----BEGIN CERTIFICATE-----
xxxx
xxxx

This certificate does not end with -----END CERTIFICATE----- , but it can still pass the check of this function. It will return true to tell you that it is correct, but it is not actually. If you upload this certificate to your application, such as Nginx , Nginx will tell you an error.

This doesn't seem to be an error that only appears in PHP. If you check with the openssl function on the command line, it will tell you the same result.

So I think the best way is that you need to check whether the paragraphs of the certificate are complete.

After confirming that the format is correct, use this function to verify the certificate and private key.