Subversion Repositories oidplus

Rev

Rev 846 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
827 daniel-mar 1
<?php
2
 
3
/**
4
 * PKCS#8 Formatted RSA-PSS Key Handler
5
 *
6
 * PHP version 5
7
 *
8
 * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
9
 *
10
 * Processes keys with the following headers:
11
 *
12
 * -----BEGIN ENCRYPTED PRIVATE KEY-----
13
 * -----BEGIN PRIVATE KEY-----
14
 * -----BEGIN PUBLIC KEY-----
15
 *
16
 * Analogous to "openssl genpkey -algorithm rsa-pss".
17
 *
874 daniel-mar 18
 * @category  Crypt
19
 * @package   RSA
827 daniel-mar 20
 * @author    Jim Wigginton <terrafrost@php.net>
21
 * @copyright 2015 Jim Wigginton
22
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
23
 * @link      http://phpseclib.sourceforge.net
24
 */
25
 
26
namespace phpseclib3\Crypt\RSA\Formats\Keys;
27
 
28
use phpseclib3\Common\Functions\Strings;
29
use phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor;
30
use phpseclib3\File\ASN1;
31
use phpseclib3\File\ASN1\Maps;
32
use phpseclib3\Math\BigInteger;
33
 
34
/**
35
 * PKCS#8 Formatted RSA-PSS Key Handler
36
 *
874 daniel-mar 37
 * @package RSA
827 daniel-mar 38
 * @author  Jim Wigginton <terrafrost@php.net>
874 daniel-mar 39
 * @access  public
827 daniel-mar 40
 */
41
abstract class PSS extends Progenitor
42
{
43
    /**
44
     * OID Name
45
     *
46
     * @var string
874 daniel-mar 47
     * @access private
827 daniel-mar 48
     */
49
    const OID_NAME = 'id-RSASSA-PSS';
50
 
51
    /**
52
     * OID Value
53
     *
54
     * @var string
874 daniel-mar 55
     * @access private
827 daniel-mar 56
     */
57
    const OID_VALUE = '1.2.840.113549.1.1.10';
58
 
59
    /**
60
     * OIDs loaded
61
     *
62
     * @var bool
874 daniel-mar 63
     * @access private
827 daniel-mar 64
     */
65
    private static $oidsLoaded = false;
66
 
67
    /**
68
     * Child OIDs loaded
69
     *
70
     * @var bool
874 daniel-mar 71
     * @access private
827 daniel-mar 72
     */
73
    protected static $childOIDsLoaded = false;
74
 
75
    /**
76
     * Initialize static variables
77
     */
78
    private static function initialize_static_variables()
79
    {
80
        if (!self::$oidsLoaded) {
81
            ASN1::loadOIDs([
82
                'md2' => '1.2.840.113549.2.2',
83
                'md4' => '1.2.840.113549.2.4',
84
                'md5' => '1.2.840.113549.2.5',
85
                'id-sha1' => '1.3.14.3.2.26',
86
                'id-sha256' => '2.16.840.1.101.3.4.2.1',
87
                'id-sha384' => '2.16.840.1.101.3.4.2.2',
88
                'id-sha512' => '2.16.840.1.101.3.4.2.3',
89
                'id-sha224' => '2.16.840.1.101.3.4.2.4',
90
                'id-sha512/224' => '2.16.840.1.101.3.4.2.5',
91
                'id-sha512/256' => '2.16.840.1.101.3.4.2.6',
92
 
93
                'id-mgf1' => '1.2.840.113549.1.1.8'
94
            ]);
95
            self::$oidsLoaded = true;
96
        }
97
    }
98
 
99
    /**
100
     * Break a public or private key down into its constituent components
101
     *
874 daniel-mar 102
     * @access public
827 daniel-mar 103
     * @param string $key
104
     * @param string $password optional
105
     * @return array
106
     */
107
    public static function load($key, $password = '')
108
    {
109
        self::initialize_static_variables();
110
 
111
        if (!Strings::is_stringable($key)) {
112
            throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key));
113
        }
114
 
115
        $components = ['isPublicKey' => strpos($key, 'PUBLIC') !== false];
116
 
117
        $key = parent::load($key, $password);
118
 
119
        $type = isset($key['privateKey']) ? 'private' : 'public';
120
 
121
        $result = $components + PKCS1::load($key[$type . 'Key']);
122
 
123
        if (isset($key[$type . 'KeyAlgorithm']['parameters'])) {
124
            $decoded = ASN1::decodeBER($key[$type . 'KeyAlgorithm']['parameters']);
125
            if ($decoded === false) {
126
                throw new \UnexpectedValueException('Unable to decode parameters');
127
            }
128
            $params = ASN1::asn1map($decoded[0], Maps\RSASSA_PSS_params::MAP);
129
        } else {
130
            $params = [];
131
        }
132
 
133
        if (isset($params['maskGenAlgorithm']['parameters'])) {
134
            $decoded = ASN1::decodeBER($params['maskGenAlgorithm']['parameters']);
135
            if ($decoded === false) {
136
                throw new \UnexpectedValueException('Unable to decode parameters');
137
            }
138
            $params['maskGenAlgorithm']['parameters'] = ASN1::asn1map($decoded[0], Maps\HashAlgorithm::MAP);
139
        } else {
140
            $params['maskGenAlgorithm'] = [
141
                'algorithm' => 'id-mgf1',
142
                'parameters' => ['algorithm' => 'id-sha1']
143
            ];
144
        }
145
 
146
        if (!isset($params['hashAlgorithm']['algorithm'])) {
147
            $params['hashAlgorithm']['algorithm'] = 'id-sha1';
148
        }
149
 
150
        $result['hash'] = str_replace('id-', '', $params['hashAlgorithm']['algorithm']);
151
        $result['MGFHash'] = str_replace('id-', '', $params['maskGenAlgorithm']['parameters']['algorithm']);
152
        if (isset($params['saltLength'])) {
153
            $result['saltLength'] = (int) $params['saltLength']->toString();
154
        }
155
 
156
        if (isset($key['meta'])) {
157
            $result['meta'] = $key['meta'];
158
        }
159
 
160
        return $result;
161
    }
162
 
163
    /**
164
     * Convert a private key to the appropriate format.
165
     *
874 daniel-mar 166
     * @access public
827 daniel-mar 167
     * @param \phpseclib3\Math\BigInteger $n
168
     * @param \phpseclib3\Math\BigInteger $e
169
     * @param \phpseclib3\Math\BigInteger $d
170
     * @param array $primes
171
     * @param array $exponents
172
     * @param array $coefficients
173
     * @param string $password optional
174
     * @param array $options optional
175
     * @return string
176
     */
177
    public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '', array $options = [])
178
    {
179
        self::initialize_static_variables();
180
 
181
        $key = PKCS1::savePrivateKey($n, $e, $d, $primes, $exponents, $coefficients);
182
        $key = ASN1::extractBER($key);
183
        $params = self::savePSSParams($options);
184
        return self::wrapPrivateKey($key, [], $params, $password, null, '', $options);
185
    }
186
 
187
    /**
188
     * Convert a public key to the appropriate format
189
     *
874 daniel-mar 190
     * @access public
827 daniel-mar 191
     * @param \phpseclib3\Math\BigInteger $n
192
     * @param \phpseclib3\Math\BigInteger $e
193
     * @param array $options optional
194
     * @return string
195
     */
196
    public static function savePublicKey(BigInteger $n, BigInteger $e, array $options = [])
197
    {
198
        self::initialize_static_variables();
199
 
200
        $key = PKCS1::savePublicKey($n, $e);
201
        $key = ASN1::extractBER($key);
202
        $params = self::savePSSParams($options);
203
        return self::wrapPublicKey($key, $params);
204
    }
205
 
206
    /**
207
     * Encodes PSS parameters
208
     *
874 daniel-mar 209
     * @access public
827 daniel-mar 210
     * @param array $options
211
     * @return string
212
     */
213
    public static function savePSSParams(array $options)
214
    {
215
        /*
216
         The trailerField field is an integer.  It provides
217
         compatibility with IEEE Std 1363a-2004 [P1363A].  The value
218
         MUST be 1, which represents the trailer field with hexadecimal
219
         value 0xBC.  Other trailer fields, including the trailer field
220
         composed of HashID concatenated with 0xCC that is specified in
221
         IEEE Std 1363a, are not supported.  Implementations that
222
         perform signature generation MUST omit the trailerField field,
223
         indicating that the default trailer field value was used.
224
         Implementations that perform signature validation MUST
225
         recognize both a present trailerField field with value 1 and an
226
         absent trailerField field.
227
 
228
         source: https://tools.ietf.org/html/rfc4055#page-9
229
        */
230
        $params = [
231
            'trailerField' => new BigInteger(1)
232
        ];
233
        if (isset($options['hash'])) {
234
            $params['hashAlgorithm']['algorithm'] = 'id-' . $options['hash'];
235
        }
236
        if (isset($options['MGFHash'])) {
237
            $temp = ['algorithm' => 'id-' . $options['MGFHash']];
238
            $temp = ASN1::encodeDER($temp, Maps\HashAlgorithm::MAP);
239
            $params['maskGenAlgorithm'] = [
240
                'algorithm' => 'id-mgf1',
241
                'parameters' => new ASN1\Element($temp)
242
            ];
243
        }
244
        if (isset($options['saltLength'])) {
245
            $params['saltLength'] = new BigInteger($options['saltLength']);
246
        }
247
 
248
        return new ASN1\Element(ASN1::encodeDER($params, Maps\RSASSA_PSS_params::MAP));
249
    }
250
}