Passed
Pull Request — master (#43)
by Tim
02:10
created

X509Certificate::getCertificate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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