Issues (234)

src/CryptoEncoding/PEM.php (1 issue)

Labels
Severity
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\XMLSecurity\CryptoEncoding;
6
7
use SimpleSAML\XMLSecurity\Exception\IOException;
8
use UnexpectedValueException;
9
10
use function base64_decode;
11
use function base64_encode;
12
use function chunk_split;
13
use function file_get_contents;
14
use function is_readable;
15
use function preg_match;
16
use function preg_replace;
17
use function sprintf;
18
use function trim;
19
20
/**
21
 * Implements PEM file encoding and decoding.
22
 *
23
 * @see https://tools.ietf.org/html/rfc7468
24
 */
25
class PEM
26
{
27
    // well-known PEM types
28
    public const string TYPE_CERTIFICATE = 'CERTIFICATE';
0 ignored issues
show
A parse error occurred: Syntax error, unexpected T_STRING, expecting '=' on line 28 at column 24
Loading history...
29
30
    public const string TYPE_CRL = 'X509 CRL';
31
32
    public const string TYPE_CERTIFICATE_REQUEST = 'CERTIFICATE REQUEST';
33
34
    public const string TYPE_ATTRIBUTE_CERTIFICATE = 'ATTRIBUTE CERTIFICATE';
35
36
    public const string TYPE_PRIVATE_KEY = 'PRIVATE KEY';
37
38
    public const string TYPE_PUBLIC_KEY = 'PUBLIC KEY';
39
40
    public const string TYPE_ENCRYPTED_PRIVATE_KEY = 'ENCRYPTED PRIVATE KEY';
41
42
    public const string TYPE_RSA_PRIVATE_KEY = 'RSA PRIVATE KEY';
43
44
    public const string TYPE_RSA_PUBLIC_KEY = 'RSA PUBLIC KEY';
45
46
    public const string TYPE_EC_PRIVATE_KEY = 'EC PRIVATE KEY';
47
48
    public const string TYPE_PKCS7 = 'PKCS7';
49
50
    public const string TYPE_CMS = 'CMS';
51
52
    /**
53
     * Regular expression to match PEM block.
54
     */
55
    public const string PEM_REGEX =
56
        '/' .
57
        '(?:^|[\r\n])' .                 // line start
58
        '-----BEGIN (.+?)-----[\r\n]+' . // header
59
        '(.+?)' .                        // payload
60
        '[\r\n]+-----END \\1-----' .     // footer
61
        '/ms';
62
63
64
    /**
65
     * Constructor.
66
     *
67
     * @param string $type Content type
68
     * @param string $data Payload
69
     */
70
    public function __construct(
71
        protected string $type,
72
        protected string $data,
73
    ) {
74
    }
75
76
77
    /**
78
     */
79
    public function __toString(): string
80
    {
81
        return $this->string();
82
    }
83
84
85
    /**
86
     * Initialize from a PEM-formatted string.
87
     *
88
     * @throws \UnexpectedValueException If string is not valid PEM
89
     */
90
    public static function fromString(string $str): self
91
    {
92
        if (!preg_match(self::PEM_REGEX, $str, $match)) {
93
            throw new UnexpectedValueException('Not a PEM formatted string.');
94
        }
95
96
        $payload = preg_replace('/\s+/', '', $match[2]);
97
        $data = base64_decode($payload, true);
98
        if (empty($data)) {
99
            throw new UnexpectedValueException('Failed to decode PEM data.');
100
        }
101
102
        return new self($match[1], $data);
103
    }
104
105
106
    /**
107
     * Initialize from a file.
108
     *
109
     * @param string $filename Path to file
110
     *
111
     * @throws \RuntimeException If file reading fails
112
     */
113
    public static function fromFile(string $filename): self
114
    {
115
        error_clear_last();
116
        $str = @file_get_contents($filename);
117
118
        if (!is_readable($filename) || ($str === false)) {
119
            $e = error_get_last();
120
            $error = $e['message'] ?? "Check that the file exists and can be read.";
121
            throw new IOException(sprintf("File '%s' was not loaded;  %s", $filename, $error));
122
        }
123
124
        return self::fromString($str);
125
    }
126
127
128
    /**
129
     * Get content type.
130
     */
131
    public function type(): string
132
    {
133
        return $this->type;
134
    }
135
136
137
    /**
138
     * Get payload.
139
     */
140
    public function data(): string
141
    {
142
        return $this->data;
143
    }
144
145
146
    /**
147
     * Encode to PEM string.
148
     */
149
    public function string(): string
150
    {
151
        return sprintf(
152
            "-----BEGIN %s-----\n%s\n-----END %s-----",
153
            $this->type,
154
            trim(chunk_split(base64_encode($this->data), 64, "\n")),
155
            $this->type,
156
        );
157
    }
158
}
159