Add certification authority support

git-svn-id: https://svn.fournier38.fr/svn/ProgSVN/trunk@5107 bf3deb0d-5f1a-0410-827f-c0cc1f45334c
This commit is contained in:
2019-03-14 20:17:04 +00:00
parent 4b5e472309
commit e5696e7d49
2 changed files with 335 additions and 0 deletions

View File

@@ -0,0 +1,113 @@
<?php
/** Test the certification Authority
*/
class test_certificationauthority extends PHPUnit_Framework_TestCase
{
public function test_createCA_1 ()
{
$certificationauthority = new certificationauthority ();
$certificationauthority->createCA ("FR", "FOURNIER38", "CATEST");
$caCert = explode ("\n", $certificationauthority->caCert ());
$caKey = explode ("\n", $certificationauthority->caKey ());
$res = $caCert[0] . $caKey[0];
$this->assertSame ($res,
"-----BEGIN CERTIFICATE----------BEGIN PRIVATE KEY-----");
}
public function test_createPK_1 ()
{
$certificationauthority = new certificationauthority ();
$privateKey = $certificationauthority->createPrivateKey () -> privateKey ();
$privateKey = explode ("\n", $privateKey);
$this->assertSame ($privateKey[0], "-----BEGIN PRIVATE KEY-----");
}
public function test_createCSR_1 ()
{
$certificationauthority = new certificationauthority ();
$csr = $certificationauthority->createCSR ("FR", "FOURNIER38", "CSR");
$csr = explode ("\n", $csr);
$this->assertSame ($csr[0], "-----BEGIN CERTIFICATE REQUEST-----");
}
public function test_signCSR_1 ()
{
$certificationauthority = new certificationauthority ();
$certificationauthority->createCA ("FR", "FOURNIER38", "CATEST");
$caCert = $certificationauthority->caCert ();
$caKey = $certificationauthority->caKey ();
$csr = $certificationauthority->createCSR ("FR", "FOURNIER38", "CSR");
$cert = $certificationauthority->signCSR ($csr, $caCert, $caKey);
$cert = explode ("\n", $cert);
$this->assertSame ($cert[0], "-----BEGIN CERTIFICATE-----");
}
public function test_signCSR_2 ()
{
$certificationauthority = new certificationauthority ();
$certificationauthority->createCA ("FR", "FOURNIER38", "CATEST");
$caCert = $certificationauthority->caCert ();
$caKey = $certificationauthority->caKey ();
$csr = $certificationauthority->createCSR ("FR", "FOURNIER38", "CSR");
$cert = $certificationauthority->signCSR ($csr, $caCert, $caKey);
file_put_contents ("/tmp/test_signCSR_2", $cert);
exec ("openssl x509 -in - -text -noout < /tmp/test_signCSR_2", $output);
$res = preg_match ("#Subject: C = FR, .+ CN = CSR#",
implode ("\n", $output));
unlink ("/tmp/test_signCSR_2");
$this->assertSame ($res, 1);
}
public function test_signCSR_3 ()
{
// Check if generated cert X509v3 Extended Key Usage are valid
$certificationauthority = new certificationauthority ();
$certificationauthority->createCA ("FR", "FOURNIER38", "CATEST");
$caCert = $certificationauthority->caCert ();
$caKey = $certificationauthority->caKey ();
$csr = $certificationauthority->createCSR ("FR", "FOURNIER38", "CSR");
$cert = $certificationauthority->signCSR ($csr, $caCert, $caKey);
file_put_contents ("/tmp/test_signCSR_3", $cert);
exec ("openssl x509 -in - -text -noout < /tmp/test_signCSR_3", $output);
$res = preg_match (
"#TLS Web Server Authentication, TLS Web Client Authentication#",
implode ("\n", $output));
unlink ("/tmp/test_signCSR_3");
$this->assertSame ($res, 1);
}
public function test_signCSR_4 ()
{
// Check if generated cert issuer name is valid
$certificationauthority = new certificationauthority ();
$certificationauthority->createCA ("FR", "FOURNIER38", "CATEST");
$caCert = $certificationauthority->caCert ();
$caKey = $certificationauthority->caKey ();
$csr = $certificationauthority->createCSR ("FR", "FOURNIER38", "CSR");
$cert = $certificationauthority->signCSR ($csr, $caCert, $caKey);
file_put_contents ("/tmp/test_signCSR_4", $cert);
exec ("openssl x509 -in - -text -noout < /tmp/test_signCSR_4", $output);
$res = preg_match ("#Issuer: C = FR, O = FOURNIER38, CN = CATEST#",
implode ("\n", $output));
unlink ("/tmp/test_signCSR_4");
$this->assertSame ($res, 1);
}
public function test_signCSR_5 ()
{
// Check if generated cert is not tagged CA
$certificationauthority = new certificationauthority ();
$certificationauthority->createCA ("FR", "FOURNIER38", "CATEST");
$caCert = $certificationauthority->caCert ();
$caKey = $certificationauthority->caKey ();
$csr = $certificationauthority->createCSR ("FR", "FOURNIER38", "CSR");
$cert = $certificationauthority->signCSR ($csr, $caCert, $caKey);
file_put_contents ("/tmp/test_signCSR_5", $cert);
exec ("openssl x509 -in - -text -noout < /tmp/test_signCSR_5", $output);
$res = preg_match ("# CA:FALSE#",
implode ("\n", $output));
unlink ("/tmp/test_signCSR_5");
$this->assertSame ($res, 1);
}
}

222
certificationauthority.php Normal file
View File

@@ -0,0 +1,222 @@
<?php
/** DomFirewall
* @package domfirewall
* @author Dominique Fournier <dominique@fournier38.fr>
*/
/** An certificate authority
*/
class certificationauthority
{
// PROPERTIES
/** The opensslCnfPath file
*/
private $opensslCnfPath;
/** The CA public cert
*/
private $caCert = "";
/** The CA private key
*/
private $caKey = "";
/** The user private key resource
*/
private $privateKey;
/** The configuration arguments
*/
private $configargs = array ();
/** Check if /usr/bin/openssl is available */
public function __construct ()
// {{{
{
if (! function_exists ("openssl_csr_new"))
throw new \Exception (_("No openssl support in PHP"),
500);
$this->opensslCnfPath = tempnam ("/tmp", "openssl-");
file_put_contents ($this->opensslCnfPath,
'HOME = .
RANDFILE = $ENV::HOME/.rnd
[ req ]
distinguished_name = req_distinguished_name
[ req_distinguished_name ]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
[ v3_ca ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints = critical,CA:true
keyUsage = cRLSign, keyCertSign
');
$this->configargs = array (
"config" => $this->opensslCnfPath,
"digest_alg" => "sha256WithRSAEncryption",
"private_key_bits" => 4096,
);
}
// }}}
public function __destruct ()
// {{{
{
if (file_exists ($this->opensslCnfPath))
unlink ($this->opensslCnfPath);
}
// }}}
/** Create the pair key/cert for authority
* @param string $countryName Country name (like FR)
* @param string $organizationName Name of organization
* @param string $commonName Common name of authority
* @param integer|null $days The number of days of validity of the CA (3650
* by default)
* @return $this
*/
public function createCA ($countryName, $organizationName, $commonName,
$days = 3650)
// {{{
{
$req_key = openssl_pkey_new (array (
"config" => $this->opensslCnfPath,
"private_key_bits" => 4096,
"private_key_type" => OPENSSL_KEYTYPE_RSA,
));
$dn = array (
"countryName" => $countryName,
"organizationName" => $organizationName,
"commonName" => $commonName,
);
$this->configargs["x509_extensions"] = "v3_ca";
// x509_extensions must be defined in /etc/ssl/openssl.cnf
$req_csr = openssl_csr_new ($dn, $req_key, $this->configargs);
$req_cert = openssl_csr_sign ($req_csr, NULL, $req_key, $days,
$this->configargs);
openssl_x509_export ($req_cert, $this->caCert);
openssl_pkey_export ($req_key, $this->caKey);
return $this;
}
// }}}
/** Get/Set the ca cert
* @param string|null The CA cert to get/set
* @return the CA if get in PEM, $this if set
*/
public function caCert ($caCert = null)
// {{{
{
if ($caCert === null)
return $this->caCert;
if (! is_string ($caCert))
throw new \Exception ("AC : Invalid caCert provided : not a string", 500);
$this->caCert = $caCert;
return $this;
}
// }}}
/** Get/Set the ca key
* @param string|null The CA key to get/set
* @return the CA if get, $this if set
*/
public function caKey ($caKey = null)
// {{{
{
if ($caKey === null)
return $this->caKey;
if (! is_string ($caKey))
throw new \Exception ("AC : invalid caKey provided : not a string", 500);
$this->caKey = $caKey;
return $this;
}
// }}}
/** Create a private key
* @return $this;
*/
public function createPrivateKey ()
// {{{
{
$this->privateKey = openssl_pkey_new (array (
"config" => $this->opensslCnfPath,
"private_key_bits" => 4096,
"private_key_type" => OPENSSL_KEYTYPE_RSA,
));
return $this;
}
// }}}
/** Get in PEM/Set the private key
* @param string|null The private key to use
* @return the privatekey if get in PEM, $this if set
*/
public function privateKey ($privateKey = null)
// {{{
{
if ($privateKey === null)
{
openssl_pkey_export($this->privateKey, $out);
return $out;
}
if (! is_string ($privateKey))
throw new \Exception ("AC : invalid privateKey provided : not a string",
500);
$this->privateKey = openssl_pkey_get_private ($privateKey);
return $this;
}
// }}}
/** Create a CSR
* @param string $countryName Country name (like FR)
* @param string $organizationName Name of organization
* @param string $commonName Common name of authority
* @return the CSR created in PEM
*/
public function createCSR ($countryName, $organizationName, $commonName)
// {{{
{
if ($this->privateKey === null)
$this->createPrivateKey ();
$dn = array (
"countryName" => $countryName,
"organizationName" => $organizationName,
"commonName" => $commonName,
);
$this->configargs["x509_extensions"] = "v3_req";
$req_csr = openssl_csr_new ($dn, $this->privateKey, $this->configargs);
if ($req_csr === false)
throw new \Exception ("CA : Can not generate a CSR", 500);
openssl_csr_export ($req_csr, $out);
return $out;
}
// }}}
/** Sign a CSR with an CA cert and key and return the signed certificate in
* PEM mode
* The caCert and caKey must be defined
* @param string $csr The CSR to sign
* @param string $caCert The CA Certificate
* @param string $caKey The CA private key
* @param integer|null $days The number of days of validity (365 by default)
* @return the signed certificate in PEM
*/
public function signCSR ($csr, $caCert, $caKey, $days = 365)
// {{{
{
$this->configargs["x509_extensions"] = "v3_req";
$usercert = openssl_csr_sign ($csr, $caCert, $caKey, $days,
$this->configargs, date ("YmdHis"));
if ($usercert === false)
throw new \Exception ("Can not create certificate : " .
openssl_error_string (), 500);
openssl_x509_export($usercert, $certout);
return $certout;
}
// }}}
}