1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace X509\AttributeCertificate; |
4
|
|
|
|
5
|
|
|
use ASN1\Element; |
6
|
|
|
use ASN1\Type\Constructed\Sequence; |
7
|
|
|
use ASN1\Type\Primitive\Integer; |
8
|
|
|
use CryptoUtil\ASN1\AlgorithmIdentifier; |
9
|
|
|
use CryptoUtil\ASN1\AlgorithmIdentifier\Feature\SignatureAlgorithmIdentifier; |
10
|
|
|
use CryptoUtil\ASN1\PrivateKeyInfo; |
11
|
|
|
use CryptoUtil\Crypto\Crypto; |
12
|
|
|
use X509\Certificate\Extension\Extension; |
13
|
|
|
use X509\Certificate\Extensions; |
14
|
|
|
use X509\Certificate\UniqueIdentifier; |
15
|
|
|
|
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* Implements <i>AttributeCertificateInfo</i> ASN.1 type. |
19
|
|
|
* |
20
|
|
|
* @link https://tools.ietf.org/html/rfc5755#section-4.1 |
21
|
|
|
*/ |
22
|
|
|
class AttributeCertificateInfo |
23
|
|
|
{ |
24
|
|
|
const VERSION_2 = 1; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* AC version. |
28
|
|
|
* |
29
|
|
|
* @var int $_version |
30
|
|
|
*/ |
31
|
|
|
protected $_version; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* AC holder. |
35
|
|
|
* |
36
|
|
|
* @var Holder $_holder |
37
|
|
|
*/ |
38
|
|
|
protected $_holder; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* AC issuer. |
42
|
|
|
* |
43
|
|
|
* @var AttCertIssuer $_issuer |
44
|
|
|
*/ |
45
|
|
|
protected $_issuer; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* Signature algorithm identifier. |
49
|
|
|
* |
50
|
|
|
* @var SignatureAlgorithmIdentifier $_signature |
51
|
|
|
*/ |
52
|
|
|
protected $_signature; |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* AC serial number. |
56
|
|
|
* |
57
|
|
|
* @var int|string $_serialNumber |
58
|
|
|
*/ |
59
|
|
|
protected $_serialNumber; |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* Validity period. |
63
|
|
|
* |
64
|
|
|
* @var AttCertValidityPeriod $_attrCertValidityPeriod |
65
|
|
|
*/ |
66
|
|
|
protected $_attrCertValidityPeriod; |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* Attributes. |
70
|
|
|
* |
71
|
|
|
* @var Attributes $_attributes |
72
|
|
|
*/ |
73
|
|
|
protected $_attributes; |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* Issuer unique identifier. |
77
|
|
|
* |
78
|
|
|
* @var UniqueIdentifier|null $_issuerUniqueID |
79
|
|
|
*/ |
80
|
|
|
protected $_issuerUniqueID; |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* Extensions. |
84
|
|
|
* |
85
|
|
|
* @var Extensions $_extensions |
86
|
|
|
*/ |
87
|
|
|
protected $_extensions; |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* Constructor |
91
|
|
|
* |
92
|
|
|
* @param Holder $holder AC holder |
93
|
|
|
* @param AttCertIssuer $issuer AC issuer |
94
|
|
|
* @param AttCertValidityPeriod $validity Validity |
95
|
|
|
* @param Attributes $attribs Attributes |
96
|
|
|
*/ |
97
|
8 |
|
public function __construct(Holder $holder, AttCertIssuer $issuer, |
98
|
|
|
AttCertValidityPeriod $validity, Attributes $attribs) { |
99
|
8 |
|
$this->_version = self::VERSION_2; |
100
|
8 |
|
$this->_holder = $holder; |
101
|
8 |
|
$this->_issuer = $issuer; |
102
|
8 |
|
$this->_attrCertValidityPeriod = $validity; |
103
|
8 |
|
$this->_attributes = $attribs; |
104
|
8 |
|
$this->_extensions = new Extensions(); |
105
|
8 |
|
} |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* Initialize from ASN.1. |
109
|
|
|
* |
110
|
|
|
* @param Sequence $seq |
111
|
|
|
* @throws \UnexpectedValueException |
112
|
|
|
* @return self |
113
|
|
|
*/ |
114
|
7 |
|
public static function fromASN1(Sequence $seq) { |
115
|
7 |
|
$version = $seq->at(0) |
116
|
7 |
|
->asInteger() |
117
|
7 |
|
->number(); |
118
|
7 |
|
if ($version != self::VERSION_2) { |
119
|
1 |
|
throw new \UnexpectedValueException("Version must be 2."); |
120
|
|
|
} |
121
|
6 |
|
$holder = Holder::fromASN1($seq->at(1)->asSequence()); |
122
|
6 |
|
$issuer = AttCertIssuer::fromASN1($seq->at(2)); |
123
|
6 |
|
$signature = AlgorithmIdentifier::fromASN1($seq->at(3)->asSequence()); |
124
|
6 |
|
if (!$signature instanceof SignatureAlgorithmIdentifier) { |
125
|
1 |
|
throw new \UnexpectedValueException( |
126
|
1 |
|
"Unsupported signature algorithm " . $signature->oid() . "."); |
127
|
|
|
} |
128
|
5 |
|
$serial = $seq->at(4) |
129
|
5 |
|
->asInteger() |
130
|
5 |
|
->number(); |
131
|
5 |
|
$validity = AttCertValidityPeriod::fromASN1($seq->at(5)->asSequence()); |
132
|
5 |
|
$attribs = Attributes::fromASN1($seq->at(6)->asSequence()); |
133
|
5 |
|
$obj = new self($holder, $issuer, $validity, $attribs); |
134
|
5 |
|
$obj->_signature = $signature; |
135
|
5 |
|
$obj->_serialNumber = $serial; |
136
|
5 |
|
$idx = 7; |
137
|
5 |
|
if ($seq->has($idx, Element::TYPE_BIT_STRING)) { |
138
|
1 |
|
$obj->_issuerUniqueID = UniqueIdentifier::fromASN1( |
139
|
1 |
|
$seq->at($idx++)->asBitString()); |
140
|
1 |
|
} |
141
|
5 |
|
if ($seq->has($idx, Element::TYPE_SEQUENCE)) { |
142
|
3 |
|
$obj->_extensions = Extensions::fromASN1( |
143
|
3 |
|
$seq->at($idx++)->asSequence()); |
144
|
3 |
|
} |
145
|
5 |
|
return $obj; |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
/** |
149
|
|
|
* Get self with holder. |
150
|
|
|
* |
151
|
|
|
* @param Holder $holder |
152
|
|
|
* @return self |
153
|
|
|
*/ |
154
|
1 |
|
public function withHolder(Holder $holder) { |
155
|
1 |
|
$obj = clone $this; |
156
|
1 |
|
$obj->_holder = $holder; |
157
|
1 |
|
return $obj; |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
/** |
161
|
|
|
* Get self with issuer. |
162
|
|
|
* |
163
|
|
|
* @param AttCertIssuer $issuer |
164
|
|
|
* @return self |
165
|
|
|
*/ |
166
|
1 |
|
public function withIssuer(AttCertIssuer $issuer) { |
167
|
1 |
|
$obj = clone $this; |
168
|
1 |
|
$obj->_issuer = $issuer; |
169
|
1 |
|
return $obj; |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
/** |
173
|
|
|
* Get self with signature algorithm identifier. |
174
|
|
|
* |
175
|
|
|
* @param SignatureAlgorithmIdentifier $algo |
176
|
|
|
* @return self |
177
|
|
|
*/ |
178
|
3 |
|
public function withSignature(SignatureAlgorithmIdentifier $algo) { |
179
|
3 |
|
$obj = clone $this; |
180
|
3 |
|
$obj->_signature = $algo; |
181
|
3 |
|
return $obj; |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* Get self with serial number. |
186
|
|
|
* |
187
|
|
|
* @param int|string $serial |
188
|
|
|
* @return self |
189
|
|
|
*/ |
190
|
4 |
|
public function withSerialNumber($serial) { |
191
|
4 |
|
$obj = clone $this; |
192
|
4 |
|
$obj->_serialNumber = $serial; |
193
|
4 |
|
return $obj; |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
/** |
197
|
|
|
* Get self with random positive serial number. |
198
|
|
|
* |
199
|
|
|
* @param int $size Number of random bytes |
200
|
|
|
* @return self |
201
|
|
|
*/ |
202
|
1 |
View Code Duplication |
public function withRandomSerialNumber($size = 16) { |
|
|
|
|
203
|
|
|
// ensure that first byte is always non-zero and having first bit unset |
204
|
1 |
|
$num = gmp_init(mt_rand(1, 0x7f), 10); |
205
|
1 |
|
for ($i = 1; $i < $size; ++$i) { |
206
|
1 |
|
$num <<= 8; |
207
|
1 |
|
$num += mt_rand(0, 0xff); |
208
|
1 |
|
} |
209
|
1 |
|
return $this->withSerialNumber(gmp_strval($num, 10)); |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
/** |
213
|
|
|
* Get self with validity period. |
214
|
|
|
* |
215
|
|
|
* @param AttCertValidityPeriod $validity |
216
|
|
|
* @return self |
217
|
|
|
*/ |
218
|
1 |
|
public function withValidity(AttCertValidityPeriod $validity) { |
219
|
1 |
|
$obj = clone $this; |
220
|
1 |
|
$obj->_attrCertValidityPeriod = $validity; |
221
|
1 |
|
return $obj; |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
/** |
225
|
|
|
* Get self with attributes. |
226
|
|
|
* |
227
|
|
|
* @param Attributes $attribs |
228
|
|
|
* @return self |
229
|
|
|
*/ |
230
|
1 |
|
public function withAttributes(Attributes $attribs) { |
231
|
1 |
|
$obj = clone $this; |
232
|
1 |
|
$obj->_attributes = $attribs; |
233
|
1 |
|
return $obj; |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* Get self with issuer unique identifier. |
238
|
|
|
* |
239
|
|
|
* @param UniqueIdentifier $uid |
240
|
|
|
* @return self |
241
|
|
|
*/ |
242
|
2 |
|
public function withIssuerUniqueID(UniqueIdentifier $uid) { |
243
|
2 |
|
$obj = clone $this; |
244
|
2 |
|
$obj->_issuerUniqueID = $uid; |
245
|
2 |
|
return $obj; |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
/** |
249
|
|
|
* Get self with extensions. |
250
|
|
|
* |
251
|
|
|
* @param Extensions $extensions |
252
|
|
|
* @return self |
253
|
|
|
*/ |
254
|
2 |
|
public function withExtensions(Extensions $extensions) { |
255
|
2 |
|
$obj = clone $this; |
256
|
2 |
|
$obj->_extensions = $extensions; |
257
|
2 |
|
return $obj; |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
/** |
261
|
|
|
* Get self with extensions added. |
262
|
|
|
* |
263
|
|
|
* @param Extension ...$exts One or more Extension objects |
264
|
|
|
* @return self |
265
|
|
|
*/ |
266
|
1 |
|
public function withAdditionalExtensions(Extension ...$exts) { |
267
|
1 |
|
$obj = clone $this; |
268
|
1 |
|
$obj->_extensions = $obj->_extensions->withExtensions(...$exts); |
269
|
1 |
|
return $obj; |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
/** |
273
|
|
|
* Get version. |
274
|
|
|
* |
275
|
|
|
* @return int |
276
|
|
|
*/ |
277
|
1 |
|
public function version() { |
278
|
1 |
|
return $this->_version; |
279
|
|
|
} |
280
|
|
|
|
281
|
|
|
/** |
282
|
|
|
* Get AC holder. |
283
|
|
|
* |
284
|
|
|
* @return Holder |
285
|
|
|
*/ |
286
|
1 |
|
public function holder() { |
287
|
1 |
|
return $this->_holder; |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
/** |
291
|
|
|
* Get AC issuer. |
292
|
|
|
* |
293
|
|
|
* @return AttCertIssuer |
294
|
|
|
*/ |
295
|
1 |
|
public function issuer() { |
296
|
1 |
|
return $this->_issuer; |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
/** |
300
|
|
|
* Check whether signature is set. |
301
|
|
|
* |
302
|
|
|
* @return bool |
303
|
|
|
*/ |
304
|
13 |
|
public function hasSignature() { |
305
|
13 |
|
return isset($this->_signature); |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
/** |
309
|
|
|
* Get signature algorithm identifier. |
310
|
|
|
* |
311
|
|
|
* @return SignatureAlgorithmIdentifier |
312
|
|
|
*/ |
313
|
13 |
|
public function signature() { |
314
|
13 |
|
if (!$this->hasSignature()) { |
315
|
1 |
|
throw new \LogicException("signature not set."); |
316
|
|
|
} |
317
|
12 |
|
return $this->_signature; |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
/** |
321
|
|
|
* Check whether serial number is present. |
322
|
|
|
* |
323
|
|
|
* @return bool |
324
|
|
|
*/ |
325
|
14 |
|
public function hasSerialNumber() { |
326
|
14 |
|
return isset($this->_serialNumber); |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
/** |
330
|
|
|
* Get AC serial number. |
331
|
|
|
* |
332
|
|
|
* @return int|string |
333
|
|
|
*/ |
334
|
14 |
|
public function serialNumber() { |
335
|
14 |
|
if (!$this->hasSerialNumber()) { |
336
|
1 |
|
throw new \LogicException("serialNumber not set."); |
337
|
|
|
} |
338
|
13 |
|
return $this->_serialNumber; |
339
|
|
|
} |
340
|
|
|
|
341
|
|
|
/** |
342
|
|
|
* Get validity period. |
343
|
|
|
* |
344
|
|
|
* @return AttCertValidityPeriod |
345
|
|
|
*/ |
346
|
1 |
|
public function validityPeriod() { |
347
|
1 |
|
return $this->_attrCertValidityPeriod; |
348
|
|
|
} |
349
|
|
|
|
350
|
|
|
/** |
351
|
|
|
* Get attributes. |
352
|
|
|
* |
353
|
|
|
* @return Attributes |
354
|
|
|
*/ |
355
|
1 |
|
public function attributes() { |
356
|
1 |
|
return $this->_attributes; |
357
|
|
|
} |
358
|
|
|
|
359
|
|
|
/** |
360
|
|
|
* Check whether issuer unique identifier is present. |
361
|
|
|
* |
362
|
|
|
* @return bool |
363
|
|
|
*/ |
364
|
2 |
|
public function hasIssuerUniqueID() { |
365
|
2 |
|
return isset($this->_issuerUniqueID); |
366
|
|
|
} |
367
|
|
|
|
368
|
|
|
/** |
369
|
|
|
* Get issuer unique identifier. |
370
|
|
|
* |
371
|
|
|
* @return UniqueIdentifier |
372
|
|
|
*/ |
373
|
2 |
|
public function issuerUniqueID() { |
374
|
2 |
|
if (!$this->hasIssuerUniqueID()) { |
375
|
1 |
|
throw new \LogicException("issuerUniqueID not set."); |
376
|
|
|
} |
377
|
1 |
|
return $this->_issuerUniqueID; |
378
|
|
|
} |
379
|
|
|
|
380
|
|
|
/** |
381
|
|
|
* Get extensions. |
382
|
|
|
* |
383
|
|
|
* @return Extensions |
384
|
|
|
*/ |
385
|
1 |
|
public function extensions() { |
386
|
1 |
|
return $this->_extensions; |
387
|
|
|
} |
388
|
|
|
|
389
|
|
|
/** |
390
|
|
|
* Get ASN.1 structure. |
391
|
|
|
* |
392
|
|
|
* @return Sequence |
393
|
|
|
*/ |
394
|
11 |
|
public function toASN1() { |
395
|
11 |
|
$elements = array(new Integer($this->_version), |
396
|
11 |
|
$this->_holder->toASN1(), $this->_issuer->toASN1(), |
397
|
11 |
|
$this->signature()->toASN1(), new Integer($this->serialNumber()), |
398
|
11 |
|
$this->_attrCertValidityPeriod->toASN1(), |
399
|
11 |
|
$this->_attributes->toASN1()); |
400
|
11 |
|
if (isset($this->_issuerUniqueID)) { |
401
|
3 |
|
$elements[] = $this->_issuerUniqueID->toASN1(); |
402
|
3 |
|
} |
403
|
11 |
|
if (count($this->_extensions)) { |
404
|
6 |
|
$elements[] = $this->_extensions->toASN1(); |
405
|
6 |
|
} |
406
|
11 |
|
return new Sequence(...$elements); |
407
|
|
|
} |
408
|
|
|
|
409
|
|
|
/** |
410
|
|
|
* Create signed attribute certificate. |
411
|
|
|
* |
412
|
|
|
* @param Crypto $crypto |
413
|
|
|
* @param SignatureAlgorithmIdentifier $algo Signature algorithm |
414
|
|
|
* @param PrivateKeyInfo $privkey_info Private key |
415
|
|
|
* @return AttributeCertificate |
416
|
|
|
*/ |
417
|
1 |
|
public function sign(Crypto $crypto, SignatureAlgorithmIdentifier $algo, |
418
|
|
|
PrivateKeyInfo $privkey_info) { |
419
|
1 |
|
$aci = clone $this; |
420
|
1 |
|
if (!isset($aci->_serialNumber)) { |
421
|
1 |
|
$aci->_serialNumber = 0; |
422
|
1 |
|
} |
423
|
1 |
|
$aci->_signature = $algo; |
424
|
1 |
|
$data = $aci->toASN1()->toDER(); |
425
|
1 |
|
$signature = $crypto->sign($data, $privkey_info, $algo); |
426
|
1 |
|
return new AttributeCertificate($aci, $algo, $signature); |
427
|
|
|
} |
428
|
|
|
} |
429
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.