Certificate   A
last analyzed

Complexity

Total Complexity 22

Size/Duplication

Total Lines 234
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 11

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 22
lcom 2
cbo 11
dl 0
loc 234
ccs 61
cts 61
cp 1
rs 10
c 0
b 0
f 0

17 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A fromASN1() 0 14 2
A fromDER() 0 4 1
A fromPEM() 0 7 2
A tbsCertificate() 0 4 1
A signatureAlgorithm() 0 4 1
A signatureValue() 0 4 1
A isSelfIssued() 0 5 1
A equals() 0 5 3
A _hasEqualSerialNumber() 0 6 1
A _hasEqualPublicKey() 0 6 1
A _hasEqualSubject() 0 6 1
A toASN1() 0 6 1
A toDER() 0 4 1
A toPEM() 0 4 1
A verify() 0 7 2
A __toString() 0 4 1
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace X509\Certificate;
6
7
use ASN1\Type\UnspecifiedType;
8
use ASN1\Type\Constructed\Sequence;
9
use Sop\CryptoBridge\Crypto;
10
use Sop\CryptoEncoding\PEM;
11
use Sop\CryptoTypes\AlgorithmIdentifier\AlgorithmIdentifier;
12
use Sop\CryptoTypes\AlgorithmIdentifier\Feature\SignatureAlgorithmIdentifier;
13
use Sop\CryptoTypes\Asymmetric\PublicKeyInfo;
14
use Sop\CryptoTypes\Signature\Signature;
15
16
/**
17
 * Implements <i>Certificate</i> ASN.1 type.
18
 *
19
 * @link https://tools.ietf.org/html/rfc5280#section-4.1
20
 */
21
class Certificate
22
{
23
    /**
24
     * "To be signed" certificate information.
25
     *
26
     * @var TBSCertificate $_tbsCertificate
27
     */
28
    protected $_tbsCertificate;
29
    
30
    /**
31
     * Signature algorithm.
32
     *
33
     * @var SignatureAlgorithmIdentifier $_signatureAlgorithm
34
     */
35
    protected $_signatureAlgorithm;
36
    
37
    /**
38
     * Signature value.
39
     *
40
     * @var Signature $_signatureValue
41
     */
42
    protected $_signatureValue;
43
    
44
    /**
45
     * Constructor.
46
     *
47
     * @param TBSCertificate $tbsCert
48
     * @param SignatureAlgorithmIdentifier $algo
49
     * @param Signature $signature
50
     */
51 30
    public function __construct(TBSCertificate $tbsCert,
52
        SignatureAlgorithmIdentifier $algo, Signature $signature)
53
    {
54 30
        $this->_tbsCertificate = $tbsCert;
55 30
        $this->_signatureAlgorithm = $algo;
56 30
        $this->_signatureValue = $signature;
57 30
    }
58
    
59
    /**
60
     * Initialize from ASN.1.
61
     *
62
     * @param Sequence $seq
63
     * @return self
64
     */
65 18
    public static function fromASN1(Sequence $seq): self
66
    {
67 18
        $tbsCert = TBSCertificate::fromASN1($seq->at(0)->asSequence());
68 18
        $algo = AlgorithmIdentifier::fromASN1($seq->at(1)->asSequence());
69 18
        if (!$algo instanceof SignatureAlgorithmIdentifier) {
70 1
            throw new \UnexpectedValueException(
71 1
                "Unsupported signature algorithm " . $algo->oid() . ".");
72
        }
73 17
        $signature = Signature::fromSignatureData(
74 17
            $seq->at(2)
75 17
                ->asBitString()
76 17
                ->string(), $algo);
77 17
        return new self($tbsCert, $algo, $signature);
78
    }
79
    
80
    /**
81
     * Initialize from DER.
82
     *
83
     * @param string $data
84
     * @return self
85
     */
86 16
    public static function fromDER(string $data): self
87
    {
88 16
        return self::fromASN1(UnspecifiedType::fromDER($data)->asSequence());
89
    }
90
    
91
    /**
92
     * Initialize from PEM.
93
     *
94
     * @param PEM $pem
95
     * @throws \UnexpectedValueException
96
     * @return self
97
     */
98 17
    public static function fromPEM(PEM $pem): self
99
    {
100 17
        if ($pem->type() != PEM::TYPE_CERTIFICATE) {
101 1
            throw new \UnexpectedValueException("Invalid PEM type.");
102
        }
103 16
        return self::fromDER($pem->data());
104
    }
105
    
106
    /**
107
     * Get certificate information.
108
     *
109
     * @return TBSCertificate
110
     */
111 88
    public function tbsCertificate(): TBSCertificate
112
    {
113 88
        return $this->_tbsCertificate;
114
    }
115
    
116
    /**
117
     * Get signature algorithm.
118
     *
119
     * @return SignatureAlgorithmIdentifier
120
     */
121 49
    public function signatureAlgorithm(): SignatureAlgorithmIdentifier
122
    {
123 49
        return $this->_signatureAlgorithm;
124
    }
125
    
126
    /**
127
     * Get signature value.
128
     *
129
     * @return Signature
130
     */
131 2
    public function signatureValue(): Signature
132
    {
133 2
        return $this->_signatureValue;
134
    }
135
    
136
    /**
137
     * Check whether certificate is self-issued.
138
     *
139
     * @return bool
140
     */
141 48
    public function isSelfIssued(): bool
142
    {
143 48
        return $this->_tbsCertificate->subject()->equals(
144 48
            $this->_tbsCertificate->issuer());
145
    }
146
    
147
    /**
148
     * Check whether certificate is semantically equal to another.
149
     *
150
     * @param Certificate $cert Certificate to compare to
151
     * @return bool
152
     */
153 17
    public function equals(Certificate $cert): bool
154
    {
155 17
        return $this->_hasEqualSerialNumber($cert) &&
156 17
             $this->_hasEqualPublicKey($cert) && $this->_hasEqualSubject($cert);
157
    }
158
    
159
    /**
160
     * Check whether certificate has serial number equal to another.
161
     *
162
     * @param Certificate $cert
163
     * @return bool
164
     */
165 17
    private function _hasEqualSerialNumber(Certificate $cert): bool
166
    {
167 17
        $sn1 = $this->_tbsCertificate->serialNumber();
168 17
        $sn2 = $cert->_tbsCertificate->serialNumber();
169 17
        return $sn1 == $sn2;
170
    }
171
    
172
    /**
173
     * Check whether certificate has public key equal to another.
174
     *
175
     * @param Certificate $cert
176
     * @return bool
177
     */
178 16
    private function _hasEqualPublicKey(Certificate $cert): bool
179
    {
180 16
        $kid1 = $this->_tbsCertificate->subjectPublicKeyInfo()->keyIdentifier();
181 16
        $kid2 = $cert->_tbsCertificate->subjectPublicKeyInfo()->keyIdentifier();
182 16
        return $kid1 == $kid2;
183
    }
184
    
185
    /**
186
     * Check whether certificate has subject equal to another.
187
     *
188
     * @param Certificate $cert
189
     * @return bool
190
     */
191 7
    private function _hasEqualSubject(Certificate $cert): bool
192
    {
193 7
        $dn1 = $this->_tbsCertificate->subject();
194 7
        $dn2 = $cert->_tbsCertificate->subject();
195 7
        return $dn1->equals($dn2);
196
    }
197
    
198
    /**
199
     * Generate ASN.1 structure.
200
     *
201
     * @return Sequence
202
     */
203 5
    public function toASN1(): Sequence
204
    {
205 5
        return new Sequence($this->_tbsCertificate->toASN1(),
206 5
            $this->_signatureAlgorithm->toASN1(),
207 5
            $this->_signatureValue->bitString());
208
    }
209
    
210
    /**
211
     * Get certificate as a DER.
212
     *
213
     * @return string
214
     */
215 3
    public function toDER(): string
216
    {
217 3
        return $this->toASN1()->toDER();
218
    }
219
    
220
    /**
221
     * Get certificate as a PEM.
222
     *
223
     * @return PEM
224
     */
225 3
    public function toPEM(): PEM
226
    {
227 3
        return new PEM(PEM::TYPE_CERTIFICATE, $this->toDER());
228
    }
229
    
230
    /**
231
     * Verify certificate signature.
232
     *
233
     * @param PublicKeyInfo $pubkey_info Issuer's public key
234
     * @param Crypto|null $crypto Crypto engine, use default if not set
235
     * @return bool True if certificate signature is valid
236
     */
237 44
    public function verify(PublicKeyInfo $pubkey_info, Crypto $crypto = null): bool
238
    {
239 44
        $crypto = $crypto ?: Crypto::getDefault();
240 44
        $data = $this->_tbsCertificate->toASN1()->toDER();
241 44
        return $crypto->verify($data, $this->_signatureValue, $pubkey_info,
242 44
            $this->_signatureAlgorithm);
243
    }
244
    
245
    /**
246
     * Get certificate as a PEM formatted string.
247
     *
248
     * @return string
249
     */
250 1
    public function __toString()
251
    {
252 1
        return $this->toPEM()->string();
253
    }
254
}
255