1

I'm trying to create a simple PKI infrastucture for internal use, and I want to use the html <keygen> tag.

I know this tag sends an SPKAK to server, wich will have to sign it. Since I can't use exec to launch openssl, and have php 5.5, the only way to process SPKAK is with phpseclib.

This is my code:

<?PHP
    if(isset($_POST['key'])){
        header('Content-type: application/x-x509-user-cert');
        header('Content-disposition: attachment; filename=user.crt');
        include('File/X509.php');

        $capem = file_get_contents('root-ca.crt');

        $subject = new File_X509();
        $subject->loadCA($capem);
        $subject->loadSPKAC($_POST['key']);
        $subject->setDN('CN=Username');

        $issuer = new File_X509();
        $issuer->loadX509($capem);
        $cakey = new Crypt_RSA();
        $cakey->setPassword('SECRETPASSWORD'); 
        $cakey->loadKey(file_get_contents('root-ca.key'));
        $issuer->setPrivateKey($cakey);

        $x509 = new File_X509();
        $cert = $x509->sign($issuer, $subject);

        $x509->loadX509($cert);
        $x509->setExtension('id-ce-keyUsage', array('digitalSignature', 'keyEncipherment'));
        $x509->setStartDate('-1 day');
        $x509->setEndDate('+ 3 year');
        $x509->setSerialNumber('1235', 10);
        $cert = $x509->sign($issuer, $x509);

        echo $x509->saveX509($cert);
    }else{
?>
<form method="POST">
    <keygen name="key" keytype="RSA" challenge="ucert">
    <button>SEND</button>
</form>
<?PHP
    }
?>

The strange thing is that the generated certificate is valid (windows recognises it) but the browser (both Chrome and Firefox in my testings) doesn't recognise it, giving error 201 INVALID CERT, so it's not associated with the private key stored on browser.

What's the correct way to do this?

JohnKiller
  • 2,008
  • 18
  • 28
  • Are you using the X509 cert to do authentication or just to establish the https connection for a particular website? If the later you'll probably need to specify the the website. eg. `$subject->setDomain('www.google.com', 'www.yahoo.com')` or something. Also, if you were wanting to use it as a CA you'd need to do `$x509->makeCA();`. idk if that answers your question or not. – neubert Nov 24 '14 at 19:27
  • I want the certificate for client authentication. I've already built the CA certificate, and I can successfully create new valid certificates, even with this script. The problem is that Chrome gives error 201 instead of associating the generated certificate with its private key – JohnKiller Nov 24 '14 at 19:33
  • My guess is that it's missing some extension that's needed. Or maybe there's a keyUsage field that's needed. I can play around with this when I get home but if you can, yourself, generate a sample X509 cert that you can login with and then post it here via a pastebin.com link I might be able to do it while I'm at work idk. – neubert Nov 24 '14 at 21:03

1 Answers1

0
<?php
include('File/X509.php');
include('Crypt/RSA.php');

// create private key / x.509 cert for stunnel / website
$privKey = new Crypt_RSA();
extract($privKey->createKey());
$privKey->loadKey($privatekey);

$pubKey = new Crypt_RSA();
$pubKey->loadKey($publickey);
$pubKey->setPublicKey();

$subject = new File_X509();
$subject->setDNProp('id-at-organizationName', 'phpseclib demo cert');
$subject->setPublicKey($pubKey);

$issuer = new File_X509();
$issuer->setPrivateKey($privKey);
$issuer->setDN($subject->getDN());

$x509 = new File_X509();

$x509->loadX509($x509->saveX509($x509->sign($issuer, $subject)));

$x509->setExtension('id-ce-keyUsage', array('digitalSignature', 'keyEncipherment', 'dataEncipherment'));
$x509->setExtension('id-ce-extKeyUsage', array('id-kp-serverAuth', 'id-kp-clientAuth'));

$result = $x509->sign($issuer, $x509);

file_put_contents('key.pem', $privKey->getPrivateKey() . "\r\n" . $x509->saveX509($result));

exec('openssl pkcs12 -export -out file.pfx -in key.pem');

I was able to import the resultant file.pfx file into Google Chrome. It shows up now as a "Personal Certificate".

neubert
  • 15,947
  • 24
  • 120
  • 212
  • nope. The private key is generated by the tag, so it's already on the browser. I only need to sign the CSR (that's inside SPKAK) and then output it to the browser. and in fact this is working, the only problem is that chrome, instead of importing it automatically, gives me error 201 – JohnKiller Nov 27 '14 at 08:09
  • When I try to run your script through Google Chrome I get an error 207 - not an error 201. idk atm what the browsers are expecting. I had the signing cert in the browsers CA and it didn't seem to make a difference. Who knows tho - maybe it'd make a difference for you since you're getting a different error? Also, `$subject->loadCA()` has no effect. `loadCA()` is only used for verifying certs - not for creating them. – neubert Nov 27 '14 at 16:13
  • In Firefox I don't get any error - I just get asked if I want to download the file. I posted an attempt to figure out the error 207 at http://security.stackexchange.com/q/73810/15922 – neubert Nov 27 '14 at 16:54