KeyGenerator   A
last analyzed

Complexity

Total Complexity 13

Size/Duplication

Total Lines 110
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 13
eloc 50
c 0
b 0
f 0
dl 0
loc 110
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A csr() 0 43 5
A key() 0 20 4
A rsa() 0 13 2
A ec() 0 13 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the LetsEncrypt ACME client.
7
 *
8
 * @author    Ivanov Aleksandr <[email protected]>
9
 * @copyright 2019-2020
10
 * @license   https://github.com/misantron/letsencrypt-client/blob/master/LICENSE MIT License
11
 */
12
13
namespace LetsEncrypt\Helper;
14
15
use LetsEncrypt\Enum\ECKeyAlgorithm;
16
use LetsEncrypt\Enum\RSAKeyLength;
17
use LetsEncrypt\Exception\KeyGeneratorException;
18
use LetsEncrypt\Exception\KeyPairException;
19
20
final class KeyGenerator
21
{
22
    public function rsa(string $privateKeyPath, string $publicKeyPath, RSAKeyLength $length = null): void
23
    {
24
        if ($length === null) {
25
            $length = RSAKeyLength::bit2048();
26
        }
27
28
        $this->key(
29
            [
30
                'private_key_type' => OPENSSL_KEYTYPE_RSA,
31
                'private_key_bits' => (int) $length->getValue(),
32
            ],
33
            $privateKeyPath,
34
            $publicKeyPath
35
        );
36
    }
37
38
    public function ec(string $privateKeyPath, string $publicKeyPath, ECKeyAlgorithm $type = null): void
39
    {
40
        if ($type === null) {
41
            $type = ECKeyAlgorithm::prime256v1();
42
        }
43
44
        $this->key(
45
            [
46
                'private_key_type' => OPENSSL_KEYTYPE_EC,
47
                'curve_name' => $type->getValue(),
48
            ],
49
            $privateKeyPath,
50
            $publicKeyPath
51
        );
52
    }
53
54
    /**
55
     * @throws KeyGeneratorException
56
     * @throws KeyPairException
57
     */
58
    public function csr(string $commonName, array $subjects, string $privateKeyPath): string
59
    {
60
        $domains = [];
61
        foreach (array_values($subjects) as $index => $subject) {
62
            $domains[] = 'DNS.' . ($index + 1) . ' = ' . $subject;
63
        }
64
65
        $csrConfigTemplate = FileSystem::readFileContent(dirname(__DIR__, 2) . '/resources/csr.template');
66
        $csrConfigContent = sprintf($csrConfigTemplate, implode(PHP_EOL, $domains));
67
        $csrConfigFilePath = tempnam(sys_get_temp_dir(), 'lec_');
68
69
        try {
70
            FileSystem::writeFileContent($csrConfigFilePath, $csrConfigContent);
71
72
            $privateKey = openssl_pkey_get_private('file://' . $privateKeyPath);
73
            if ($privateKey === false) {
74
                throw KeyPairException::privateKeyInvalid();
75
            }
76
77
            $resource = openssl_csr_new(
78
                [
79
                    'commonName' => $commonName,
80
                ],
81
                $privateKey,
82
                [
83
                    'digest_alg' => 'sha256',
84
                    'config' => $csrConfigFilePath,
85
                ]
86
            );
87
            if ($resource === false) {
88
                throw KeyGeneratorException::csrCreateError();
89
            }
90
91
            if (openssl_csr_export($resource, $csr) === false) {
92
                throw KeyGeneratorException::csrExportError();
93
            }
94
95
            openssl_free_key($privateKey);
96
97
            return $csr;
98
        } finally {
99
            // delete temporary file anyway
100
            unlink($csrConfigFilePath);
101
        }
102
    }
103
104
    /**
105
     * Create OpenSSL key pair and store in file system.
106
     *
107
     * @throws KeyGeneratorException
108
     * @throws KeyPairException
109
     */
110
    private function key(array $config, string $privateKeyPath, string $publicKeyPath): void
111
    {
112
        $resource = openssl_pkey_new($config);
113
        if ($resource === false) {
114
            throw KeyGeneratorException::keyCreateError();
115
        }
116
117
        if (openssl_pkey_export($resource, $privateKey) === false) {
118
            throw KeyGeneratorException::keyExportError();
119
        }
120
121
        $details = openssl_pkey_get_details($resource);
122
        if ($details === false) {
123
            throw KeyPairException::privateKeyDetailsError();
124
        }
125
126
        FileSystem::writeFileContent($privateKeyPath, $privateKey);
127
        FileSystem::writeFileContent($publicKeyPath, $details['key']);
128
129
        openssl_pkey_free($resource);
130
    }
131
}
132