Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 1234
0.00% covered (danger)
0.00%
0 / 59
CRAP
0.00% covered (danger)
0.00%
0 / 1
Crypt_RSA
0.00% covered (danger)
0.00%
0 / 1207
0.00% covered (danger)
0.00%
0 / 59
134322
0.00% covered (danger)
0.00%
0 / 1
 Crypt_RSA
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 1
272
 createKey
0.00% covered (danger)
0.00%
0 / 98
0.00% covered (danger)
0.00%
0 / 1
420
 _convertPrivateKey
0.00% covered (danger)
0.00%
0 / 185
0.00% covered (danger)
0.00%
0 / 1
600
 _convertPublicKey
0.00% covered (danger)
0.00%
0 / 44
0.00% covered (danger)
0.00%
0 / 1
42
 _parseKey
0.00% covered (danger)
0.00%
0 / 272
0.00% covered (danger)
0.00%
0 / 1
7482
 getSize
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 _start_element_handler
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
90
 _stop_element_handler
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 _data_handler
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 loadKey
0.00% covered (danger)
0.00%
0 / 70
0.00% covered (danger)
0.00%
0 / 1
600
 setPassword
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setPublicKey
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
110
 setPrivateKey
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
 getPublicKey
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 getPublicKeyFingerprint
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
42
 getPrivateKey
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 _getPrivatePublicKey
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 __toString
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 __clone
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 _generateMinMax
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
6
 _decodeLength
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 _encodeLength
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 _string_shift
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 setPrivateKeyFormat
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setPublicKeyFormat
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setHash
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
72
 setMGFHash
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
72
 setSaltLength
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 _i2osp
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 _os2ip
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 _exponentiate
0.00% covered (danger)
0.00%
0 / 43
0.00% covered (danger)
0.00%
0 / 1
90
 _blind
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 _equals
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 _rsaep
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 _rsadp
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 _rsasp1
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 _rsavp1
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 _mgf1
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 _rsaes_oaep_encrypt
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
6
 _rsaes_oaep_decrypt
0.00% covered (danger)
0.00%
0 / 27
0.00% covered (danger)
0.00%
0 / 1
42
 _raw_encrypt
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 _rsaes_pkcs1_v1_5_encrypt
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
42
 _rsaes_pkcs1_v1_5_decrypt
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
42
 _emsa_pss_encode
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
12
 _emsa_pss_verify
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
56
 _rsassa_pss_sign
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 _rsassa_pss_verify
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
20
 _emsa_pkcs1_v1_5_encode
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
90
 _rsassa_pkcs1_v1_5_sign
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 _rsassa_pkcs1_v1_5_verify
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
30
 setEncryptionMode
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setSignatureMode
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setComment
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getComment
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 encrypt
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
90
 decrypt
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
56
 sign
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
30
 verify
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
30
 _extractBER
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3/**
4 * Pure-PHP PKCS#1 (v2.1) compliant implementation of RSA.
5 *
6 * PHP versions 4 and 5
7 *
8 * Here's an example of how to encrypt and decrypt text with this library:
9 * <code>
10 * <?php
11 *    include 'Crypt/RSA.php';
12 *
13 *    $rsa = new Crypt_RSA();
14 *    extract($rsa->createKey());
15 *
16 *    $plaintext = 'terrafrost';
17 *
18 *    $rsa->loadKey($privatekey);
19 *    $ciphertext = $rsa->encrypt($plaintext);
20 *
21 *    $rsa->loadKey($publickey);
22 *    echo $rsa->decrypt($ciphertext);
23 * ?>
24 * </code>
25 *
26 * Here's an example of how to create signatures and verify signatures with this library:
27 * <code>
28 * <?php
29 *    include 'Crypt/RSA.php';
30 *
31 *    $rsa = new Crypt_RSA();
32 *    extract($rsa->createKey());
33 *
34 *    $plaintext = 'terrafrost';
35 *
36 *    $rsa->loadKey($privatekey);
37 *    $signature = $rsa->sign($plaintext);
38 *
39 *    $rsa->loadKey($publickey);
40 *    echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified';
41 * ?>
42 * </code>
43 *
44 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
45 * of this software and associated documentation files (the "Software"), to deal
46 * in the Software without restriction, including without limitation the rights
47 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
48 * copies of the Software, and to permit persons to whom the Software is
49 * furnished to do so, subject to the following conditions:
50 *
51 * The above copyright notice and this permission notice shall be included in
52 * all copies or substantial portions of the Software.
53 *
54 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
55 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
56 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
57 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
58 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
59 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
60 * THE SOFTWARE.
61 *
62 * @category  Crypt
63 * @package   Crypt_RSA
64 * @author    Jim Wigginton <terrafrost@php.net>
65 * @copyright 2009 Jim Wigginton
66 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
67 * @link      http://phpseclib.sourceforge.net
68 */
69
70/**
71 * Include Crypt_Random
72 */
73// the class_exists() will only be called if the crypt_random_string function hasn't been defined and
74// will trigger a call to __autoload() if you're wanting to auto-load classes
75// call function_exists() a second time to stop the include_once from being called outside
76// of the auto loader
77if (!function_exists('crypt_random_string')) {
78    include_once 'Random.php';
79}
80
81/**
82 * Include Crypt_Hash
83 */
84if (!class_exists('Crypt_Hash')) {
85    include_once 'Hash.php';
86}
87
88/**#@+
89 * @access public
90 * @see Crypt_RSA::encrypt()
91 * @see Crypt_RSA::decrypt()
92 */
93/**
94 * Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding}
95 * (OAEP) for encryption / decryption.
96 *
97 * Uses sha1 by default.
98 *
99 * @see Crypt_RSA::setHash()
100 * @see Crypt_RSA::setMGFHash()
101 */
102define('CRYPT_RSA_ENCRYPTION_OAEP',  1);
103/**
104 * Use PKCS#1 padding.
105 *
106 * Although CRYPT_RSA_ENCRYPTION_OAEP offers more security, including PKCS#1 padding is necessary for purposes of backwards
107 * compatibility with protocols (like SSH-1) written before OAEP's introduction.
108 */
109define('CRYPT_RSA_ENCRYPTION_PKCS1', 2);
110/**
111 * Do not use any padding
112 *
113 * Although this method is not recommended it can none-the-less sometimes be useful if you're trying to decrypt some legacy
114 * stuff, if you're trying to diagnose why an encrypted message isn't decrypting, etc.
115 */
116define('CRYPT_RSA_ENCRYPTION_NONE', 3);
117/**#@-*/
118
119/**#@+
120 * @access public
121 * @see Crypt_RSA::sign()
122 * @see Crypt_RSA::verify()
123 * @see Crypt_RSA::setHash()
124 */
125/**
126 * Use the Probabilistic Signature Scheme for signing
127 *
128 * Uses sha1 by default.
129 *
130 * @see Crypt_RSA::setSaltLength()
131 * @see Crypt_RSA::setMGFHash()
132 */
133define('CRYPT_RSA_SIGNATURE_PSS',  1);
134/**
135 * Use the PKCS#1 scheme by default.
136 *
137 * Although CRYPT_RSA_SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards
138 * compatibility with protocols (like SSH-2) written before PSS's introduction.
139 */
140define('CRYPT_RSA_SIGNATURE_PKCS1', 2);
141/**#@-*/
142
143/**#@+
144 * @access private
145 * @see Crypt_RSA::createKey()
146 */
147/**
148 * ASN1 Integer
149 */
150define('CRYPT_RSA_ASN1_INTEGER',     2);
151/**
152 * ASN1 Bit String
153 */
154define('CRYPT_RSA_ASN1_BITSTRING',   3);
155/**
156 * ASN1 Octet String
157 */
158define('CRYPT_RSA_ASN1_OCTETSTRING', 4);
159/**
160 * ASN1 Object Identifier
161 */
162define('CRYPT_RSA_ASN1_OBJECT',      6);
163/**
164 * ASN1 Sequence (with the constucted bit set)
165 */
166define('CRYPT_RSA_ASN1_SEQUENCE',   48);
167/**#@-*/
168
169/**#@+
170 * @access private
171 * @see Crypt_RSA::Crypt_RSA()
172 */
173/**
174 * To use the pure-PHP implementation
175 */
176define('CRYPT_RSA_MODE_INTERNAL', 1);
177/**
178 * To use the OpenSSL library
179 *
180 * (if enabled; otherwise, the internal implementation will be used)
181 */
182define('CRYPT_RSA_MODE_OPENSSL', 2);
183/**#@-*/
184
185/**
186 * Default openSSL configuration file.
187 */
188define('CRYPT_RSA_OPENSSL_CONFIG', dirname(__FILE__) . '/../openssl.cnf');
189
190/**#@+
191 * @access public
192 * @see Crypt_RSA::createKey()
193 * @see Crypt_RSA::setPrivateKeyFormat()
194 */
195/**
196 * PKCS#1 formatted private key
197 *
198 * Used by OpenSSH
199 */
200define('CRYPT_RSA_PRIVATE_FORMAT_PKCS1', 0);
201/**
202 * PuTTY formatted private key
203 */
204define('CRYPT_RSA_PRIVATE_FORMAT_PUTTY', 1);
205/**
206 * XML formatted private key
207 */
208define('CRYPT_RSA_PRIVATE_FORMAT_XML', 2);
209/**
210 * PKCS#8 formatted private key
211 */
212define('CRYPT_RSA_PRIVATE_FORMAT_PKCS8', 3);
213/**#@-*/
214
215/**#@+
216 * @access public
217 * @see Crypt_RSA::createKey()
218 * @see Crypt_RSA::setPublicKeyFormat()
219 */
220/**
221 * Raw public key
222 *
223 * An array containing two Math_BigInteger objects.
224 *
225 * The exponent can be indexed with any of the following:
226 *
227 * 0, e, exponent, publicExponent
228 *
229 * The modulus can be indexed with any of the following:
230 *
231 * 1, n, modulo, modulus
232 */
233define('CRYPT_RSA_PUBLIC_FORMAT_RAW', 3);
234/**
235 * PKCS#1 formatted public key (raw)
236 *
237 * Used by File/X509.php
238 *
239 * Has the following header:
240 *
241 * -----BEGIN RSA PUBLIC KEY-----
242 *
243 * Analogous to ssh-keygen's pem format (as specified by -m)
244 */
245define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1', 4);
246define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW', 4);
247/**
248 * XML formatted public key
249 */
250define('CRYPT_RSA_PUBLIC_FORMAT_XML', 5);
251/**
252 * OpenSSH formatted public key
253 *
254 * Place in $HOME/.ssh/authorized_keys
255 */
256define('CRYPT_RSA_PUBLIC_FORMAT_OPENSSH', 6);
257/**
258 * PKCS#1 formatted public key (encapsulated)
259 *
260 * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
261 *
262 * Has the following header:
263 *
264 * -----BEGIN PUBLIC KEY-----
265 *
266 * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
267 * is specific to private keys it's basically creating a DER-encoded wrapper
268 * for keys. This just extends that same concept to public keys (much like ssh-keygen)
269 */
270define('CRYPT_RSA_PUBLIC_FORMAT_PKCS8', 7);
271/**#@-*/
272
273/**
274 * Pure-PHP PKCS#1 compliant implementation of RSA.
275 *
276 * @package Crypt_RSA
277 * @author  Jim Wigginton <terrafrost@php.net>
278 * @access  public
279 */
280class Crypt_RSA
281{
282    /**
283     * Precomputed Zero
284     *
285     * @var Array
286     * @access private
287     */
288    var $zero;
289
290    /**
291     * Precomputed One
292     *
293     * @var Array
294     * @access private
295     */
296    var $one;
297
298    /**
299     * Private Key Format
300     *
301     * @var Integer
302     * @access private
303     */
304    var $privateKeyFormat = CRYPT_RSA_PRIVATE_FORMAT_PKCS1;
305
306    /**
307     * Public Key Format
308     *
309     * @var Integer
310     * @access public
311     */
312    var $publicKeyFormat = CRYPT_RSA_PUBLIC_FORMAT_PKCS8;
313
314    /**
315     * Modulus (ie. n)
316     *
317     * @var Math_BigInteger
318     * @access private
319     */
320    var $modulus;
321
322    /**
323     * Modulus length
324     *
325     * @var Math_BigInteger
326     * @access private
327     */
328    var $k;
329
330    /**
331     * Exponent (ie. e or d)
332     *
333     * @var Math_BigInteger
334     * @access private
335     */
336    var $exponent;
337
338    /**
339     * Primes for Chinese Remainder Theorem (ie. p and q)
340     *
341     * @var Array
342     * @access private
343     */
344    var $primes;
345
346    /**
347     * Exponents for Chinese Remainder Theorem (ie. dP and dQ)
348     *
349     * @var Array
350     * @access private
351     */
352    var $exponents;
353
354    /**
355     * Coefficients for Chinese Remainder Theorem (ie. qInv)
356     *
357     * @var Array
358     * @access private
359     */
360    var $coefficients;
361
362    /**
363     * Hash name
364     *
365     * @var String
366     * @access private
367     */
368    var $hashName;
369
370    /**
371     * Hash function
372     *
373     * @var Crypt_Hash
374     * @access private
375     */
376    var $hash;
377
378    /**
379     * Length of hash function output
380     *
381     * @var Integer
382     * @access private
383     */
384    var $hLen;
385
386    /**
387     * Length of salt
388     *
389     * @var Integer
390     * @access private
391     */
392    var $sLen;
393
394    /**
395     * Hash function for the Mask Generation Function
396     *
397     * @var Crypt_Hash
398     * @access private
399     */
400    var $mgfHash;
401
402    /**
403     * Length of MGF hash function output
404     *
405     * @var Integer
406     * @access private
407     */
408    var $mgfHLen;
409
410    /**
411     * Encryption mode
412     *
413     * @var Integer
414     * @access private
415     */
416    var $encryptionMode = CRYPT_RSA_ENCRYPTION_OAEP;
417
418    /**
419     * Signature mode
420     *
421     * @var Integer
422     * @access private
423     */
424    var $signatureMode = CRYPT_RSA_SIGNATURE_PSS;
425
426    /**
427     * Public Exponent
428     *
429     * @var Mixed
430     * @access private
431     */
432    var $publicExponent = false;
433
434    /**
435     * Password
436     *
437     * @var String
438     * @access private
439     */
440    var $password = false;
441
442    /**
443     * Components
444     *
445     * For use with parsing XML formatted keys.  PHP's XML Parser functions use utilized - instead of PHP's DOM functions -
446     * because PHP's XML Parser functions work on PHP4 whereas PHP's DOM functions - although surperior - don't.
447     *
448     * @see Crypt_RSA::_start_element_handler()
449     * @var Array
450     * @access private
451     */
452    var $components = array();
453
454    /**
455     * Current String
456     *
457     * For use with parsing XML formatted keys.
458     *
459     * @see Crypt_RSA::_character_handler()
460     * @see Crypt_RSA::_stop_element_handler()
461     * @var Mixed
462     * @access private
463     */
464    var $current;
465
466    /**
467     * OpenSSL configuration file name.
468     *
469     * Set to null to use system configuration file.
470     * @see Crypt_RSA::createKey()
471     * @var Mixed
472     * @Access public
473     */
474    var $configFile;
475
476    /**
477     * Public key comment field.
478     *
479     * @var String
480     * @access private
481     */
482    var $comment = 'phpseclib-generated-key';
483
484    /**
485     * The constructor
486     *
487     * If you want to make use of the openssl extension, you'll need to set the mode manually, yourself.  The reason
488     * Crypt_RSA doesn't do it is because OpenSSL doesn't fail gracefully.  openssl_pkey_new(), in particular, requires
489     * openssl.cnf be present somewhere and, unfortunately, the only real way to find out is too late.
490     *
491     * @return Crypt_RSA
492     * @access public
493     */
494    function Crypt_RSA()
495    {
496        if (!class_exists('Math_BigInteger')) {
497            include_once 'Math/BigInteger.php';
498        }
499
500        $this->configFile = CRYPT_RSA_OPENSSL_CONFIG;
501
502        if (!defined('CRYPT_RSA_MODE')) {
503            switch (true) {
504                // Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular,
505                // Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger
506                // can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either.
507                case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'):
508                    define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
509                    break;
510                // openssl_pkey_get_details - which is used in the only place Crypt/RSA.php uses OpenSSL - was introduced in PHP 5.2.0
511                case !function_exists('openssl_pkey_get_details'):
512                    define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
513                    break;
514                case extension_loaded('openssl') && version_compare(PHP_VERSION, '4.2.0', '>=') && file_exists($this->configFile):
515                    // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
516                    ob_start();
517                    @phpinfo();
518                    $content = ob_get_contents();
519                    ob_end_clean();
520
521                    preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches);
522
523                    $versions = array();
524                    if (!empty($matches[1])) {
525                        for ($i = 0; $i < count($matches[1]); $i++) {
526                            $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i])));
527
528                            // Remove letter part in OpenSSL version
529                            if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) {
530                                $versions[$matches[1][$i]] = $fullVersion;
531                            } else {
532                                $versions[$matches[1][$i]] = $m[0];
533                            }
534                        }
535                    }
536
537                    // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+
538                    switch (true) {
539                        case !isset($versions['Header']):
540                        case !isset($versions['Library']):
541                        case $versions['Header'] == $versions['Library']:
542                            define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_OPENSSL);
543                            break;
544                        default:
545                            define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
546                            define('MATH_BIGINTEGER_OPENSSL_DISABLE', true);
547                    }
548                    break;
549                default:
550                    define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
551            }
552        }
553
554        $this->zero = new Math_BigInteger();
555        $this->one = new Math_BigInteger(1);
556
557        $this->hash = new Crypt_Hash('sha1');
558        $this->hLen = $this->hash->getLength();
559        $this->hashName = 'sha1';
560        $this->mgfHash = new Crypt_Hash('sha1');
561        $this->mgfHLen = $this->mgfHash->getLength();
562    }
563
564    /**
565     * Create public / private key pair
566     *
567     * Returns an array with the following three elements:
568     *  - 'privatekey': The private key.
569     *  - 'publickey':  The public key.
570     *  - 'partialkey': A partially computed key (if the execution time exceeded $timeout).
571     *                  Will need to be passed back to Crypt_RSA::createKey() as the third parameter for further processing.
572     *
573     * @access public
574     * @param optional Integer $bits
575     * @param optional Integer $timeout
576     * @param optional Math_BigInteger $p
577     */
578    function createKey($bits = 1024, $timeout = false, $partial = array())
579    {
580        if (!defined('CRYPT_RSA_EXPONENT')) {
581            // http://en.wikipedia.org/wiki/65537_%28number%29
582            define('CRYPT_RSA_EXPONENT', '65537');
583        }
584        // per <http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number ought not result in primes smaller
585        // than 256 bits. as a consequence if the key you're trying to create is 1024 bits and you've set CRYPT_RSA_SMALLEST_PRIME
586        // to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). at least if
587        // CRYPT_RSA_MODE is set to CRYPT_RSA_MODE_INTERNAL. if CRYPT_RSA_MODE is set to CRYPT_RSA_MODE_OPENSSL then
588        // CRYPT_RSA_SMALLEST_PRIME is ignored (ie. multi-prime RSA support is more intended as a way to speed up RSA key
589        // generation when there's a chance neither gmp nor OpenSSL are installed)
590        if (!defined('CRYPT_RSA_SMALLEST_PRIME')) {
591            define('CRYPT_RSA_SMALLEST_PRIME', 4096);
592        }
593
594        // OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum
595        if (CRYPT_RSA_MODE == CRYPT_RSA_MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) {
596            $config = array();
597            if (isset($this->configFile)) {
598                $config['config'] = $this->configFile;
599            }
600            $rsa = openssl_pkey_new(array('private_key_bits' => $bits) + $config);
601            openssl_pkey_export($rsa, $privatekey, null, $config);
602            $publickey = openssl_pkey_get_details($rsa);
603            $publickey = $publickey['key'];
604
605            $privatekey = call_user_func_array(array($this, '_convertPrivateKey'), array_values($this->_parseKey($privatekey, CRYPT_RSA_PRIVATE_FORMAT_PKCS1)));
606            $publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, CRYPT_RSA_PUBLIC_FORMAT_PKCS1)));
607
608            // clear the buffer of error strings stemming from a minimalistic openssl.cnf
609            while (openssl_error_string() !== false) {
610            }
611
612            return array(
613                'privatekey' => $privatekey,
614                'publickey' => $publickey,
615                'partialkey' => false
616            );
617        }
618
619        static $e;
620        if (!isset($e)) {
621            $e = new Math_BigInteger(CRYPT_RSA_EXPONENT);
622        }
623
624        extract($this->_generateMinMax($bits));
625        $absoluteMin = $min;
626        $temp = $bits >> 1; // divide by two to see how many bits P and Q would be
627        if ($temp > CRYPT_RSA_SMALLEST_PRIME) {
628            $num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME);
629            $temp = CRYPT_RSA_SMALLEST_PRIME;
630        } else {
631            $num_primes = 2;
632        }
633        extract($this->_generateMinMax($temp + $bits % $temp));
634        $finalMax = $max;
635        extract($this->_generateMinMax($temp));
636
637        $generator = new Math_BigInteger();
638
639        $n = $this->one->copy();
640        if (!empty($partial)) {
641            extract(unserialize($partial));
642        } else {
643            $exponents = $coefficients = $primes = array();
644            $lcm = array(
645                'top' => $this->one->copy(),
646                'bottom' => false
647            );
648        }
649
650        $start = time();
651        $i0 = count($primes) + 1;
652
653        do {
654            for ($i = $i0; $i <= $num_primes; $i++) {
655                if ($timeout !== false) {
656                    $timeout-= time() - $start;
657                    $start = time();
658                    if ($timeout <= 0) {
659                        return array(
660                            'privatekey' => '',
661                            'publickey'  => '',
662                            'partialkey' => serialize(array(
663                                'primes' => $primes,
664                                'coefficients' => $coefficients,
665                                'lcm' => $lcm,
666                                'exponents' => $exponents
667                            ))
668                        );
669                    }
670                }
671
672                if ($i == $num_primes) {
673                    list($min, $temp) = $absoluteMin->divide($n);
674                    if (!$temp->equals($this->zero)) {
675                        $min = $min->add($this->one); // ie. ceil()
676                    }
677                    $primes[$i] = $generator->randomPrime($min, $finalMax, $timeout);
678                } else {
679                    $primes[$i] = $generator->randomPrime($min, $max, $timeout);
680                }
681
682                if ($primes[$i] === false) { // if we've reached the timeout
683                    if (count($primes) > 1) {
684                        $partialkey = '';
685                    } else {
686                        array_pop($primes);
687                        $partialkey = serialize(array(
688                            'primes' => $primes,
689                            'coefficients' => $coefficients,
690                            'lcm' => $lcm,
691                            'exponents' => $exponents
692                        ));
693                    }
694
695                    return array(
696                        'privatekey' => '',
697                        'publickey'  => '',
698                        'partialkey' => $partialkey
699                    );
700                }
701
702                // the first coefficient is calculated differently from the rest
703                // ie. instead of being $primes[1]->modInverse($primes[2]), it's $primes[2]->modInverse($primes[1])
704                if ($i > 2) {
705                    $coefficients[$i] = $n->modInverse($primes[$i]);
706                }
707
708                $n = $n->multiply($primes[$i]);
709
710                $temp = $primes[$i]->subtract($this->one);
711
712                // textbook RSA implementations use Euler's totient function instead of the least common multiple.
713                // see http://en.wikipedia.org/wiki/Euler%27s_totient_function
714                $lcm['top'] = $lcm['top']->multiply($temp);
715                $lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp);
716
717                $exponents[$i] = $e->modInverse($temp);
718            }
719
720            list($temp) = $lcm['top']->divide($lcm['bottom']);
721            $gcd = $temp->gcd($e);
722            $i0 = 1;
723        } while (!$gcd->equals($this->one));
724
725        $d = $e->modInverse($temp);
726
727        $coefficients[2] = $primes[2]->modInverse($primes[1]);
728
729        // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.2>:
730        // RSAPrivateKey ::= SEQUENCE {
731        //     version           Version,
732        //     modulus           INTEGER,  -- n
733        //     publicExponent    INTEGER,  -- e
734        //     privateExponent   INTEGER,  -- d
735        //     prime1            INTEGER,  -- p
736        //     prime2            INTEGER,  -- q
737        //     exponent1         INTEGER,  -- d mod (p-1)
738        //     exponent2         INTEGER,  -- d mod (q-1)
739        //     coefficient       INTEGER,  -- (inverse of q) mod p
740        //     otherPrimeInfos   OtherPrimeInfos OPTIONAL
741        // }
742
743        return array(
744            'privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients),
745            'publickey'  => $this->_convertPublicKey($n, $e),
746            'partialkey' => false
747        );
748    }
749
750    /**
751     * Convert a private key to the appropriate format.
752     *
753     * @access private
754     * @see setPrivateKeyFormat()
755     * @param String $RSAPrivateKey
756     * @return String
757     */
758    function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients)
759    {
760        $signed = $this->privateKeyFormat != CRYPT_RSA_PRIVATE_FORMAT_XML;
761        $num_primes = count($primes);
762        $raw = array(
763            'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi
764            'modulus' => $n->toBytes($signed),
765            'publicExponent' => $e->toBytes($signed),
766            'privateExponent' => $d->toBytes($signed),
767            'prime1' => $primes[1]->toBytes($signed),
768            'prime2' => $primes[2]->toBytes($signed),
769            'exponent1' => $exponents[1]->toBytes($signed),
770            'exponent2' => $exponents[2]->toBytes($signed),
771            'coefficient' => $coefficients[2]->toBytes($signed)
772        );
773
774        // if the format in question does not support multi-prime rsa and multi-prime rsa was used,
775        // call _convertPublicKey() instead.
776        switch ($this->privateKeyFormat) {
777            case CRYPT_RSA_PRIVATE_FORMAT_XML:
778                if ($num_primes != 2) {
779                    return false;
780                }
781                return "<RSAKeyValue>\r\n" .
782                       '  <Modulus>' . base64_encode($raw['modulus']) . "</Modulus>\r\n" .
783                       '  <Exponent>' . base64_encode($raw['publicExponent']) . "</Exponent>\r\n" .
784                       '  <P>' . base64_encode($raw['prime1']) . "</P>\r\n" .
785                       '  <Q>' . base64_encode($raw['prime2']) . "</Q>\r\n" .
786                       '  <DP>' . base64_encode($raw['exponent1']) . "</DP>\r\n" .
787                       '  <DQ>' . base64_encode($raw['exponent2']) . "</DQ>\r\n" .
788                       '  <InverseQ>' . base64_encode($raw['coefficient']) . "</InverseQ>\r\n" .
789                       '  <D>' . base64_encode($raw['privateExponent']) . "</D>\r\n" .
790                       '</RSAKeyValue>';
791                break;
792            case CRYPT_RSA_PRIVATE_FORMAT_PUTTY:
793                if ($num_primes != 2) {
794                    return false;
795                }
796                $key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: ";
797                $encryption = (!empty($this->password) || is_string($this->password)) ? 'aes256-cbc' : 'none';
798                $key.= $encryption;
799                $key.= "\r\nComment: " . $this->comment . "\r\n";
800                $public = pack(
801                    'Na*Na*Na*',
802                    strlen('ssh-rsa'),
803                    'ssh-rsa',
804                    strlen($raw['publicExponent']),
805                    $raw['publicExponent'],
806                    strlen($raw['modulus']),
807                    $raw['modulus']
808                );
809                $source = pack(
810                    'Na*Na*Na*Na*',
811                    strlen('ssh-rsa'),
812                    'ssh-rsa',
813                    strlen($encryption),
814                    $encryption,
815                    strlen($this->comment),
816                    $this->comment,
817                    strlen($public),
818                    $public
819                );
820                $public = base64_encode($public);
821                $key.= "Public-Lines: " . ((strlen($public) + 63) >> 6) . "\r\n";
822                $key.= chunk_split($public, 64);
823                $private = pack(
824                    'Na*Na*Na*Na*',
825                    strlen($raw['privateExponent']),
826                    $raw['privateExponent'],
827                    strlen($raw['prime1']),
828                    $raw['prime1'],
829                    strlen($raw['prime2']),
830                    $raw['prime2'],
831                    strlen($raw['coefficient']),
832                    $raw['coefficient']
833                );
834                if (empty($this->password) && !is_string($this->password)) {
835                    $source.= pack('Na*', strlen($private), $private);
836                    $hashkey = 'putty-private-key-file-mac-key';
837                } else {
838                    $private.= crypt_random_string(16 - (strlen($private) & 15));
839                    $source.= pack('Na*', strlen($private), $private);
840                    if (!class_exists('Crypt_AES')) {
841                        include_once 'Crypt/AES.php';
842                    }
843                    $sequence = 0;
844                    $symkey = '';
845                    while (strlen($symkey) < 32) {
846                        $temp = pack('Na*', $sequence++, $this->password);
847                        $symkey.= pack('H*', sha1($temp));
848                    }
849                    $symkey = substr($symkey, 0, 32);
850                    $crypto = new Crypt_AES();
851
852                    $crypto->setKey($symkey);
853                    $crypto->disablePadding();
854                    $private = $crypto->encrypt($private);
855                    $hashkey = 'putty-private-key-file-mac-key' . $this->password;
856                }
857
858                $private = base64_encode($private);
859                $key.= 'Private-Lines: ' . ((strlen($private) + 63) >> 6) . "\r\n";
860                $key.= chunk_split($private, 64);
861                if (!class_exists('Crypt_Hash')) {
862                    include_once 'Crypt/Hash.php';
863                }
864                $hash = new Crypt_Hash('sha1');
865                $hash->setKey(pack('H*', sha1($hashkey)));
866                $key.= 'Private-MAC: ' . bin2hex($hash->hash($source)) . "\r\n";
867
868                return $key;
869            default: // eg. CRYPT_RSA_PRIVATE_FORMAT_PKCS1
870                $components = array();
871                foreach ($raw as $name => $value) {
872                    $components[$name] = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value);
873                }
874
875                $RSAPrivateKey = implode('', $components);
876
877                if ($num_primes > 2) {
878                    $OtherPrimeInfos = '';
879                    for ($i = 3; $i <= $num_primes; $i++) {
880                        // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
881                        //
882                        // OtherPrimeInfo ::= SEQUENCE {
883                        //     prime             INTEGER,  -- ri
884                        //     exponent          INTEGER,  -- di
885                        //     coefficient       INTEGER   -- ti
886                        // }
887                        $OtherPrimeInfo = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true));
888                        $OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true));
889                        $OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true));
890                        $OtherPrimeInfos.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo);
891                    }
892                    $RSAPrivateKey.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos);
893                }
894
895                $RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
896
897                if ($this->privateKeyFormat == CRYPT_RSA_PRIVATE_FORMAT_PKCS8) {
898                    $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
899                    $RSAPrivateKey = pack(
900                        'Ca*a*Ca*a*',
901                        CRYPT_RSA_ASN1_INTEGER,
902                        "\01\00",
903                        $rsaOID,
904                        4,
905                        $this->_encodeLength(strlen($RSAPrivateKey)),
906                        $RSAPrivateKey
907                    );
908                    $RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
909                    if (!empty($this->password) || is_string($this->password)) {
910                        $salt = crypt_random_string(8);
911                        $iterationCount = 2048;
912
913                        if (!class_exists('Crypt_DES')) {
914                            include_once 'Crypt/DES.php';
915                        }
916                        $crypto = new Crypt_DES();
917                        $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount);
918                        $RSAPrivateKey = $crypto->encrypt($RSAPrivateKey);
919
920                        $parameters = pack(
921                            'Ca*a*Ca*N',
922                            CRYPT_RSA_ASN1_OCTETSTRING,
923                            $this->_encodeLength(strlen($salt)),
924                            $salt,
925                            CRYPT_RSA_ASN1_INTEGER,
926                            $this->_encodeLength(4),
927                            $iterationCount
928                        );
929                        $pbeWithMD5AndDES_CBC = "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03";
930
931                        $encryptionAlgorithm = pack(
932                            'Ca*a*Ca*a*',
933                            CRYPT_RSA_ASN1_OBJECT,
934                            $this->_encodeLength(strlen($pbeWithMD5AndDES_CBC)),
935                            $pbeWithMD5AndDES_CBC,
936                            CRYPT_RSA_ASN1_SEQUENCE,
937                            $this->_encodeLength(strlen($parameters)),
938                            $parameters
939                        );
940
941                        $RSAPrivateKey = pack(
942                            'Ca*a*Ca*a*',
943                            CRYPT_RSA_ASN1_SEQUENCE,
944                            $this->_encodeLength(strlen($encryptionAlgorithm)),
945                            $encryptionAlgorithm,
946                            CRYPT_RSA_ASN1_OCTETSTRING,
947                            $this->_encodeLength(strlen($RSAPrivateKey)),
948                            $RSAPrivateKey
949                        );
950
951                        $RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
952
953                        $RSAPrivateKey = "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" .
954                                         chunk_split(base64_encode($RSAPrivateKey), 64) .
955                                         '-----END ENCRYPTED PRIVATE KEY-----';
956                    } else {
957                        $RSAPrivateKey = "-----BEGIN PRIVATE KEY-----\r\n" .
958                                         chunk_split(base64_encode($RSAPrivateKey), 64) .
959                                         '-----END PRIVATE KEY-----';
960                    }
961                    return $RSAPrivateKey;
962                }
963
964                if (!empty($this->password) || is_string($this->password)) {
965                    $iv = crypt_random_string(8);
966                    $symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key
967                    $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8);
968                    if (!class_exists('Crypt_TripleDES')) {
969                        include_once 'Crypt/TripleDES.php';
970                    }
971                    $des = new Crypt_TripleDES();
972                    $des->setKey($symkey);
973                    $des->setIV($iv);
974                    $iv = strtoupper(bin2hex($iv));
975                    $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
976                                     "Proc-Type: 4,ENCRYPTED\r\n" .
977                                     "DEK-Info: DES-EDE3-CBC,$iv\r\n" .
978                                     "\r\n" .
979                                     chunk_split(base64_encode($des->encrypt($RSAPrivateKey)), 64) .
980                                     '-----END RSA PRIVATE KEY-----';
981                } else {
982                    $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
983                                     chunk_split(base64_encode($RSAPrivateKey), 64) .
984                                     '-----END RSA PRIVATE KEY-----';
985                }
986
987                return $RSAPrivateKey;
988        }
989    }
990
991    /**
992     * Convert a public key to the appropriate format
993     *
994     * @access private
995     * @see setPublicKeyFormat()
996     * @param String $RSAPrivateKey
997     * @return String
998     */
999    function _convertPublicKey($n, $e)
1000    {
1001        $signed = $this->publicKeyFormat != CRYPT_RSA_PUBLIC_FORMAT_XML;
1002
1003        $modulus = $n->toBytes($signed);
1004        $publicExponent = $e->toBytes($signed);
1005
1006        switch ($this->publicKeyFormat) {
1007            case CRYPT_RSA_PUBLIC_FORMAT_RAW:
1008                return array('e' => $e->copy(), 'n' => $n->copy());
1009            case CRYPT_RSA_PUBLIC_FORMAT_XML:
1010                return "<RSAKeyValue>\r\n" .
1011                       '  <Modulus>' . base64_encode($modulus) . "</Modulus>\r\n" .
1012                       '  <Exponent>' . base64_encode($publicExponent) . "</Exponent>\r\n" .
1013                       '</RSAKeyValue>';
1014                break;
1015            case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
1016                // from <http://tools.ietf.org/html/rfc4253#page-15>:
1017                // string    "ssh-rsa"
1018                // mpint     e
1019                // mpint     n
1020                $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);
1021                $RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment;
1022
1023                return $RSAPublicKey;
1024            default: // eg. CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW or CRYPT_RSA_PUBLIC_FORMAT_PKCS1
1025                // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>:
1026                // RSAPublicKey ::= SEQUENCE {
1027                //     modulus           INTEGER,  -- n
1028                //     publicExponent    INTEGER   -- e
1029                // }
1030                $components = array(
1031                    'modulus' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus),
1032                    'publicExponent' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent)
1033                );
1034
1035                $RSAPublicKey = pack(
1036                    'Ca*a*a*',
1037                    CRYPT_RSA_ASN1_SEQUENCE,
1038                    $this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])),
1039                    $components['modulus'],
1040                    $components['publicExponent']
1041                );
1042
1043                if ($this->publicKeyFormat == CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW) {
1044                    $RSAPublicKey = "-----BEGIN RSA PUBLIC KEY-----\r\n" .
1045                                    chunk_split(base64_encode($RSAPublicKey), 64) .
1046                                    '-----END RSA PUBLIC KEY-----';
1047                } else {
1048                    // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
1049                    $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
1050                    $RSAPublicKey = chr(0) . $RSAPublicKey;
1051                    $RSAPublicKey = chr(3) . $this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;
1052
1053                    $RSAPublicKey = pack(
1054                        'Ca*a*',
1055                        CRYPT_RSA_ASN1_SEQUENCE,
1056                        $this->_encodeLength(strlen($rsaOID . $RSAPublicKey)),
1057                        $rsaOID . $RSAPublicKey
1058                    );
1059
1060                    $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
1061                                     chunk_split(base64_encode($RSAPublicKey), 64) .
1062                                     '-----END PUBLIC KEY-----';
1063                }
1064
1065                return $RSAPublicKey;
1066        }
1067    }
1068
1069    /**
1070     * Break a public or private key down into its constituant components
1071     *
1072     * @access private
1073     * @see _convertPublicKey()
1074     * @see _convertPrivateKey()
1075     * @param String $key
1076     * @param Integer $type
1077     * @return Array
1078     */
1079    function _parseKey($key, $type)
1080    {
1081        if ($type != CRYPT_RSA_PUBLIC_FORMAT_RAW && !is_string($key)) {
1082            return false;
1083        }
1084
1085        switch ($type) {
1086            case CRYPT_RSA_PUBLIC_FORMAT_RAW:
1087                if (!is_array($key)) {
1088                    return false;
1089                }
1090                $components = array();
1091                switch (true) {
1092                    case isset($key['e']):
1093                        $components['publicExponent'] = $key['e']->copy();
1094                        break;
1095                    case isset($key['exponent']):
1096                        $components['publicExponent'] = $key['exponent']->copy();
1097                        break;
1098                    case isset($key['publicExponent']):
1099                        $components['publicExponent'] = $key['publicExponent']->copy();
1100                        break;
1101                    case isset($key[0]):
1102                        $components['publicExponent'] = $key[0]->copy();
1103                }
1104                switch (true) {
1105                    case isset($key['n']):
1106                        $components['modulus'] = $key['n']->copy();
1107                        break;
1108                    case isset($key['modulo']):
1109                        $components['modulus'] = $key['modulo']->copy();
1110                        break;
1111                    case isset($key['modulus']):
1112                        $components['modulus'] = $key['modulus']->copy();
1113                        break;
1114                    case isset($key[1]):
1115                        $components['modulus'] = $key[1]->copy();
1116                }
1117                return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false;
1118            case CRYPT_RSA_PRIVATE_FORMAT_PKCS1:
1119            case CRYPT_RSA_PRIVATE_FORMAT_PKCS8:
1120            case CRYPT_RSA_PUBLIC_FORMAT_PKCS1:
1121                /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
1122                   "outside the scope" of PKCS#1.  PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
1123                   protect private keys, however, that's not what OpenSSL* does.  OpenSSL protects private keys by adding
1124                   two new "fields" to the key - DEK-Info and Proc-Type.  These fields are discussed here:
1125
1126                   http://tools.ietf.org/html/rfc1421#section-4.6.1.1
1127                   http://tools.ietf.org/html/rfc1421#section-4.6.1.3
1128
1129                   DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
1130                   DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
1131                   function.  As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
1132                   own implementation.  ie. the implementation *is* the standard and any bugs that may exist in that
1133                   implementation are part of the standard, as well.
1134
1135                   * OpenSSL is the de facto standard.  It's utilized by OpenSSH and other projects */
1136                if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
1137                    $iv = pack('H*', trim($matches[2]));
1138                    $symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); // symkey is short for symmetric key
1139                    $symkey.= pack('H*', md5($symkey . $this->password . substr($iv, 0, 8)));
1140                    // remove the Proc-Type / DEK-Info sections as they're no longer needed
1141                    $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key);
1142                    $ciphertext = $this->_extractBER($key);
1143                    if ($ciphertext === false) {
1144                        $ciphertext = $key;
1145                    }
1146                    switch ($matches[1]) {
1147                        case 'AES-256-CBC':
1148                            if (!class_exists('Crypt_AES')) {
1149                                include_once 'Crypt/AES.php';
1150                            }
1151                            $crypto = new Crypt_AES();
1152                            break;
1153                        case 'AES-128-CBC':
1154                            if (!class_exists('Crypt_AES')) {
1155                                include_once 'Crypt/AES.php';
1156                            }
1157                            $symkey = substr($symkey, 0, 16);
1158                            $crypto = new Crypt_AES();
1159                            break;
1160                        case 'DES-EDE3-CFB':
1161                            if (!class_exists('Crypt_TripleDES')) {
1162                                include_once 'Crypt/TripleDES.php';
1163                            }
1164                            $crypto = new Crypt_TripleDES(CRYPT_DES_MODE_CFB);
1165                            break;
1166                        case 'DES-EDE3-CBC':
1167                            if (!class_exists('Crypt_TripleDES')) {
1168                                include_once 'Crypt/TripleDES.php';
1169                            }
1170                            $symkey = substr($symkey, 0, 24);
1171                            $crypto = new Crypt_TripleDES();
1172                            break;
1173                        case 'DES-CBC':
1174                            if (!class_exists('Crypt_DES')) {
1175                                include_once 'Crypt/DES.php';
1176                            }
1177                            $crypto = new Crypt_DES();
1178                            break;
1179                        default:
1180                            return false;
1181                    }
1182                    $crypto->setKey($symkey);
1183                    $crypto->setIV($iv);
1184                    $decoded = $crypto->decrypt($ciphertext);
1185                } else {
1186                    $decoded = $this->_extractBER($key);
1187                }
1188
1189                if ($decoded !== false) {
1190                    $key = $decoded;
1191                }
1192
1193                $components = array();
1194
1195                if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
1196                    return false;
1197                }
1198                if ($this->_decodeLength($key) != strlen($key)) {
1199                    return false;
1200                }
1201
1202                $tag = ord($this->_string_shift($key));
1203                /* intended for keys for which OpenSSL's asn1parse returns the following:
1204
1205                    0:d=0  hl=4 l= 631 cons: SEQUENCE
1206                    4:d=1  hl=2 l=   1 prim:  INTEGER           :00
1207                    7:d=1  hl=2 l=  13 cons:  SEQUENCE
1208                    9:d=2  hl=2 l=   9 prim:   OBJECT            :rsaEncryption
1209                   20:d=2  hl=2 l=   0 prim:   NULL
1210                   22:d=1  hl=4 l= 609 prim:  OCTET STRING
1211
1212                   ie. PKCS8 keys*/
1213
1214                if ($tag == CRYPT_RSA_ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") {
1215                    $this->_string_shift($key, 3);
1216                    $tag = CRYPT_RSA_ASN1_SEQUENCE;
1217                }
1218
1219                if ($tag == CRYPT_RSA_ASN1_SEQUENCE) {
1220                    $temp = $this->_string_shift($key, $this->_decodeLength($key));
1221                    if (ord($this->_string_shift($temp)) != CRYPT_RSA_ASN1_OBJECT) {
1222                        return false;
1223                    }
1224                    $length = $this->_decodeLength($temp);
1225                    switch ($this->_string_shift($temp, $length)) {
1226                        case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption
1227                            break;
1228                        case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC
1229                            /*
1230                               PBEParameter ::= SEQUENCE {
1231                                   salt OCTET STRING (SIZE(8)),
1232                                   iterationCount INTEGER }
1233                            */
1234                            if (ord($this->_string_shift($temp)) != CRYPT_RSA_ASN1_SEQUENCE) {
1235                                return false;
1236                            }
1237                            if ($this->_decodeLength($temp) != strlen($temp)) {
1238                                return false;
1239                            }
1240                            $this->_string_shift($temp); // assume it's an octet string
1241                            $salt = $this->_string_shift($temp, $this->_decodeLength($temp));
1242                            if (ord($this->_string_shift($temp)) != CRYPT_RSA_ASN1_INTEGER) {
1243                                return false;
1244                            }
1245                            $this->_decodeLength($temp);
1246                            list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT));
1247                            $this->_string_shift($key); // assume it's an octet string
1248                            $length = $this->_decodeLength($key);
1249                            if (strlen($key) != $length) {
1250                                return false;
1251                            }
1252
1253                            if (!class_exists('Crypt_DES')) {
1254                                include_once 'Crypt/DES.php';
1255                            }
1256                            $crypto = new Crypt_DES();
1257                            $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount);
1258                            $key = $crypto->decrypt($key);
1259                            if ($key === false) {
1260                                return false;
1261                            }
1262                            return $this->_parseKey($key, CRYPT_RSA_PRIVATE_FORMAT_PKCS1);
1263                        default:
1264                            return false;
1265                    }
1266                    /* intended for keys for which OpenSSL's asn1parse returns the following:
1267
1268                        0:d=0  hl=4 l= 290 cons: SEQUENCE
1269                        4:d=1  hl=2 l=  13 cons:  SEQUENCE
1270                        6:d=2  hl=2 l=   9 prim:   OBJECT            :rsaEncryption
1271                       17:d=2  hl=2 l=   0 prim:   NULL
1272                       19:d=1  hl=4 l= 271 prim:  BIT STRING */
1273                    $tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag
1274                    $this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length
1275                    // "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of
1276                    //  unused bits in the final subsequent octet. The number shall be in the range zero to seven."
1277                    //  -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2)
1278                    if ($tag == CRYPT_RSA_ASN1_BITSTRING) {
1279                        $this->_string_shift($key);
1280                    }
1281                    if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
1282                        return false;
1283                    }
1284                    if ($this->_decodeLength($key) != strlen($key)) {
1285                        return false;
1286                    }
1287                    $tag = ord($this->_string_shift($key));
1288                }
1289                if ($tag != CRYPT_RSA_ASN1_INTEGER) {
1290                    return false;
1291                }
1292
1293                $length = $this->_decodeLength($key);
1294                $temp = $this->_string_shift($key, $length);
1295                if (strlen($temp) != 1 || ord($temp) > 2) {
1296                    $components['modulus'] = new Math_BigInteger($temp, 256);
1297                    $this->_string_shift($key); // skip over CRYPT_RSA_ASN1_INTEGER
1298                    $length = $this->_decodeLength($key);
1299                    $components[$type == CRYPT_RSA_PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1300
1301                    return $components;
1302                }
1303                if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_INTEGER) {
1304                    return false;
1305                }
1306                $length = $this->_decodeLength($key);
1307                $components['modulus'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1308                $this->_string_shift($key);
1309                $length = $this->_decodeLength($key);
1310                $components['publicExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1311                $this->_string_shift($key);
1312                $length = $this->_decodeLength($key);
1313                $components['privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1314                $this->_string_shift($key);
1315                $length = $this->_decodeLength($key);
1316                $components['primes'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), 256));
1317                $this->_string_shift($key);
1318                $length = $this->_decodeLength($key);
1319                $components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1320                $this->_string_shift($key);
1321                $length = $this->_decodeLength($key);
1322                $components['exponents'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), 256));
1323                $this->_string_shift($key);
1324                $length = $this->_decodeLength($key);
1325                $components['exponents'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1326                $this->_string_shift($key);
1327                $length = $this->_decodeLength($key);
1328                $components['coefficients'] = array(2 => new Math_BigInteger($this->_string_shift($key, $length), 256));
1329
1330                if (!empty($key)) {
1331                    if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
1332                        return false;
1333                    }
1334                    $this->_decodeLength($key);
1335                    while (!empty($key)) {
1336                        if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
1337                            return false;
1338                        }
1339                        $this->_decodeLength($key);
1340                        $key = substr($key, 1);
1341                        $length = $this->_decodeLength($key);
1342                        $components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1343                        $this->_string_shift($key);
1344                        $length = $this->_decodeLength($key);
1345                        $components['exponents'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1346                        $this->_string_shift($key);
1347                        $length = $this->_decodeLength($key);
1348                        $components['coefficients'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
1349                    }
1350                }
1351
1352                return $components;
1353            case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
1354                $parts = explode(' ', $key, 3);
1355
1356                $key = isset($parts[1]) ? base64_decode($parts[1]) : false;
1357                if ($key === false) {
1358                    return false;
1359                }
1360
1361                $comment = isset($parts[2]) ? $parts[2] : false;
1362
1363                $cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa";
1364
1365                if (strlen($key) <= 4) {
1366                    return false;
1367                }
1368                extract(unpack('Nlength', $this->_string_shift($key, 4)));
1369                $publicExponent = new Math_BigInteger($this->_string_shift($key, $length), -256);
1370                if (strlen($key) <= 4) {
1371                    return false;
1372                }
1373                extract(unpack('Nlength', $this->_string_shift($key, 4)));
1374                $modulus = new Math_BigInteger($this->_string_shift($key, $length), -256);
1375
1376                if ($cleanup && strlen($key)) {
1377                    if (strlen($key) <= 4) {
1378                        return false;
1379                    }
1380                    extract(unpack('Nlength', $this->_string_shift($key, 4)));
1381                    $realModulus = new Math_BigInteger($this->_string_shift($key, $length), -256);
1382                    return strlen($key) ? false : array(
1383                        'modulus' => $realModulus,
1384                        'publicExponent' => $modulus,
1385                        'comment' => $comment
1386                    );
1387                } else {
1388                    return strlen($key) ? false : array(
1389                        'modulus' => $modulus,
1390                        'publicExponent' => $publicExponent,
1391                        'comment' => $comment
1392                    );
1393                }
1394            // http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue
1395            // http://en.wikipedia.org/wiki/XML_Signature
1396            case CRYPT_RSA_PRIVATE_FORMAT_XML:
1397            case CRYPT_RSA_PUBLIC_FORMAT_XML:
1398                $this->components = array();
1399
1400                $xml = xml_parser_create('UTF-8');
1401                xml_set_object($xml, $this);
1402                xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler');
1403                xml_set_character_data_handler($xml, '_data_handler');
1404                // add <xml></xml> to account for "dangling" tags like <BitStrength>...</BitStrength> that are sometimes added
1405                if (!xml_parse($xml, '<xml>' . $key . '</xml>')) {
1406                    return false;
1407                }
1408
1409                return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false;
1410            // from PuTTY's SSHPUBK.C
1411            case CRYPT_RSA_PRIVATE_FORMAT_PUTTY:
1412                $components = array();
1413                $key = preg_split('#\r\n|\r|\n#', $key);
1414                $type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0]));
1415                if ($type != 'ssh-rsa') {
1416                    return false;
1417                }
1418                $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1]));
1419                $comment = trim(preg_replace('#Comment: (.+)#', '$1', $key[2]));
1420
1421                $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3]));
1422                $public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength))));
1423                $public = substr($public, 11);
1424                extract(unpack('Nlength', $this->_string_shift($public, 4)));
1425                $components['publicExponent'] = new Math_BigInteger($this->_string_shift($public, $length), -256);
1426                extract(unpack('Nlength', $this->_string_shift($public, 4)));
1427                $components['modulus'] = new Math_BigInteger($this->_string_shift($public, $length), -256);
1428
1429                $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$publicLength + 4]));
1430                $private = base64_decode(implode('', array_map('trim', array_slice($key, $publicLength + 5, $privateLength))));
1431
1432                switch ($encryption) {
1433                    case 'aes256-cbc':
1434                        if (!class_exists('Crypt_AES')) {
1435                            include_once 'Crypt/AES.php';
1436                        }
1437                        $symkey = '';
1438                        $sequence = 0;
1439                        while (strlen($symkey) < 32) {
1440                            $temp = pack('Na*', $sequence++, $this->password);
1441                            $symkey.= pack('H*', sha1($temp));
1442                        }
1443                        $symkey = substr($symkey, 0, 32);
1444                        $crypto = new Crypt_AES();
1445                }
1446
1447                if ($encryption != 'none') {
1448                    $crypto->setKey($symkey);
1449                    $crypto->disablePadding();
1450                    $private = $crypto->decrypt($private);
1451                    if ($private === false) {
1452                        return false;
1453                    }
1454                }
1455
1456                extract(unpack('Nlength', $this->_string_shift($private, 4)));
1457                if (strlen($private) < $length) {
1458                    return false;
1459                }
1460                $components['privateExponent'] = new Math_BigInteger($this->_string_shift($private, $length), -256);
1461                extract(unpack('Nlength', $this->_string_shift($private, 4)));
1462                if (strlen($private) < $length) {
1463                    return false;
1464                }
1465                $components['primes'] = array(1 => new Math_BigInteger($this->_string_shift($private, $length), -256));
1466                extract(unpack('Nlength', $this->_string_shift($private, 4)));
1467                if (strlen($private) < $length) {
1468                    return false;
1469                }
1470                $components['primes'][] = new Math_BigInteger($this->_string_shift($private, $length), -256);
1471
1472                $temp = $components['primes'][1]->subtract($this->one);
1473                $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp));
1474                $temp = $components['primes'][2]->subtract($this->one);
1475                $components['exponents'][] = $components['publicExponent']->modInverse($temp);
1476
1477                extract(unpack('Nlength', $this->_string_shift($private, 4)));
1478                if (strlen($private) < $length) {
1479                    return false;
1480                }
1481                $components['coefficients'] = array(2 => new Math_BigInteger($this->_string_shift($private, $length), -256));
1482
1483                return $components;
1484        }
1485    }
1486
1487    /**
1488     * Returns the key size
1489     *
1490     * More specifically, this returns the size of the modulo in bits.
1491     *
1492     * @access public
1493     * @return Integer
1494     */
1495    function getSize()
1496    {
1497        return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits());
1498    }
1499
1500    /**
1501     * Start Element Handler
1502     *
1503     * Called by xml_set_element_handler()
1504     *
1505     * @access private
1506     * @param Resource $parser
1507     * @param String $name
1508     * @param Array $attribs
1509     */
1510    function _start_element_handler($parser, $name, $attribs)
1511    {
1512        //$name = strtoupper($name);
1513        switch ($name) {
1514            case 'MODULUS':
1515                $this->current = &$this->components['modulus'];
1516                break;
1517            case 'EXPONENT':
1518                $this->current = &$this->components['publicExponent'];
1519                break;
1520            case 'P':
1521                $this->current = &$this->components['primes'][1];
1522                break;
1523            case 'Q':
1524                $this->current = &$this->components['primes'][2];
1525                break;
1526            case 'DP':
1527                $this->current = &$this->components['exponents'][1];
1528                break;
1529            case 'DQ':
1530                $this->current = &$this->components['exponents'][2];
1531                break;
1532            case 'INVERSEQ':
1533                $this->current = &$this->components['coefficients'][2];
1534                break;
1535            case 'D':
1536                $this->current = &$this->components['privateExponent'];
1537        }
1538        $this->current = '';
1539    }
1540
1541    /**
1542     * Stop Element Handler
1543     *
1544     * Called by xml_set_element_handler()
1545     *
1546     * @access private
1547     * @param Resource $parser
1548     * @param String $name
1549     */
1550    function _stop_element_handler($parser, $name)
1551    {
1552        if (isset($this->current)) {
1553            $this->current = new Math_BigInteger(base64_decode($this->current), 256);
1554            unset($this->current);
1555        }
1556    }
1557
1558    /**
1559     * Data Handler
1560     *
1561     * Called by xml_set_character_data_handler()
1562     *
1563     * @access private
1564     * @param Resource $parser
1565     * @param String $data
1566     */
1567    function _data_handler($parser, $data)
1568    {
1569        if (!isset($this->current) || is_object($this->current)) {
1570            return;
1571        }
1572        $this->current.= trim($data);
1573    }
1574
1575    /**
1576     * Loads a public or private key
1577     *
1578     * Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed)
1579     *
1580     * @access public
1581     * @param String $key
1582     * @param Integer $type optional
1583     */
1584    function loadKey($key, $type = false)
1585    {
1586        if (is_object($key) && strtolower(get_class($key)) == 'crypt_rsa') {
1587            $this->privateKeyFormat = $key->privateKeyFormat;
1588            $this->publicKeyFormat = $key->publicKeyFormat;
1589            $this->k = $key->k;
1590            $this->hLen = $key->hLen;
1591            $this->sLen = $key->sLen;
1592            $this->mgfHLen = $key->mgfHLen;
1593            $this->encryptionMode = $key->encryptionMode;
1594            $this->signatureMode = $key->signatureMode;
1595            $this->password = $key->password;
1596            $this->configFile = $key->configFile;
1597            $this->comment = $key->comment;
1598
1599            if (is_object($key->hash)) {
1600                $this->hash = new Crypt_Hash($key->hash->getHash());
1601            }
1602            if (is_object($key->mgfHash)) {
1603                $this->mgfHash = new Crypt_Hash($key->mgfHash->getHash());
1604            }
1605
1606            if (is_object($key->modulus)) {
1607                $this->modulus = $key->modulus->copy();
1608            }
1609            if (is_object($key->exponent)) {
1610                $this->exponent = $key->exponent->copy();
1611            }
1612            if (is_object($key->publicExponent)) {
1613                $this->publicExponent = $key->publicExponent->copy();
1614            }
1615
1616            $this->primes = array();
1617            $this->exponents = array();
1618            $this->coefficients = array();
1619
1620            foreach ($this->primes as $prime) {
1621                $this->primes[] = $prime->copy();
1622            }
1623            foreach ($this->exponents as $exponent) {
1624                $this->exponents[] = $exponent->copy();
1625            }
1626            foreach ($this->coefficients as $coefficient) {
1627                $this->coefficients[] = $coefficient->copy();
1628            }
1629
1630            return true;
1631        }
1632
1633        if ($type === false) {
1634            $types = array(
1635                CRYPT_RSA_PUBLIC_FORMAT_RAW,
1636                CRYPT_RSA_PRIVATE_FORMAT_PKCS1,
1637                CRYPT_RSA_PRIVATE_FORMAT_XML,
1638                CRYPT_RSA_PRIVATE_FORMAT_PUTTY,
1639                CRYPT_RSA_PUBLIC_FORMAT_OPENSSH
1640            );
1641            foreach ($types as $type) {
1642                $components = $this->_parseKey($key, $type);
1643                if ($components !== false) {
1644                    break;
1645                }
1646            }
1647
1648        } else {
1649            $components = $this->_parseKey($key, $type);
1650        }
1651
1652        if ($components === false) {
1653            return false;
1654        }
1655
1656        if (isset($components['comment']) && $components['comment'] !== false) {
1657            $this->comment = $components['comment'];
1658        }
1659        $this->modulus = $components['modulus'];
1660        $this->k = strlen($this->modulus->toBytes());
1661        $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent'];
1662        if (isset($components['primes'])) {
1663            $this->primes = $components['primes'];
1664            $this->exponents = $components['exponents'];
1665            $this->coefficients = $components['coefficients'];
1666            $this->publicExponent = $components['publicExponent'];
1667        } else {
1668            $this->primes = array();
1669            $this->exponents = array();
1670            $this->coefficients = array();
1671            $this->publicExponent = false;
1672        }
1673
1674        switch ($type) {
1675            case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
1676            case CRYPT_RSA_PUBLIC_FORMAT_RAW:
1677                $this->setPublicKey();
1678                break;
1679            case CRYPT_RSA_PRIVATE_FORMAT_PKCS1:
1680                switch (true) {
1681                    case strpos($key, '-BEGIN PUBLIC KEY-') !== false:
1682                    case strpos($key, '-BEGIN RSA PUBLIC KEY-') !== false:
1683                        $this->setPublicKey();
1684                }
1685        }
1686
1687        return true;
1688    }
1689
1690    /**
1691     * Sets the password
1692     *
1693     * Private keys can be encrypted with a password.  To unset the password, pass in the empty string or false.
1694     * Or rather, pass in $password such that empty($password) && !is_string($password) is true.
1695     *
1696     * @see createKey()
1697     * @see loadKey()
1698     * @access public
1699     * @param String $password
1700     */
1701    function setPassword($password = false)
1702    {
1703        $this->password = $password;
1704    }
1705
1706    /**
1707     * Defines the public key
1708     *
1709     * Some private key formats define the public exponent and some don't.  Those that don't define it are problematic when
1710     * used in certain contexts.  For example, in SSH-2, RSA authentication works by sending the public key along with a
1711     * message signed by the private key to the server.  The SSH-2 server looks the public key up in an index of public keys
1712     * and if it's present then proceeds to verify the signature.  Problem is, if your private key doesn't include the public
1713     * exponent this won't work unless you manually add the public exponent. phpseclib tries to guess if the key being used
1714     * is the public key but in the event that it guesses incorrectly you might still want to explicitly set the key as being
1715     * public.
1716     *
1717     * Do note that when a new key is loaded the index will be cleared.
1718     *
1719     * Returns true on success, false on failure
1720     *
1721     * @see getPublicKey()
1722     * @access public
1723     * @param String $key optional
1724     * @param Integer $type optional
1725     * @return Boolean
1726     */
1727    function setPublicKey($key = false, $type = false)
1728    {
1729        // if a public key has already been loaded return false
1730        if (!empty($this->publicExponent)) {
1731            return false;
1732        }
1733
1734        if ($key === false && !empty($this->modulus)) {
1735            $this->publicExponent = $this->exponent;
1736            return true;
1737        }
1738
1739        if ($type === false) {
1740            $types = array(
1741                CRYPT_RSA_PUBLIC_FORMAT_RAW,
1742                CRYPT_RSA_PUBLIC_FORMAT_PKCS1,
1743                CRYPT_RSA_PUBLIC_FORMAT_XML,
1744                CRYPT_RSA_PUBLIC_FORMAT_OPENSSH
1745            );
1746            foreach ($types as $type) {
1747                $components = $this->_parseKey($key, $type);
1748                if ($components !== false) {
1749                    break;
1750                }
1751            }
1752        } else {
1753            $components = $this->_parseKey($key, $type);
1754        }
1755
1756        if ($components === false) {
1757            return false;
1758        }
1759
1760        if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) {
1761            $this->modulus = $components['modulus'];
1762            $this->exponent = $this->publicExponent = $components['publicExponent'];
1763            return true;
1764        }
1765
1766        $this->publicExponent = $components['publicExponent'];
1767
1768        return true;
1769    }
1770
1771    /**
1772     * Defines the private key
1773     *
1774     * If phpseclib guessed a private key was a public key and loaded it as such it might be desirable to force
1775     * phpseclib to treat the key as a private key. This function will do that.
1776     *
1777     * Do note that when a new key is loaded the index will be cleared.
1778     *
1779     * Returns true on success, false on failure
1780     *
1781     * @see getPublicKey()
1782     * @access public
1783     * @param String $key optional
1784     * @param Integer $type optional
1785     * @return Boolean
1786     */
1787    function setPrivateKey($key = false, $type = false)
1788    {
1789        if ($key === false && !empty($this->publicExponent)) {
1790            unset($this->publicExponent);
1791            return true;
1792        }
1793
1794        $rsa = new Crypt_RSA();
1795        if (!$rsa->loadKey($key, $type)) {
1796            return false;
1797        }
1798        unset($rsa->publicExponent);
1799
1800        // don't overwrite the old key if the new key is invalid
1801        $this->loadKey($rsa);
1802        return true;
1803    }
1804
1805    /**
1806     * Returns the public key
1807     *
1808     * The public key is only returned under two circumstances - if the private key had the public key embedded within it
1809     * or if the public key was set via setPublicKey().  If the currently loaded key is supposed to be the public key this
1810     * function won't return it since this library, for the most part, doesn't distinguish between public and private keys.
1811     *
1812     * @see getPublicKey()
1813     * @access public
1814     * @param String $key
1815     * @param Integer $type optional
1816     */
1817    function getPublicKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS8)
1818    {
1819        if (empty($this->modulus) || empty($this->publicExponent)) {
1820            return false;
1821        }
1822
1823        $oldFormat = $this->publicKeyFormat;
1824        $this->publicKeyFormat = $type;
1825        $temp = $this->_convertPublicKey($this->modulus, $this->publicExponent);
1826        $this->publicKeyFormat = $oldFormat;
1827        return $temp;
1828    }
1829
1830    /**
1831     * Returns the public key's fingerprint
1832     *
1833     * The public key's fingerprint is returned, which is equivalent to running `ssh-keygen -lf rsa.pub`. If there is
1834     * no public key currently loaded, false is returned.
1835     * Example output (md5): "c1:b1:30:29:d7:b8:de:6c:97:77:10:d7:46:41:63:87" (as specified by RFC 4716)
1836     *
1837     * @access public
1838     * @param String $algorithm The hashing algorithm to be used. Valid options are 'md5' and 'sha256'. False is returned
1839     * for invalid values.
1840     */
1841    public function getPublicKeyFingerprint($algorithm = 'md5')
1842    {
1843        if (empty($this->modulus) || empty($this->publicExponent)) {
1844            return false;
1845        }
1846
1847        $modulus = $this->modulus->toBytes(true);
1848        $publicExponent = $this->publicExponent->toBytes(true);
1849
1850        $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);
1851
1852        switch ($algorithm) {
1853            case 'sha256':
1854                $hash = new Crypt_Hash('sha256');
1855                $base = base64_encode($hash->hash($RSAPublicKey));
1856                return substr($base, 0, strlen($base) - 1);
1857            case 'md5':
1858                return substr(chunk_split(md5($RSAPublicKey), 2, ':'), 0, -1);
1859            default:
1860                return false;
1861        }
1862
1863    }
1864
1865    /**
1866     * Returns the private key
1867     *
1868     * The private key is only returned if the currently loaded key contains the constituent prime numbers.
1869     *
1870     * @see getPublicKey()
1871     * @access public
1872     * @param String $key
1873     * @param Integer $type optional
1874     */
1875    function getPrivateKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1)
1876    {
1877        if (empty($this->primes)) {
1878            return false;
1879        }
1880
1881        $oldFormat = $this->privateKeyFormat;
1882        $this->privateKeyFormat = $type;
1883        $temp = $this->_convertPrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients);
1884        $this->privateKeyFormat = $oldFormat;
1885        return $temp;
1886    }
1887
1888    /**
1889     * Returns a minimalistic private key
1890     *
1891     * Returns the private key without the prime number constituants.  Structurally identical to a public key that
1892     * hasn't been set as the public key
1893     *
1894     * @see getPrivateKey()
1895     * @access private
1896     * @param String $key
1897     * @param Integer $type optional
1898     */
1899    function _getPrivatePublicKey($mode = CRYPT_RSA_PUBLIC_FORMAT_PKCS8)
1900    {
1901        if (empty($this->modulus) || empty($this->exponent)) {
1902            return false;
1903        }
1904
1905        $oldFormat = $this->publicKeyFormat;
1906        $this->publicKeyFormat = $mode;
1907        $temp = $this->_convertPublicKey($this->modulus, $this->exponent);
1908        $this->publicKeyFormat = $oldFormat;
1909        return $temp;
1910    }
1911
1912    /**
1913     *  __toString() magic method
1914     *
1915     * @access public
1916     */
1917    function __toString()
1918    {
1919        $key = $this->getPrivateKey($this->privateKeyFormat);
1920        if ($key !== false) {
1921            return $key;
1922        }
1923        $key = $this->_getPrivatePublicKey($this->publicKeyFormat);
1924        return $key !== false ? $key : '';
1925    }
1926
1927    /**
1928     *  __clone() magic method
1929     *
1930     * @access public
1931     */
1932    function __clone()
1933    {
1934        $key = new Crypt_RSA();
1935        $key->loadKey($this);
1936        return $key;
1937    }
1938
1939    /**
1940     * Generates the smallest and largest numbers requiring $bits bits
1941     *
1942     * @access private
1943     * @param Integer $bits
1944     * @return Array
1945     */
1946    function _generateMinMax($bits)
1947    {
1948        $bytes = $bits >> 3;
1949        $min = str_repeat(chr(0), $bytes);
1950        $max = str_repeat(chr(0xFF), $bytes);
1951        $msb = $bits & 7;
1952        if ($msb) {
1953            $min = chr(1 << ($msb - 1)) . $min;
1954            $max = chr((1 << $msb) - 1) . $max;
1955        } else {
1956            $min[0] = chr(0x80);
1957        }
1958
1959        return array(
1960            'min' => new Math_BigInteger($min, 256),
1961            'max' => new Math_BigInteger($max, 256)
1962        );
1963    }
1964
1965    /**
1966     * DER-decode the length
1967     *
1968     * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4.  See
1969     * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
1970     *
1971     * @access private
1972     * @param String $string
1973     * @return Integer
1974     */
1975    function _decodeLength(&$string)
1976    {
1977        $length = ord($this->_string_shift($string));
1978        if ($length & 0x80) { // definite length, long form
1979            $length&= 0x7F;
1980            $temp = $this->_string_shift($string, $length);
1981            list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4));
1982        }
1983        return $length;
1984    }
1985
1986    /**
1987     * DER-encode the length
1988     *
1989     * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4.  See
1990     * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
1991     *
1992     * @access private
1993     * @param Integer $length
1994     * @return String
1995     */
1996    function _encodeLength($length)
1997    {
1998        if ($length <= 0x7F) {
1999            return chr($length);
2000        }
2001
2002        $temp = ltrim(pack('N', $length), chr(0));
2003        return pack('Ca*', 0x80 | strlen($temp), $temp);
2004    }
2005
2006    /**
2007     * String Shift
2008     *
2009     * Inspired by array_shift
2010     *
2011     * @param String $string
2012     * @param optional Integer $index
2013     * @return String
2014     * @access private
2015     */
2016    function _string_shift(&$string, $index = 1)
2017    {
2018        $substr = substr($string, 0, $index);
2019        $string = substr($string, $index);
2020        return $substr;
2021    }
2022
2023    /**
2024     * Determines the private key format
2025     *
2026     * @see createKey()
2027     * @access public
2028     * @param Integer $format
2029     */
2030    function setPrivateKeyFormat($format)
2031    {
2032        $this->privateKeyFormat = $format;
2033    }
2034
2035    /**
2036     * Determines the public key format
2037     *
2038     * @see createKey()
2039     * @access public
2040     * @param Integer $format
2041     */
2042    function setPublicKeyFormat($format)
2043    {
2044        $this->publicKeyFormat = $format;
2045    }
2046
2047    /**
2048     * Determines which hashing function should be used
2049     *
2050     * Used with signature production / verification and (if the encryption mode is CRYPT_RSA_ENCRYPTION_OAEP) encryption and
2051     * decryption.  If $hash isn't supported, sha1 is used.
2052     *
2053     * @access public
2054     * @param String $hash
2055     */
2056    function setHash($hash)
2057    {
2058        // Crypt_Hash supports algorithms that PKCS#1 doesn't support.  md5-96 and sha1-96, for example.
2059        switch ($hash) {
2060            case 'md2':
2061            case 'md5':
2062            case 'sha1':
2063            case 'sha256':
2064            case 'sha384':
2065            case 'sha512':
2066                $this->hash = new Crypt_Hash($hash);
2067                $this->hashName = $hash;
2068                break;
2069            default:
2070                $this->hash = new Crypt_Hash('sha1');
2071                $this->hashName = 'sha1';
2072        }
2073        $this->hLen = $this->hash->getLength();
2074    }
2075
2076    /**
2077     * Determines which hashing function should be used for the mask generation function
2078     *
2079     * The mask generation function is used by CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_SIGNATURE_PSS and although it's
2080     * best if Hash and MGFHash are set to the same thing this is not a requirement.
2081     *
2082     * @access public
2083     * @param String $hash
2084     */
2085    function setMGFHash($hash)
2086    {
2087        // Crypt_Hash supports algorithms that PKCS#1 doesn't support.  md5-96 and sha1-96, for example.
2088        switch ($hash) {
2089            case 'md2':
2090            case 'md5':
2091            case 'sha1':
2092            case 'sha256':
2093            case 'sha384':
2094            case 'sha512':
2095                $this->mgfHash = new Crypt_Hash($hash);
2096                break;
2097            default:
2098                $this->mgfHash = new Crypt_Hash('sha1');
2099        }
2100        $this->mgfHLen = $this->mgfHash->getLength();
2101    }
2102
2103    /**
2104     * Determines the salt length
2105     *
2106     * To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}:
2107     *
2108     *    Typical salt lengths in octets are hLen (the length of the output
2109     *    of the hash function Hash) and 0.
2110     *
2111     * @access public
2112     * @param Integer $format
2113     */
2114    function setSaltLength($sLen)
2115    {
2116        $this->sLen = $sLen;
2117    }
2118
2119    /**
2120     * Integer-to-Octet-String primitive
2121     *
2122     * See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}.
2123     *
2124     * @access private
2125     * @param Math_BigInteger $x
2126     * @param Integer $xLen
2127     * @return String
2128     */
2129    function _i2osp($x, $xLen)
2130    {
2131        $x = $x->toBytes();
2132        if (strlen($x) > $xLen) {
2133            user_error('Integer too large');
2134            return false;
2135        }
2136        return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
2137    }
2138
2139    /**
2140     * Octet-String-to-Integer primitive
2141     *
2142     * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}.
2143     *
2144     * @access private
2145     * @param String $x
2146     * @return Math_BigInteger
2147     */
2148    function _os2ip($x)
2149    {
2150        return new Math_BigInteger($x, 256);
2151    }
2152
2153    /**
2154     * Exponentiate with or without Chinese Remainder Theorem
2155     *
2156     * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.2}.
2157     *
2158     * @access private
2159     * @param Math_BigInteger $x
2160     * @return Math_BigInteger
2161     */
2162    function _exponentiate($x)
2163    {
2164        if (empty($this->primes) || empty($this->coefficients) || empty($this->exponents)) {
2165            return $x->modPow($this->exponent, $this->modulus);
2166        }
2167
2168        $num_primes = count($this->primes);
2169
2170        if (defined('CRYPT_RSA_DISABLE_BLINDING')) {
2171            $m_i = array(
2172                1 => $x->modPow($this->exponents[1], $this->primes[1]),
2173                2 => $x->modPow($this->exponents[2], $this->primes[2])
2174            );
2175            $h = $m_i[1]->subtract($m_i[2]);
2176            $h = $h->multiply($this->coefficients[2]);
2177            list(, $h) = $h->divide($this->primes[1]);
2178            $m = $m_i[2]->add($h->multiply($this->primes[2]));
2179
2180            $r = $this->primes[1];
2181            for ($i = 3; $i <= $num_primes; $i++) {
2182                $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]);
2183
2184                $r = $r->multiply($this->primes[$i - 1]);
2185
2186                $h = $m_i->subtract($m);
2187                $h = $h->multiply($this->coefficients[$i]);
2188                list(, $h) = $h->divide($this->primes[$i]);
2189
2190                $m = $m->add($r->multiply($h));
2191            }
2192        } else {
2193            $smallest = $this->primes[1];
2194            for ($i = 2; $i <= $num_primes; $i++) {
2195                if ($smallest->compare($this->primes[$i]) > 0) {
2196                    $smallest = $this->primes[$i];
2197                }
2198            }
2199
2200            $one = new Math_BigInteger(1);
2201
2202            $r = $one->random($one, $smallest->subtract($one));
2203
2204            $m_i = array(
2205                1 => $this->_blind($x, $r, 1),
2206                2 => $this->_blind($x, $r, 2)
2207            );
2208            $h = $m_i[1]->subtract($m_i[2]);
2209            $h = $h->multiply($this->coefficients[2]);
2210            list(, $h) = $h->divide($this->primes[1]);
2211            $m = $m_i[2]->add($h->multiply($this->primes[2]));
2212
2213            $r = $this->primes[1];
2214            for ($i = 3; $i <= $num_primes; $i++) {
2215                $m_i = $this->_blind($x, $r, $i);
2216
2217                $r = $r->multiply($this->primes[$i - 1]);
2218
2219                $h = $m_i->subtract($m);
2220                $h = $h->multiply($this->coefficients[$i]);
2221                list(, $h) = $h->divide($this->primes[$i]);
2222
2223                $m = $m->add($r->multiply($h));
2224            }
2225        }
2226
2227        return $m;
2228    }
2229
2230    /**
2231     * Performs RSA Blinding
2232     *
2233     * Protects against timing attacks by employing RSA Blinding.
2234     * Returns $x->modPow($this->exponents[$i], $this->primes[$i])
2235     *
2236     * @access private
2237     * @param Math_BigInteger $x
2238     * @param Math_BigInteger $r
2239     * @param Integer $i
2240     * @return Math_BigInteger
2241     */
2242    function _blind($x, $r, $i)
2243    {
2244        $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i]));
2245        $x = $x->modPow($this->exponents[$i], $this->primes[$i]);
2246
2247        $r = $r->modInverse($this->primes[$i]);
2248        $x = $x->multiply($r);
2249        list(, $x) = $x->divide($this->primes[$i]);
2250
2251        return $x;
2252    }
2253
2254    /**
2255     * Performs blinded RSA equality testing
2256     *
2257     * Protects against a particular type of timing attack described.
2258     *
2259     * See {@link http://codahale.com/a-lesson-in-timing-attacks/ A Lesson In Timing Attacks (or, Don't use MessageDigest.isEquals)}
2260     *
2261     * Thanks for the heads up singpolyma!
2262     *
2263     * @access private
2264     * @param String $x
2265     * @param String $y
2266     * @return Boolean
2267     */
2268    function _equals($x, $y)
2269    {
2270        if (strlen($x) != strlen($y)) {
2271            return false;
2272        }
2273
2274        $result = 0;
2275        for ($i = 0; $i < strlen($x); $i++) {
2276            $result |= ord($x[$i]) ^ ord($y[$i]);
2277        }
2278
2279        return $result == 0;
2280    }
2281
2282    /**
2283     * RSAEP
2284     *
2285     * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}.
2286     *
2287     * @access private
2288     * @param Math_BigInteger $m
2289     * @return Math_BigInteger
2290     */
2291    function _rsaep($m)
2292    {
2293        if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
2294            user_error('Message representative out of range');
2295            return false;
2296        }
2297        return $this->_exponentiate($m);
2298    }
2299
2300    /**
2301     * RSADP
2302     *
2303     * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}.
2304     *
2305     * @access private
2306     * @param Math_BigInteger $c
2307     * @return Math_BigInteger
2308     */
2309    function _rsadp($c)
2310    {
2311        if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) {
2312            user_error('Ciphertext representative out of range');
2313            return false;
2314        }
2315        return $this->_exponentiate($c);
2316    }
2317
2318    /**
2319     * RSASP1
2320     *
2321     * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}.
2322     *
2323     * @access private
2324     * @param Math_BigInteger $m
2325     * @return Math_BigInteger
2326     */
2327    function _rsasp1($m)
2328    {
2329        if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
2330            user_error('Message representative out of range');
2331            return false;
2332        }
2333        return $this->_exponentiate($m);
2334    }
2335
2336    /**
2337     * RSAVP1
2338     *
2339     * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}.
2340     *
2341     * @access private
2342     * @param Math_BigInteger $s
2343     * @return Math_BigInteger
2344     */
2345    function _rsavp1($s)
2346    {
2347        if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) {
2348            user_error('Signature representative out of range');
2349            return false;
2350        }
2351        return $this->_exponentiate($s);
2352    }
2353
2354    /**
2355     * MGF1
2356     *
2357     * See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}.
2358     *
2359     * @access private
2360     * @param String $mgfSeed
2361     * @param Integer $mgfLen
2362     * @return String
2363     */
2364    function _mgf1($mgfSeed, $maskLen)
2365    {
2366        // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output.
2367
2368        $t = '';
2369        $count = ceil($maskLen / $this->mgfHLen);
2370        for ($i = 0; $i < $count; $i++) {
2371            $c = pack('N', $i);
2372            $t.= $this->mgfHash->hash($mgfSeed . $c);
2373        }
2374
2375        return substr($t, 0, $maskLen);
2376    }
2377
2378    /**
2379     * RSAES-OAEP-ENCRYPT
2380     *
2381     * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and
2382     * {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}.
2383     *
2384     * @access private
2385     * @param String $m
2386     * @param String $l
2387     * @return String
2388     */
2389    function _rsaes_oaep_encrypt($m, $l = '')
2390    {
2391        $mLen = strlen($m);
2392
2393        // Length checking
2394
2395        // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
2396        // be output.
2397
2398        if ($mLen > $this->k - 2 * $this->hLen - 2) {
2399            user_error('Message too long');
2400            return false;
2401        }
2402
2403        // EME-OAEP encoding
2404
2405        $lHash = $this->hash->hash($l);
2406        $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2);
2407        $db = $lHash . $ps . chr(1) . $m;
2408        $seed = crypt_random_string($this->hLen);
2409        $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
2410        $maskedDB = $db ^ $dbMask;
2411        $seedMask = $this->_mgf1($maskedDB, $this->hLen);
2412        $maskedSeed = $seed ^ $seedMask;
2413        $em = chr(0) . $maskedSeed . $maskedDB;
2414
2415        // RSA encryption
2416
2417        $m = $this->_os2ip($em);
2418        $c = $this->_rsaep($m);
2419        $c = $this->_i2osp($c, $this->k);
2420
2421        // Output the ciphertext C
2422
2423        return $c;
2424    }
2425
2426    /**
2427     * RSAES-OAEP-DECRYPT
2428     *
2429     * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}.  The fact that the error
2430     * messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2:
2431     *
2432     *    Note.  Care must be taken to ensure that an opponent cannot
2433     *    distinguish the different error conditions in Step 3.g, whether by
2434     *    error message or timing, or, more generally, learn partial
2435     *    information about the encoded message EM.  Otherwise an opponent may
2436     *    be able to obtain useful information about the decryption of the
2437     *    ciphertext C, leading to a chosen-ciphertext attack such as the one
2438     *    observed by Manger [36].
2439     *
2440     * As for $l...  to quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}:
2441     *
2442     *    Both the encryption and the decryption operations of RSAES-OAEP take
2443     *    the value of a label L as input.  In this version of PKCS #1, L is
2444     *    the empty string; other uses of the label are outside the scope of
2445     *    this document.
2446     *
2447     * @access private
2448     * @param String $c
2449     * @param String $l
2450     * @return String
2451     */
2452    function _rsaes_oaep_decrypt($c, $l = '')
2453    {
2454        // Length checking
2455
2456        // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
2457        // be output.
2458
2459        if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) {
2460            user_error('Decryption error');
2461            return false;
2462        }
2463
2464        // RSA decryption
2465
2466        $c = $this->_os2ip($c);
2467        $m = $this->_rsadp($c);
2468        if ($m === false) {
2469            user_error('Decryption error');
2470            return false;
2471        }
2472        $em = $this->_i2osp($m, $this->k);
2473
2474        // EME-OAEP decoding
2475
2476        $lHash = $this->hash->hash($l);
2477        $y = ord($em[0]);
2478        $maskedSeed = substr($em, 1, $this->hLen);
2479        $maskedDB = substr($em, $this->hLen + 1);
2480        $seedMask = $this->_mgf1($maskedDB, $this->hLen);
2481        $seed = $maskedSeed ^ $seedMask;
2482        $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
2483        $db = $maskedDB ^ $dbMask;
2484        $lHash2 = substr($db, 0, $this->hLen);
2485        $m = substr($db, $this->hLen);
2486        if ($lHash != $lHash2) {
2487            user_error('Decryption error');
2488            return false;
2489        }
2490        $m = ltrim($m, chr(0));
2491        if (ord($m[0]) != 1) {
2492            user_error('Decryption error');
2493            return false;
2494        }
2495
2496        // Output the message M
2497
2498        return substr($m, 1);
2499    }
2500
2501    /**
2502     * Raw Encryption / Decryption
2503     *
2504     * Doesn't use padding and is not recommended.
2505     *
2506     * @access private
2507     * @param String $m
2508     * @return String
2509     */
2510    function _raw_encrypt($m)
2511    {
2512        $temp = $this->_os2ip($m);
2513        $temp = $this->_rsaep($temp);
2514        return  $this->_i2osp($temp, $this->k);
2515    }
2516
2517    /**
2518     * RSAES-PKCS1-V1_5-ENCRYPT
2519     *
2520     * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}.
2521     *
2522     * @access private
2523     * @param String $m
2524     * @return String
2525     */
2526    function _rsaes_pkcs1_v1_5_encrypt($m)
2527    {
2528        $mLen = strlen($m);
2529
2530        // Length checking
2531
2532        if ($mLen > $this->k - 11) {
2533            user_error('Message too long');
2534            return false;
2535        }
2536
2537        // EME-PKCS1-v1_5 encoding
2538
2539        $psLen = $this->k - $mLen - 3;
2540        $ps = '';
2541        while (strlen($ps) != $psLen) {
2542            $temp = crypt_random_string($psLen - strlen($ps));
2543            $temp = str_replace("\x00", '', $temp);
2544            $ps.= $temp;
2545        }
2546        $type = 2;
2547        // see the comments of _rsaes_pkcs1_v1_5_decrypt() to understand why this is being done
2548        if (defined('CRYPT_RSA_PKCS15_COMPAT') && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) {
2549            $type = 1;
2550            // "The padding string PS shall consist of k-3-||D|| octets. ... for block type 01, they shall have value FF"
2551            $ps = str_repeat("\xFF", $psLen);
2552        }
2553        $em = chr(0) . chr($type) . $ps . chr(0) . $m;
2554
2555        // RSA encryption
2556        $m = $this->_os2ip($em);
2557        $c = $this->_rsaep($m);
2558        $c = $this->_i2osp($c, $this->k);
2559
2560        // Output the ciphertext C
2561
2562        return $c;
2563    }
2564
2565    /**
2566     * RSAES-PKCS1-V1_5-DECRYPT
2567     *
2568     * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}.
2569     *
2570     * For compatibility purposes, this function departs slightly from the description given in RFC3447.
2571     * The reason being that RFC2313#section-8.1 (PKCS#1 v1.5) states that ciphertext's encrypted by the
2572     * private key should have the second byte set to either 0 or 1 and that ciphertext's encrypted by the
2573     * public key should have the second byte set to 2.  In RFC3447 (PKCS#1 v2.1), the second byte is supposed
2574     * to be 2 regardless of which key is used.  For compatibility purposes, we'll just check to make sure the
2575     * second byte is 2 or less.  If it is, we'll accept the decrypted string as valid.
2576     *
2577     * As a consequence of this, a private key encrypted ciphertext produced with Crypt_RSA may not decrypt
2578     * with a strictly PKCS#1 v1.5 compliant RSA implementation.  Public key encrypted ciphertext's should but
2579     * not private key encrypted ciphertext's.
2580     *
2581     * @access private
2582     * @param String $c
2583     * @return String
2584     */
2585    function _rsaes_pkcs1_v1_5_decrypt($c)
2586    {
2587        // Length checking
2588
2589        if (strlen($c) != $this->k) { // or if k < 11
2590            user_error('Decryption error');
2591            return false;
2592        }
2593
2594        // RSA decryption
2595
2596        $c = $this->_os2ip($c);
2597        $m = $this->_rsadp($c);
2598
2599        if ($m === false) {
2600            user_error('Decryption error');
2601            return false;
2602        }
2603        $em = $this->_i2osp($m, $this->k);
2604
2605        // EME-PKCS1-v1_5 decoding
2606
2607        if (ord($em[0]) != 0 || ord($em[1]) > 2) {
2608            user_error('Decryption error');
2609            return false;
2610        }
2611
2612        $ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
2613        $m = substr($em, strlen($ps) + 3);
2614
2615        if (strlen($ps) < 8) {
2616            user_error('Decryption error');
2617            return false;
2618        }
2619
2620        // Output M
2621
2622        return $m;
2623    }
2624
2625    /**
2626     * EMSA-PSS-ENCODE
2627     *
2628     * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}.
2629     *
2630     * @access private
2631     * @param String $m
2632     * @param Integer $emBits
2633     */
2634    function _emsa_pss_encode($m, $emBits)
2635    {
2636        // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
2637        // be output.
2638
2639        $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
2640        $sLen = $this->sLen === false ? $this->hLen : $this->sLen;
2641
2642        $mHash = $this->hash->hash($m);
2643        if ($emLen < $this->hLen + $sLen + 2) {
2644            user_error('Encoding error');
2645            return false;
2646        }
2647
2648        $salt = crypt_random_string($sLen);
2649        $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
2650        $h = $this->hash->hash($m2);
2651        $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
2652        $db = $ps . chr(1) . $salt;
2653        $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
2654        $maskedDB = $db ^ $dbMask;
2655        $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0];
2656        $em = $maskedDB . $h . chr(0xBC);
2657
2658        return $em;
2659    }
2660
2661    /**
2662     * EMSA-PSS-VERIFY
2663     *
2664     * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}.
2665     *
2666     * @access private
2667     * @param String $m
2668     * @param String $em
2669     * @param Integer $emBits
2670     * @return String
2671     */
2672    function _emsa_pss_verify($m, $em, $emBits)
2673    {
2674        // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
2675        // be output.
2676
2677        $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8);
2678        $sLen = $this->sLen === false ? $this->hLen : $this->sLen;
2679
2680        $mHash = $this->hash->hash($m);
2681        if ($emLen < $this->hLen + $sLen + 2) {
2682            return false;
2683        }
2684
2685        if ($em[strlen($em) - 1] != chr(0xBC)) {
2686            return false;
2687        }
2688
2689        $maskedDB = substr($em, 0, -$this->hLen - 1);
2690        $h = substr($em, -$this->hLen - 1, $this->hLen);
2691        $temp = chr(0xFF << ($emBits & 7));
2692        if ((~$maskedDB[0] & $temp) != $temp) {
2693            return false;
2694        }
2695        $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
2696        $db = $maskedDB ^ $dbMask;
2697        $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
2698        $temp = $emLen - $this->hLen - $sLen - 2;
2699        if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
2700            return false;
2701        }
2702        $salt = substr($db, $temp + 1); // should be $sLen long
2703        $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
2704        $h2 = $this->hash->hash($m2);
2705        return $this->_equals($h, $h2);
2706    }
2707
2708    /**
2709     * RSASSA-PSS-SIGN
2710     *
2711     * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}.
2712     *
2713     * @access private
2714     * @param String $m
2715     * @return String
2716     */
2717    function _rsassa_pss_sign($m)
2718    {
2719        // EMSA-PSS encoding
2720
2721        $em = $this->_emsa_pss_encode($m, 8 * $this->k - 1);
2722
2723        // RSA signature
2724
2725        $m = $this->_os2ip($em);
2726        $s = $this->_rsasp1($m);
2727        $s = $this->_i2osp($s, $this->k);
2728
2729        // Output the signature S
2730
2731        return $s;
2732    }
2733
2734    /**
2735     * RSASSA-PSS-VERIFY
2736     *
2737     * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}.
2738     *
2739     * @access private
2740     * @param String $m
2741     * @param String $s
2742     * @return String
2743     */
2744    function _rsassa_pss_verify($m, $s)
2745    {
2746        // Length checking
2747
2748        if (strlen($s) != $this->k) {
2749            user_error('Invalid signature');
2750            return false;
2751        }
2752
2753        // RSA verification
2754
2755        $modBits = 8 * $this->k;
2756
2757        $s2 = $this->_os2ip($s);
2758        $m2 = $this->_rsavp1($s2);
2759        if ($m2 === false) {
2760            user_error('Invalid signature');
2761            return false;
2762        }
2763        $em = $this->_i2osp($m2, $modBits >> 3);
2764        if ($em === false) {
2765            user_error('Invalid signature');
2766            return false;
2767        }
2768
2769        // EMSA-PSS verification
2770
2771        return $this->_emsa_pss_verify($m, $em, $modBits - 1);
2772    }
2773
2774    /**
2775     * EMSA-PKCS1-V1_5-ENCODE
2776     *
2777     * See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}.
2778     *
2779     * @access private
2780     * @param String $m
2781     * @param Integer $emLen
2782     * @return String
2783     */
2784    function _emsa_pkcs1_v1_5_encode($m, $emLen)
2785    {
2786        $h = $this->hash->hash($m);
2787        if ($h === false) {
2788            return false;
2789        }
2790
2791        // see http://tools.ietf.org/html/rfc3447#page-43
2792        switch ($this->hashName) {
2793            case 'md2':
2794                $t = pack('H*', '3020300c06082a864886f70d020205000410');
2795                break;
2796            case 'md5':
2797                $t = pack('H*', '3020300c06082a864886f70d020505000410');
2798                break;
2799            case 'sha1':
2800                $t = pack('H*', '3021300906052b0e03021a05000414');
2801                break;
2802            case 'sha256':
2803                $t = pack('H*', '3031300d060960864801650304020105000420');
2804                break;
2805            case 'sha384':
2806                $t = pack('H*', '3041300d060960864801650304020205000430');
2807                break;
2808            case 'sha512':
2809                $t = pack('H*', '3051300d060960864801650304020305000440');
2810        }
2811        $t.= $h;
2812        $tLen = strlen($t);
2813
2814        if ($emLen < $tLen + 11) {
2815            user_error('Intended encoded message length too short');
2816            return false;
2817        }
2818
2819        $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
2820
2821        $em = "\0\1$ps\0$t";
2822
2823        return $em;
2824    }
2825
2826    /**
2827     * RSASSA-PKCS1-V1_5-SIGN
2828     *
2829     * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}.
2830     *
2831     * @access private
2832     * @param String $m
2833     * @return String
2834     */
2835    function _rsassa_pkcs1_v1_5_sign($m)
2836    {
2837        // EMSA-PKCS1-v1_5 encoding
2838
2839        $em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
2840        if ($em === false) {
2841            user_error('RSA modulus too short');
2842            return false;
2843        }
2844
2845        // RSA signature
2846
2847        $m = $this->_os2ip($em);
2848        $s = $this->_rsasp1($m);
2849        $s = $this->_i2osp($s, $this->k);
2850
2851        // Output the signature S
2852
2853        return $s;
2854    }
2855
2856    /**
2857     * RSASSA-PKCS1-V1_5-VERIFY
2858     *
2859     * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}.
2860     *
2861     * @access private
2862     * @param String $m
2863     * @return String
2864     */
2865    function _rsassa_pkcs1_v1_5_verify($m, $s)
2866    {
2867        // Length checking
2868
2869        if (strlen($s) != $this->k) {
2870            user_error('Invalid signature');
2871            return false;
2872        }
2873
2874        // RSA verification
2875
2876        $s = $this->_os2ip($s);
2877        $m2 = $this->_rsavp1($s);
2878        if ($m2 === false) {
2879            user_error('Invalid signature');
2880            return false;
2881        }
2882        $em = $this->_i2osp($m2, $this->k);
2883        if ($em === false) {
2884            user_error('Invalid signature');
2885            return false;
2886        }
2887
2888        // EMSA-PKCS1-v1_5 encoding
2889
2890        $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
2891        if ($em2 === false) {
2892            user_error('RSA modulus too short');
2893            return false;
2894        }
2895
2896        // Compare
2897        return $this->_equals($em, $em2);
2898    }
2899
2900    /**
2901     * Set Encryption Mode
2902     *
2903     * Valid values include CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1.
2904     *
2905     * @access public
2906     * @param Integer $mode
2907     */
2908    function setEncryptionMode($mode)
2909    {
2910        $this->encryptionMode = $mode;
2911    }
2912
2913    /**
2914     * Set Signature Mode
2915     *
2916     * Valid values include CRYPT_RSA_SIGNATURE_PSS and CRYPT_RSA_SIGNATURE_PKCS1
2917     *
2918     * @access public
2919     * @param Integer $mode
2920     */
2921    function setSignatureMode($mode)
2922    {
2923        $this->signatureMode = $mode;
2924    }
2925
2926    /**
2927     * Set public key comment.
2928     *
2929     * @access public
2930     * @param String $comment
2931     */
2932    function setComment($comment)
2933    {
2934        $this->comment = $comment;
2935    }
2936
2937    /**
2938     * Get public key comment.
2939     *
2940     * @access public
2941     * @return String
2942     */
2943    function getComment()
2944    {
2945        return $this->comment;
2946    }
2947
2948    /**
2949     * Encryption
2950     *
2951     * Both CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1 both place limits on how long $plaintext can be.
2952     * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
2953     * be concatenated together.
2954     *
2955     * @see decrypt()
2956     * @access public
2957     * @param String $plaintext
2958     * @return String
2959     */
2960    function encrypt($plaintext)
2961    {
2962        switch ($this->encryptionMode) {
2963            case CRYPT_RSA_ENCRYPTION_NONE:
2964                $plaintext = str_split($plaintext, $this->k);
2965                $ciphertext = '';
2966                foreach ($plaintext as $m) {
2967                    $ciphertext.= $this->_raw_encrypt($m);
2968                }
2969                return $ciphertext;
2970            case CRYPT_RSA_ENCRYPTION_PKCS1:
2971                $length = $this->k - 11;
2972                if ($length <= 0) {
2973                    return false;
2974                }
2975
2976                $plaintext = str_split($plaintext, $length);
2977                $ciphertext = '';
2978                foreach ($plaintext as $m) {
2979                    $ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m);
2980                }
2981                return $ciphertext;
2982            //case CRYPT_RSA_ENCRYPTION_OAEP:
2983            default:
2984                $length = $this->k - 2 * $this->hLen - 2;
2985                if ($length <= 0) {
2986                    return false;
2987                }
2988
2989                $plaintext = str_split($plaintext, $length);
2990                $ciphertext = '';
2991                foreach ($plaintext as $m) {
2992                    $ciphertext.= $this->_rsaes_oaep_encrypt($m);
2993                }
2994                return $ciphertext;
2995        }
2996    }
2997
2998    /**
2999     * Decryption
3000     *
3001     * @see encrypt()
3002     * @access public
3003     * @param String $plaintext
3004     * @return String
3005     */
3006    function decrypt($ciphertext)
3007    {
3008        if ($this->k <= 0) {
3009            return false;
3010        }
3011
3012        $ciphertext = str_split($ciphertext, $this->k);
3013        $ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0), STR_PAD_LEFT);
3014
3015        $plaintext = '';
3016
3017        switch ($this->encryptionMode) {
3018            case CRYPT_RSA_ENCRYPTION_NONE:
3019                $decrypt = '_raw_encrypt';
3020                break;
3021            case CRYPT_RSA_ENCRYPTION_PKCS1:
3022                $decrypt = '_rsaes_pkcs1_v1_5_decrypt';
3023                break;
3024            //case CRYPT_RSA_ENCRYPTION_OAEP:
3025            default:
3026                $decrypt = '_rsaes_oaep_decrypt';
3027        }
3028
3029        foreach ($ciphertext as $c) {
3030            $temp = $this->$decrypt($c);
3031            if ($temp === false) {
3032                return false;
3033            }
3034            $plaintext.= $temp;
3035        }
3036
3037        return $plaintext;
3038    }
3039
3040    /**
3041     * Create a signature
3042     *
3043     * @see verify()
3044     * @access public
3045     * @param String $message
3046     * @return String
3047     */
3048    function sign($message)
3049    {
3050        if (empty($this->modulus) || empty($this->exponent)) {
3051            return false;
3052        }
3053
3054        switch ($this->signatureMode) {
3055            case CRYPT_RSA_SIGNATURE_PKCS1:
3056                return $this->_rsassa_pkcs1_v1_5_sign($message);
3057            //case CRYPT_RSA_SIGNATURE_PSS:
3058            default:
3059                return $this->_rsassa_pss_sign($message);
3060        }
3061    }
3062
3063    /**
3064     * Verifies a signature
3065     *
3066     * @see sign()
3067     * @access public
3068     * @param String $message
3069     * @param String $signature
3070     * @return Boolean
3071     */
3072    function verify($message, $signature)
3073    {
3074        if (empty($this->modulus) || empty($this->exponent)) {
3075            return false;
3076        }
3077
3078        switch ($this->signatureMode) {
3079            case CRYPT_RSA_SIGNATURE_PKCS1:
3080                return $this->_rsassa_pkcs1_v1_5_verify($message, $signature);
3081            //case CRYPT_RSA_SIGNATURE_PSS:
3082            default:
3083                return $this->_rsassa_pss_verify($message, $signature);
3084        }
3085    }
3086
3087    /**
3088     * Extract raw BER from Base64 encoding
3089     *
3090     * @access private
3091     * @param String $str
3092     * @return String
3093     */
3094    function _extractBER($str)
3095    {
3096        /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them
3097         * above and beyond the ceritificate.
3098         * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line:
3099         *
3100         * Bag Attributes
3101         *     localKeyID: 01 00 00 00
3102         * subject=/O=organization/OU=org unit/CN=common name
3103         * issuer=/O=organization/CN=common name
3104         */
3105        $temp = preg_replace('#.*?^-+[^-]+-+#ms', '', $str, 1);
3106        // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
3107        $temp = preg_replace('#-+[^-]+-+#', '', $temp);
3108        // remove new lines
3109        $temp = str_replace(array("\r", "\n", ' '), '', $temp);
3110        $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
3111        return $temp != false ? $temp : $str;
3112    }
3113}