1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace X509\AttributeCertificate; |
4
|
|
|
|
5
|
|
|
use ASN1\Element; |
6
|
|
|
use ASN1\Type\Constructed\Sequence; |
7
|
|
|
use ASN1\Type\Tagged\ImplicitlyTaggedType; |
8
|
|
|
use X509\Certificate\Certificate; |
9
|
|
|
use X509\GeneralName\DirectoryName; |
10
|
|
|
use X509\GeneralName\GeneralName; |
11
|
|
|
use X509\GeneralName\GeneralNames; |
12
|
|
|
|
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* Implements <i>Holder</i> ASN.1 type. |
16
|
|
|
* |
17
|
|
|
* @link https://tools.ietf.org/html/rfc5755#section-4.1 |
18
|
|
|
*/ |
19
|
|
|
class Holder |
20
|
|
|
{ |
21
|
|
|
/** |
22
|
|
|
* Holder PKC's issuer and serial. |
23
|
|
|
* |
24
|
|
|
* @var IssuerSerial|null $_baseCertificateID |
25
|
|
|
*/ |
26
|
|
|
protected $_baseCertificateID; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* Holder PKC's subject. |
30
|
|
|
* |
31
|
|
|
* @var GeneralNames|null $_entityName |
32
|
|
|
*/ |
33
|
|
|
protected $_entityName; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* Linked object. |
37
|
|
|
* |
38
|
|
|
* @var ObjectDigestInfo|null $_objectDigestInfo |
39
|
|
|
*/ |
40
|
|
|
protected $_objectDigestInfo; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* Constructor |
44
|
|
|
* |
45
|
|
|
* @param IssuerSerial|null $issuer_serial |
46
|
|
|
* @param GeneralNames|null $entity_name |
47
|
|
|
*/ |
48
|
21 |
|
public function __construct(IssuerSerial $issuer_serial = null, |
49
|
|
|
GeneralNames $entity_name = null) { |
50
|
21 |
|
$this->_baseCertificateID = $issuer_serial; |
51
|
21 |
|
$this->_entityName = $entity_name; |
52
|
21 |
|
} |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* Initialize from a holder's public key certificate. |
56
|
|
|
* |
57
|
|
|
* @param Certificate $cert |
58
|
|
|
* @return self |
59
|
|
|
*/ |
60
|
1 |
|
public static function fromPKC(Certificate $cert) { |
61
|
1 |
|
return new self(IssuerSerial::fromPKC($cert)); |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* Initialize from ASN.1. |
66
|
|
|
* |
67
|
|
|
* @param Sequence $seq |
68
|
|
|
*/ |
69
|
7 |
View Code Duplication |
public static function fromASN1(Sequence $seq) { |
|
|
|
|
70
|
7 |
|
$cert_id = null; |
71
|
7 |
|
$entity_name = null; |
72
|
7 |
|
$digest_info = null; |
73
|
7 |
|
if ($seq->hasTagged(0)) { |
74
|
7 |
|
$cert_id = IssuerSerial::fromASN1( |
75
|
7 |
|
$seq->getTagged(0) |
76
|
7 |
|
->asImplicit(Element::TYPE_SEQUENCE) |
77
|
7 |
|
->asSequence()); |
78
|
7 |
|
} |
79
|
7 |
|
if ($seq->hasTagged(1)) { |
80
|
3 |
|
$entity_name = GeneralNames::fromASN1( |
81
|
3 |
|
$seq->getTagged(1) |
82
|
3 |
|
->asImplicit(Element::TYPE_SEQUENCE) |
83
|
3 |
|
->asSequence()); |
84
|
3 |
|
} |
85
|
7 |
|
if ($seq->hasTagged(2)) { |
86
|
1 |
|
$digest_info = ObjectDigestInfo::fromASN1( |
87
|
1 |
|
$seq->getTagged(2) |
88
|
1 |
|
->asImplicit(Element::TYPE_SEQUENCE) |
89
|
1 |
|
->asSequence()); |
90
|
1 |
|
} |
91
|
7 |
|
$obj = new self($cert_id, $entity_name); |
92
|
7 |
|
$obj->_objectDigestInfo = $digest_info; |
93
|
7 |
|
return $obj; |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
/** |
97
|
|
|
* Get self with base certificate ID. |
98
|
|
|
* |
99
|
|
|
* @param IssuerSerial $issuer |
100
|
|
|
* @return self |
101
|
|
|
*/ |
102
|
1 |
|
public function withBaseCertificateID(IssuerSerial $issuer) { |
103
|
1 |
|
$obj = clone $this; |
104
|
1 |
|
$obj->_baseCertificateID = $issuer; |
105
|
1 |
|
return $obj; |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* Get self with entity name. |
110
|
|
|
* |
111
|
|
|
* @param GeneralNames $names |
112
|
|
|
* @return self |
113
|
|
|
*/ |
114
|
1 |
|
public function withEntityName(GeneralNames $names) { |
115
|
1 |
|
$obj = clone $this; |
116
|
1 |
|
$obj->_entityName = $names; |
117
|
1 |
|
return $obj; |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* Get self with object digest info. |
122
|
|
|
* |
123
|
|
|
* @param ObjectDigestInfo $odi |
124
|
|
|
* @return self |
125
|
|
|
*/ |
126
|
2 |
|
public function withObjectDigestInfo(ObjectDigestInfo $odi) { |
127
|
2 |
|
$obj = clone $this; |
128
|
2 |
|
$obj->_objectDigestInfo = $odi; |
129
|
2 |
|
return $obj; |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* Check whether base certificate ID is present. |
134
|
|
|
* |
135
|
|
|
* @return bool |
136
|
|
|
*/ |
137
|
2 |
|
public function hasBaseCertificateID() { |
138
|
2 |
|
return isset($this->_baseCertificateID); |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
/** |
142
|
|
|
* Get base certificate ID. |
143
|
|
|
* |
144
|
|
|
* @throws \LogicException |
145
|
|
|
* @return IssuerSerial |
146
|
|
|
*/ |
147
|
2 |
|
public function baseCertificateID() { |
148
|
2 |
|
if (!$this->hasBaseCertificateID()) { |
149
|
1 |
|
throw new \LogicException("baseCertificateID not set."); |
150
|
|
|
} |
151
|
1 |
|
return $this->_baseCertificateID; |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* Check whether entity name is present. |
156
|
|
|
* |
157
|
|
|
* @return bool |
158
|
|
|
*/ |
159
|
2 |
|
public function hasEntityName() { |
160
|
2 |
|
return isset($this->_entityName); |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
/** |
164
|
|
|
* Get entity name |
165
|
|
|
* |
166
|
|
|
* @throws \LogicException |
167
|
|
|
* @return GeneralNames |
168
|
|
|
*/ |
169
|
2 |
|
public function entityName() { |
170
|
2 |
|
if (!$this->hasEntityName()) { |
171
|
1 |
|
throw new \LogicException("entityName not set."); |
172
|
|
|
} |
173
|
1 |
|
return $this->_entityName; |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
/** |
177
|
|
|
* Check whether object digest info is present. |
178
|
|
|
* |
179
|
|
|
* @return bool |
180
|
|
|
*/ |
181
|
2 |
|
public function hasObjectDigestInfo() { |
182
|
2 |
|
return isset($this->_objectDigestInfo); |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* Get object digest info |
187
|
|
|
* |
188
|
|
|
* @throws \LogicException |
189
|
|
|
* @return ObjectDigestInfo |
190
|
|
|
*/ |
191
|
2 |
|
public function objectDigestInfo() { |
192
|
2 |
|
if (!$this->hasObjectDigestInfo()) { |
193
|
1 |
|
throw new \LogicException("objectDigestInfo not set."); |
194
|
|
|
} |
195
|
1 |
|
return $this->_objectDigestInfo; |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
/** |
199
|
|
|
* Generate ASN.1 structure. |
200
|
|
|
* |
201
|
|
|
* @return Sequence |
202
|
|
|
*/ |
203
|
20 |
View Code Duplication |
public function toASN1() { |
|
|
|
|
204
|
20 |
|
$elements = array(); |
205
|
20 |
|
if (isset($this->_baseCertificateID)) { |
206
|
20 |
|
$elements[] = new ImplicitlyTaggedType(0, |
207
|
20 |
|
$this->_baseCertificateID->toASN1()); |
208
|
20 |
|
} |
209
|
20 |
|
if (isset($this->_entityName)) { |
210
|
4 |
|
$elements[] = new ImplicitlyTaggedType(1, |
211
|
4 |
|
$this->_entityName->toASN1()); |
212
|
4 |
|
} |
213
|
20 |
|
if (isset($this->_objectDigestInfo)) { |
214
|
1 |
|
$elements[] = new ImplicitlyTaggedType(2, |
215
|
1 |
|
$this->_objectDigestInfo->toASN1()); |
216
|
1 |
|
} |
217
|
20 |
|
return new Sequence(...$elements); |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
/** |
221
|
|
|
* Check whether Holder identifies given certificate. |
222
|
|
|
* |
223
|
|
|
* @param Certificate $cert |
224
|
|
|
* @return boolean |
225
|
|
|
*/ |
226
|
19 |
|
public function identifiesPKC(Certificate $cert) { |
227
|
|
|
// if neither baseCertificateID nor entityName are present |
228
|
19 |
|
if (!$this->_baseCertificateID && !$this->_entityName) { |
229
|
1 |
|
return false; |
230
|
|
|
} |
231
|
|
|
// if baseCertificateID is present, but doesn't match |
232
|
18 |
|
if ($this->_baseCertificateID && |
233
|
18 |
|
!$this->_baseCertificateID->identifiesPKC($cert)) { |
234
|
3 |
|
return false; |
235
|
|
|
} |
236
|
|
|
// if entityName is present, but doesn't match |
237
|
15 |
|
if ($this->_entityName && !$this->_checkEntityName($cert)) { |
238
|
1 |
|
return false; |
239
|
|
|
} |
240
|
14 |
|
return true; |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
/** |
244
|
|
|
* Check whether entityName matches the given certificate. |
245
|
|
|
* |
246
|
|
|
* @param Certificate $cert |
247
|
|
|
* @return boolean |
248
|
|
|
*/ |
249
|
4 |
|
private function _checkEntityName(Certificate $cert) { |
250
|
4 |
|
$name = $this->_entityName->firstDN(); |
251
|
4 |
|
if ($cert->tbsCertificate() |
252
|
4 |
|
->subject() |
253
|
4 |
|
->equals($name)) { |
254
|
2 |
|
return true; |
255
|
|
|
} |
256
|
2 |
|
$exts = $cert->tbsCertificate()->extensions(); |
257
|
2 |
|
if ($exts->hasSubjectAlternativeName()) { |
258
|
2 |
|
$ext = $exts->subjectAlternativeName(); |
259
|
2 |
|
if ($this->_checkEntityAlternativeNames($ext->names())) { |
260
|
1 |
|
return true; |
261
|
|
|
} |
262
|
1 |
|
} |
263
|
1 |
|
return false; |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
/** |
267
|
|
|
* Check whether any of the subject alternative names match entityName. |
268
|
|
|
* |
269
|
|
|
* @param GeneralNames $san |
270
|
|
|
* @return boolean |
271
|
|
|
*/ |
272
|
2 |
|
private function _checkEntityAlternativeNames(GeneralNames $san) { |
273
|
|
|
// only directory names supported for now |
274
|
2 |
|
$name = $this->_entityName->firstDN(); |
275
|
2 |
|
foreach ($san->allOf(GeneralName::TAG_DIRECTORY_NAME) as $dn) { |
276
|
2 |
|
if ($dn instanceof DirectoryName && $dn->dn()->equals($name)) { |
277
|
1 |
|
return true; |
278
|
|
|
} |
279
|
1 |
|
} |
280
|
1 |
|
return false; |
281
|
|
|
} |
282
|
|
|
} |
283
|
|
|
|
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.