2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
\r
5 * Pure-PHP PKCS#1 (v2.1) compliant implementation of RSA.
\r
7 * PHP versions 4 and 5
\r
9 * Here's an example of how to encrypt and decrypt text with this library:
\r
12 * include('Crypt/RSA.php');
\r
14 * $rsa = new Crypt_RSA();
\r
15 * extract($rsa->createKey());
\r
17 * $plaintext = 'terrafrost';
\r
19 * $rsa->loadKey($privatekey);
\r
20 * $ciphertext = $rsa->encrypt($plaintext);
\r
22 * $rsa->loadKey($publickey);
\r
23 * echo $rsa->decrypt($ciphertext);
\r
27 * Here's an example of how to create signatures and verify signatures with this library:
\r
30 * include('Crypt/RSA.php');
\r
32 * $rsa = new Crypt_RSA();
\r
33 * extract($rsa->createKey());
\r
35 * $plaintext = 'terrafrost';
\r
37 * $rsa->loadKey($privatekey);
\r
38 * $signature = $rsa->sign($plaintext);
\r
40 * $rsa->loadKey($publickey);
\r
41 * echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified';
\r
45 * LICENSE: This library is free software; you can redistribute it and/or
\r
46 * modify it under the terms of the GNU Lesser General Public
\r
47 * License as published by the Free Software Foundation; either
\r
48 * version 2.1 of the License, or (at your option) any later version.
\r
50 * This library is distributed in the hope that it will be useful,
\r
51 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
52 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
53 * Lesser General Public License for more details.
\r
55 * You should have received a copy of the GNU Lesser General Public
\r
56 * License along with this library; if not, write to the Free Software
\r
57 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
\r
61 * @package Crypt_RSA
\r
62 * @author Jim Wigginton <terrafrost@php.net>
\r
63 * @copyright MMIX Jim Wigginton
\r
64 * @license http://www.gnu.org/licenses/lgpl.txt
\r
65 * @version $Id: RSA.php,v 1.15 2010/04/10 15:57:02 terrafrost Exp $
\r
66 * @link http://phpseclib.sourceforge.net
\r
70 * Include Math_BigInteger
\r
72 require_once('Math/BigInteger.php');
\r
75 * Include Crypt_Random
\r
77 require_once('Crypt/Random.php');
\r
80 * Include Crypt_Hash
\r
82 require_once('Crypt/Hash.php');
\r
86 * @see Crypt_RSA::encrypt()
\r
87 * @see Crypt_RSA::decrypt()
\r
90 * Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding}
\r
91 * (OAEP) for encryption / decryption.
\r
93 * Uses sha1 by default.
\r
95 * @see Crypt_RSA::setHash()
\r
96 * @see Crypt_RSA::setMGFHash()
\r
98 define('CRYPT_RSA_ENCRYPTION_OAEP', 1);
\r
100 * Use PKCS#1 padding.
\r
102 * Although CRYPT_RSA_ENCRYPTION_OAEP offers more security, including PKCS#1 padding is necessary for purposes of backwards
\r
103 * compatability with protocols (like SSH-1) written before OAEP's introduction.
\r
105 define('CRYPT_RSA_ENCRYPTION_PKCS1', 2);
\r
110 * @see Crypt_RSA::sign()
\r
111 * @see Crypt_RSA::verify()
\r
112 * @see Crypt_RSA::setHash()
\r
115 * Use the Probabilistic Signature Scheme for signing
\r
117 * Uses sha1 by default.
\r
119 * @see Crypt_RSA::setSaltLength()
\r
120 * @see Crypt_RSA::setMGFHash()
\r
122 define('CRYPT_RSA_SIGNATURE_PSS', 1);
\r
124 * Use the PKCS#1 scheme by default.
\r
126 * Although CRYPT_RSA_SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards
\r
127 * compatability with protocols (like SSH-2) written before PSS's introduction.
\r
129 define('CRYPT_RSA_SIGNATURE_PKCS1', 2);
\r
134 * @see Crypt_RSA::createKey()
\r
139 define('CRYPT_RSA_ASN1_INTEGER', 2);
\r
141 * ASN1 Sequence (with the constucted bit set)
\r
143 define('CRYPT_RSA_ASN1_SEQUENCE', 48);
\r
148 * @see Crypt_RSA::Crypt_RSA()
\r
151 * To use the pure-PHP implementation
\r
153 define('CRYPT_RSA_MODE_INTERNAL', 1);
\r
155 * To use the OpenSSL library
\r
157 * (if enabled; otherwise, the internal implementation will be used)
\r
159 define('CRYPT_RSA_MODE_OPENSSL', 2);
\r
164 * @see Crypt_RSA::createKey()
\r
165 * @see Crypt_RSA::setPrivateKeyFormat()
\r
168 * PKCS#1 formatted private key
\r
172 define('CRYPT_RSA_PRIVATE_FORMAT_PKCS1', 0);
\r
177 * @see Crypt_RSA::createKey()
\r
178 * @see Crypt_RSA::setPublicKeyFormat()
\r
183 * An array containing two Math_BigInteger objects.
\r
185 * The exponent can be indexed with any of the following:
\r
187 * 0, e, exponent, publicExponent
\r
189 * The modulus can be indexed with any of the following:
\r
191 * 1, n, modulo, modulus
\r
193 define('CRYPT_RSA_PUBLIC_FORMAT_RAW', 1);
\r
195 * PKCS#1 formatted public key
\r
197 define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1', 2);
\r
199 * OpenSSH formatted public key
\r
201 * Place in $HOME/.ssh/authorized_keys
\r
203 define('CRYPT_RSA_PUBLIC_FORMAT_OPENSSH', 3);
\r
207 * Pure-PHP PKCS#1 compliant implementation of RSA.
\r
209 * @author Jim Wigginton <terrafrost@php.net>
\r
212 * @package Crypt_RSA
\r
232 * Private Key Format
\r
237 var $privateKeyFormat = CRYPT_RSA_PRIVATE_FORMAT_PKCS1;
\r
240 * Public Key Format
\r
245 var $publicKeyFormat = CRYPT_RSA_PUBLIC_FORMAT_PKCS1;
\r
250 * @var Math_BigInteger
\r
258 * @var Math_BigInteger
\r
264 * Exponent (ie. e or d)
\r
266 * @var Math_BigInteger
\r
272 * Primes for Chinese Remainder Theorem (ie. p and q)
\r
280 * Exponents for Chinese Remainder Theorem (ie. dP and dQ)
\r
288 * Coefficients for Chinese Remainder Theorem (ie. qInv)
\r
312 * Length of hash function output
\r
328 * Hash function for the Mask Generation Function
\r
336 * Length of MGF hash function output
\r
349 var $encryptionMode = CRYPT_RSA_ENCRYPTION_OAEP;
\r
357 var $signatureMode = CRYPT_RSA_SIGNATURE_PSS;
\r
365 var $publicExponent = false;
\r
373 var $password = '';
\r
378 * If you want to make use of the openssl extension, you'll need to set the mode manually, yourself. The reason
\r
379 * Crypt_RSA doesn't do it is because OpenSSL doesn't fail gracefully. openssl_pkey_new(), in particular, requires
\r
380 * openssl.cnf be present somewhere and, unfortunately, the only real way to find out is too late.
\r
382 * @return Crypt_RSA
\r
385 function Crypt_RSA()
\r
387 if ( !defined('CRYPT_RSA_MODE') ) {
\r
389 //case extension_loaded('openssl') && version_compare(PHP_VERSION, '4.2.0', '>='):
\r
390 // define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_OPENSSL);
\r
393 define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
\r
397 $this->zero = new Math_BigInteger();
\r
398 $this->one = new Math_BigInteger(1);
\r
400 $this->hash = new Crypt_Hash('sha1');
\r
401 $this->hLen = $this->hash->getLength();
\r
402 $this->hashName = 'sha1';
\r
403 $this->mgfHash = new Crypt_Hash('sha1');
\r
404 $this->mgfHLen = $this->mgfHash->getLength();
\r
408 * Create public / private key pair
\r
410 * Returns an array with the following three elements:
\r
411 * - 'privatekey': The private key.
\r
412 * - 'publickey': The public key.
\r
413 * - 'partialkey': A partially computed key (if the execution time exceeded $timeout).
\r
414 * Will need to be passed back to Crypt_RSA::createKey() as the third parameter for further processing.
\r
417 * @param optional Integer $bits
\r
418 * @param optional Integer $timeout
\r
419 * @param optional Math_BigInteger $p
\r
421 function createKey($bits = 1024, $timeout = false, $partial = array())
\r
423 if ( CRYPT_RSA_MODE == CRYPT_RSA_MODE_OPENSSL ) {
\r
424 $rsa = openssl_pkey_new(array('private_key_bits' => $bits));
\r
425 openssl_pkey_export($rsa, $privatekey);
\r
426 $publickey = openssl_pkey_get_details($rsa);
\r
427 $publickey = $publickey['key'];
\r
429 if ($this->privateKeyFormat != CRYPT_RSA_PRIVATE_FORMAT_PKCS1) {
\r
430 $privatekey = call_user_func_array(array($this, '_convertPrivateKey'), array_values($this->_parseKey($privatekey, CRYPT_RSA_PRIVATE_FORMAT_PKCS1)));
\r
431 $publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, CRYPT_RSA_PUBLIC_FORMAT_PKCS1)));
\r
435 'privatekey' => $privatekey,
\r
436 'publickey' => $publickey,
\r
437 'partialkey' => false
\r
443 if (!defined('CRYPT_RSA_EXPONENT')) {
\r
444 // http://en.wikipedia.org/wiki/65537_%28number%29
\r
445 define('CRYPT_RSA_EXPONENT', '65537');
\r
447 if (!defined('CRYPT_RSA_COMMENT')) {
\r
448 define('CRYPT_RSA_COMMENT', 'phpseclib-generated-key');
\r
450 // per <http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number ought not result in primes smaller
\r
452 if (!defined('CRYPT_RSA_SMALLEST_PRIME')) {
\r
453 define('CRYPT_RSA_SMALLEST_PRIME', 4096);
\r
456 $e = new Math_BigInteger(CRYPT_RSA_EXPONENT);
\r
459 extract($this->_generateMinMax($bits));
\r
460 $absoluteMin = $min;
\r
461 $temp = $bits >> 1;
\r
462 if ($temp > CRYPT_RSA_SMALLEST_PRIME) {
\r
463 $num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME);
\r
464 $temp = CRYPT_RSA_SMALLEST_PRIME;
\r
468 extract($this->_generateMinMax($temp + $bits % $temp));
\r
470 extract($this->_generateMinMax($temp));
\r
472 $generator = new Math_BigInteger();
\r
473 $generator->setRandomGenerator('crypt_random');
\r
475 $n = $this->one->copy();
\r
476 if (!empty($partial)) {
\r
477 extract(unserialize($partial));
\r
479 $exponents = $coefficients = $primes = array();
\r
481 'top' => $this->one->copy(),
\r
487 $i0 = count($primes) + 1;
\r
490 for ($i = $i0; $i <= $num_primes; $i++) {
\r
491 if ($timeout !== false) {
\r
492 $timeout-= time() - $start;
\r
494 if ($timeout <= 0) {
\r
495 return serialize(array(
\r
496 'privatekey' => '',
\r
498 'partialkey' => array(
\r
499 'primes' => $primes,
\r
500 'coefficients' => $coefficients,
\r
502 'exponents' => $exponents
\r
508 if ($i == $num_primes) {
\r
509 list($min, $temp) = $absoluteMin->divide($n);
\r
510 if (!$temp->equals($this->zero)) {
\r
511 $min = $min->add($this->one); // ie. ceil()
\r
513 $primes[$i] = $generator->randomPrime($min, $finalMax, $timeout);
\r
515 $primes[$i] = $generator->randomPrime($min, $max, $timeout);
\r
518 if ($primes[$i] === false) { // if we've reached the timeout
\r
520 'privatekey' => '',
\r
522 'partialkey' => empty($primes) ? '' : serialize(array(
\r
523 'primes' => array_slice($primes, 0, $i - 1),
\r
524 'coefficients' => $coefficients,
\r
526 'exponents' => $exponents
\r
531 // the first coefficient is calculated differently from the rest
\r
532 // ie. instead of being $primes[1]->modInverse($primes[2]), it's $primes[2]->modInverse($primes[1])
\r
534 $coefficients[$i] = $n->modInverse($primes[$i]);
\r
537 $n = $n->multiply($primes[$i]);
\r
539 $temp = $primes[$i]->subtract($this->one);
\r
541 // textbook RSA implementations use Euler's totient function instead of the least common multiple.
\r
542 // see http://en.wikipedia.org/wiki/Euler%27s_totient_function
\r
543 $lcm['top'] = $lcm['top']->multiply($temp);
\r
544 $lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp);
\r
546 $exponents[$i] = $e->modInverse($temp);
\r
549 list($lcm) = $lcm['top']->divide($lcm['bottom']);
\r
550 $gcd = $lcm->gcd($e);
\r
552 } while (!$gcd->equals($this->one));
\r
554 $d = $e->modInverse($lcm);
\r
556 $coefficients[2] = $primes[2]->modInverse($primes[1]);
\r
558 // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.2>:
\r
559 // RSAPrivateKey ::= SEQUENCE {
\r
560 // version Version,
\r
561 // modulus INTEGER, -- n
\r
562 // publicExponent INTEGER, -- e
\r
563 // privateExponent INTEGER, -- d
\r
564 // prime1 INTEGER, -- p
\r
565 // prime2 INTEGER, -- q
\r
566 // exponent1 INTEGER, -- d mod (p-1)
\r
567 // exponent2 INTEGER, -- d mod (q-1)
\r
568 // coefficient INTEGER, -- (inverse of q) mod p
\r
569 // otherPrimeInfos OtherPrimeInfos OPTIONAL
\r
573 'privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients),
\r
574 'publickey' => $this->_convertPublicKey($n, $e),
\r
575 'partialkey' => false
\r
580 * Convert a private key to the appropriate format.
\r
583 * @see setPrivateKeyFormat()
\r
584 * @param String $RSAPrivateKey
\r
587 function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients)
\r
589 $num_primes = count($primes);
\r
591 'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi
\r
592 'modulus' => $n->toBytes(true),
\r
593 'publicExponent' => $e->toBytes(true),
\r
594 'privateExponent' => $d->toBytes(true),
\r
595 'prime1' => $primes[1]->toBytes(true),
\r
596 'prime2' => $primes[2]->toBytes(true),
\r
597 'exponent1' => $exponents[1]->toBytes(true),
\r
598 'exponent2' => $exponents[2]->toBytes(true),
\r
599 'coefficient' => $coefficients[2]->toBytes(true)
\r
602 // if the format in question does not support multi-prime rsa and multi-prime rsa was used,
\r
603 // call _convertPublicKey() instead.
\r
604 switch ($this->privateKeyFormat) {
\r
605 default: // eg. CRYPT_RSA_PRIVATE_FORMAT_PKCS1
\r
606 $components = array();
\r
607 foreach ($raw as $name => $value) {
\r
608 $components[$name] = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value);
\r
611 $RSAPrivateKey = implode('', $components);
\r
613 if ($num_primes > 2) {
\r
614 $OtherPrimeInfos = '';
\r
615 for ($i = 3; $i <= $num_primes; $i++) {
\r
616 // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
\r
618 // OtherPrimeInfo ::= SEQUENCE {
\r
619 // prime INTEGER, -- ri
\r
620 // exponent INTEGER, -- di
\r
621 // coefficient INTEGER -- ti
\r
623 $OtherPrimeInfo = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true));
\r
624 $OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true));
\r
625 $OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true));
\r
626 $OtherPrimeInfos.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo);
\r
628 $RSAPrivateKey.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos);
\r
631 $RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
\r
633 if (!empty($this->password)) {
\r
634 $iv = $this->_random(8);
\r
635 $symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key
\r
636 $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8);
\r
637 if (!class_exists('Crypt_TripleDES')) {
\r
638 require_once('Crypt/TripleDES.php');
\r
640 $des = new Crypt_TripleDES();
\r
641 $des->setKey($symkey);
\r
643 $iv = strtoupper(bin2hex($iv));
\r
644 $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
\r
645 "Proc-Type: 4,ENCRYPTED\r\n" .
\r
646 "DEK-Info: DES-EDE3-CBC,$iv\r\n" .
\r
648 chunk_split(base64_encode($des->encrypt($RSAPrivateKey))) .
\r
649 '-----END RSA PRIVATE KEY-----';
\r
651 $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
\r
652 chunk_split(base64_encode($RSAPrivateKey)) .
\r
653 '-----END RSA PRIVATE KEY-----';
\r
656 return $RSAPrivateKey;
\r
661 * Convert a public key to the appropriate format
\r
664 * @see setPublicKeyFormat()
\r
665 * @param String $RSAPrivateKey
\r
668 function _convertPublicKey($n, $e)
\r
670 $modulus = $n->toBytes(true);
\r
671 $publicExponent = $e->toBytes(true);
\r
673 switch ($this->publicKeyFormat) {
\r
674 case CRYPT_RSA_PUBLIC_FORMAT_RAW:
\r
675 return array('e' => $e->copy(), 'n' => $n->copy());
\r
676 case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
\r
677 // from <http://tools.ietf.org/html/rfc4253#page-15>:
\r
678 // string "ssh-rsa"
\r
681 $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);
\r
682 $RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . CRYPT_RSA_COMMENT;
\r
684 return $RSAPublicKey;
\r
685 default: // eg. CRYPT_RSA_PUBLIC_FORMAT_PKCS1
\r
686 // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>:
\r
687 // RSAPublicKey ::= SEQUENCE {
\r
688 // modulus INTEGER, -- n
\r
689 // publicExponent INTEGER -- e
\r
691 $components = array(
\r
692 'modulus' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus),
\r
693 'publicExponent' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent)
\r
696 $RSAPublicKey = pack('Ca*a*a*',
\r
697 CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])),
\r
698 $components['modulus'], $components['publicExponent']
\r
701 $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
\r
702 chunk_split(base64_encode($RSAPublicKey)) .
\r
703 '-----END PUBLIC KEY-----';
\r
705 return $RSAPublicKey;
\r
710 * Break a public or private key down into its constituant components
\r
713 * @see _convertPublicKey()
\r
714 * @see _convertPrivateKey()
\r
715 * @param String $key
\r
716 * @param Integer $type
\r
719 function _parseKey($key, $type)
\r
722 case CRYPT_RSA_PUBLIC_FORMAT_RAW:
\r
723 if (!is_array($key)) {
\r
726 $components = array();
\r
728 case isset($key['e']):
\r
729 $components['publicExponent'] = $key['e']->copy();
\r
731 case isset($key['exponent']):
\r
732 $components['publicExponent'] = $key['exponent']->copy();
\r
734 case isset($key['publicExponent']):
\r
735 $components['publicExponent'] = $key['publicExponent']->copy();
\r
737 case isset($key[0]):
\r
738 $components['publicExponent'] = $key[0]->copy();
\r
741 case isset($key['n']):
\r
742 $components['modulus'] = $key['n']->copy();
\r
744 case isset($key['modulo']):
\r
745 $components['modulus'] = $key['modulo']->copy();
\r
747 case isset($key['modulus']):
\r
748 $components['modulus'] = $key['modulus']->copy();
\r
750 case isset($key[1]):
\r
751 $components['modulus'] = $key[1]->copy();
\r
753 return $components;
\r
754 case CRYPT_RSA_PRIVATE_FORMAT_PKCS1:
\r
755 case CRYPT_RSA_PUBLIC_FORMAT_PKCS1:
\r
756 /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
\r
757 "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
\r
758 protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding
\r
759 two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here:
\r
761 http://tools.ietf.org/html/rfc1421#section-4.6.1.1
\r
762 http://tools.ietf.org/html/rfc1421#section-4.6.1.3
\r
764 DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
\r
765 DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
\r
766 function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
\r
767 own implementation. ie. the implementation *is* the standard and any bugs that may exist in that
\r
768 implementation are part of the standard, as well.
\r
770 * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */
\r
771 if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
\r
772 $iv = pack('H*', trim($matches[2]));
\r
773 $symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key
\r
774 $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8);
\r
775 $ciphertext = preg_replace('#.+(\r|\n|\r\n)\1|[\r\n]|-.+-#s', '', $key);
\r
776 $ciphertext = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $ciphertext) ? base64_decode($ciphertext) : false;
\r
777 if ($ciphertext === false) {
\r
778 $ciphertext = $key;
\r
780 switch ($matches[1]) {
\r
781 case 'DES-EDE3-CBC':
\r
782 if (!class_exists('Crypt_TripleDES')) {
\r
783 require_once('Crypt/TripleDES.php');
\r
785 $crypto = new Crypt_TripleDES();
\r
788 if (!class_exists('Crypt_DES')) {
\r
789 require_once('Crypt/DES.php');
\r
791 $crypto = new Crypt_DES();
\r
796 $crypto->setKey($symkey);
\r
797 $crypto->setIV($iv);
\r
798 $decoded = $crypto->decrypt($ciphertext);
\r
800 $decoded = preg_replace('#-.+-|[\r\n]#', '', $key);
\r
801 $decoded = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $decoded) ? base64_decode($decoded) : false;
\r
804 if ($decoded !== false) {
\r
808 $components = array();
\r
810 if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
\r
813 if ($this->_decodeLength($key) != strlen($key)) {
\r
817 $tag = ord($this->_string_shift($key));
\r
818 if ($tag == CRYPT_RSA_ASN1_SEQUENCE) {
\r
819 /* intended for keys for which OpenSSL's asn1parse returns the following:
\r
821 0:d=0 hl=4 l= 290 cons: SEQUENCE
\r
822 4:d=1 hl=2 l= 13 cons: SEQUENCE
\r
823 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
\r
824 17:d=2 hl=2 l= 0 prim: NULL
\r
825 19:d=1 hl=4 l= 271 prim: BIT STRING */
\r
826 $this->_string_shift($key, $this->_decodeLength($key));
\r
827 $this->_string_shift($key); // skip over the BIT STRING tag
\r
828 $this->_decodeLength($key); // skip over the BIT STRING length
\r
829 // "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of
\r
830 // unused bits in teh final subsequent octet. The number shall be in the range zero to seven."
\r
831 // -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2)
\r
832 $this->_string_shift($key);
\r
833 if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
\r
836 if ($this->_decodeLength($key) != strlen($key)) {
\r
839 $tag = ord($this->_string_shift($key));
\r
841 if ($tag != CRYPT_RSA_ASN1_INTEGER) {
\r
845 $length = $this->_decodeLength($key);
\r
846 $temp = $this->_string_shift($key, $length);
\r
847 if (strlen($temp) != 1 || ord($temp) > 2) {
\r
848 $components['modulus'] = new Math_BigInteger($temp, -256);
\r
849 $this->_string_shift($key); // skip over CRYPT_RSA_ASN1_INTEGER
\r
850 $length = $this->_decodeLength($key);
\r
851 $components[$type == CRYPT_RSA_PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), -256);
\r
853 return $components;
\r
855 if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_INTEGER) {
\r
858 $length = $this->_decodeLength($key);
\r
859 $components['modulus'] = new Math_BigInteger($this->_string_shift($key, $length), -256);
\r
860 $this->_string_shift($key);
\r
861 $length = $this->_decodeLength($key);
\r
862 $components['publicExponent'] = new Math_BigInteger($this->_string_shift($key, $length), -256);
\r
863 $this->_string_shift($key);
\r
864 $length = $this->_decodeLength($key);
\r
865 $components['privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), -256);
\r
866 $this->_string_shift($key);
\r
867 $length = $this->_decodeLength($key);
\r
868 $components['primes'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), -256));
\r
869 $this->_string_shift($key);
\r
870 $length = $this->_decodeLength($key);
\r
871 $components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), -256);
\r
872 $this->_string_shift($key);
\r
873 $length = $this->_decodeLength($key);
\r
874 $components['exponents'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), -256));
\r
875 $this->_string_shift($key);
\r
876 $length = $this->_decodeLength($key);
\r
877 $components['exponents'][] = new Math_BigInteger($this->_string_shift($key, $length), -256);
\r
878 $this->_string_shift($key);
\r
879 $length = $this->_decodeLength($key);
\r
880 $components['coefficients'] = array(2 => new Math_BigInteger($this->_string_shift($key, $length), -256));
\r
882 if (!empty($key)) {
\r
883 if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
\r
886 $this->_decodeLength($key);
\r
887 while (!empty($key)) {
\r
888 if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
\r
891 $this->_decodeLength($key);
\r
892 $key = substr($key, 1);
\r
893 $length = $this->_decodeLength($key);
\r
894 $components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), -256);
\r
895 $this->_string_shift($key);
\r
896 $length = $this->_decodeLength($key);
\r
897 $components['exponents'][] = new Math_BigInteger($this->_string_shift($key, $length), -256);
\r
898 $this->_string_shift($key);
\r
899 $length = $this->_decodeLength($key);
\r
900 $components['coefficients'][] = new Math_BigInteger($this->_string_shift($key, $length), -256);
\r
904 return $components;
\r
905 case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
\r
906 $key = base64_decode(preg_replace('#^ssh-rsa | .+$#', '', $key));
\r
907 if ($key === false) {
\r
911 $cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa";
\r
913 extract(unpack('Nlength', $this->_string_shift($key, 4)));
\r
914 $publicExponent = new Math_BigInteger($this->_string_shift($key, $length), -256);
\r
915 extract(unpack('Nlength', $this->_string_shift($key, 4)));
\r
916 $modulus = new Math_BigInteger($this->_string_shift($key, $length), -256);
\r
918 if ($cleanup && strlen($key)) {
\r
919 extract(unpack('Nlength', $this->_string_shift($key, 4)));
\r
921 'modulus' => new Math_BigInteger($this->_string_shift($key, $length), -256),
\r
922 'publicExponent' => $modulus
\r
926 'modulus' => $modulus,
\r
927 'publicExponent' => $publicExponent
\r
934 * Loads a public or private key
\r
936 * Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed)
\r
939 * @param String $key
\r
940 * @param Integer $type optional
\r
942 function loadKey($key, $type = CRYPT_RSA_PRIVATE_FORMAT_PKCS1)
\r
944 $components = $this->_parseKey($key, $type);
\r
945 if ($components === false) {
\r
949 $this->modulus = $components['modulus'];
\r
950 $this->k = strlen($this->modulus->toBytes());
\r
951 $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent'];
\r
952 if (isset($components['primes'])) {
\r
953 $this->primes = $components['primes'];
\r
954 $this->exponents = $components['exponents'];
\r
955 $this->coefficients = $components['coefficients'];
\r
956 $this->publicExponent = $components['publicExponent'];
\r
958 $this->primes = array();
\r
959 $this->exponents = array();
\r
960 $this->coefficients = array();
\r
961 $this->publicExponent = false;
\r
968 * Sets the password
\r
970 * Private keys can be encrypted with a password. To unset the password, pass in the empty string or false.
\r
971 * Or rather, pass in $password such that empty($password) is true.
\r
976 * @param String $password
\r
978 function setPassword($password)
\r
980 $this->password = $password;
\r
984 * Defines the public key
\r
986 * Some private key formats define the public exponent and some don't. Those that don't define it are problematic when
\r
987 * used in certain contexts. For example, in SSH-2, RSA authentication works by sending the public key along with a
\r
988 * message signed by the private key to the server. The SSH-2 server looks the public key up in an index of public keys
\r
989 * and if it's present then proceeds to verify the signature. Problem is, if your private key doesn't include the public
\r
990 * exponent this won't work unless you manually add the public exponent.
\r
992 * Do note that when a new key is loaded the index will be cleared.
\r
994 * Returns true on success, false on failure
\r
996 * @see getPublicKey()
\r
998 * @param String $key
\r
999 * @param Integer $type optional
\r
1002 function setPublicKey($key, $type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1)
\r
1004 $components = $this->_parseKey($key, $type);
\r
1005 if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) {
\r
1008 $this->publicExponent = $components['publicExponent'];
\r
1012 * Returns the public key
\r
1014 * The public key is only returned under two circumstances - if the private key had the public key embedded within it
\r
1015 * or if the public key was set via setPublicKey(). If the currently loaded key is supposed to be the public key this
\r
1016 * function won't return it since this library, for the most part, doesn't distinguish between public and private keys.
\r
1018 * @see getPublicKey()
\r
1020 * @param String $key
\r
1021 * @param Integer $type optional
\r
1023 function getPublicKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1)
\r
1025 if (empty($this->modulus) || empty($this->publicExponent)) {
\r
1029 $oldFormat = $this->publicKeyFormat;
\r
1030 $this->publicKeyFormat = $type;
\r
1031 $temp = $this->_convertPublicKey($this->modulus, $this->publicExponent);
\r
1032 $this->publicKeyFormat = $oldFormat;
\r
1037 * Generates the smallest and largest numbers requiring $bits bits
\r
1040 * @param Integer $bits
\r
1043 function _generateMinMax($bits)
\r
1045 $bytes = $bits >> 3;
\r
1046 $min = str_repeat(chr(0), $bytes);
\r
1047 $max = str_repeat(chr(0xFF), $bytes);
\r
1050 $min = chr(1 << ($msb - 1)) . $min;
\r
1051 $max = chr((1 << $msb) - 1) . $max;
\r
1053 $min[0] = chr(0x80);
\r
1057 'min' => new Math_BigInteger($min, 256),
\r
1058 'max' => new Math_BigInteger($max, 256)
\r
1063 * DER-decode the length
\r
1065 * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
\r
1066 * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 § 8.1.3} for more information.
\r
1069 * @param String $string
\r
1072 function _decodeLength(&$string)
\r
1074 $length = ord($this->_string_shift($string));
\r
1075 if ( $length & 0x80 ) { // definite length, long form
\r
1077 $temp = $this->_string_shift($string, $length);
\r
1078 list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4));
\r
1084 * DER-encode the length
\r
1086 * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
\r
1087 * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 § 8.1.3} for more information.
\r
1090 * @param Integer $length
\r
1093 function _encodeLength($length)
\r
1095 if ($length <= 0x7F) {
\r
1096 return chr($length);
\r
1099 $temp = ltrim(pack('N', $length), chr(0));
\r
1100 return pack('Ca*', 0x80 | strlen($temp), $temp);
\r
1106 * Inspired by array_shift
\r
1108 * @param String $string
\r
1109 * @param optional Integer $index
\r
1113 function _string_shift(&$string, $index = 1)
\r
1115 $substr = substr($string, 0, $index);
\r
1116 $string = substr($string, $index);
\r
1121 * Determines the private key format
\r
1123 * @see createKey()
\r
1125 * @param Integer $format
\r
1127 function setPrivateKeyFormat($format)
\r
1129 $this->privateKeyFormat = $format;
\r
1133 * Determines the public key format
\r
1135 * @see createKey()
\r
1137 * @param Integer $format
\r
1139 function setPublicKeyFormat($format)
\r
1141 $this->publicKeyFormat = $format;
\r
1145 * Determines which hashing function should be used
\r
1147 * Used with signature production / verification and (if the encryption mode is CRYPT_RSA_ENCRYPTION_OAEP) encryption and
\r
1148 * decryption. If $hash isn't supported, sha1 is used.
\r
1151 * @param String $hash
\r
1153 function setHash($hash)
\r
1155 // Crypt_Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example.
\r
1163 $this->hash = new Crypt_Hash($hash);
\r
1164 $this->hashName = $hash;
\r
1167 $this->hash = new Crypt_Hash('sha1');
\r
1168 $this->hashName = 'sha1';
\r
1170 $this->hLen = $this->hash->getLength();
\r
1174 * Determines which hashing function should be used for the mask generation function
\r
1176 * The mask generation function is used by CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_SIGNATURE_PSS and although it's
\r
1177 * best if Hash and MGFHash are set to the same thing this is not a requirement.
\r
1180 * @param String $hash
\r
1182 function setMGFHash($hash)
\r
1184 // Crypt_Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example.
\r
1192 $this->mgfHash = new Crypt_Hash($hash);
\r
1195 $this->mgfHash = new Crypt_Hash('sha1');
\r
1197 $this->mgfHLen = $this->mgfHash->getLength();
\r
1201 * Determines the salt length
\r
1203 * To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}:
\r
1205 * Typical salt lengths in octets are hLen (the length of the output
\r
1206 * of the hash function Hash) and 0.
\r
1209 * @param Integer $format
\r
1211 function setSaltLength($sLen)
\r
1213 $this->sLen = $sLen;
\r
1217 * Generates a random string x bytes long
\r
1220 * @param Integer $bytes
\r
1221 * @param optional Integer $nonzero
\r
1224 function _random($bytes, $nonzero = false)
\r
1228 for ($i = 0; $i < $bytes; $i++) {
\r
1229 $temp.= chr(crypt_random(1, 255));
\r
1232 $ints = ($bytes + 1) >> 2;
\r
1233 for ($i = 0; $i < $ints; $i++) {
\r
1234 $temp.= pack('N', crypt_random());
\r
1236 $temp = substr($temp, 0, $bytes);
\r
1242 * Integer-to-Octet-String primitive
\r
1244 * See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}.
\r
1247 * @param Math_BigInteger $x
\r
1248 * @param Integer $xLen
\r
1251 function _i2osp($x, $xLen)
\r
1253 $x = $x->toBytes();
\r
1254 if (strlen($x) > $xLen) {
\r
1255 user_error('Integer too large', E_USER_NOTICE);
\r
1258 return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
\r
1262 * Octet-String-to-Integer primitive
\r
1264 * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}.
\r
1267 * @param String $x
\r
1268 * @return Math_BigInteger
\r
1270 function _os2ip($x)
\r
1272 return new Math_BigInteger($x, 256);
\r
1276 * Exponentiate with or without Chinese Remainder Theorem
\r
1278 * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.2}.
\r
1281 * @param Math_BigInteger $x
\r
1282 * @return Math_BigInteger
\r
1284 function _exponentiate($x)
\r
1286 if (empty($this->primes) || empty($this->coefficients) || empty($this->exponents)) {
\r
1287 return $x->modPow($this->exponent, $this->modulus);
\r
1290 $num_primes = count($this->primes);
\r
1292 if (defined('CRYPT_RSA_DISABLE_BLINDING')) {
\r
1294 1 => $x->modPow($this->exponents[1], $this->primes[1]),
\r
1295 2 => $x->modPow($this->exponents[2], $this->primes[2])
\r
1297 $h = $m_i[1]->subtract($m_i[2]);
\r
1298 $h = $h->multiply($this->coefficients[2]);
\r
1299 list(, $h) = $h->divide($this->primes[1]);
\r
1300 $m = $m_i[2]->add($h->multiply($this->primes[2]));
\r
1302 $r = $this->primes[1];
\r
1303 for ($i = 3; $i <= $num_primes; $i++) {
\r
1304 $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]);
\r
1306 $r = $r->multiply($this->primes[$i - 1]);
\r
1308 $h = $m_i->subtract($m);
\r
1309 $h = $h->multiply($this->coefficients[$i]);
\r
1310 list(, $h) = $h->divide($this->primes[$i]);
\r
1312 $m = $m->add($r->multiply($h));
\r
1315 $smallest = $this->primes[1];
\r
1316 for ($i = 2; $i <= $num_primes; $i++) {
\r
1317 if ($smallest->compare($this->primes[$i]) > 0) {
\r
1318 $smallest = $this->primes[$i];
\r
1322 $one = new Math_BigInteger(1);
\r
1323 $one->setRandomGenerator('crypt_random');
\r
1325 $r = $one->random($one, $smallest->subtract($one));
\r
1328 1 => $this->_blind($x, $r, 1),
\r
1329 2 => $this->_blind($x, $r, 2)
\r
1331 $h = $m_i[1]->subtract($m_i[2]);
\r
1332 $h = $h->multiply($this->coefficients[2]);
\r
1333 list(, $h) = $h->divide($this->primes[1]);
\r
1334 $m = $m_i[2]->add($h->multiply($this->primes[2]));
\r
1336 $r = $this->primes[1];
\r
1337 for ($i = 3; $i <= $num_primes; $i++) {
\r
1338 $m_i = $this->_blind($x, $r, $i);
\r
1340 $r = $r->multiply($this->primes[$i - 1]);
\r
1342 $h = $m_i->subtract($m);
\r
1343 $h = $h->multiply($this->coefficients[$i]);
\r
1344 list(, $h) = $h->divide($this->primes[$i]);
\r
1346 $m = $m->add($r->multiply($h));
\r
1354 * Performs RSA Blinding
\r
1356 * Protects against timing attacks by employing RSA Blinding.
\r
1357 * Returns $x->modPow($this->exponents[$i], $this->primes[$i])
\r
1360 * @param Math_BigInteger $x
\r
1361 * @param Math_BigInteger $r
\r
1362 * @param Integer $i
\r
1363 * @return Math_BigInteger
\r
1365 function _blind($x, $r, $i)
\r
1367 $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i]));
\r
1369 $x = $x->modPow($this->exponents[$i], $this->primes[$i]);
\r
1371 $r = $r->modInverse($this->primes[$i]);
\r
1372 $x = $x->multiply($r);
\r
1373 list(, $x) = $x->divide($this->primes[$i]);
\r
1381 * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}.
\r
1384 * @param Math_BigInteger $m
\r
1385 * @return Math_BigInteger
\r
1387 function _rsaep($m)
\r
1389 if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
\r
1390 user_error('Message representative out of range', E_USER_NOTICE);
\r
1393 return $this->_exponentiate($m);
\r
1399 * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}.
\r
1402 * @param Math_BigInteger $c
\r
1403 * @return Math_BigInteger
\r
1405 function _rsadp($c)
\r
1407 if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) {
\r
1408 user_error('Ciphertext representative out of range', E_USER_NOTICE);
\r
1411 return $this->_exponentiate($c);
\r
1417 * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}.
\r
1420 * @param Math_BigInteger $m
\r
1421 * @return Math_BigInteger
\r
1423 function _rsasp1($m)
\r
1425 if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
\r
1426 user_error('Message representative out of range', E_USER_NOTICE);
\r
1429 return $this->_exponentiate($m);
\r
1435 * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}.
\r
1438 * @param Math_BigInteger $s
\r
1439 * @return Math_BigInteger
\r
1441 function _rsavp1($s)
\r
1443 if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) {
\r
1444 user_error('Signature representative out of range', E_USER_NOTICE);
\r
1447 return $this->_exponentiate($s);
\r
1453 * See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}.
\r
1456 * @param String $mgfSeed
\r
1457 * @param Integer $mgfLen
\r
1460 function _mgf1($mgfSeed, $maskLen)
\r
1462 // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output.
\r
1465 $count = ceil($maskLen / $this->mgfHLen);
\r
1466 for ($i = 0; $i < $count; $i++) {
\r
1467 $c = pack('N', $i);
\r
1468 $t.= $this->mgfHash->hash($mgfSeed . $c);
\r
1471 return substr($t, 0, $maskLen);
\r
1475 * RSAES-OAEP-ENCRYPT
\r
1477 * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and
\r
1478 * {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}.
\r
1481 * @param String $m
\r
1482 * @param String $l
\r
1485 function _rsaes_oaep_encrypt($m, $l = '')
\r
1487 $mLen = strlen($m);
\r
1489 // Length checking
\r
1491 // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
\r
1494 if ($mLen > $this->k - 2 * $this->hLen - 2) {
\r
1495 user_error('Message too long', E_USER_NOTICE);
\r
1499 // EME-OAEP encoding
\r
1501 $lHash = $this->hash->hash($l);
\r
1502 $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2);
\r
1503 $db = $lHash . $ps . chr(1) . $m;
\r
1504 $seed = $this->_random($this->hLen);
\r
1505 $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
\r
1506 $maskedDB = $db ^ $dbMask;
\r
1507 $seedMask = $this->_mgf1($maskedDB, $this->hLen);
\r
1508 $maskedSeed = $seed ^ $seedMask;
\r
1509 $em = chr(0) . $maskedSeed . $maskedDB;
\r
1513 $m = $this->_os2ip($em);
\r
1514 $c = $this->_rsaep($m);
\r
1515 $c = $this->_i2osp($c, $this->k);
\r
1517 // Output the ciphertext C
\r
1523 * RSAES-OAEP-DECRYPT
\r
1525 * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}. The fact that the error
\r
1526 * messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2:
\r
1528 * Note. Care must be taken to ensure that an opponent cannot
\r
1529 * distinguish the different error conditions in Step 3.g, whether by
\r
1530 * error message or timing, or, more generally, learn partial
\r
1531 * information about the encoded message EM. Otherwise an opponent may
\r
1532 * be able to obtain useful information about the decryption of the
\r
1533 * ciphertext C, leading to a chosen-ciphertext attack such as the one
\r
1534 * observed by Manger [36].
\r
1536 * As for $l... to quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}:
\r
1538 * Both the encryption and the decryption operations of RSAES-OAEP take
\r
1539 * the value of a label L as input. In this version of PKCS #1, L is
\r
1540 * the empty string; other uses of the label are outside the scope of
\r
1544 * @param String $c
\r
1545 * @param String $l
\r
1548 function _rsaes_oaep_decrypt($c, $l = '')
\r
1550 // Length checking
\r
1552 // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
\r
1555 if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) {
\r
1556 user_error('Decryption error', E_USER_NOTICE);
\r
1562 $c = $this->_os2ip($c);
\r
1563 $m = $this->_rsadp($c);
\r
1564 if ($m === false) {
\r
1565 user_error('Decryption error', E_USER_NOTICE);
\r
1568 $em = $this->_i2osp($m, $this->k);
\r
1570 // EME-OAEP decoding
\r
1572 $lHash = $this->hash->hash($l);
\r
1574 $maskedSeed = substr($em, 1, $this->hLen);
\r
1575 $maskedDB = substr($em, $this->hLen + 1);
\r
1576 $seedMask = $this->_mgf1($maskedDB, $this->hLen);
\r
1577 $seed = $maskedSeed ^ $seedMask;
\r
1578 $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
\r
1579 $db = $maskedDB ^ $dbMask;
\r
1580 $lHash2 = substr($db, 0, $this->hLen);
\r
1581 $m = substr($db, $this->hLen);
\r
1582 if ($lHash != $lHash2) {
\r
1583 user_error('Decryption error', E_USER_NOTICE);
\r
1586 $m = ltrim($m, chr(0));
\r
1587 if (ord($m[0]) != 1) {
\r
1588 user_error('Decryption error', E_USER_NOTICE);
\r
1592 // Output the message M
\r
1594 return substr($m, 1);
\r
1598 * RSAES-PKCS1-V1_5-ENCRYPT
\r
1600 * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}.
\r
1603 * @param String $m
\r
1606 function _rsaes_pkcs1_v1_5_encrypt($m)
\r
1608 $mLen = strlen($m);
\r
1610 // Length checking
\r
1612 if ($mLen > $this->k - 11) {
\r
1613 user_error('Message too long', E_USER_NOTICE);
\r
1617 // EME-PKCS1-v1_5 encoding
\r
1619 $ps = $this->_random($this->k - $mLen - 3, true);
\r
1620 $em = chr(0) . chr(2) . $ps . chr(0) . $m;
\r
1623 $m = $this->_os2ip($em);
\r
1624 $c = $this->_rsaep($m);
\r
1625 $c = $this->_i2osp($c, $this->k);
\r
1627 // Output the ciphertext C
\r
1633 * RSAES-PKCS1-V1_5-DECRYPT
\r
1635 * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}.
\r
1637 * For compatability purposes, this function departs slightly from the description given in RFC3447.
\r
1638 * The reason being that RFC2313#section-8.1 (PKCS#1 v1.5) states that ciphertext's encrypted by the
\r
1639 * private key should have the second byte set to either 0 or 1 and that ciphertext's encrypted by the
\r
1640 * public key should have the second byte set to 2. In RFC3447 (PKCS#1 v2.1), the second byte is supposed
\r
1641 * to be 2 regardless of which key is used. for compatability purposes, we'll just check to make sure the
\r
1642 * second byte is 2 or less. If it is, we'll accept the decrypted string as valid.
\r
1644 * As a consequence of this, a private key encrypted ciphertext produced with Crypt_RSA may not decrypt
\r
1645 * with a strictly PKCS#1 v1.5 compliant RSA implementation. Public key encrypted ciphertext's should but
\r
1646 * not private key encrypted ciphertext's.
\r
1649 * @param String $c
\r
1652 function _rsaes_pkcs1_v1_5_decrypt($c)
\r
1654 // Length checking
\r
1656 if (strlen($c) != $this->k) { // or if k < 11
\r
1657 user_error('Decryption error', E_USER_NOTICE);
\r
1663 $c = $this->_os2ip($c);
\r
1664 $m = $this->_rsadp($c);
\r
1665 if ($m === false) {
\r
1666 user_error('Decryption error', E_USER_NOTICE);
\r
1669 $em = $this->_i2osp($m, $this->k);
\r
1671 // EME-PKCS1-v1_5 decoding
\r
1673 if (ord($em[0]) != 0 || ord($em[1]) > 2) {
\r
1674 user_error('Decryption error', E_USER_NOTICE);
\r
1678 $ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
\r
1679 $m = substr($em, strlen($ps) + 3);
\r
1681 if (strlen($ps) < 8) {
\r
1682 user_error('Decryption error', E_USER_NOTICE);
\r
1694 * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}.
\r
1697 * @param String $m
\r
1698 * @param Integer $emBits
\r
1700 function _emsa_pss_encode($m, $emBits)
\r
1702 // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
\r
1705 $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
\r
1706 $sLen = $this->sLen == false ? $this->hLen : $this->sLen;
\r
1708 $mHash = $this->hash->hash($m);
\r
1709 if ($emLen < $this->hLen + $sLen + 2) {
\r
1710 user_error('Encoding error', E_USER_NOTICE);
\r
1714 $salt = $this->_random($sLen);
\r
1715 $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
\r
1716 $h = $this->hash->hash($m2);
\r
1717 $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
\r
1718 $db = $ps . chr(1) . $salt;
\r
1719 $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
\r
1720 $maskedDB = $db ^ $dbMask;
\r
1721 $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0];
\r
1722 $em = $maskedDB . $h . chr(0xBC);
\r
1730 * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}.
\r
1733 * @param String $m
\r
1734 * @param String $em
\r
1735 * @param Integer $emBits
\r
1738 function _emsa_pss_verify($m, $em, $emBits)
\r
1740 // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
\r
1743 $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8);
\r
1744 $sLen = $this->sLen == false ? $this->hLen : $this->sLen;
\r
1746 $mHash = $this->hash->hash($m);
\r
1747 if ($emLen < $this->hLen + $sLen + 2) {
\r
1751 if ($em[strlen($em) - 1] != chr(0xBC)) {
\r
1755 $maskedDB = substr($em, 0, $em - $this->hLen - 1);
\r
1756 $h = substr($em, $em - $this->hLen - 1, $this->hLen);
\r
1757 $temp = chr(0xFF << ($emBits & 7));
\r
1758 if ((~$maskedDB[0] & $temp) != $temp) {
\r
1761 $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
\r
1762 $db = $maskedDB ^ $dbMask;
\r
1763 $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
\r
1764 $temp = $emLen - $this->hLen - $sLen - 2;
\r
1765 if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
\r
1768 $salt = substr($db, $temp + 1); // should be $sLen long
\r
1769 $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
\r
1770 $h2 = $this->hash->hash($m2);
\r
1777 * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}.
\r
1780 * @param String $m
\r
1783 function _rsassa_pss_sign($m)
\r
1785 // EMSA-PSS encoding
\r
1787 $em = $this->_emsa_pss_encode($m, 8 * $this->k - 1);
\r
1791 $m = $this->_os2ip($em);
\r
1792 $s = $this->_rsasp1($m);
\r
1793 $s = $this->_i2osp($s, $this->k);
\r
1795 // Output the signature S
\r
1801 * RSASSA-PSS-VERIFY
\r
1803 * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}.
\r
1806 * @param String $m
\r
1807 * @param String $s
\r
1810 function _rsassa_pss_verify($m, $s)
\r
1812 // Length checking
\r
1814 if (strlen($s) != $this->k) {
\r
1815 user_error('Invalid signature', E_USER_NOTICE);
\r
1819 // RSA verification
\r
1821 $modBits = 8 * $this->k;
\r
1823 $s2 = $this->_os2ip($s);
\r
1824 $m2 = $this->_rsavp1($s2);
\r
1825 if ($m2 === false) {
\r
1826 user_error('Invalid signature', E_USER_NOTICE);
\r
1829 $em = $this->_i2osp($m2, $modBits >> 3);
\r
1830 if ($em === false) {
\r
1831 user_error('Invalid signature', E_USER_NOTICE);
\r
1835 // EMSA-PSS verification
\r
1837 return $this->_emsa_pss_verify($m, $em, $modBits - 1);
\r
1841 * EMSA-PKCS1-V1_5-ENCODE
\r
1843 * See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}.
\r
1846 * @param String $m
\r
1847 * @param Integer $emLen
\r
1850 function _emsa_pkcs1_v1_5_encode($m, $emLen)
\r
1852 $h = $this->hash->hash($m);
\r
1853 if ($h === false) {
\r
1857 // see http://tools.ietf.org/html/rfc3447#page-43
\r
1858 switch ($this->hashName) {
\r
1860 $t = pack('H*', '3020300c06082a864886f70d020205000410');
\r
1863 $t = pack('H*', '3020300c06082a864886f70d020505000410');
\r
1866 $t = pack('H*', '3021300906052b0e03021a05000414');
\r
1869 $t = pack('H*', '3031300d060960864801650304020105000420');
\r
1872 $t = pack('H*', '3041300d060960864801650304020205000430');
\r
1875 $t = pack('H*', '3051300d060960864801650304020305000440');
\r
1878 $tLen = strlen($t);
\r
1880 if ($emLen < $tLen + 11) {
\r
1881 user_error('Intended encoded message length too short', E_USER_NOTICE);
\r
1885 $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
\r
1887 $em = "\0\1$ps\0$t";
\r
1893 * RSASSA-PKCS1-V1_5-SIGN
\r
1895 * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}.
\r
1898 * @param String $m
\r
1901 function _rsassa_pkcs1_v1_5_sign($m)
\r
1903 // EMSA-PKCS1-v1_5 encoding
\r
1905 $em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
\r
1906 if ($em === false) {
\r
1907 user_error('RSA modulus too short', E_USER_NOTICE);
\r
1913 $m = $this->_os2ip($em);
\r
1914 $s = $this->_rsasp1($m);
\r
1915 $s = $this->_i2osp($s, $this->k);
\r
1917 // Output the signature S
\r
1923 * RSASSA-PKCS1-V1_5-VERIFY
\r
1925 * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}.
\r
1928 * @param String $m
\r
1931 function _rsassa_pkcs1_v1_5_verify($m, $s)
\r
1933 // Length checking
\r
1935 if (strlen($s) != $this->k) {
\r
1936 user_error('Invalid signature', E_USER_NOTICE);
\r
1940 // RSA verification
\r
1942 $s = $this->_os2ip($s);
\r
1943 $m2 = $this->_rsavp1($s);
\r
1944 if ($m2 === false) {
\r
1945 user_error('Invalid signature', E_USER_NOTICE);
\r
1948 $em = $this->_i2osp($m2, $this->k);
\r
1949 if ($em === false) {
\r
1950 user_error('Invalid signature', E_USER_NOTICE);
\r
1954 // EMSA-PKCS1-v1_5 encoding
\r
1956 $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
\r
1957 if ($em2 === false) {
\r
1958 user_error('RSA modulus too short', E_USER_NOTICE);
\r
1964 return $em === $em2;
\r
1968 * Set Encryption Mode
\r
1970 * Valid values include CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1.
\r
1973 * @param Integer $mode
\r
1975 function setEncryptionMode($mode)
\r
1977 $this->encryptionMode = $mode;
\r
1981 * Set Signature Mode
\r
1983 * Valid values include CRYPT_RSA_SIGNATURE_PSS and CRYPT_RSA_SIGNATURE_PKCS1
\r
1986 * @param Integer $mode
\r
1988 function setSignatureMode($mode)
\r
1990 $this->signatureMode = $mode;
\r
1996 * Both CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1 both place limits on how long $plaintext can be.
\r
1997 * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
\r
1998 * be concatenated together.
\r
2002 * @param String $plaintext
\r
2005 function encrypt($plaintext)
\r
2007 switch ($this->encryptionMode) {
\r
2008 case CRYPT_RSA_ENCRYPTION_PKCS1:
\r
2009 $length = $this->k - 11;
\r
2010 if ($length <= 0) {
\r
2014 $plaintext = str_split($plaintext, $length);
\r
2016 foreach ($plaintext as $m) {
\r
2017 $ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m);
\r
2019 return $ciphertext;
\r
2020 //case CRYPT_RSA_ENCRYPTION_OAEP:
\r
2022 $length = $this->k - 2 * $this->hLen - 2;
\r
2023 if ($length <= 0) {
\r
2027 $plaintext = str_split($plaintext, $length);
\r
2029 foreach ($plaintext as $m) {
\r
2030 $ciphertext.= $this->_rsaes_oaep_encrypt($m);
\r
2032 return $ciphertext;
\r
2041 * @param String $plaintext
\r
2044 function decrypt($ciphertext)
\r
2046 if ($this->k <= 0) {
\r
2050 $ciphertext = str_split($ciphertext, $this->k);
\r
2053 switch ($this->encryptionMode) {
\r
2054 case CRYPT_RSA_ENCRYPTION_PKCS1:
\r
2055 $decrypt = '_rsaes_pkcs1_v1_5_decrypt';
\r
2057 //case CRYPT_RSA_ENCRYPTION_OAEP:
\r
2059 $decrypt = '_rsaes_oaep_decrypt';
\r
2062 foreach ($ciphertext as $c) {
\r
2063 $temp = $this->$decrypt($c);
\r
2064 if ($temp === false) {
\r
2067 $plaintext.= $temp;
\r
2070 return $plaintext;
\r
2074 * Create a signature
\r
2078 * @param String $message
\r
2081 function sign($message)
\r
2083 if (empty($this->modulus) || empty($this->exponent)) {
\r
2087 switch ($this->signatureMode) {
\r
2088 case CRYPT_RSA_SIGNATURE_PKCS1:
\r
2089 return $this->_rsassa_pkcs1_v1_5_sign($message);
\r
2090 //case CRYPT_RSA_SIGNATURE_PSS:
\r
2092 return $this->_rsassa_pss_sign($message);
\r
2097 * Verifies a signature
\r
2101 * @param String $message
\r
2102 * @param String $signature
\r
2105 function verify($message, $signature)
\r
2107 if (empty($this->modulus) || empty($this->exponent)) {
\r
2111 switch ($this->signatureMode) {
\r
2112 case CRYPT_RSA_SIGNATURE_PKCS1:
\r
2113 return $this->_rsassa_pkcs1_v1_5_verify($message, $signature);
\r
2114 //case CRYPT_RSA_SIGNATURE_PSS:
\r
2116 return $this->_rsassa_pss_verify($message, $signature);
\r