X509Certificate::__construct()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 3
nop 1
dl 0
loc 17
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\XMLSecurity\Key;
6
7
use SimpleSAML\Assert\Assert;
8
use SimpleSAML\XMLSecurity\Constants as C;
9
use SimpleSAML\XMLSecurity\CryptoEncoding\PEM;
10
use SimpleSAML\XMLSecurity\Exception\OpenSSLException;
11
use SimpleSAML\XMLSecurity\Exception\UnsupportedAlgorithmException;
12
13
use function openssl_pkey_get_details;
14
use function openssl_pkey_get_public;
15
use function openssl_x509_fingerprint;
16
use function openssl_x509_parse;
17
18
/**
19
 * A class modeling X509 certificates.
20
 *
21
 * @package simplesamlphp/xml-security
22
 */
23
class X509Certificate
24
{
25
    /** @var \SimpleSAML\XMLSecurity\Key\PublicKey */
26
    protected PublicKey $publicKey;
27
28
    /** @var array<string, string> */
29
    protected array $thumbprint = [];
30
31
    /** @var array<string, mixed> */
32
    protected array $parsed = [];
33
34
35
    /**
36
     * Create a new X509 certificate from its PEM-encoded representation.
37
     *
38
     * @param \SimpleSAML\XMLSecurity\CryptoEncoding\PEM $material
39
     *   The PEM-encoded certificate or the path to a file containing it.
40
     *
41
     * @throws \SimpleSAML\XMLSecurity\Exception\OpenSSLException If the certificate cannot be exported to PEM format.
42
     */
43
    final public function __construct(
44
        protected PEM $material,
45
    ) {
46
        Assert::oneOf($material->type(), [PEM::TYPE_CERTIFICATE], "PEM structure has the wrong type %s.");
47
48
        if (($key = openssl_pkey_get_public($material->string())) === false) {
49
            throw new OpenSSLException('Failed to read key');
50
        }
51
52
        if (($details = openssl_pkey_get_details($key)) === false) {
53
            throw new OpenSSLException('Failed to export key');
54
        }
55
56
        $this->publicKey = new PublicKey(PEM::fromString($details['key']));
57
58
        $this->thumbprint[C::DIGEST_SHA1] = $this->getRawThumbprint();
59
        $this->parsed = openssl_x509_parse($material->string());
60
    }
61
62
63
    /**
64
     * Return the public key associated with this certificate.
65
     *
66
     * @return \SimpleSAML\XMLSecurity\Key\PublicKey The public key.
67
     */
68
    public function getPublicKey(): PublicKey
69
    {
70
        return $this->publicKey;
71
    }
72
73
74
    /**
75
     * Return the key material associated with this key.
76
     *
77
     * @return string The key material.
78
     */
79
    public function getMaterial(): string
80
    {
81
        return $this->material->string();
82
    }
83
84
85
    /**
86
     * Return the raw PEM-object associated with this key.
87
     *
88
     * @return \SimpleSAML\XMLSecurity\CryptoEncoding\PEM The raw material.
89
     */
90
    public function getPEM(): PEM
91
    {
92
        return $this->material;
93
    }
94
95
96
    /**
97
     * Get the raw thumbprint of a certificate
98
     *
99
     * @param string $alg The digest algorithm to use. Defaults to SHA1.
100
     *
101
     * @return string The thumbprint associated with the given certificate.
102
     */
103
    public function getRawThumbprint(string $alg = C::DIGEST_SHA1): string
104
    {
105
        if (isset($this->thumbprint[$alg])) {
106
            return $this->thumbprint[$alg];
107
        }
108
109
        Assert::keyExists(
110
            C::$DIGEST_ALGORITHMS,
111
            $alg,
112
            'Invalid digest algorithm identifier',
113
            UnsupportedAlgorithmException::class,
114
        );
115
116
        return $this->thumbprint[$alg] = openssl_x509_fingerprint(
117
            $this->material->string(),
118
            C::$DIGEST_ALGORITHMS[$alg],
119
        );
120
    }
121
122
123
    /**
124
     * Get the details of this certificate.
125
     *
126
     * @return array<string, mixed> An array with all the details of the certificate.
127
     *
128
     * @see openssl_x509_parse()
129
     */
130
    public function getCertificateDetails(): array
131
    {
132
        return $this->parsed;
133
    }
134
135
136
    /**
137
     * Get a new X509 certificate from a file.
138
     *
139
     * @param string $file The file where the PEM-encoded certificate is stored.
140
     *
141
     * @return static A new X509Certificate key.
142
     */
143
    public static function fromFile(string $file): static
144
    {
145
        return new static(PEM::fromFile($file));
146
    }
147
}
148