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