In a nutshell...
$result = $x509->sign($issuer, $subject); $x509->saveX509($result);
$issuer
and $subject
are, themselves, File_X509 objects. They need private and public keys, respectively. Here's an example of how to create them from scratch for a brand new self-signed cert:
$privKey = new Crypt_RSA(); extract($privKey->createKey()); $privKey->loadKey($privatekey); $pubKey = new Crypt_RSA(); $pubKey->loadKey($publickey); $pubKey->setPublicKey(); $issuer->setPrivateKey($privKey); $subject->setPublicKey($pubKey);
The public key can also be set by $subject->loadCSR('...')
or $subject->loadX509('...')
. If you're using $subject->loadX509()
you'll effectively be re-signing $subject
. The issuer DN of $subject will be updated as will some of the other "transactional properties" (see below), if appropriate, but all (well, most) of the extensions will be preserved.
Sometimes an $issuer
or a $subject
will have multiple private keys. To disambiguate between them key identifiers are used. Quoting RFC5280 ยง 4.2.1.1, "the value of the keyIdentifier field SHOULD be derived from the public key used to verify the certificate's signature or a method that generates unique values". An example follows:
$issuer->setKeyIdentifier($issuer->computeKeyIdentifier($pubKey));
Note that computeKeyIdentifier is already called by saveX509()
unless you've specifically over-written the key identifier.
$issuer
and $subject
also need to have their distinguished names set. That can be done in a number of ways. The following are all equivalent to one another:
$subject->setDNProp('id-at-organizationName', 'phpseclib demo cert'); $subject->setDN( array( 'rdnSequence' => array( array( array( 'type' => 'id-at-organizationName', 'value'=> 'phpseclib demo cert' ) ) ) ) ); $subject->setDN(array( 'O' => 'phpseclib demo cert' )); $subject->setDN('/O=phpseclib demo cert');
DN properties can also be removed thusly:
$subject->removeDNProp('id-at-organizationName');
DN properties can be set via $subject->loadCSR('...')
and $subject->loadX509('...')
as well.
In the case of $subject->setDN()
.. the value will be assumed to be utf8String unless you explicitely tell phpseclib to set it to another type by doing something like 'value'=> array('ia5String' => 'phpseclib demo cert')
.
See also: List of DN property names
If your X.509 certificate is supposed to be valid for a single domain name that domain name can be set by either setting the id-at-commonName or CN distinguished name property. If your X.509 certificate is supposed to be valid for multiple domain names an X.509 extension - id-ce-subjectAltName - is required. setDomain()
takes care of all of this for you. Here's an example of how to make your certificate valid for two domains:
$subject->setDomain('www.google.com', 'www.yahoo.com');
Some properties belong neither to $issuer or $subject. Rather they belong to "transaction". The functions to set those properties are as follows:
$x509->setSerialNumber(...); $x509->makeCA(); $x509->setStartDate(...); $x509->setEndDate(...);
$subject->setDomain()
isn't considered a transactional property because, in theory, the issuing certificate could have at least one domain too since that's based on a particular distinguished name property.
The start time is, by default, when the cert is created. The current time is converted to UTC time and the fact that it's UTC time is denoted in the cert. Other X.509 decoders (eg. browsers or email clients or whatever) should decode this to their timezone so there's no need to set it to do $x509->setStartDate('-1 day')
or anything like that.
The end date, by default, is one year from the current time.
setStartDate() / setEndDate()
are passed through strtotime() internally. If you want the cert to last forever pass 'lifetime'
to it.
Functions affecting properties common to both $issuer and $subject are as follows:
setDNProp
removeDNProp
setDN
loadCSR
loadX509
setKeyIdentifier / computeKeyIdentifier
Functions affecting $issuer only properties:
setPrivateKey
Functions affecting $subject only properties:
setPublicKey
setDomain
Functions affecting $x509 properties:
setSerialNumber
makeCA
setStartDate
setEndDate
$subject = new File_X509(); $subject->setPublicKey($pubKey); // $pubKey is Crypt_RSA object $subject->setDN('/O=phpseclib demo cert'); $issuer = new File_X509(); $issuer->setPrivateKey($privKey); // $privKey is Crypt_RSA object $issuer->setDN('/O=phpseclib demo cert'); $x509 = new File_X509(); $result = $x509->sign($issuer, $subject); echo $x509->saveX509($result);
For self-signed certs the DN of the subject and issuer will match and the issuer's private key will correspond to the subject's public key.
This example creates the key pair from scratch.
<?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(); $result = $x509->sign($issuer, $subject); echo "the stunnel.pem contents are as follows:\r\n\r\n"; echo $privKey->getPrivateKey(); echo "\r\n"; echo $x509->saveX509($result); echo "\r\n"; ?>
Click below to view the output
Creates both the issuer and subject certs and the keypairs for both. We could just mark the cert generated above as a certificate authority via $x509->makeCA()
and copy / paste the private key / cert that the last script generated into this script but I figure a self-contained example that doesn't require any copy / pasting might be better.
<?php include('File/X509.php'); include('Crypt/RSA.php'); // create private key for CA cert $CAPrivKey = new Crypt_RSA(); extract($CAPrivKey->createKey()); $CAPrivKey->loadKey($privatekey); $pubKey = new Crypt_RSA(); $pubKey->loadKey($publickey); $pubKey->setPublicKey(); echo "the private key for the CA cert (can be discarded):\r\n\r\n"; echo $privatekey; echo "\r\n\r\n"; // create a self-signed cert that'll serve as the CA $subject = new File_X509(); $subject->setDNProp('id-at-organizationName', 'phpseclib demo CA'); $subject->setPublicKey($pubKey); $issuer = new File_X509(); $issuer->setPrivateKey($CAPrivKey); $issuer->setDN($CASubject = $subject->getDN()); $x509 = new File_X509(); $x509->makeCA(); $result = $x509->sign($issuer, $subject); echo "the CA cert to be imported into the browser is as follows:\r\n\r\n"; echo $x509->saveX509($result); echo "\r\n\r\n"; // 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($CAPrivKey); $issuer->setDN($CASubject); $x509 = new File_X509(); $result = $x509->sign($issuer, $subject); echo "the stunnel.pem contents are as follows:\r\n\r\n"; echo $privKey->getPrivateKey(); echo "\r\n"; echo $x509->saveX509($result); echo "\r\n"; ?>
Click below to view the output