From 2ea8f73868f27a5bfef1657e9c9e662682e24566 Mon Sep 17 00:00:00 2001 From: Dominique Fournier Date: Fri, 15 Mar 2019 08:43:07 +0000 Subject: [PATCH] certificationauthority : Add alternative names git-svn-id: https://svn.fournier38.fr/svn/ProgSVN/trunk@5109 bf3deb0d-5f1a-0410-827f-c0cc1f45334c --- Tests/certificationauthorityTest.php | 20 +++++++ certificationauthority.php | 78 ++++++++++++++++++++-------- 2 files changed, 76 insertions(+), 22 deletions(-) diff --git a/Tests/certificationauthorityTest.php b/Tests/certificationauthorityTest.php index e27ab63..1244ddf 100644 --- a/Tests/certificationauthorityTest.php +++ b/Tests/certificationauthorityTest.php @@ -123,4 +123,24 @@ class test_certificationauthority extends PHPUnit_Framework_TestCase unlink ("/tmp/test_signCSR_5"); $this->assertSame ($res, 1); } + + public function test_signCSR_6 () + { + // Check if generated cert has Alternative Names + $certificationauthority = new certificationauthority (); + $certificationauthority->createCA ("FR", "FOURNIER38", "CATEST"); + $caCert = $certificationauthority->caCert (); + $caKey = $certificationauthority->caKey (); + $csr = $certificationauthority->createCSR ("FR", "FOURNIER38", + "CSR.fournier38.fr"); + $cert = $certificationauthority->signCSR ($csr, $caCert, $caKey, null, + ["ALT1.example.com","ALT2.example.com"]); + file_put_contents ("/tmp/test_signCSR_6", $cert); + exec ("openssl x509 -in - -text -noout < /tmp/test_signCSR_6", $output); + $res = preg_match ("#DNS:CSR.fournier38.fr, DNS:ALT1.example.com, DNS:ALT#", + implode ("\n", $output)); +print_r ($output); + unlink ("/tmp/test_signCSR_6"); + $this->assertSame ($res, 1); + } } diff --git a/certificationauthority.php b/certificationauthority.php index fe4b004..bd65ae5 100644 --- a/certificationauthority.php +++ b/certificationauthority.php @@ -9,6 +9,7 @@ class certificationauthority { // PROPERTIES + // {{{ /** The opensslCnfPath file */ private $opensslCnfPath; @@ -28,16 +29,9 @@ class certificationauthority */ 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 = . + /** The basic openssl.cnf configuration + */ + private $opensslConf = 'HOME = . RANDFILE = $ENV::HOME/.rnd [ req ] @@ -45,17 +39,29 @@ 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 -'); + +[ 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", @@ -64,6 +70,8 @@ keyUsage = cRLSign, keyCertSign } // }}} + /** Remove the temporary files when destroying the object + */ public function __destruct () // {{{ { @@ -99,6 +107,9 @@ keyUsage = cRLSign, keyCertSign $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; @@ -106,7 +117,7 @@ keyUsage = cRLSign, keyCertSign // }}} /** Get/Set the ca cert - * @param string|null The CA cert to get/set + * @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) @@ -122,7 +133,7 @@ keyUsage = cRLSign, keyCertSign // }}} /** Get/Set the ca key - * @param string|null The CA key to get/set + * @param string|null $caKey The CA key to get/set * @return the CA if get, $this if set */ public function caKey ($caKey = null) @@ -153,7 +164,7 @@ keyUsage = cRLSign, keyCertSign // }}} /** Get in PEM/Set the private key - * @param string|null The private key to use + * @param string|null $privateKey The private key to use * @return the privatekey if get in PEM, $this if set */ public function privateKey ($privateKey = null) @@ -191,24 +202,46 @@ keyUsage = cRLSign, keyCertSign $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); + 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 and key and return the signed certificate in + /** 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) + 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")); @@ -216,6 +249,7 @@ keyUsage = cRLSign, keyCertSign 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; } // }}}