EncryptedPrivateKeyInfo   A
last analyzed

Complexity

Total Complexity 16

Size/Duplication

Total Lines 188
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 38
dl 0
loc 188
ccs 47
cts 47
cp 1
rs 10
c 0
b 0
f 0
wmc 16

12 Methods

Rating   Name   Duplication   Size   Complexity  
A fromPEM() 0 6 2
A fromDER() 0 3 1
A toASN1() 0 3 1
A encryptWithPassword() 0 6 1
A fromASN1() 0 9 2
A encryptionAlgorithm() 0 3 1
A decryptWithPassword() 0 15 3
A toPEM() 0 3 1
A encryptedData() 0 3 1
A toDER() 0 3 1
A encryptWithDerivedKey() 0 6 1
A __construct() 0 4 1
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace Sop\PKCS8;
6
7
use Sop\ASN1\Type\Constructed\Sequence;
8
use Sop\ASN1\Type\Primitive\OctetString;
9
use Sop\ASN1\Type\UnspecifiedType;
10
use Sop\CryptoBridge\Crypto;
11
use Sop\CryptoEncoding\PEM;
12
use Sop\CryptoTypes\AlgorithmIdentifier\Feature\EncryptionAlgorithmIdentifier;
13
use Sop\CryptoTypes\Asymmetric\PrivateKeyInfo;
14
use Sop\PKCS5\ASN1\AlgorithmIdentifier\PBEAlgorithmIdentifier;
15
use Sop\PKCS5\PBEScheme;
16
17
/**
18
 * Implements PKCS #8 *EncryptedPrivateKeyInfo* ASN.1 type.
19
 *
20
 * @see https://tools.ietf.org/html/rfc5208#section-6
21
 */
22
class EncryptedPrivateKeyInfo
23
{
24
    /**
25
     * Encryption algorithm.
26
     *
27
     * @var EncryptionAlgorithmIdentifier
28
     */
29
    protected $_algo;
30
31
    /**
32
     * Encrypted data.
33
     *
34
     * @var string
35
     */
36
    protected $_data;
37
38
    /**
39
     * Constructor.
40
     *
41
     * @param EncryptionAlgorithmIdentifier $algo
42
     * @param string                        $data Ciphertext
43
     */
44 13
    protected function __construct(EncryptionAlgorithmIdentifier $algo, string $data)
45
    {
46 13
        $this->_algo = $algo;
47 13
        $this->_data = $data;
48 13
    }
49
50
    /**
51
     * Initialize from ASN.1.
52
     *
53
     * @param Sequence $seq
54
     *
55
     * @throws \UnexpectedValueException
56
     *
57
     * @return self
58
     */
59 10
    public static function fromASN1(Sequence $seq): self
60
    {
61 10
        $algo = PBEAlgorithmIdentifier::fromASN1($seq->at(0)->asSequence());
62 10
        if (!($algo instanceof EncryptionAlgorithmIdentifier)) {
63 1
            throw new \UnexpectedValueException(
64 1
                sprintf('Algorithm %s not supported.', $algo->name()));
65
        }
66 9
        $data = $seq->at(1)->asOctetString()->string();
67 9
        return new self($algo, $data);
68
    }
69
70
    /**
71
     * Initialize from DER data.
72
     *
73
     * @param string $data
74
     *
75
     * @return self
76
     */
77 9
    public static function fromDER(string $data): self
78
    {
79 9
        return self::fromASN1(UnspecifiedType::fromDER($data)->asSequence());
80
    }
81
82
    /**
83
     * Initialize from PEM.
84
     *
85
     * @param PEM $pem
86
     *
87
     * @throws \UnexpectedValueException
88
     *
89
     * @return self
90
     */
91 10
    public static function fromPEM(PEM $pem): self
92
    {
93 10
        if (PEM::TYPE_ENCRYPTED_PRIVATE_KEY !== $pem->type()) {
94 1
            throw new \UnexpectedValueException('Invalid PEM type.');
95
        }
96 9
        return self::fromDER($pem->data());
97
    }
98
99
    /**
100
     * Get the encryption algorithm.
101
     *
102
     * @return EncryptionAlgorithmIdentifier
103
     */
104 4
    public function encryptionAlgorithm(): EncryptionAlgorithmIdentifier
105
    {
106 4
        return $this->_algo;
107
    }
108
109
    /**
110
     * Get the encrypted private key data.
111
     *
112
     * @return string
113
     */
114 1
    public function encryptedData(): string
115
    {
116 1
        return $this->_data;
117
    }
118
119
    /**
120
     * Get ASN.1 structure.
121
     *
122
     * @return Sequence
123
     */
124 5
    public function toASN1(): Sequence
125
    {
126 5
        return new Sequence($this->_algo->toASN1(), new OctetString($this->_data));
127
    }
128
129
    /**
130
     * Generate DER encoding.
131
     *
132
     * @return string
133
     */
134 5
    public function toDER(): string
135
    {
136 5
        return $this->toASN1()->toDER();
137
    }
138
139
    /**
140
     * Get encrypted private key PEM.
141
     *
142
     * @return PEM
143
     */
144 1
    public function toPEM(): PEM
145
    {
146 1
        return new PEM(PEM::TYPE_ENCRYPTED_PRIVATE_KEY, $this->toDER());
147
    }
148
149
    /**
150
     * Decrypt PrivateKeyInfo from the encrypted data using password based encryption.
151
     *
152
     * @param string      $password Password
153
     * @param null|Crypto $crypto   Crypto engine, use default if not set
154
     *
155
     * @return PrivateKeyInfo
156
     */
157 11
    public function decryptWithPassword(string $password, ?Crypto $crypto = null): PrivateKeyInfo
158
    {
159 11
        $ai = $this->_algo;
160 11
        if (!($ai instanceof PBEAlgorithmIdentifier)) {
161 1
            throw new \RuntimeException(
162 1
                sprintf('Algorithm %s does not support' .
163 1
                    ' password based encryption.', $ai->name()));
164
        }
165
        try {
166 10
            $scheme = PBEScheme::fromAlgorithmIdentifier($ai, $crypto);
167 10
            $data = $scheme->decrypt($this->_data, $password);
168 9
            return PrivateKeyInfo::fromASN1(
169 9
                UnspecifiedType::fromDER($data)->asSequence());
170 1
        } catch (\RuntimeException $e) {
171 1
            throw new \RuntimeException('Failed to decrypt private key.', 0, $e);
172
        }
173
    }
174
175
    /**
176
     * Initialize by encrypting a PrivateKeyInfo using password based encryption.
177
     *
178
     * @param PrivateKeyInfo         $pki      Private key info
179
     * @param PBEAlgorithmIdentifier $algo     Encryption algorithm
180
     * @param string                 $password Password
181
     * @param null|Crypto            $crypto   Crypto engine, use default if not set
182
     *
183
     * @return self
184
     */
185 3
    public static function encryptWithPassword(PrivateKeyInfo $pki,
186
        PBEAlgorithmIdentifier $algo, string $password, ?Crypto $crypto = null): self
187
    {
188 3
        $scheme = PBEScheme::fromAlgorithmIdentifier($algo, $crypto);
189 3
        $ciphertext = $scheme->encrypt($pki->toDER(), $password);
190 3
        return new self($algo, $ciphertext);
191
    }
192
193
    /**
194
     * Initialize by encrypting a PrivateKeyInfo using password based encryption
195
     * with pre-derived key.
196
     *
197
     * @param PrivateKeyInfo         $pki    Private key info
198
     * @param PBEAlgorithmIdentifier $algo   Encryption algorithm
199
     * @param string                 $key    Key derived from a password
200
     * @param null|Crypto            $crypto Crypto engine, use default if not set
201
     *
202
     * @return self
203
     */
204 1
    public static function encryptWithDerivedKey(PrivateKeyInfo $pki,
205
        PBEAlgorithmIdentifier $algo, string $key, ?Crypto $crypto = null): self
206
    {
207 1
        $scheme = PBEScheme::fromAlgorithmIdentifier($algo, $crypto);
208 1
        $ciphertext = $scheme->encryptWithKey($pki->toDER(), $key);
209 1
        return new self($algo, $ciphertext);
210
    }
211
}
212