Mar 22, 2013

Managing X.509 certificates using PHP



To generate, sign and verify certificates you need to have OpenSSL module installed and provide file with OpenSSL configuration.


Here is an example OpenSSL configuration file:

 [ req ]
 default_bits           = 1024
 default_keyfile        = privkey.pem
 distinguished_name     = req_distinguished_name
 attributes             = req_attributes
 x509_extensions        = v3_ca
 dirstring_type = nobmp
 [ req_distinguished_name ]
 countryName                    = Country Name (2 letter code)
 countryName_default            = AU
 countryName_min                = 2
 countryName_max                = 2
 localityName                   = Locality Name (eg, city)
 organizationalUnitName         = Organizational Unit Name (eg, section)
 commonName                     = Common Name (eg, YOUR name)
 commonName_max                 = 64
 emailAddress                   = Email Address
 emailAddress_max               = 40
 [ req_attributes ]
 challengePassword              = A challenge password
 challengePassword_min          = 4
 challengePassword_max          = 20
 [ v3_ca ]
 subjectKeyIdentifier=hash
 authorityKeyIdentifier=keyid:always,issuer:always
 basicConstraints = CA:true

Name it as openssl.cnf and put in the same folder, where you want to work with certificates.

Here are some basic actions you can perform with X.509 certificates.

Create certificate with public key, private key and certificate's signature:

<?php
// Returns a cer encoded SSL certificate
function create_identity_cer(
    $countryName,  $stateOrProvinceName, $localityName, $organizationName,
    $organizationalUnitName, $commonName, $emailAddress, $foafLocation)
{
    // Create the DN array for the openssl function calls
    if ($countryName)
        $dn = array("countryName" => $countryName);

    if ($stateOrProvinceName)
    {  
        if ($dn)
            $dn = array_merge($dn,
                array("stateOrProvinceName" => $stateOrProvinceName));
        else
            $dn = array("stateOrProvinceName" => $stateOrProvinceName);
    }

    if ($localityName)
    {
        if ($dn)
            $dn = array_merge($dn, array("localityName" => $localityName));
        else
            $dn = array("localityName" => $localityName);
    }

    if ($organizationName)
    {
        if ($dn)
            $dn = array_merge($dn, array("organizationName" => $organizationName));
        else
            $dn = array("organizationName" => $organizationName);
    }

    if ($organizationalUnitName)
    {
        if ($dn)
            $dn = array_merge($dn,
                array("organizationalUnitName" => $organizationalUnitName));
        else
            $dn = array("organizationalUnitName" => $organizationalUnitName);
    }

    if ($commonName)
    {
        if ($dn)
            $dn = array_merge($dn, array("commonName" => $commonName));
        else
            $dn = array("commonName" => $commonName);
    }

    if ($emailAddress)
    {
        if ($dn)
            $dn = array_merge($dn, array("emailAddress" => $emailAddress));
        else
            $dn = array("emailAddress" => $emailAddress);
    }

    // if the $dn array is NULL at this point set country name to the default of GB
    if (!$dn)
        $dn = array("countryName" => "GB");

    // Setup the contents of the subjectAltName
    if ($foafLocation)
        $SAN="URI:$foafLocation";

    if ($emailAddress)
    {
        if ($SAN)
            $SAN.=",email:$emailAddress";
        else
            $SAN="email:$emailAddress";
    }

    // Export the subjectAltName to be picked up by the openssl.cnf file
    if ($SAN)
    {
        putenv("SAN=$SAN");
    }

    // Create the array to hold the configuration options for the openssl function calls
    // You may need to change the slash "" (for windows) to "/" for linux
    $config = array('config'=>dirname(__file__) . '\openssl.cnf');
   
    if ($SAN)
    {
        // TODO - This should be more easily configured
        //$config = array_merge($config, array('x509_extensions' => 'usr_cert'));
    }

    // Generate a new private (and public) key pair
    $privkey = openssl_pkey_new($config);

    if ($privkey==FALSE)
    {
        // Show any errors that occurred here
        while (($e = openssl_error_string()) !== false)
        {
            echo $e . "n";
            print "<br><br>";
        }
    }

    // Generate a certificate signing request
    $csr = openssl_csr_new($dn, $privkey, $config);

    if (!$csr)
    {
        // Show any errors that occurred here
        while (($e = openssl_error_string()) !== false)
        {
            echo $e . "n";
            print "<br><br>";
        }
    }

    // You will usually want to create a self-signed certificate at this
    // point until your CA fulfills your request.
    // This creates a self-signed cert that is valid for 365 days
    $sscert = openssl_csr_sign($csr, null, $privkey, 365, $config);
   
    if ($sscert==FALSE)
    {
        // Show any errors that occurred here
        while (($e = openssl_error_string()) !== false)
        {
            echo $e . "n";
            print "<br><br>";
        }
    }

    if (openssl_x509_export($sscert, $certout)==FALSE)
    {
        // Show any errors that occurred here
        while (($e = openssl_error_string()) !== false)
        {
            echo $e . "n";
            print "<br><br>";
        }
    }
    $pkout = "";
    if (openssl_pkey_export($privkey, $pkout,"test123", $config)==FALSE)
    {
        // Show any errors that occurred here
        while (($e = openssl_error_string()) !== false)
        {
            echo $e . "n";
            print "<br><br>";
        }
    }
    openssl_sign($certout, $signature, $privkey);
    return array("cer" => $certout, "pem" => $pkout, "signature" => $signature);
}
/*******************************
* Modify these parameters
*******************************/
$foafLocation = "as07142";
$countryName            = "LV";
$stateOrProvinceName    = "Rigas rajons";
$localityName           = "Riga";
$organizationName       = "Latvian Unversity";
$organizationalUnitName = "LU";
$commonName             = "Arturs Sosins";
$emailAddress           = "youremail@domain.com";
$p12Password            = "test123";

/*******************************
* End modifications
*******************************/

// Create a cer encoded SSL certificate
if ( $data = create_identity_cer(
            $countryName, $stateOrProvinceName, $localityName, $organizationName,
            $organizationalUnitName, $commonName, $emailAddress,
            $foafLocation ) )
{  
    // Create files
    file_put_contents("./".$foafLocation.".cer", $data["cer"]);
    file_put_contents("./".$foafLocation.".pem", $data["pem"]);
    file_put_contents("./".$foafLocation."_signature.txt", $data["signature"]);
    echo "<p><a href='./".$foafLocation.".cer' target='_blank'>".
    "Generated certificate</a></p>";
    echo "<p><a href='./".$foafLocation.".pem' target='_blank'>".
    "Generated Private Key</a></p>";
    echo "<p><a href='./".$foafLocation."_signature.txt' target='_blank'>".
    "Signature</a></p>";
}
?>

After this script, there should be 3 files generated, cer file, which is your certificate with public key, pem while, which stores your private key and simple txt file containing signature, by which we can verify your certificate.

So now let's verify certificate. Let's create php file with these contents:

<?php
/*******************************
* Modify these parameters
*******************************/
$foafLocation = "as07142";
/*******************************
* End modifications
*******************************/

//get certificate
$cert = file_get_contents('./'.$foafLocation.'.cer');
//get signature
$sig = file_get_contents('./'.$foafLocation.'_signature.txt');
//parse certificate
$data = openssl_x509_parse($cert);
//output issuer and subject
echo "<p>Subject: ".$data["subject"]["CN"]."</p>";
echo "<p>Issuer: ".$data["issuer"]["CN"]."</p>";
//comparing suject and issuer
if($data["subject"]["CN"] == $data["issuer"]["CN"])
{
    echo "<p>Subject matches issuer</p>";
}
echo "<p>Verifying signature:</p>";

// fetch public key from certificate and ready it
$pubkeyid = openssl_pkey_get_public($cert);

// state whether signature is okay or not
$ok = openssl_verify($cert, $sig, $pubkeyid);
if ($ok == 1) {
    echo "<p>Verified</p>";
} elseif ($ok == 0) {
    echo "<p>Not Verified</p>";
} else {
    echo "<p>Error cheking signature</p>";
}
// free the key from memory
openssl_free_key($pubkeyid);
?>

Verification verifies if subject and issuer is the same person, you can modify it to fit your needs. And then we verify if certificate itself using signature.

Next thing we can do is to encrypt any information using our public key in certificate and then decrypt it with our private key. Meaning anyone can take your certificate and encrypt information with it, and only you could decrypt it using your private key.

<?php
/*******************************
* Modify these parameters
*******************************/
$foafLocation = "as07142";
/*******************************
* End modifications
*******************************/
if(isset($_FILES["data"]) && trim($_FILES["data"]["name"]) != "")
{
    //time for filename
    $time = time();
    //get contents of file
    $_POST["data"] = file_get_contents($_FILES["data"]["tmp_name"]);
    //encrypting
    if($_POST["mode"] == "enc")
    {
        //get certificate
        $cert = file_get_contents('./'.$foafLocation.'.cer');

        //get public key
        $pubkeyid = openssl_pkey_get_public($cert);
        //encrypt
        openssl_public_encrypt($_POST["data"] , $encrypted , $pubkeyid);
        //store result in file
        file_put_contents("./files/".$time."_encrypted.txt", $encrypted);
        openssl_free_key($pubkeyid);
    }
    else
    {
        //get private key
        $pem = file_get_contents('./'.$foafLocation.'.pem');
        $privat_key = openssl_pkey_get_private(array($pem, "test123"));
        //decrypt
        openssl_private_decrypt ($_POST["data"] , $decrypted , $privat_key);
        //store result in file
        file_put_contents("./files/".$time."_decrypted.txt", $decrypted);
        openssl_free_key($privat_key);
    }
}
?>
<form method='post' action='' enctype='multipart/form-data'>
<p><select name='mode'>
<option value='enc'>Encrypt</option>
<option value='dec'>Decrypt</option>
</select></p>
<p>File: <input type='file' name='data'/></p>
<p><input type='submit' value='Submit'/></p>
</form>
<fieldset>
<legend>Existing files</legend>
<table>
<?php
$keys = "<tr><td><ul>";
$d = new DirectoryIterator("./files");
foreach($d as $f)
{
    $name = $f->getFilename();
    if($name != "." && $name != "..")
    {
        $keys .= "<li><a href='./files/".$name."' target='_blank'>".$name."</a></li>";
    }
}
echo $keys."</ul></td></tr>";
?>
</table>
</fieldset>

No comments:

Post a Comment