@@ -4,26 +4,33 @@ class CertificateGenerator
44{
55 const CONFIG = __DIR__ . DIRECTORY_SEPARATOR . 'openssl.cnf ' ;
66
7- /** @var OpenSSLCertificate */
8- private $ ca ;
7+ /** @var OpenSSLCertificate|false */
8+ private $ ca = false ;
99
10- /** @var resource */
11- private $ caKey ;
10+ /** @var OpenSSLAsymmetricKey|false */
11+ private $ caKey = false ;
1212
13- /** @var resource|null */
13+ /** @var bool */
14+ private $ useSelfSignedCert ;
15+
16+ /** @var OpenSSLCertificate|null */
1417 private $ lastCert ;
1518
16- /** @var resource |null */
19+ /** @var OpenSSLAsymmetricKey |null */
1720 private $ lastKey ;
1821
19- public function __construct ()
22+ public function __construct (bool $ useSelfSignedCert = false )
2023 {
2124 if (!extension_loaded ('openssl ' )) {
2225 throw new RuntimeException (
2326 'openssl extension must be loaded to generate certificates '
2427 );
2528 }
26- $ this ->generateCa ();
29+ $ this ->useSelfSignedCert = $ useSelfSignedCert ;
30+
31+ if (!$ this ->useSelfSignedCert ) {
32+ $ this ->generateCa ();
33+ }
2734 }
2835
2936 /**
@@ -54,40 +61,32 @@ class CertificateGenerator
5461 'commonName ' => 'CA for PHP Tests '
5562 ];
5663
57- $ this ->ca = openssl_csr_sign (
58- openssl_csr_new (
59- $ dn ,
60- $ this ->caKey ,
61- [
62- 'x509_extensions ' => 'v3_ca ' ,
63- 'config ' => self ::CONFIG ,
64- ]
65- ),
66- null ,
67- $ this ->caKey ,
68- 2 ,
69- [
70- 'config ' => self ::CONFIG ,
71- ]
72- );
64+ $ csr = openssl_csr_new ($ dn , $ this ->caKey , ['config ' => self ::CONFIG ]);
65+ $ this ->ca = openssl_csr_sign ($ csr , null , $ this ->caKey , 365 , ['config ' => self ::CONFIG ]);
7366 }
7467
7568 public function getCaCert ()
7669 {
70+ if ($ this ->useSelfSignedCert ) {
71+ throw new RuntimeException ("CA is not generated in self-signed mode. " );
72+ }
73+
7774 $ output = '' ;
7875 openssl_x509_export ($ this ->ca , $ output );
79-
8076 return $ output ;
8177 }
8278
8379 public function saveCaCert ($ file )
8480 {
81+ if ($ this ->useSelfSignedCert ) {
82+ throw new RuntimeException ("CA is not available in self-signed mode. " );
83+ }
84+
8585 openssl_x509_export_to_file ($ this ->ca , $ file );
8686 }
8787
88- private function generateCertAndKey (
89- $ commonNameForCert , $ file , $ keyLength = null , $ subjectAltName = null
90- ) {
88+ private function generateCertAndKey ($ commonNameForCert , $ file , $ keyLength = null , $ subjectAltName = null )
89+ {
9190 $ dn = [
9291 'countryName ' => 'BY ' ,
9392 'stateOrProvinceName ' => 'Minsk ' ,
@@ -98,8 +97,7 @@ class CertificateGenerator
9897 $ dn ['commonName ' ] = $ commonNameForCert ;
9998 }
10099
101- $ subjectAltNameConfig =
102- $ subjectAltName ? "subjectAltName = $ subjectAltName " : "" ;
100+ $ subjectAltNameConfig = $ subjectAltName ? "subjectAltName = $ subjectAltName " : "" ;
103101 $ configCode = <<<CONFIG
104102[ req ]
105103distinguished_name = req_distinguished_name
@@ -128,12 +126,17 @@ CONFIG;
128126
129127 $ this ->lastKey = self ::generateKey ($ keyLength );
130128 $ csr = openssl_csr_new ($ dn , $ this ->lastKey , $ config );
129+
130+ // If in self-signed mode, sign with the same key, otherwise use CA
131+ $ signingCert = $ this ->useSelfSignedCert ? null : $ this ->ca ;
132+ $ signingKey = $ this ->useSelfSignedCert ? $ this ->lastKey : $ this ->caKey ;
133+
131134 $ this ->lastCert = openssl_csr_sign (
132135 $ csr ,
133- $ this -> ca ,
134- $ this -> caKey ,
135- /* days */ 2 ,
136- $ config,
136+ $ signingCert ,
137+ $ signingKey ,
138+ 365 , // 1 year validity
139+ $ config
137140 );
138141
139142 return $ config ;
@@ -166,6 +169,22 @@ CONFIG;
166169 unlink ($ config ['config ' ]);
167170 }
168171
172+ public function saveNewCertAndPubKey (
173+ $ commonNameForCert , $ certFile , $ pubKeyFile , $ keyLength = null , $ subjectAltName = null
174+ ) {
175+ $ config = $ this ->generateCertAndKey ($ commonNameForCert , $ certFile , $ keyLength , $ subjectAltName );
176+
177+ openssl_x509_export_to_file ($ this ->lastCert , $ certFile );
178+
179+ $ keyDetails = openssl_pkey_get_details ($ this ->lastKey );
180+ if ($ keyDetails === false || !isset ($ keyDetails ['key ' ])) {
181+ throw new RuntimeException ("Failed to extract public key. " );
182+ }
183+
184+ file_put_contents ($ pubKeyFile , $ keyDetails ['key ' ]);
185+ unlink ($ config ['config ' ]);
186+ }
187+
169188 public function getCertDigest ($ algo )
170189 {
171190 return openssl_x509_fingerprint ($ this ->lastCert , $ algo );
0 commit comments