phpseclib: X.509 Creation Guide

Contents

The General Idea

In a nutshell...

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

Public / Private Keys

$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.

Key Identifiers

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.

Distinguished Names

$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

Domain 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');

Transactional Properties

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.

Function List

Functions affecting properties common to both $issuer and $subject are as follows:

Functions affecting $issuer only properties:

Functions affecting $subject only properties:

Functions affecting $x509 properties:

Minimalistic Example

$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.

Example: Self-signed cert

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

Output

the stunnel.pem contents are as follows:

-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQCtYr+TcpSQ043ZZi+akC1LR5Q6MJPJ6/0MQ7IFPt/SCywaxsdFsNQ40+TOSFNk
G68nscyB5nEPDkNzLJ7AklNSRHItqxTwohuW4a+fBfzAi0vXS9IrM2iep13cHE9r5QW9pouRQiYf
bi5FegEWbtIc5SrmAxHAH9K3KGRaXEeufwIDAQABAoGAFijgwz+JrVjccESXIPH8V/q1/xnmSZBX
rxGX1wPKJ1Y2NNXi8g0/kmPCgnrL7ad8I16d/JwvJvVzuWyifYo2C7++wHjPKvqTXgg7OiCai3KV
O0y3r3xhVdvKOOmD42zPuPkHXSORrWP0wgH07pPThKRLmc2I1EuRtXzLvGrCOMECQQDbD7YRotnC
biL7OzoFuzUKEpSN464LhdkTcDWeRUEvQQa/hTiKIeJ5aTdvyEOKBpuYzPTh6/79nBkgS7Lg5KtV
AkEAyp9X06ZMXiOIFZszARddoALM1FXJkAVsa5T+pUJAscKU5JHFrgckk+D9snnYvR7eN5OMHOkP
fH9yzKS4Zif6gwJABojicoY8HruwWXQ7193syB8jxVPMZlWY0yaewtjoB+PVsrLjq+M04VBNMg10
TlLCI33BtFeY5LHaYgdAD8tifQJBAKnBGZmuR2jPJ+HCJtcRNlUqQ2TZgobwpEb1iun1ObIzrP5Z
yl3kihaCdsmiH51CUTYKnWZTM7BALnOYxQyBDfUCQQCI1JOGXOveAxsr4sP6vornAU/G7ekX2n9x
LAjktKBd0pX25L9fdqrZPH61bOvF9S3VjuiX5vSp8OyUxoWMPbPn
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIB4zCCAU6gAwIBAgIAMAsGCSqGSIb3DQEBBTA5MRwwGgYDVQQKDBNwaHBzZWNsaWIgZGVtbyBj
ZXJ0MRkwFwYDVQQDDBB3d3cud2hhdGV2ZXIuY29tMCIYDzIwMTIwNjA0MDMxMDMxWhgPMjAxMzA2
MDQwMzEwMzFaMDkxHDAaBgNVBAoME3BocHNlY2xpYiBkZW1vIGNlcnQxGTAXBgNVBAMMEHd3dy53
aGF0ZXZlci5jb20wgZ0wCwYJKoZIhvcNAQEBA4GNADCBiQKBgQCtYr+TcpSQ043ZZi+akC1LR5Q6
MJPJ6/0MQ7IFPt/SCywaxsdFsNQ40+TOSFNkG68nscyB5nEPDkNzLJ7AklNSRHItqxTwohuW4a+f
BfzAi0vXS9IrM2iep13cHE9r5QW9pouRQiYfbi5FegEWbtIc5SrmAxHAH9K3KGRaXEeufwIDAQAB
MAsGCSqGSIb3DQEBBQOBgQBYEsMuWBA9ie4ulXxeLhLoQvEo6vgl5LDRFMuP+AhkKzfXUo2yEMWP
/QxbSglcPT/ycb+5+FhYGWxGatM5V+sB43ZBHZD14ZWPN35ePmDIfqXdRmphhXuhdNU7DWwp97ZR
c26CQXzHurRf29VloV8k5JKwsfnLRPVCrbJySMB6dg==
-----END CERTIFICATE-----

Example: CA-signed cert

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

Output

the private key for the CA cert (can be discarded):

-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCeSAGfM4nHdIaBwThHYGW3MABTvrSMrawnGN/Mx5ttyLQF4YcUJFSpGAJ8y2hV
iak3irbLtc6nlpJFOHtMB4VsuG7S+MySYKBm5l7eApfWynHMLKVml3URXCMVMXju4SxiI+D10nNO
aq+eSEIFlPOY+7LgSdIAka6/9+z2hgoITQIDAQABAoGADS1RdT+FKOW6jPLZDJgp36ZPYKlvvoZ2
3opjl1jGx8z9r7+mzzZ3Fdy52EnW1cgwHj+eGDd6jNksNDcPjLTTlqfwoKMZhf1gpYBKA5PtnqFU
rxM5eW3SzLWkPOZoS/i8fxze0uvoh0zcm6u1nictcjkj0Hqql4hbTrDRENxTfgECQQC9qYFWsH8q
r/ztbl6WBxW1NT9JgthvteiINpAjeN9tmuP9BgTGPk4j3MhTVsPp5ogzdn3MLch2YJ9cJuEeHon1
AkEA1aSiiZwGH9tCI8EFJrDqbCqDgeOmlkgMp5YQ8TqBg6/nkM4wWz9cXSmFPO3L9BQhvXE8RlGJ
DChCksbBcjnV+QJBALXZ7w+lz2iosYUejEahqGjFE1dyw//olXr1hiLc5K39E5kpyi4i012Ddfii
etqsle9yL5A0Xb9TSblb9gGhGnkCQDyj8Gay3KIqMAnJ/dZPnRp6rr0titlZNg5TcBeojZ59TXMy
s1MTFvpykJkMvjlXhYQqIfurpQTIYK0csUfk1rECQBPVqVAG45VBn6yV1cDaqvg/0aQ6yZc7nQTr
7aLuUCTaf/xP4wSWiQpGiRFdTLt3pkkI3XdngV6j8RCcpgED6qU=
-----END RSA PRIVATE KEY-----

the CA cert to be imported into the browser is as follows:

-----BEGIN CERTIFICATE-----
MIIBzzCCATqgAwIBAgIBATALBgkqhkiG9w0BAQUwHDEaMBgGA1UECgwRcGhwc2VjbGliIGRlbW8g
Q0EwIhgPMjAxMjA1MDQwMzQxNDhaGA8yMDEzMDYwNDAzNDE0OFowHDEaMBgGA1UECgwRcGhwc2Vj
bGliIGRlbW8gQ0EwgZ0wCwYJKoZIhvcNAQEBA4GNADCBiQKBgQCeSAGfM4nHdIaBwThHYGW3MABT
vrSMrawnGN/Mx5ttyLQF4YcUJFSpGAJ8y2hViak3irbLtc6nlpJFOHtMB4VsuG7S+MySYKBm5l7e
ApfWynHMLKVml3URXCMVMXju4SxiI+D10nNOaq+eSEIFlPOY+7LgSdIAka6/9+z2hgoITQIDAQAB
oyMwITAOBgNVHQ8BAQAEBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zALBgkqhkiG9w0BAQUDgYEAc31s
EL3tWfwFIHYK+96wzvAS7F/4FCMcZOdrW9LZ1IniXUSf601MIOVy0pAc17Xf6QjlVOhziA3cLMi3
3CWwEDgnhXU8s6YRAa6WD8eeHwmJKE3vIU0+klscIdOO+14BrC+mRUoPaZ1hmixMvrK/pVhXAkef
XTSk5kq9f2Bfm+M=
-----END CERTIFICATE-----

the stunnel.pem contents are as follows:

-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQCBsisUty8OFR2gz3sweCoIfyzFWcvaTA55noaI6LXZMebI3iB7u5dJjsbI2yRh
IgDaHBDVyu6R9W/NA9QFkKGoHmnnvHsEo8Jc9Ca3nVIfjVBUj+kGMnadUtHoxZaRXLdwV9nX+LCa
rfEz9VDS21BOJJ6OzUmvvkM0AUkGQlmcnwIDAQABAoGAN2+c63dbNRVAe0aEHNUX/lbBn79Am5fL
zh882RNC+0nRoAUV9F1cDpRpElxdAemjXJcHeSKK87dpaMOcoh5Ogi2rXXgB++igxNdx19+z4Ku1
A1JG0O08Z/n7DQQb5g+V4Z9oIorpfufh7lIj4HgDLoznCj2lWq6SpzWobmtYnC0CQQDJDjYDRJRR
ujepI6yz+9ckD6Jr2AwjSrdQKUPMzeexlTQgDqW/Jec5k3dS1zcvM+FctzNfnp9LyK/CABFYLb6r
AkEApSOpwpzAsL1rDUqdV3iBEPPBmLLeTef4+pyggGGYFeu+TR3hNrHZPphXi6F/c+kmt7kDWgq1
K3QZPQkadXgJ3QJAF5Bb0jw6ae26Uk+FET8l8usGLT+QBJ7gJzv+30UOr6h+CHfPAz0tAXmAFax1
okmhUx2BnuZXYyltN/6MCdqqqwJAVqU5owTADXu1OYoWuBhgD9b1kvr2rQWzmzqm8/VA6O+CL9An
pJz4EWG5PWgFCXoheHA4k/veRuhpfZ7TJSNQeQJAFVH1RQYAYOTVyyXYbUJnXEbVnEYeTVUKLJmx
VSmAwFxx6s/EHVmiDH/WPgV4NkZt7gW7epkqoz3gfTDWYfx8Qg==
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIBxTCCATCgAwIBAgIBATALBgkqhkiG9w0BAQUwHDEaMBgGA1UECgwRcGhwc2VjbGliIGRlbW8g
Q0EwIhgPMjAxMjA1MDQwMzQxNDhaGA8yMDEzMDYwNDAzNDE0OFowNzEcMBoGA1UECgwTcGhwc2Vj
bGliIGRlbW8gY2VydDEXMBUGA1UEAwwOd3d3Lmdvb2dsZS5jb20wgZ0wCwYJKoZIhvcNAQEBA4GN
ADCBiQKBgQCBsisUty8OFR2gz3sweCoIfyzFWcvaTA55noaI6LXZMebI3iB7u5dJjsbI2yRhIgDa
HBDVyu6R9W/NA9QFkKGoHmnnvHsEo8Jc9Ca3nVIfjVBUj+kGMnadUtHoxZaRXLdwV9nX+LCarfEz
9VDS21BOJJ6OzUmvvkM0AUkGQlmcnwIDAQABMAsGCSqGSIb3DQEBBQOBgQBzWpz47IfG6vWf5WlN
R+iTEBwr8kKJOop38otzxwGBvYk7YBHI2DZW0tivpceN7JjkhpkBUNd+vg/qQSp0UVp38MrVR0cw
ZP0iAJmLHTq/Jpymrqpz3Cn0bvKFvmYvzHgy9bjS5dy8AuFZY16qieNJovrV2c4dnCh4xo2c+MbK
Ig==
-----END CERTIFICATE-----