@@ -4,26 +4,33 @@ class CertificateGenerator
4
4
{
5
5
const CONFIG = __DIR__ . DIRECTORY_SEPARATOR . 'openssl.cnf ' ;
6
6
7
- /** @var OpenSSLCertificate */
8
- private $ ca ;
7
+ /** @var OpenSSLCertificate|false */
8
+ private $ ca = false ;
9
9
10
- /** @var resource */
11
- private $ caKey ;
10
+ /** @var OpenSSLAsymmetricKey|false */
11
+ private $ caKey = false ;
12
12
13
- /** @var resource|null */
13
+ /** @var bool */
14
+ private $ useSelfSignedCert ;
15
+
16
+ /** @var OpenSSLCertificate|null */
14
17
private $ lastCert ;
15
18
16
- /** @var resource |null */
19
+ /** @var OpenSSLAsymmetricKey |null */
17
20
private $ lastKey ;
18
21
19
- public function __construct ()
22
+ public function __construct (bool $ useSelfSignedCert = false )
20
23
{
21
24
if (!extension_loaded ('openssl ' )) {
22
25
throw new RuntimeException (
23
26
'openssl extension must be loaded to generate certificates '
24
27
);
25
28
}
26
- $ this ->generateCa ();
29
+ $ this ->useSelfSignedCert = $ useSelfSignedCert ;
30
+
31
+ if (!$ this ->useSelfSignedCert ) {
32
+ $ this ->generateCa ();
33
+ }
27
34
}
28
35
29
36
/**
@@ -54,40 +61,32 @@ class CertificateGenerator
54
61
'commonName ' => 'CA for PHP Tests '
55
62
];
56
63
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 ]);
73
66
}
74
67
75
68
public function getCaCert ()
76
69
{
70
+ if ($ this ->useSelfSignedCert ) {
71
+ throw new RuntimeException ("CA is not generated in self-signed mode. " );
72
+ }
73
+
77
74
$ output = '' ;
78
75
openssl_x509_export ($ this ->ca , $ output );
79
-
80
76
return $ output ;
81
77
}
82
78
83
79
public function saveCaCert ($ file )
84
80
{
81
+ if ($ this ->useSelfSignedCert ) {
82
+ throw new RuntimeException ("CA is not available in self-signed mode. " );
83
+ }
84
+
85
85
openssl_x509_export_to_file ($ this ->ca , $ file );
86
86
}
87
87
88
- private function generateCertAndKey (
89
- $ commonNameForCert , $ file , $ keyLength = null , $ subjectAltName = null
90
- ) {
88
+ private function generateCertAndKey ($ commonNameForCert , $ file , $ keyLength = null , $ subjectAltName = null )
89
+ {
91
90
$ dn = [
92
91
'countryName ' => 'BY ' ,
93
92
'stateOrProvinceName ' => 'Minsk ' ,
@@ -98,8 +97,7 @@ class CertificateGenerator
98
97
$ dn ['commonName ' ] = $ commonNameForCert ;
99
98
}
100
99
101
- $ subjectAltNameConfig =
102
- $ subjectAltName ? "subjectAltName = $ subjectAltName " : "" ;
100
+ $ subjectAltNameConfig = $ subjectAltName ? "subjectAltName = $ subjectAltName " : "" ;
103
101
$ configCode = <<<CONFIG
104
102
[ req ]
105
103
distinguished_name = req_distinguished_name
@@ -128,12 +126,17 @@ CONFIG;
128
126
129
127
$ this ->lastKey = self ::generateKey ($ keyLength );
130
128
$ 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
+
131
134
$ this ->lastCert = openssl_csr_sign (
132
135
$ csr ,
133
- $ this -> ca ,
134
- $ this -> caKey ,
135
- /* days */ 2 ,
136
- $ config,
136
+ $ signingCert ,
137
+ $ signingKey ,
138
+ 365 , // 1 year validity
139
+ $ config
137
140
);
138
141
139
142
return $ config ;
@@ -166,6 +169,22 @@ CONFIG;
166
169
unlink ($ config ['config ' ]);
167
170
}
168
171
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
+
169
188
public function getCertDigest ($ algo )
170
189
{
171
190
return openssl_x509_fingerprint ($ this ->lastCert , $ algo );
0 commit comments