Files
DomFramework/certificationauthority.php
2019-03-15 12:57:24 +00:00

258 lines
7.2 KiB
PHP

<?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 ();
/** The basic openssl.cnf configuration
*/
private $opensslConf = 'HOME = .
RANDFILE = $ENV::HOME/.rnd
[ req ]
distinguished_name = req_distinguished_name
[ req_distinguished_name ]
[ v3_ca ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints = critical,CA:true
keyUsage = cRLSign, keyCertSign
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
';
// }}}
/** Check if openssl support is available in PHP
*/
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, $this->opensslConf);
$this->configargs = array (
"config" => $this->opensslCnfPath,
"digest_alg" => "sha256WithRSAEncryption",
"private_key_bits" => 4096,
);
}
// }}}
/** Remove the temporary files when destroying the object
*/
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);
if ($req_cert === false)
throw new \Exception ("Can not create CA certificate : " .
openssl_error_string (), 500);
openssl_x509_export ($req_cert, $this->caCert);
openssl_pkey_export ($req_key, $this->caKey);
return $this;
}
// }}}
/** Get/Set the ca cert
* @param string|null $caCert 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 $caKey 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 $privateKey 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.
* Will create a private key if none is already exists
* @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 : ".
openssl_error_string (), 500);
openssl_csr_export ($req_csr, $out);
return $out;
}
// }}}
/** Sign a CSR with an CA cert/key pair 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)
* @param array|null $altNames The alternative names allowed in cert
* @return the signed certificate in PEM
*/
public function signCSR ($csr, $caCert, $caKey, $days = 365,
$altNames = array ())
// {{{
{
$conf = $this->opensslConf;
if (! empty($altNames))
{
// Copy the commonName from CSR request into subjectAltName
$subject = openssl_csr_get_subject($csr);
if (key_exists ("CN", $subject))
$commonName = $subject["CN"];
else
throw new \Exception ("Can not get the CN from CSR", 500);
// Add all the alternateNames
$conf .=
"subjectAltName = @san\n\n[ san ]\n";
$conf .= "DNS.0 = $commonName\n";
foreach ($altNames as $nb => $name)
{
$conf .= "DNS.".($nb+1)." = $name\n";
}
file_put_contents ($this->opensslCnfPath, $conf);
}
$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);
file_put_contents ($this->opensslCnfPath, $this->opensslConf);
return $certout;
}
// }}}
}