|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace Gameap\Services; |
|
4
|
|
|
|
|
5
|
|
|
use Gameap\Exceptions\GameapException; |
|
6
|
|
|
use Illuminate\Support\Facades\Storage; |
|
7
|
|
|
use Carbon\Carbon; |
|
8
|
|
|
use phpseclib\Crypt\RSA; |
|
9
|
|
|
use phpseclib\File\X509; |
|
10
|
|
|
|
|
11
|
|
|
class CertificateService |
|
12
|
|
|
{ |
|
13
|
|
|
const ROOT_CA = 'certs/root.crt'; |
|
14
|
|
|
const ROOT_KEY = 'certs/root.key'; |
|
15
|
|
|
|
|
16
|
|
|
// const CERT_DN = [ |
|
17
|
|
|
// "countryName" => "RU", |
|
18
|
|
|
// "stateOrProvinceName" => "GameAP", |
|
19
|
|
|
// "localityName" => "GameAP", |
|
20
|
|
|
// "organizationName" => "GameAP.ru", |
|
21
|
|
|
// "organizationalUnitName" => "Development", |
|
22
|
|
|
// "commonName" => "GameAP", |
|
23
|
|
|
// "emailAddress" => "[email protected]" |
|
24
|
|
|
// ]; |
|
25
|
|
|
|
|
26
|
|
|
const CERT_DN = '/CN=GameAP/O=GameAP'; |
|
27
|
|
|
|
|
28
|
|
|
const CERT_DAYS = 3650; |
|
29
|
|
|
|
|
30
|
|
|
/** |
|
31
|
|
|
* Generate root certificate |
|
32
|
|
|
*/ |
|
33
|
|
|
static public function generateRoot() |
|
34
|
|
|
{ |
|
35
|
|
|
$privKey = new RSA(); |
|
36
|
|
|
$keys = $privKey->createKey(2048); |
|
37
|
|
|
$privKey->loadKey($keys['privatekey']); |
|
38
|
|
|
|
|
39
|
|
|
$pubKey = new RSA(); |
|
40
|
|
|
$pubKey->loadKey($keys['publickey']); |
|
41
|
|
|
$pubKey->setPublicKey(); |
|
42
|
|
|
|
|
43
|
|
|
$subject = new X509(); |
|
44
|
|
|
$subject->setDN(self::CERT_DN); |
|
45
|
|
|
$subject->setPublicKey($pubKey); |
|
46
|
|
|
|
|
47
|
|
|
$issuer = new X509(); |
|
48
|
|
|
$issuer->setPrivateKey($privKey); |
|
49
|
|
|
$issuer->setDN($subject->getDN()); |
|
50
|
|
|
|
|
51
|
|
|
$x509 = new X509(); |
|
52
|
|
|
$x509->setEndDate('+10 year'); |
|
53
|
|
|
$x509->makeCA(); |
|
54
|
|
|
|
|
55
|
|
|
$result = $x509->sign($issuer, $subject, 'sha256WithRSAEncryption'); |
|
56
|
|
|
|
|
57
|
|
|
if ($result == false) { |
|
|
|
|
|
|
58
|
|
|
throw new GameapException('CA Self-signing has failed.'); |
|
59
|
|
|
} |
|
60
|
|
|
|
|
61
|
|
|
Storage::put(self::ROOT_CA, $x509->saveX509($result)); |
|
62
|
|
|
Storage::put(self::ROOT_KEY, $privKey->getPrivateKey()); |
|
63
|
|
|
} |
|
64
|
|
|
|
|
65
|
|
|
/** |
|
66
|
|
|
* @param $certificatePath string path to certificate in storage |
|
67
|
|
|
* @param $keyPath string path to key in storage |
|
68
|
|
|
*/ |
|
69
|
|
|
static public function generate($certificatePath, $keyPath) |
|
70
|
|
|
{ |
|
71
|
|
|
if (!Storage::exists(self::ROOT_CA)) { |
|
72
|
|
|
self::generateRoot(); |
|
73
|
|
|
} |
|
74
|
|
|
|
|
75
|
|
|
$privKey = new RSA(); |
|
76
|
|
|
$keys = $privKey->createKey(2048); |
|
77
|
|
|
$privKey->loadKey($keys['privatekey']); |
|
78
|
|
|
|
|
79
|
|
|
$pubKey = new RSA(); |
|
80
|
|
|
$pubKey->loadKey($keys['publickey']); |
|
81
|
|
|
$pubKey->setPublicKey(); |
|
82
|
|
|
|
|
83
|
|
|
// Sign |
|
84
|
|
|
$CAPrivKey = new RSA(); |
|
85
|
|
|
if (! $CAPrivKey->loadKey(Storage::get(self::ROOT_KEY))) { |
|
|
|
|
|
|
86
|
|
|
throw new GameapException('Failed to load CA Private Key'); |
|
87
|
|
|
} |
|
88
|
|
|
|
|
89
|
|
|
$issuer = new X509(); |
|
90
|
|
|
$issuer->loadCA(Storage::get(self::ROOT_CA)); |
|
|
|
|
|
|
91
|
|
|
$issuer->loadX509(Storage::get(self::ROOT_CA)); |
|
|
|
|
|
|
92
|
|
|
// $issuer->setDN($CASubject); |
|
93
|
|
|
$issuer->setPrivateKey($CAPrivKey); |
|
94
|
|
|
|
|
95
|
|
|
$subject = new X509(); |
|
96
|
|
|
$subject->setDN(self::CERT_DN); |
|
97
|
|
|
$subject->setPublicKey($pubKey); |
|
98
|
|
|
|
|
99
|
|
|
$x509 = new X509(); |
|
100
|
|
|
$x509->setEndDate('+10 year'); |
|
101
|
|
|
|
|
102
|
|
|
$result = $x509->sign($issuer, $subject, 'sha256WithRSAEncryption'); |
|
103
|
|
|
$result['tbsCertificate']['version'] = 'v1'; |
|
104
|
|
|
|
|
105
|
|
|
if ($result == false) { |
|
|
|
|
|
|
106
|
|
|
throw new GameapException('Signing has failed.'); |
|
107
|
|
|
} |
|
108
|
|
|
|
|
109
|
|
|
Storage::put($certificatePath, $x509->saveX509($result)); |
|
110
|
|
|
Storage::put($keyPath, $privKey->getPrivateKey()); |
|
111
|
|
|
} |
|
112
|
|
|
|
|
113
|
|
|
/** |
|
114
|
|
|
* @param $csrPath |
|
115
|
|
|
* |
|
116
|
|
|
* @return string |
|
117
|
|
|
* @throws GameapException |
|
118
|
|
|
*/ |
|
119
|
|
|
static public function signCsr($csrPath) |
|
120
|
|
|
{ |
|
121
|
|
|
if (!Storage::exists(self::ROOT_CA)) { |
|
122
|
|
|
self::generateRoot(); |
|
123
|
|
|
} |
|
124
|
|
|
|
|
125
|
|
|
$rootCa = Storage::get(self::ROOT_CA); |
|
126
|
|
|
$rootKey = Storage::get(self::ROOT_KEY); |
|
127
|
|
|
|
|
128
|
|
|
if (file_exists($csrPath)) { |
|
129
|
|
|
$csr = file_get_contents($csrPath); |
|
130
|
|
|
$fromStorage = false; |
|
131
|
|
|
} else if (Storage::exists($csrPath)) { |
|
132
|
|
|
$csr = Storage::get($csrPath); |
|
133
|
|
|
$fromStorage = true; |
|
134
|
|
|
} else { |
|
135
|
|
|
throw new GameapException('Invalid csr path'); |
|
136
|
|
|
} |
|
137
|
|
|
|
|
138
|
|
|
$pathinfo = pathinfo($csrPath); |
|
139
|
|
|
$signedCertificatePath = $pathinfo['dirname'] . '/' . $pathinfo['filename'] . '.crt'; |
|
140
|
|
|
|
|
141
|
|
|
$CAPrivKey = new RSA(); |
|
142
|
|
|
$CAPrivKey->loadKey($rootKey); |
|
|
|
|
|
|
143
|
|
|
|
|
144
|
|
|
$issuer = new X509(); |
|
145
|
|
|
$issuer->loadCA($rootCa); |
|
|
|
|
|
|
146
|
|
|
$issuer->loadX509($rootCa); |
|
|
|
|
|
|
147
|
|
|
$issuer->setPrivateKey($CAPrivKey); |
|
148
|
|
|
|
|
149
|
|
|
$subject = new X509(); |
|
150
|
|
|
$subject->loadCSR($csr); |
|
|
|
|
|
|
151
|
|
|
|
|
152
|
|
|
$x509 = new X509(); |
|
153
|
|
|
$x509->setEndDate('+10 year'); |
|
154
|
|
|
|
|
155
|
|
|
$result = $x509->sign($issuer, $subject, 'sha256WithRSAEncryption'); |
|
156
|
|
|
$result['tbsCertificate']['version'] = 'v1'; |
|
157
|
|
|
|
|
158
|
|
|
if ($result == false) { |
|
|
|
|
|
|
159
|
|
|
throw new GameapException('Signing has failed.'); |
|
160
|
|
|
} |
|
161
|
|
|
|
|
162
|
|
|
$pemCertificate = $x509->saveX509($result); |
|
163
|
|
|
|
|
164
|
|
|
if (empty($pemCertificate)) { |
|
165
|
|
|
throw new GameapException('X509 Saving has failed.'); |
|
166
|
|
|
} |
|
167
|
|
|
|
|
168
|
|
|
if ($fromStorage) { |
|
169
|
|
|
Storage::put($signedCertificatePath, $pemCertificate); |
|
170
|
|
|
} else { |
|
171
|
|
|
file_put_contents($signedCertificatePath, $pemCertificate); |
|
172
|
|
|
} |
|
173
|
|
|
|
|
174
|
|
|
return $signedCertificatePath; |
|
175
|
|
|
} |
|
176
|
|
|
|
|
177
|
|
|
/** |
|
178
|
|
|
* @param $certificatePath |
|
179
|
|
|
* |
|
180
|
|
|
* @return string |
|
181
|
|
|
*/ |
|
182
|
|
|
static public function fingerprintString($certificatePath) |
|
183
|
|
|
{ |
|
184
|
|
|
$fingerprint = openssl_x509_fingerprint(Storage::get($certificatePath), 'sha256'); |
|
|
|
|
|
|
185
|
|
|
return strtoupper(implode(':', str_split($fingerprint, 2))); |
|
186
|
|
|
} |
|
187
|
|
|
|
|
188
|
|
|
/** |
|
189
|
|
|
* @param $certificatePath |
|
190
|
|
|
* |
|
191
|
|
|
* @return array |
|
192
|
|
|
*/ |
|
193
|
|
|
static public function certificateInfo($certificatePath) |
|
194
|
|
|
{ |
|
195
|
|
|
$parsed = openssl_x509_parse(Storage::get($certificatePath)); |
|
196
|
|
|
|
|
197
|
|
|
return [ |
|
198
|
|
|
'expires' => Carbon::createFromTimestamp($parsed['validTo_time_t'])->toDateTimeString(), |
|
199
|
|
|
|
|
200
|
|
|
'signature_type' => $parsed['signatureTypeSN'], |
|
201
|
|
|
|
|
202
|
|
|
'country' => $parsed['subject']['C'] ?? '', |
|
203
|
|
|
'state' => $parsed['subject']['ST'] ?? '', |
|
204
|
|
|
'locality' => $parsed['subject']['L'] ?? '', |
|
205
|
|
|
'organization' => $parsed['subject']['O'] ?? '', |
|
206
|
|
|
'organizational_unit' => $parsed['subject']['OU'] ?? '', |
|
207
|
|
|
'common_name' => $parsed['subject']['CN'] ?? '', |
|
208
|
|
|
'email' => $parsed['subject']['emailAddress'] ?? '', |
|
209
|
|
|
|
|
210
|
|
|
'issuer_country' => $parsed['issuer']['C'] ?? '', |
|
211
|
|
|
'issuer_state' => $parsed['issuer']['ST'] ?? '', |
|
212
|
|
|
'issuer_locality' => $parsed['issuer']['L'] ?? '', |
|
213
|
|
|
'issuer_organization' => $parsed['issuer']['O'] ?? '', |
|
214
|
|
|
'issuer_organizational_unit' => $parsed['issuer']['OU'] ?? '', |
|
215
|
|
|
'issuer_common_name' => $parsed['issuer']['CN'] ?? '', |
|
216
|
|
|
'issuer_email' => $parsed['issuer']['emailAddress'] ?? '', |
|
217
|
|
|
]; |
|
218
|
|
|
} |
|
219
|
|
|
} |