ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
RSA.php
Go to the documentation of this file.
1 <?php
2 
52 namespace phpseclib\Crypt;
53 
62 
70 class RSA
71 {
86  const ENCRYPTION_OAEP = 1;
93  const ENCRYPTION_PKCS1 = 2;
100  const ENCRYPTION_NONE = 3;
117  const SIGNATURE_PSS = 1;
124  const SIGNATURE_PKCS1 = 2;
134  const ASN1_INTEGER = 2;
138  const ASN1_BITSTRING = 3;
142  const ASN1_OCTETSTRING = 4;
146  const ASN1_OBJECT = 6;
150  const ASN1_SEQUENCE = 48;
160  const MODE_INTERNAL = 1;
166  const MODE_OPENSSL = 2;
212  const PUBLIC_FORMAT_RAW = 3;
229  const PUBLIC_FORMAT_XML = 5;
258  var $zero;
259 
266  var $one;
267 
274  var $privateKeyFormat = self::PRIVATE_FORMAT_PKCS1;
275 
282  var $publicKeyFormat = self::PUBLIC_FORMAT_PKCS8;
283 
290  var $modulus;
291 
298  var $k;
299 
307 
314  var $primes;
315 
323 
331 
339 
346  var $hash;
347 
354  var $hLen;
355 
362  var $sLen;
363 
370  var $mgfHash;
371 
378  var $mgfHLen;
379 
386  var $encryptionMode = self::ENCRYPTION_OAEP;
387 
394  var $signatureMode = self::SIGNATURE_PSS;
395 
402  var $publicExponent = false;
403 
410  var $password = false;
411 
422  var $components = array();
423 
434  var $current;
435 
445 
452  var $comment = 'phpseclib-generated-key';
453 
464  function __construct()
465  {
466  $this->configFile = dirname(__FILE__) . '/../openssl.cnf';
467 
468  if (!defined('CRYPT_RSA_MODE')) {
469  switch (true) {
470  // Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular,
471  // Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger
472  // can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either.
473  case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'):
474  define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
475  break;
476  case extension_loaded('openssl') && file_exists($this->configFile):
477  // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
478  ob_start();
479  @phpinfo();
480  $content = ob_get_contents();
481  ob_end_clean();
482 
483  preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches);
484 
485  $versions = array();
486  if (!empty($matches[1])) {
487  for ($i = 0; $i < count($matches[1]); $i++) {
488  $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i])));
489 
490  // Remove letter part in OpenSSL version
491  if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) {
492  $versions[$matches[1][$i]] = $fullVersion;
493  } else {
494  $versions[$matches[1][$i]] = $m[0];
495  }
496  }
497  }
498 
499  // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+
500  switch (true) {
501  case !isset($versions['Header']):
502  case !isset($versions['Library']):
503  case $versions['Header'] == $versions['Library']:
504  define('CRYPT_RSA_MODE', self::MODE_OPENSSL);
505  break;
506  default:
507  define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
508  define('MATH_BIGINTEGER_OPENSSL_DISABLE', true);
509  }
510  break;
511  default:
512  define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
513  }
514  }
515 
516  $this->zero = new BigInteger();
517  $this->one = new BigInteger(1);
518 
519  $this->hash = new Hash('sha1');
520  $this->hLen = $this->hash->getLength();
521  $this->hashName = 'sha1';
522  $this->mgfHash = new Hash('sha1');
523  $this->mgfHLen = $this->mgfHash->getLength();
524  }
525 
540  function createKey($bits = 1024, $timeout = false, $partial = array())
541  {
542  if (!defined('CRYPT_RSA_EXPONENT')) {
543  // http://en.wikipedia.org/wiki/65537_%28number%29
544  define('CRYPT_RSA_EXPONENT', '65537');
545  }
546  // per <http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number ought not result in primes smaller
547  // 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
548  // to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). at least if
549  // CRYPT_RSA_MODE is set to self::MODE_INTERNAL. if CRYPT_RSA_MODE is set to self::MODE_OPENSSL then
550  // CRYPT_RSA_SMALLEST_PRIME is ignored (ie. multi-prime RSA support is more intended as a way to speed up RSA key
551  // generation when there's a chance neither gmp nor OpenSSL are installed)
552  if (!defined('CRYPT_RSA_SMALLEST_PRIME')) {
553  define('CRYPT_RSA_SMALLEST_PRIME', 4096);
554  }
555 
556  // OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum
557  if (CRYPT_RSA_MODE == self::MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) {
558  $config = array();
559  if (isset($this->configFile)) {
560  $config['config'] = $this->configFile;
561  }
562  $rsa = openssl_pkey_new(array('private_key_bits' => $bits) + $config);
563  openssl_pkey_export($rsa, $privatekey, null, $config);
564  $publickey = openssl_pkey_get_details($rsa);
565  $publickey = $publickey['key'];
566 
567  $privatekey = call_user_func_array(array($this, '_convertPrivateKey'), array_values($this->_parseKey($privatekey, self::PRIVATE_FORMAT_PKCS1)));
568  $publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, self::PUBLIC_FORMAT_PKCS1)));
569 
570  // clear the buffer of error strings stemming from a minimalistic openssl.cnf
571  while (openssl_error_string() !== false) {
572  }
573 
574  return array(
575  'privatekey' => $privatekey,
576  'publickey' => $publickey,
577  'partialkey' => false
578  );
579  }
580 
581  static $e;
582  if (!isset($e)) {
583  $e = new BigInteger(CRYPT_RSA_EXPONENT);
584  }
585 
586  extract($this->_generateMinMax($bits));
587  $absoluteMin = $min;
588  $temp = $bits >> 1; // divide by two to see how many bits P and Q would be
589  if ($temp > CRYPT_RSA_SMALLEST_PRIME) {
590  $num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME);
591  $temp = CRYPT_RSA_SMALLEST_PRIME;
592  } else {
593  $num_primes = 2;
594  }
595  extract($this->_generateMinMax($temp + $bits % $temp));
596  $finalMax = $max;
597  extract($this->_generateMinMax($temp));
598 
599  $generator = new BigInteger();
600 
601  $n = $this->one->copy();
602  if (!empty($partial)) {
603  extract(unserialize($partial));
604  } else {
605  $exponents = $coefficients = $primes = array();
606  $lcm = array(
607  'top' => $this->one->copy(),
608  'bottom' => false
609  );
610  }
611 
612  $start = time();
613  $i0 = count($primes) + 1;
614 
615  do {
616  for ($i = $i0; $i <= $num_primes; $i++) {
617  if ($timeout !== false) {
618  $timeout-= time() - $start;
619  $start = time();
620  if ($timeout <= 0) {
621  return array(
622  'privatekey' => '',
623  'publickey' => '',
624  'partialkey' => serialize(array(
625  'primes' => $primes,
626  'coefficients' => $coefficients,
627  'lcm' => $lcm,
628  'exponents' => $exponents
629  ))
630  );
631  }
632  }
633 
634  if ($i == $num_primes) {
635  list($min, $temp) = $absoluteMin->divide($n);
636  if (!$temp->equals($this->zero)) {
637  $min = $min->add($this->one); // ie. ceil()
638  }
639  $primes[$i] = $generator->randomPrime($min, $finalMax, $timeout);
640  } else {
641  $primes[$i] = $generator->randomPrime($min, $max, $timeout);
642  }
643 
644  if ($primes[$i] === false) { // if we've reached the timeout
645  if (count($primes) > 1) {
646  $partialkey = '';
647  } else {
648  array_pop($primes);
649  $partialkey = serialize(array(
650  'primes' => $primes,
651  'coefficients' => $coefficients,
652  'lcm' => $lcm,
653  'exponents' => $exponents
654  ));
655  }
656 
657  return array(
658  'privatekey' => '',
659  'publickey' => '',
660  'partialkey' => $partialkey
661  );
662  }
663 
664  // the first coefficient is calculated differently from the rest
665  // ie. instead of being $primes[1]->modInverse($primes[2]), it's $primes[2]->modInverse($primes[1])
666  if ($i > 2) {
667  $coefficients[$i] = $n->modInverse($primes[$i]);
668  }
669 
670  $n = $n->multiply($primes[$i]);
671 
672  $temp = $primes[$i]->subtract($this->one);
673 
674  // textbook RSA implementations use Euler's totient function instead of the least common multiple.
675  // see http://en.wikipedia.org/wiki/Euler%27s_totient_function
676  $lcm['top'] = $lcm['top']->multiply($temp);
677  $lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp);
678 
679  $exponents[$i] = $e->modInverse($temp);
680  }
681 
682  list($temp) = $lcm['top']->divide($lcm['bottom']);
683  $gcd = $temp->gcd($e);
684  $i0 = 1;
685  } while (!$gcd->equals($this->one));
686 
687  $d = $e->modInverse($temp);
688 
689  $coefficients[2] = $primes[2]->modInverse($primes[1]);
690 
691  // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.2>:
692  // RSAPrivateKey ::= SEQUENCE {
693  // version Version,
694  // modulus INTEGER, -- n
695  // publicExponent INTEGER, -- e
696  // privateExponent INTEGER, -- d
697  // prime1 INTEGER, -- p
698  // prime2 INTEGER, -- q
699  // exponent1 INTEGER, -- d mod (p-1)
700  // exponent2 INTEGER, -- d mod (q-1)
701  // coefficient INTEGER, -- (inverse of q) mod p
702  // otherPrimeInfos OtherPrimeInfos OPTIONAL
703  // }
704 
705  return array(
706  'privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients),
707  'publickey' => $this->_convertPublicKey($n, $e),
708  'partialkey' => false
709  );
710  }
711 
720  function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients)
721  {
722  $signed = $this->privateKeyFormat != self::PRIVATE_FORMAT_XML;
723  $num_primes = count($primes);
724  $raw = array(
725  'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi
726  'modulus' => $n->toBytes($signed),
727  'publicExponent' => $e->toBytes($signed),
728  'privateExponent' => $d->toBytes($signed),
729  'prime1' => $primes[1]->toBytes($signed),
730  'prime2' => $primes[2]->toBytes($signed),
731  'exponent1' => $exponents[1]->toBytes($signed),
732  'exponent2' => $exponents[2]->toBytes($signed),
733  'coefficient' => $coefficients[2]->toBytes($signed)
734  );
735 
736  // if the format in question does not support multi-prime rsa and multi-prime rsa was used,
737  // call _convertPublicKey() instead.
738  switch ($this->privateKeyFormat) {
739  case self::PRIVATE_FORMAT_XML:
740  if ($num_primes != 2) {
741  return false;
742  }
743  return "<RSAKeyValue>\r\n" .
744  ' <Modulus>' . base64_encode($raw['modulus']) . "</Modulus>\r\n" .
745  ' <Exponent>' . base64_encode($raw['publicExponent']) . "</Exponent>\r\n" .
746  ' <P>' . base64_encode($raw['prime1']) . "</P>\r\n" .
747  ' <Q>' . base64_encode($raw['prime2']) . "</Q>\r\n" .
748  ' <DP>' . base64_encode($raw['exponent1']) . "</DP>\r\n" .
749  ' <DQ>' . base64_encode($raw['exponent2']) . "</DQ>\r\n" .
750  ' <InverseQ>' . base64_encode($raw['coefficient']) . "</InverseQ>\r\n" .
751  ' <D>' . base64_encode($raw['privateExponent']) . "</D>\r\n" .
752  '</RSAKeyValue>';
753  break;
754  case self::PRIVATE_FORMAT_PUTTY:
755  if ($num_primes != 2) {
756  return false;
757  }
758  $key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: ";
759  $encryption = (!empty($this->password) || is_string($this->password)) ? 'aes256-cbc' : 'none';
760  $key.= $encryption;
761  $key.= "\r\nComment: " . $this->comment . "\r\n";
762  $public = pack(
763  'Na*Na*Na*',
764  strlen('ssh-rsa'),
765  'ssh-rsa',
766  strlen($raw['publicExponent']),
767  $raw['publicExponent'],
768  strlen($raw['modulus']),
769  $raw['modulus']
770  );
771  $source = pack(
772  'Na*Na*Na*Na*',
773  strlen('ssh-rsa'),
774  'ssh-rsa',
775  strlen($encryption),
776  $encryption,
777  strlen($this->comment),
778  $this->comment,
779  strlen($public),
780  $public
781  );
782  $public = base64_encode($public);
783  $key.= "Public-Lines: " . ((strlen($public) + 63) >> 6) . "\r\n";
784  $key.= chunk_split($public, 64);
785  $private = pack(
786  'Na*Na*Na*Na*',
787  strlen($raw['privateExponent']),
788  $raw['privateExponent'],
789  strlen($raw['prime1']),
790  $raw['prime1'],
791  strlen($raw['prime2']),
792  $raw['prime2'],
793  strlen($raw['coefficient']),
794  $raw['coefficient']
795  );
796  if (empty($this->password) && !is_string($this->password)) {
797  $source.= pack('Na*', strlen($private), $private);
798  $hashkey = 'putty-private-key-file-mac-key';
799  } else {
800  $private.= Random::string(16 - (strlen($private) & 15));
801  $source.= pack('Na*', strlen($private), $private);
802  $sequence = 0;
803  $symkey = '';
804  while (strlen($symkey) < 32) {
805  $temp = pack('Na*', $sequence++, $this->password);
806  $symkey.= pack('H*', sha1($temp));
807  }
808  $symkey = substr($symkey, 0, 32);
809  $crypto = new AES();
810 
811  $crypto->setKey($symkey);
812  $crypto->disablePadding();
813  $private = $crypto->encrypt($private);
814  $hashkey = 'putty-private-key-file-mac-key' . $this->password;
815  }
816 
817  $private = base64_encode($private);
818  $key.= 'Private-Lines: ' . ((strlen($private) + 63) >> 6) . "\r\n";
819  $key.= chunk_split($private, 64);
820  $hash = new Hash('sha1');
821  $hash->setKey(pack('H*', sha1($hashkey)));
822  $key.= 'Private-MAC: ' . bin2hex($hash->hash($source)) . "\r\n";
823 
824  return $key;
825  default: // eg. self::PRIVATE_FORMAT_PKCS1
826  $components = array();
827  foreach ($raw as $name => $value) {
828  $components[$name] = pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value);
829  }
830 
831  $RSAPrivateKey = implode('', $components);
832 
833  if ($num_primes > 2) {
834  $OtherPrimeInfos = '';
835  for ($i = 3; $i <= $num_primes; $i++) {
836  // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
837  //
838  // OtherPrimeInfo ::= SEQUENCE {
839  // prime INTEGER, -- ri
840  // exponent INTEGER, -- di
841  // coefficient INTEGER -- ti
842  // }
843  $OtherPrimeInfo = pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true));
844  $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true));
845  $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true));
846  $OtherPrimeInfos.= pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo);
847  }
848  $RSAPrivateKey.= pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos);
849  }
850 
851  $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
852 
853  if ($this->privateKeyFormat == self::PRIVATE_FORMAT_PKCS8) {
854  $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
855  $RSAPrivateKey = pack(
856  'Ca*a*Ca*a*',
857  self::ASN1_INTEGER,
858  "\01\00",
859  $rsaOID,
860  4,
861  $this->_encodeLength(strlen($RSAPrivateKey)),
862  $RSAPrivateKey
863  );
864  $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
865  if (!empty($this->password) || is_string($this->password)) {
866  $salt = Random::string(8);
867  $iterationCount = 2048;
868 
869  $crypto = new DES();
870  $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount);
871  $RSAPrivateKey = $crypto->encrypt($RSAPrivateKey);
872 
873  $parameters = pack(
874  'Ca*a*Ca*N',
875  self::ASN1_OCTETSTRING,
876  $this->_encodeLength(strlen($salt)),
877  $salt,
878  self::ASN1_INTEGER,
879  $this->_encodeLength(4),
880  $iterationCount
881  );
882  $pbeWithMD5AndDES_CBC = "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03";
883 
884  $encryptionAlgorithm = pack(
885  'Ca*a*Ca*a*',
886  self::ASN1_OBJECT,
887  $this->_encodeLength(strlen($pbeWithMD5AndDES_CBC)),
888  $pbeWithMD5AndDES_CBC,
889  self::ASN1_SEQUENCE,
890  $this->_encodeLength(strlen($parameters)),
891  $parameters
892  );
893 
894  $RSAPrivateKey = pack(
895  'Ca*a*Ca*a*',
896  self::ASN1_SEQUENCE,
897  $this->_encodeLength(strlen($encryptionAlgorithm)),
898  $encryptionAlgorithm,
899  self::ASN1_OCTETSTRING,
900  $this->_encodeLength(strlen($RSAPrivateKey)),
901  $RSAPrivateKey
902  );
903 
904  $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
905 
906  $RSAPrivateKey = "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" .
907  chunk_split(base64_encode($RSAPrivateKey), 64) .
908  '-----END ENCRYPTED PRIVATE KEY-----';
909  } else {
910  $RSAPrivateKey = "-----BEGIN PRIVATE KEY-----\r\n" .
911  chunk_split(base64_encode($RSAPrivateKey), 64) .
912  '-----END PRIVATE KEY-----';
913  }
914  return $RSAPrivateKey;
915  }
916 
917  if (!empty($this->password) || is_string($this->password)) {
918  $iv = Random::string(8);
919  $symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key
920  $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8);
921  $des = new TripleDES();
922  $des->setKey($symkey);
923  $des->setIV($iv);
924  $iv = strtoupper(bin2hex($iv));
925  $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
926  "Proc-Type: 4,ENCRYPTED\r\n" .
927  "DEK-Info: DES-EDE3-CBC,$iv\r\n" .
928  "\r\n" .
929  chunk_split(base64_encode($des->encrypt($RSAPrivateKey)), 64) .
930  '-----END RSA PRIVATE KEY-----';
931  } else {
932  $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
933  chunk_split(base64_encode($RSAPrivateKey), 64) .
934  '-----END RSA PRIVATE KEY-----';
935  }
936 
937  return $RSAPrivateKey;
938  }
939  }
940 
949  function _convertPublicKey($n, $e)
950  {
951  $signed = $this->publicKeyFormat != self::PUBLIC_FORMAT_XML;
952 
953  $modulus = $n->toBytes($signed);
954  $publicExponent = $e->toBytes($signed);
955 
956  switch ($this->publicKeyFormat) {
957  case self::PUBLIC_FORMAT_RAW:
958  return array('e' => $e->copy(), 'n' => $n->copy());
959  case self::PUBLIC_FORMAT_XML:
960  return "<RSAKeyValue>\r\n" .
961  ' <Modulus>' . base64_encode($modulus) . "</Modulus>\r\n" .
962  ' <Exponent>' . base64_encode($publicExponent) . "</Exponent>\r\n" .
963  '</RSAKeyValue>';
964  break;
965  case self::PUBLIC_FORMAT_OPENSSH:
966  // from <http://tools.ietf.org/html/rfc4253#page-15>:
967  // string "ssh-rsa"
968  // mpint e
969  // mpint n
970  $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);
971  $RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment;
972 
973  return $RSAPublicKey;
974  default: // eg. self::PUBLIC_FORMAT_PKCS1_RAW or self::PUBLIC_FORMAT_PKCS1
975  // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>:
976  // RSAPublicKey ::= SEQUENCE {
977  // modulus INTEGER, -- n
978  // publicExponent INTEGER -- e
979  // }
980  $components = array(
981  'modulus' => pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus),
982  'publicExponent' => pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent)
983  );
984 
985  $RSAPublicKey = pack(
986  'Ca*a*a*',
987  self::ASN1_SEQUENCE,
988  $this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])),
989  $components['modulus'],
990  $components['publicExponent']
991  );
992 
993  if ($this->publicKeyFormat == self::PUBLIC_FORMAT_PKCS1_RAW) {
994  $RSAPublicKey = "-----BEGIN RSA PUBLIC KEY-----\r\n" .
995  chunk_split(base64_encode($RSAPublicKey), 64) .
996  '-----END RSA PUBLIC KEY-----';
997  } else {
998  // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
999  $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
1000  $RSAPublicKey = chr(0) . $RSAPublicKey;
1001  $RSAPublicKey = chr(3) . $this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;
1002 
1003  $RSAPublicKey = pack(
1004  'Ca*a*',
1005  self::ASN1_SEQUENCE,
1006  $this->_encodeLength(strlen($rsaOID . $RSAPublicKey)),
1007  $rsaOID . $RSAPublicKey
1008  );
1009 
1010  $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
1011  chunk_split(base64_encode($RSAPublicKey), 64) .
1012  '-----END PUBLIC KEY-----';
1013  }
1014 
1015  return $RSAPublicKey;
1016  }
1017  }
1018 
1029  function _parseKey($key, $type)
1030  {
1031  if ($type != self::PUBLIC_FORMAT_RAW && !is_string($key)) {
1032  return false;
1033  }
1034 
1035  switch ($type) {
1036  case self::PUBLIC_FORMAT_RAW:
1037  if (!is_array($key)) {
1038  return false;
1039  }
1040  $components = array();
1041  switch (true) {
1042  case isset($key['e']):
1043  $components['publicExponent'] = $key['e']->copy();
1044  break;
1045  case isset($key['exponent']):
1046  $components['publicExponent'] = $key['exponent']->copy();
1047  break;
1048  case isset($key['publicExponent']):
1049  $components['publicExponent'] = $key['publicExponent']->copy();
1050  break;
1051  case isset($key[0]):
1052  $components['publicExponent'] = $key[0]->copy();
1053  }
1054  switch (true) {
1055  case isset($key['n']):
1056  $components['modulus'] = $key['n']->copy();
1057  break;
1058  case isset($key['modulo']):
1059  $components['modulus'] = $key['modulo']->copy();
1060  break;
1061  case isset($key['modulus']):
1062  $components['modulus'] = $key['modulus']->copy();
1063  break;
1064  case isset($key[1]):
1065  $components['modulus'] = $key[1]->copy();
1066  }
1067  return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false;
1068  case self::PRIVATE_FORMAT_PKCS1:
1069  case self::PRIVATE_FORMAT_PKCS8:
1070  case self::PUBLIC_FORMAT_PKCS1:
1071  /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
1072  "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
1073  protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding
1074  two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here:
1075 
1076  http://tools.ietf.org/html/rfc1421#section-4.6.1.1
1077  http://tools.ietf.org/html/rfc1421#section-4.6.1.3
1078 
1079  DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
1080  DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
1081  function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
1082  own implementation. ie. the implementation *is* the standard and any bugs that may exist in that
1083  implementation are part of the standard, as well.
1084 
1085  * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */
1086  if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
1087  $iv = pack('H*', trim($matches[2]));
1088  $symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); // symkey is short for symmetric key
1089  $symkey.= pack('H*', md5($symkey . $this->password . substr($iv, 0, 8)));
1090  // remove the Proc-Type / DEK-Info sections as they're no longer needed
1091  $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key);
1092  $ciphertext = $this->_extractBER($key);
1093  if ($ciphertext === false) {
1094  $ciphertext = $key;
1095  }
1096  switch ($matches[1]) {
1097  case 'AES-256-CBC':
1098  $crypto = new AES();
1099  break;
1100  case 'AES-128-CBC':
1101  $symkey = substr($symkey, 0, 16);
1102  $crypto = new AES();
1103  break;
1104  case 'DES-EDE3-CFB':
1105  $crypto = new TripleDES(Base::MODE_CFB);
1106  break;
1107  case 'DES-EDE3-CBC':
1108  $symkey = substr($symkey, 0, 24);
1109  $crypto = new TripleDES();
1110  break;
1111  case 'DES-CBC':
1112  $crypto = new DES();
1113  break;
1114  default:
1115  return false;
1116  }
1117  $crypto->setKey($symkey);
1118  $crypto->setIV($iv);
1119  $decoded = $crypto->decrypt($ciphertext);
1120  } else {
1121  $decoded = $this->_extractBER($key);
1122  }
1123 
1124  if ($decoded !== false) {
1125  $key = $decoded;
1126  }
1127 
1128  $components = array();
1129 
1130  if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
1131  return false;
1132  }
1133  if ($this->_decodeLength($key) != strlen($key)) {
1134  return false;
1135  }
1136 
1137  $tag = ord($this->_string_shift($key));
1138  /* intended for keys for which OpenSSL's asn1parse returns the following:
1139 
1140  0:d=0 hl=4 l= 631 cons: SEQUENCE
1141  4:d=1 hl=2 l= 1 prim: INTEGER :00
1142  7:d=1 hl=2 l= 13 cons: SEQUENCE
1143  9:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
1144  20:d=2 hl=2 l= 0 prim: NULL
1145  22:d=1 hl=4 l= 609 prim: OCTET STRING
1146 
1147  ie. PKCS8 keys*/
1148 
1149  if ($tag == self::ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") {
1150  $this->_string_shift($key, 3);
1151  $tag = self::ASN1_SEQUENCE;
1152  }
1153 
1154  if ($tag == self::ASN1_SEQUENCE) {
1155  $temp = $this->_string_shift($key, $this->_decodeLength($key));
1156  if (ord($this->_string_shift($temp)) != self::ASN1_OBJECT) {
1157  return false;
1158  }
1159  $length = $this->_decodeLength($temp);
1160  switch ($this->_string_shift($temp, $length)) {
1161  case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption
1162  break;
1163  case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC
1164  /*
1165  PBEParameter ::= SEQUENCE {
1166  salt OCTET STRING (SIZE(8)),
1167  iterationCount INTEGER }
1168  */
1169  if (ord($this->_string_shift($temp)) != self::ASN1_SEQUENCE) {
1170  return false;
1171  }
1172  if ($this->_decodeLength($temp) != strlen($temp)) {
1173  return false;
1174  }
1175  $this->_string_shift($temp); // assume it's an octet string
1176  $salt = $this->_string_shift($temp, $this->_decodeLength($temp));
1177  if (ord($this->_string_shift($temp)) != self::ASN1_INTEGER) {
1178  return false;
1179  }
1180  $this->_decodeLength($temp);
1181  list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT));
1182  $this->_string_shift($key); // assume it's an octet string
1183  $length = $this->_decodeLength($key);
1184  if (strlen($key) != $length) {
1185  return false;
1186  }
1187 
1188  $crypto = new DES();
1189  $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount);
1190  $key = $crypto->decrypt($key);
1191  if ($key === false) {
1192  return false;
1193  }
1194  return $this->_parseKey($key, self::PRIVATE_FORMAT_PKCS1);
1195  default:
1196  return false;
1197  }
1198  /* intended for keys for which OpenSSL's asn1parse returns the following:
1199 
1200  0:d=0 hl=4 l= 290 cons: SEQUENCE
1201  4:d=1 hl=2 l= 13 cons: SEQUENCE
1202  6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
1203  17:d=2 hl=2 l= 0 prim: NULL
1204  19:d=1 hl=4 l= 271 prim: BIT STRING */
1205  $tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag
1206  $this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length
1207  // "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of
1208  // unused bits in the final subsequent octet. The number shall be in the range zero to seven."
1209  // -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2)
1210  if ($tag == self::ASN1_BITSTRING) {
1211  $this->_string_shift($key);
1212  }
1213  if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
1214  return false;
1215  }
1216  if ($this->_decodeLength($key) != strlen($key)) {
1217  return false;
1218  }
1219  $tag = ord($this->_string_shift($key));
1220  }
1221  if ($tag != self::ASN1_INTEGER) {
1222  return false;
1223  }
1224 
1225  $length = $this->_decodeLength($key);
1226  $temp = $this->_string_shift($key, $length);
1227  if (strlen($temp) != 1 || ord($temp) > 2) {
1228  $components['modulus'] = new BigInteger($temp, 256);
1229  $this->_string_shift($key); // skip over self::ASN1_INTEGER
1230  $length = $this->_decodeLength($key);
1231  $components[$type == self::PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256);
1232 
1233  return $components;
1234  }
1235  if (ord($this->_string_shift($key)) != self::ASN1_INTEGER) {
1236  return false;
1237  }
1238  $length = $this->_decodeLength($key);
1239  $components['modulus'] = new BigInteger($this->_string_shift($key, $length), 256);
1240  $this->_string_shift($key);
1241  $length = $this->_decodeLength($key);
1242  $components['publicExponent'] = new BigInteger($this->_string_shift($key, $length), 256);
1243  $this->_string_shift($key);
1244  $length = $this->_decodeLength($key);
1245  $components['privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256);
1246  $this->_string_shift($key);
1247  $length = $this->_decodeLength($key);
1248  $components['primes'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256));
1249  $this->_string_shift($key);
1250  $length = $this->_decodeLength($key);
1251  $components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256);
1252  $this->_string_shift($key);
1253  $length = $this->_decodeLength($key);
1254  $components['exponents'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256));
1255  $this->_string_shift($key);
1256  $length = $this->_decodeLength($key);
1257  $components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256);
1258  $this->_string_shift($key);
1259  $length = $this->_decodeLength($key);
1260  $components['coefficients'] = array(2 => new BigInteger($this->_string_shift($key, $length), 256));
1261 
1262  if (!empty($key)) {
1263  if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
1264  return false;
1265  }
1266  $this->_decodeLength($key);
1267  while (!empty($key)) {
1268  if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
1269  return false;
1270  }
1271  $this->_decodeLength($key);
1272  $key = substr($key, 1);
1273  $length = $this->_decodeLength($key);
1274  $components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256);
1275  $this->_string_shift($key);
1276  $length = $this->_decodeLength($key);
1277  $components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256);
1278  $this->_string_shift($key);
1279  $length = $this->_decodeLength($key);
1280  $components['coefficients'][] = new BigInteger($this->_string_shift($key, $length), 256);
1281  }
1282  }
1283 
1284  return $components;
1285  case self::PUBLIC_FORMAT_OPENSSH:
1286  $parts = explode(' ', $key, 3);
1287 
1288  $key = isset($parts[1]) ? base64_decode($parts[1]) : false;
1289  if ($key === false) {
1290  return false;
1291  }
1292 
1293  $comment = isset($parts[2]) ? $parts[2] : false;
1294 
1295  $cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa";
1296 
1297  if (strlen($key) <= 4) {
1298  return false;
1299  }
1300  extract(unpack('Nlength', $this->_string_shift($key, 4)));
1301  $publicExponent = new BigInteger($this->_string_shift($key, $length), -256);
1302  if (strlen($key) <= 4) {
1303  return false;
1304  }
1305  extract(unpack('Nlength', $this->_string_shift($key, 4)));
1306  $modulus = new BigInteger($this->_string_shift($key, $length), -256);
1307 
1308  if ($cleanup && strlen($key)) {
1309  if (strlen($key) <= 4) {
1310  return false;
1311  }
1312  extract(unpack('Nlength', $this->_string_shift($key, 4)));
1313  $realModulus = new BigInteger($this->_string_shift($key, $length), -256);
1314  return strlen($key) ? false : array(
1315  'modulus' => $realModulus,
1316  'publicExponent' => $modulus,
1317  'comment' => $comment
1318  );
1319  } else {
1320  return strlen($key) ? false : array(
1321  'modulus' => $modulus,
1322  'publicExponent' => $publicExponent,
1323  'comment' => $comment
1324  );
1325  }
1326  // http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue
1327  // http://en.wikipedia.org/wiki/XML_Signature
1328  case self::PRIVATE_FORMAT_XML:
1329  case self::PUBLIC_FORMAT_XML:
1330  $this->components = array();
1331 
1332  $xml = xml_parser_create('UTF-8');
1333  xml_set_object($xml, $this);
1334  xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler');
1335  xml_set_character_data_handler($xml, '_data_handler');
1336  // add <xml></xml> to account for "dangling" tags like <BitStrength>...</BitStrength> that are sometimes added
1337  if (!xml_parse($xml, '<xml>' . $key . '</xml>')) {
1338  return false;
1339  }
1340 
1341  return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false;
1342  // from PuTTY's SSHPUBK.C
1343  case self::PRIVATE_FORMAT_PUTTY:
1344  $components = array();
1345  $key = preg_split('#\r\n|\r|\n#', $key);
1346  $type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0]));
1347  if ($type != 'ssh-rsa') {
1348  return false;
1349  }
1350  $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1]));
1351  $comment = trim(preg_replace('#Comment: (.+)#', '$1', $key[2]));
1352 
1353  $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3]));
1354  $public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength))));
1355  $public = substr($public, 11);
1356  extract(unpack('Nlength', $this->_string_shift($public, 4)));
1357  $components['publicExponent'] = new BigInteger($this->_string_shift($public, $length), -256);
1358  extract(unpack('Nlength', $this->_string_shift($public, 4)));
1359  $components['modulus'] = new BigInteger($this->_string_shift($public, $length), -256);
1360 
1361  $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$publicLength + 4]));
1362  $private = base64_decode(implode('', array_map('trim', array_slice($key, $publicLength + 5, $privateLength))));
1363 
1364  switch ($encryption) {
1365  case 'aes256-cbc':
1366  $symkey = '';
1367  $sequence = 0;
1368  while (strlen($symkey) < 32) {
1369  $temp = pack('Na*', $sequence++, $this->password);
1370  $symkey.= pack('H*', sha1($temp));
1371  }
1372  $symkey = substr($symkey, 0, 32);
1373  $crypto = new AES();
1374  }
1375 
1376  if ($encryption != 'none') {
1377  $crypto->setKey($symkey);
1378  $crypto->disablePadding();
1379  $private = $crypto->decrypt($private);
1380  if ($private === false) {
1381  return false;
1382  }
1383  }
1384 
1385  extract(unpack('Nlength', $this->_string_shift($private, 4)));
1386  if (strlen($private) < $length) {
1387  return false;
1388  }
1389  $components['privateExponent'] = new BigInteger($this->_string_shift($private, $length), -256);
1390  extract(unpack('Nlength', $this->_string_shift($private, 4)));
1391  if (strlen($private) < $length) {
1392  return false;
1393  }
1394  $components['primes'] = array(1 => new BigInteger($this->_string_shift($private, $length), -256));
1395  extract(unpack('Nlength', $this->_string_shift($private, 4)));
1396  if (strlen($private) < $length) {
1397  return false;
1398  }
1399  $components['primes'][] = new BigInteger($this->_string_shift($private, $length), -256);
1400 
1401  $temp = $components['primes'][1]->subtract($this->one);
1402  $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp));
1403  $temp = $components['primes'][2]->subtract($this->one);
1404  $components['exponents'][] = $components['publicExponent']->modInverse($temp);
1405 
1406  extract(unpack('Nlength', $this->_string_shift($private, 4)));
1407  if (strlen($private) < $length) {
1408  return false;
1409  }
1410  $components['coefficients'] = array(2 => new BigInteger($this->_string_shift($private, $length), -256));
1411 
1412  return $components;
1413  }
1414  }
1415 
1424  function getSize()
1425  {
1426  return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits());
1427  }
1428 
1439  function _start_element_handler($parser, $name, $attribs)
1440  {
1441  //$name = strtoupper($name);
1442  switch ($name) {
1443  case 'MODULUS':
1444  $this->current = &$this->components['modulus'];
1445  break;
1446  case 'EXPONENT':
1447  $this->current = &$this->components['publicExponent'];
1448  break;
1449  case 'P':
1450  $this->current = &$this->components['primes'][1];
1451  break;
1452  case 'Q':
1453  $this->current = &$this->components['primes'][2];
1454  break;
1455  case 'DP':
1456  $this->current = &$this->components['exponents'][1];
1457  break;
1458  case 'DQ':
1459  $this->current = &$this->components['exponents'][2];
1460  break;
1461  case 'INVERSEQ':
1462  $this->current = &$this->components['coefficients'][2];
1463  break;
1464  case 'D':
1465  $this->current = &$this->components['privateExponent'];
1466  }
1467  $this->current = '';
1468  }
1469 
1480  {
1481  if (isset($this->current)) {
1482  $this->current = new BigInteger(base64_decode($this->current), 256);
1483  unset($this->current);
1484  }
1485  }
1486 
1497  {
1498  if (!isset($this->current) || is_object($this->current)) {
1499  return;
1500  }
1501  $this->current.= trim($data);
1502  }
1503 
1513  function loadKey($key, $type = false)
1514  {
1515  if ($key instanceof RSA) {
1516  $this->privateKeyFormat = $key->privateKeyFormat;
1517  $this->publicKeyFormat = $key->publicKeyFormat;
1518  $this->k = $key->k;
1519  $this->hLen = $key->hLen;
1520  $this->sLen = $key->sLen;
1521  $this->mgfHLen = $key->mgfHLen;
1522  $this->encryptionMode = $key->encryptionMode;
1523  $this->signatureMode = $key->signatureMode;
1524  $this->password = $key->password;
1525  $this->configFile = $key->configFile;
1526  $this->comment = $key->comment;
1527 
1528  if (is_object($key->hash)) {
1529  $this->hash = new Hash($key->hash->getHash());
1530  }
1531  if (is_object($key->mgfHash)) {
1532  $this->mgfHash = new Hash($key->mgfHash->getHash());
1533  }
1534 
1535  if (is_object($key->modulus)) {
1536  $this->modulus = $key->modulus->copy();
1537  }
1538  if (is_object($key->exponent)) {
1539  $this->exponent = $key->exponent->copy();
1540  }
1541  if (is_object($key->publicExponent)) {
1542  $this->publicExponent = $key->publicExponent->copy();
1543  }
1544 
1545  $this->primes = array();
1546  $this->exponents = array();
1547  $this->coefficients = array();
1548 
1549  foreach ($this->primes as $prime) {
1550  $this->primes[] = $prime->copy();
1551  }
1552  foreach ($this->exponents as $exponent) {
1553  $this->exponents[] = $exponent->copy();
1554  }
1555  foreach ($this->coefficients as $coefficient) {
1556  $this->coefficients[] = $coefficient->copy();
1557  }
1558 
1559  return true;
1560  }
1561 
1562  if ($type === false) {
1563  $types = array(
1564  self::PUBLIC_FORMAT_RAW,
1565  self::PRIVATE_FORMAT_PKCS1,
1566  self::PRIVATE_FORMAT_XML,
1567  self::PRIVATE_FORMAT_PUTTY,
1568  self::PUBLIC_FORMAT_OPENSSH
1569  );
1570  foreach ($types as $type) {
1571  $components = $this->_parseKey($key, $type);
1572  if ($components !== false) {
1573  break;
1574  }
1575  }
1576  } else {
1577  $components = $this->_parseKey($key, $type);
1578  }
1579 
1580  if ($components === false) {
1581  return false;
1582  }
1583 
1584  if (isset($components['comment']) && $components['comment'] !== false) {
1585  $this->comment = $components['comment'];
1586  }
1587  $this->modulus = $components['modulus'];
1588  $this->k = strlen($this->modulus->toBytes());
1589  $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent'];
1590  if (isset($components['primes'])) {
1591  $this->primes = $components['primes'];
1592  $this->exponents = $components['exponents'];
1593  $this->coefficients = $components['coefficients'];
1594  $this->publicExponent = $components['publicExponent'];
1595  } else {
1596  $this->primes = array();
1597  $this->exponents = array();
1598  $this->coefficients = array();
1599  $this->publicExponent = false;
1600  }
1601 
1602  switch ($type) {
1603  case self::PUBLIC_FORMAT_OPENSSH:
1604  case self::PUBLIC_FORMAT_RAW:
1605  $this->setPublicKey();
1606  break;
1607  case self::PRIVATE_FORMAT_PKCS1:
1608  switch (true) {
1609  case strpos($key, '-BEGIN PUBLIC KEY-') !== false:
1610  case strpos($key, '-BEGIN RSA PUBLIC KEY-') !== false:
1611  $this->setPublicKey();
1612  }
1613  }
1614 
1615  return true;
1616  }
1617 
1629  function setPassword($password = false)
1630  {
1631  $this->password = $password;
1632  }
1633 
1655  function setPublicKey($key = false, $type = false)
1656  {
1657  // if a public key has already been loaded return false
1658  if (!empty($this->publicExponent)) {
1659  return false;
1660  }
1661 
1662  if ($key === false && !empty($this->modulus)) {
1663  $this->publicExponent = $this->exponent;
1664  return true;
1665  }
1666 
1667  if ($type === false) {
1668  $types = array(
1669  self::PUBLIC_FORMAT_RAW,
1670  self::PUBLIC_FORMAT_PKCS1,
1671  self::PUBLIC_FORMAT_XML,
1672  self::PUBLIC_FORMAT_OPENSSH
1673  );
1674  foreach ($types as $type) {
1675  $components = $this->_parseKey($key, $type);
1676  if ($components !== false) {
1677  break;
1678  }
1679  }
1680  } else {
1681  $components = $this->_parseKey($key, $type);
1682  }
1683 
1684  if ($components === false) {
1685  return false;
1686  }
1687 
1688  if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) {
1689  $this->modulus = $components['modulus'];
1690  $this->exponent = $this->publicExponent = $components['publicExponent'];
1691  return true;
1692  }
1693 
1694  $this->publicExponent = $components['publicExponent'];
1695 
1696  return true;
1697  }
1698 
1715  function setPrivateKey($key = false, $type = false)
1716  {
1717  if ($key === false && !empty($this->publicExponent)) {
1718  unset($this->publicExponent);
1719  return true;
1720  }
1721 
1722  $rsa = new RSA();
1723  if (!$rsa->loadKey($key, $type)) {
1724  return false;
1725  }
1726  unset($rsa->publicExponent);
1727 
1728  // don't overwrite the old key if the new key is invalid
1729  $this->loadKey($rsa);
1730  return true;
1731  }
1732 
1745  function getPublicKey($type = self::PUBLIC_FORMAT_PKCS8)
1746  {
1747  if (empty($this->modulus) || empty($this->publicExponent)) {
1748  return false;
1749  }
1750 
1751  $oldFormat = $this->publicKeyFormat;
1752  $this->publicKeyFormat = $type;
1753  $temp = $this->_convertPublicKey($this->modulus, $this->publicExponent);
1754  $this->publicKeyFormat = $oldFormat;
1755  return $temp;
1756  }
1757 
1770  public function getPublicKeyFingerprint($algorithm = 'md5')
1771  {
1772  if (empty($this->modulus) || empty($this->publicExponent)) {
1773  return false;
1774  }
1775 
1776  $modulus = $this->modulus->toBytes(true);
1777  $publicExponent = $this->publicExponent->toBytes(true);
1778 
1779  $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);
1780 
1781  switch ($algorithm) {
1782  case 'sha256':
1783  $hash = new Hash('sha256');
1784  $base = base64_encode($hash->hash($RSAPublicKey));
1785  return substr($base, 0, strlen($base) - 1);
1786  case 'md5':
1787  return substr(chunk_split(md5($RSAPublicKey), 2, ':'), 0, -1);
1788  default:
1789  return false;
1790  }
1791  }
1792 
1804  function getPrivateKey($type = self::PUBLIC_FORMAT_PKCS1)
1805  {
1806  if (empty($this->primes)) {
1807  return false;
1808  }
1809 
1810  $oldFormat = $this->privateKeyFormat;
1811  $this->privateKeyFormat = $type;
1812  $temp = $this->_convertPrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients);
1813  $this->privateKeyFormat = $oldFormat;
1814  return $temp;
1815  }
1816 
1828  function _getPrivatePublicKey($mode = self::PUBLIC_FORMAT_PKCS8)
1829  {
1830  if (empty($this->modulus) || empty($this->exponent)) {
1831  return false;
1832  }
1833 
1834  $oldFormat = $this->publicKeyFormat;
1835  $this->publicKeyFormat = $mode;
1836  $temp = $this->_convertPublicKey($this->modulus, $this->exponent);
1837  $this->publicKeyFormat = $oldFormat;
1838  return $temp;
1839  }
1840 
1847  function __toString()
1848  {
1849  $key = $this->getPrivateKey($this->privateKeyFormat);
1850  if ($key !== false) {
1851  return $key;
1852  }
1853  $key = $this->_getPrivatePublicKey($this->publicKeyFormat);
1854  return $key !== false ? $key : '';
1855  }
1856 
1863  function __clone()
1864  {
1865  $key = new RSA();
1866  $key->loadKey($this);
1867  return $key;
1868  }
1869 
1877  function _generateMinMax($bits)
1878  {
1879  $bytes = $bits >> 3;
1880  $min = str_repeat(chr(0), $bytes);
1881  $max = str_repeat(chr(0xFF), $bytes);
1882  $msb = $bits & 7;
1883  if ($msb) {
1884  $min = chr(1 << ($msb - 1)) . $min;
1885  $max = chr((1 << $msb) - 1) . $max;
1886  } else {
1887  $min[0] = chr(0x80);
1888  }
1889 
1890  return array(
1891  'min' => new BigInteger($min, 256),
1892  'max' => new BigInteger($max, 256)
1893  );
1894  }
1895 
1906  function _decodeLength(&$string)
1907  {
1908  $length = ord($this->_string_shift($string));
1909  if ($length & 0x80) { // definite length, long form
1910  $length&= 0x7F;
1911  $temp = $this->_string_shift($string, $length);
1912  list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4));
1913  }
1914  return $length;
1915  }
1916 
1927  function _encodeLength($length)
1928  {
1929  if ($length <= 0x7F) {
1930  return chr($length);
1931  }
1932 
1933  $temp = ltrim(pack('N', $length), chr(0));
1934  return pack('Ca*', 0x80 | strlen($temp), $temp);
1935  }
1936 
1947  function _string_shift(&$string, $index = 1)
1948  {
1949  $substr = substr($string, 0, $index);
1950  $string = substr($string, $index);
1951  return $substr;
1952  }
1953 
1962  {
1963  $this->privateKeyFormat = $format;
1964  }
1965 
1974  {
1975  $this->publicKeyFormat = $format;
1976  }
1977 
1987  function setHash($hash)
1988  {
1989  // \phpseclib\Crypt\Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example.
1990  switch ($hash) {
1991  case 'md2':
1992  case 'md5':
1993  case 'sha1':
1994  case 'sha256':
1995  case 'sha384':
1996  case 'sha512':
1997  $this->hash = new Hash($hash);
1998  $this->hashName = $hash;
1999  break;
2000  default:
2001  $this->hash = new Hash('sha1');
2002  $this->hashName = 'sha1';
2003  }
2004  $this->hLen = $this->hash->getLength();
2005  }
2006 
2016  function setMGFHash($hash)
2017  {
2018  // \phpseclib\Crypt\Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example.
2019  switch ($hash) {
2020  case 'md2':
2021  case 'md5':
2022  case 'sha1':
2023  case 'sha256':
2024  case 'sha384':
2025  case 'sha512':
2026  $this->mgfHash = new Hash($hash);
2027  break;
2028  default:
2029  $this->mgfHash = new Hash('sha1');
2030  }
2031  $this->mgfHLen = $this->mgfHash->getLength();
2032  }
2033 
2045  function setSaltLength($sLen)
2046  {
2047  $this->sLen = $sLen;
2048  }
2049 
2060  function _i2osp($x, $xLen)
2061  {
2062  $x = $x->toBytes();
2063  if (strlen($x) > $xLen) {
2064  user_error('Integer too large');
2065  return false;
2066  }
2067  return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
2068  }
2069 
2079  function _os2ip($x)
2080  {
2081  return new BigInteger($x, 256);
2082  }
2083 
2093  function _exponentiate($x)
2094  {
2095  if (empty($this->primes) || empty($this->coefficients) || empty($this->exponents)) {
2096  return $x->modPow($this->exponent, $this->modulus);
2097  }
2098 
2099  $num_primes = count($this->primes);
2100 
2101  if (defined('CRYPT_RSA_DISABLE_BLINDING')) {
2102  $m_i = array(
2103  1 => $x->modPow($this->exponents[1], $this->primes[1]),
2104  2 => $x->modPow($this->exponents[2], $this->primes[2])
2105  );
2106  $h = $m_i[1]->subtract($m_i[2]);
2107  $h = $h->multiply($this->coefficients[2]);
2108  list(, $h) = $h->divide($this->primes[1]);
2109  $m = $m_i[2]->add($h->multiply($this->primes[2]));
2110 
2111  $r = $this->primes[1];
2112  for ($i = 3; $i <= $num_primes; $i++) {
2113  $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]);
2114 
2115  $r = $r->multiply($this->primes[$i - 1]);
2116 
2117  $h = $m_i->subtract($m);
2118  $h = $h->multiply($this->coefficients[$i]);
2119  list(, $h) = $h->divide($this->primes[$i]);
2120 
2121  $m = $m->add($r->multiply($h));
2122  }
2123  } else {
2124  $smallest = $this->primes[1];
2125  for ($i = 2; $i <= $num_primes; $i++) {
2126  if ($smallest->compare($this->primes[$i]) > 0) {
2127  $smallest = $this->primes[$i];
2128  }
2129  }
2130 
2131  $one = new BigInteger(1);
2132 
2133  $r = $one->random($one, $smallest->subtract($one));
2134 
2135  $m_i = array(
2136  1 => $this->_blind($x, $r, 1),
2137  2 => $this->_blind($x, $r, 2)
2138  );
2139  $h = $m_i[1]->subtract($m_i[2]);
2140  $h = $h->multiply($this->coefficients[2]);
2141  list(, $h) = $h->divide($this->primes[1]);
2142  $m = $m_i[2]->add($h->multiply($this->primes[2]));
2143 
2144  $r = $this->primes[1];
2145  for ($i = 3; $i <= $num_primes; $i++) {
2146  $m_i = $this->_blind($x, $r, $i);
2147 
2148  $r = $r->multiply($this->primes[$i - 1]);
2149 
2150  $h = $m_i->subtract($m);
2151  $h = $h->multiply($this->coefficients[$i]);
2152  list(, $h) = $h->divide($this->primes[$i]);
2153 
2154  $m = $m->add($r->multiply($h));
2155  }
2156  }
2157 
2158  return $m;
2159  }
2160 
2173  function _blind($x, $r, $i)
2174  {
2175  $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i]));
2176  $x = $x->modPow($this->exponents[$i], $this->primes[$i]);
2177 
2178  $r = $r->modInverse($this->primes[$i]);
2179  $x = $x->multiply($r);
2180  list(, $x) = $x->divide($this->primes[$i]);
2181 
2182  return $x;
2183  }
2184 
2199  function _equals($x, $y)
2200  {
2201  if (strlen($x) != strlen($y)) {
2202  return false;
2203  }
2204 
2205  $result = 0;
2206  for ($i = 0; $i < strlen($x); $i++) {
2207  $result |= ord($x[$i]) ^ ord($y[$i]);
2208  }
2209 
2210  return $result == 0;
2211  }
2212 
2222  function _rsaep($m)
2223  {
2224  if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
2225  user_error('Message representative out of range');
2226  return false;
2227  }
2228  return $this->_exponentiate($m);
2229  }
2230 
2240  function _rsadp($c)
2241  {
2242  if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) {
2243  user_error('Ciphertext representative out of range');
2244  return false;
2245  }
2246  return $this->_exponentiate($c);
2247  }
2248 
2258  function _rsasp1($m)
2259  {
2260  if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
2261  user_error('Message representative out of range');
2262  return false;
2263  }
2264  return $this->_exponentiate($m);
2265  }
2266 
2276  function _rsavp1($s)
2277  {
2278  if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) {
2279  user_error('Signature representative out of range');
2280  return false;
2281  }
2282  return $this->_exponentiate($s);
2283  }
2284 
2295  function _mgf1($mgfSeed, $maskLen)
2296  {
2297  // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output.
2298 
2299  $t = '';
2300  $count = ceil($maskLen / $this->mgfHLen);
2301  for ($i = 0; $i < $count; $i++) {
2302  $c = pack('N', $i);
2303  $t.= $this->mgfHash->hash($mgfSeed . $c);
2304  }
2305 
2306  return substr($t, 0, $maskLen);
2307  }
2308 
2320  function _rsaes_oaep_encrypt($m, $l = '')
2321  {
2322  $mLen = strlen($m);
2323 
2324  // Length checking
2325 
2326  // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
2327  // be output.
2328 
2329  if ($mLen > $this->k - 2 * $this->hLen - 2) {
2330  user_error('Message too long');
2331  return false;
2332  }
2333 
2334  // EME-OAEP encoding
2335 
2336  $lHash = $this->hash->hash($l);
2337  $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2);
2338  $db = $lHash . $ps . chr(1) . $m;
2339  $seed = Random::string($this->hLen);
2340  $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
2341  $maskedDB = $db ^ $dbMask;
2342  $seedMask = $this->_mgf1($maskedDB, $this->hLen);
2343  $maskedSeed = $seed ^ $seedMask;
2344  $em = chr(0) . $maskedSeed . $maskedDB;
2345 
2346  // RSA encryption
2347 
2348  $m = $this->_os2ip($em);
2349  $c = $this->_rsaep($m);
2350  $c = $this->_i2osp($c, $this->k);
2351 
2352  // Output the ciphertext C
2353 
2354  return $c;
2355  }
2356 
2383  function _rsaes_oaep_decrypt($c, $l = '')
2384  {
2385  // Length checking
2386 
2387  // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
2388  // be output.
2389 
2390  if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) {
2391  user_error('Decryption error');
2392  return false;
2393  }
2394 
2395  // RSA decryption
2396 
2397  $c = $this->_os2ip($c);
2398  $m = $this->_rsadp($c);
2399  if ($m === false) {
2400  user_error('Decryption error');
2401  return false;
2402  }
2403  $em = $this->_i2osp($m, $this->k);
2404 
2405  // EME-OAEP decoding
2406 
2407  $lHash = $this->hash->hash($l);
2408  $y = ord($em[0]);
2409  $maskedSeed = substr($em, 1, $this->hLen);
2410  $maskedDB = substr($em, $this->hLen + 1);
2411  $seedMask = $this->_mgf1($maskedDB, $this->hLen);
2412  $seed = $maskedSeed ^ $seedMask;
2413  $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
2414  $db = $maskedDB ^ $dbMask;
2415  $lHash2 = substr($db, 0, $this->hLen);
2416  $m = substr($db, $this->hLen);
2417  if ($lHash != $lHash2) {
2418  user_error('Decryption error');
2419  return false;
2420  }
2421  $m = ltrim($m, chr(0));
2422  if (ord($m[0]) != 1) {
2423  user_error('Decryption error');
2424  return false;
2425  }
2426 
2427  // Output the message M
2428 
2429  return substr($m, 1);
2430  }
2431 
2441  function _raw_encrypt($m)
2442  {
2443  $temp = $this->_os2ip($m);
2444  $temp = $this->_rsaep($temp);
2445  return $this->_i2osp($temp, $this->k);
2446  }
2447 
2458  {
2459  $mLen = strlen($m);
2460 
2461  // Length checking
2462 
2463  if ($mLen > $this->k - 11) {
2464  user_error('Message too long');
2465  return false;
2466  }
2467 
2468  // EME-PKCS1-v1_5 encoding
2469 
2470  $psLen = $this->k - $mLen - 3;
2471  $ps = '';
2472  while (strlen($ps) != $psLen) {
2473  $temp = Random::string($psLen - strlen($ps));
2474  $temp = str_replace("\x00", '', $temp);
2475  $ps.= $temp;
2476  }
2477  $type = 2;
2478  // see the comments of _rsaes_pkcs1_v1_5_decrypt() to understand why this is being done
2479  if (defined('CRYPT_RSA_PKCS15_COMPAT') && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) {
2480  $type = 1;
2481  // "The padding string PS shall consist of k-3-||D|| octets. ... for block type 01, they shall have value FF"
2482  $ps = str_repeat("\xFF", $psLen);
2483  }
2484  $em = chr(0) . chr($type) . $ps . chr(0) . $m;
2485 
2486  // RSA encryption
2487  $m = $this->_os2ip($em);
2488  $c = $this->_rsaep($m);
2489  $c = $this->_i2osp($c, $this->k);
2490 
2491  // Output the ciphertext C
2492 
2493  return $c;
2494  }
2495 
2517  {
2518  // Length checking
2519 
2520  if (strlen($c) != $this->k) { // or if k < 11
2521  user_error('Decryption error');
2522  return false;
2523  }
2524 
2525  // RSA decryption
2526 
2527  $c = $this->_os2ip($c);
2528  $m = $this->_rsadp($c);
2529 
2530  if ($m === false) {
2531  user_error('Decryption error');
2532  return false;
2533  }
2534  $em = $this->_i2osp($m, $this->k);
2535 
2536  // EME-PKCS1-v1_5 decoding
2537 
2538  if (ord($em[0]) != 0 || ord($em[1]) > 2) {
2539  user_error('Decryption error');
2540  return false;
2541  }
2542 
2543  $ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
2544  $m = substr($em, strlen($ps) + 3);
2545 
2546  if (strlen($ps) < 8) {
2547  user_error('Decryption error');
2548  return false;
2549  }
2550 
2551  // Output M
2552 
2553  return $m;
2554  }
2555 
2565  function _emsa_pss_encode($m, $emBits)
2566  {
2567  // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
2568  // be output.
2569 
2570  $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
2571  $sLen = $this->sLen ? $this->sLen : $this->hLen;
2572 
2573  $mHash = $this->hash->hash($m);
2574  if ($emLen < $this->hLen + $sLen + 2) {
2575  user_error('Encoding error');
2576  return false;
2577  }
2578 
2579  $salt = Random::string($sLen);
2580  $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
2581  $h = $this->hash->hash($m2);
2582  $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
2583  $db = $ps . chr(1) . $salt;
2584  $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
2585  $maskedDB = $db ^ $dbMask;
2586  $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0];
2587  $em = $maskedDB . $h . chr(0xBC);
2588 
2589  return $em;
2590  }
2591 
2603  function _emsa_pss_verify($m, $em, $emBits)
2604  {
2605  // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
2606  // be output.
2607 
2608  $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8);
2609  $sLen = $this->sLen ? $this->sLen : $this->hLen;
2610 
2611  $mHash = $this->hash->hash($m);
2612  if ($emLen < $this->hLen + $sLen + 2) {
2613  return false;
2614  }
2615 
2616  if ($em[strlen($em) - 1] != chr(0xBC)) {
2617  return false;
2618  }
2619 
2620  $maskedDB = substr($em, 0, -$this->hLen - 1);
2621  $h = substr($em, -$this->hLen - 1, $this->hLen);
2622  $temp = chr(0xFF << ($emBits & 7));
2623  if ((~$maskedDB[0] & $temp) != $temp) {
2624  return false;
2625  }
2626  $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
2627  $db = $maskedDB ^ $dbMask;
2628  $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
2629  $temp = $emLen - $this->hLen - $sLen - 2;
2630  if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
2631  return false;
2632  }
2633  $salt = substr($db, $temp + 1); // should be $sLen long
2634  $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
2635  $h2 = $this->hash->hash($m2);
2636  return $this->_equals($h, $h2);
2637  }
2638 
2649  {
2650  // EMSA-PSS encoding
2651 
2652  $em = $this->_emsa_pss_encode($m, 8 * $this->k - 1);
2653 
2654  // RSA signature
2655 
2656  $m = $this->_os2ip($em);
2657  $s = $this->_rsasp1($m);
2658  $s = $this->_i2osp($s, $this->k);
2659 
2660  // Output the signature S
2661 
2662  return $s;
2663  }
2664 
2676  {
2677  // Length checking
2678 
2679  if (strlen($s) != $this->k) {
2680  user_error('Invalid signature');
2681  return false;
2682  }
2683 
2684  // RSA verification
2685 
2686  $modBits = 8 * $this->k;
2687 
2688  $s2 = $this->_os2ip($s);
2689  $m2 = $this->_rsavp1($s2);
2690  if ($m2 === false) {
2691  user_error('Invalid signature');
2692  return false;
2693  }
2694  $em = $this->_i2osp($m2, $modBits >> 3);
2695  if ($em === false) {
2696  user_error('Invalid signature');
2697  return false;
2698  }
2699 
2700  // EMSA-PSS verification
2701 
2702  return $this->_emsa_pss_verify($m, $em, $modBits - 1);
2703  }
2704 
2715  function _emsa_pkcs1_v1_5_encode($m, $emLen)
2716  {
2717  $h = $this->hash->hash($m);
2718  if ($h === false) {
2719  return false;
2720  }
2721 
2722  // see http://tools.ietf.org/html/rfc3447#page-43
2723  switch ($this->hashName) {
2724  case 'md2':
2725  $t = pack('H*', '3020300c06082a864886f70d020205000410');
2726  break;
2727  case 'md5':
2728  $t = pack('H*', '3020300c06082a864886f70d020505000410');
2729  break;
2730  case 'sha1':
2731  $t = pack('H*', '3021300906052b0e03021a05000414');
2732  break;
2733  case 'sha256':
2734  $t = pack('H*', '3031300d060960864801650304020105000420');
2735  break;
2736  case 'sha384':
2737  $t = pack('H*', '3041300d060960864801650304020205000430');
2738  break;
2739  case 'sha512':
2740  $t = pack('H*', '3051300d060960864801650304020305000440');
2741  }
2742  $t.= $h;
2743  $tLen = strlen($t);
2744 
2745  if ($emLen < $tLen + 11) {
2746  user_error('Intended encoded message length too short');
2747  return false;
2748  }
2749 
2750  $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
2751 
2752  $em = "\0\1$ps\0$t";
2753 
2754  return $em;
2755  }
2756 
2767  {
2768  // EMSA-PKCS1-v1_5 encoding
2769 
2770  $em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
2771  if ($em === false) {
2772  user_error('RSA modulus too short');
2773  return false;
2774  }
2775 
2776  // RSA signature
2777 
2778  $m = $this->_os2ip($em);
2779  $s = $this->_rsasp1($m);
2780  $s = $this->_i2osp($s, $this->k);
2781 
2782  // Output the signature S
2783 
2784  return $s;
2785  }
2786 
2797  {
2798  // Length checking
2799 
2800  if (strlen($s) != $this->k) {
2801  user_error('Invalid signature');
2802  return false;
2803  }
2804 
2805  // RSA verification
2806 
2807  $s = $this->_os2ip($s);
2808  $m2 = $this->_rsavp1($s);
2809  if ($m2 === false) {
2810  user_error('Invalid signature');
2811  return false;
2812  }
2813  $em = $this->_i2osp($m2, $this->k);
2814  if ($em === false) {
2815  user_error('Invalid signature');
2816  return false;
2817  }
2818 
2819  // EMSA-PKCS1-v1_5 encoding
2820 
2821  $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
2822  if ($em2 === false) {
2823  user_error('RSA modulus too short');
2824  return false;
2825  }
2826 
2827  // Compare
2828  return $this->_equals($em, $em2);
2829  }
2830 
2839  function setEncryptionMode($mode)
2840  {
2841  $this->encryptionMode = $mode;
2842  }
2843 
2852  function setSignatureMode($mode)
2853  {
2854  $this->signatureMode = $mode;
2855  }
2856 
2863  function setComment($comment)
2864  {
2865  $this->comment = $comment;
2866  }
2867 
2874  function getComment()
2875  {
2876  return $this->comment;
2877  }
2878 
2891  function encrypt($plaintext)
2892  {
2893  switch ($this->encryptionMode) {
2894  case self::ENCRYPTION_NONE:
2895  $plaintext = str_split($plaintext, $this->k);
2896  $ciphertext = '';
2897  foreach ($plaintext as $m) {
2898  $ciphertext.= $this->_raw_encrypt($m);
2899  }
2900  return $ciphertext;
2901  case self::ENCRYPTION_PKCS1:
2902  $length = $this->k - 11;
2903  if ($length <= 0) {
2904  return false;
2905  }
2906 
2907  $plaintext = str_split($plaintext, $length);
2908  $ciphertext = '';
2909  foreach ($plaintext as $m) {
2910  $ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m);
2911  }
2912  return $ciphertext;
2913  //case self::ENCRYPTION_OAEP:
2914  default:
2915  $length = $this->k - 2 * $this->hLen - 2;
2916  if ($length <= 0) {
2917  return false;
2918  }
2919 
2920  $plaintext = str_split($plaintext, $length);
2921  $ciphertext = '';
2922  foreach ($plaintext as $m) {
2923  $ciphertext.= $this->_rsaes_oaep_encrypt($m);
2924  }
2925  return $ciphertext;
2926  }
2927  }
2928 
2937  function decrypt($ciphertext)
2938  {
2939  if ($this->k <= 0) {
2940  return false;
2941  }
2942 
2943  $ciphertext = str_split($ciphertext, $this->k);
2944  $ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0), STR_PAD_LEFT);
2945 
2946  $plaintext = '';
2947 
2948  switch ($this->encryptionMode) {
2949  case self::ENCRYPTION_NONE:
2950  $decrypt = '_raw_encrypt';
2951  break;
2952  case self::ENCRYPTION_PKCS1:
2953  $decrypt = '_rsaes_pkcs1_v1_5_decrypt';
2954  break;
2955  //case self::ENCRYPTION_OAEP:
2956  default:
2957  $decrypt = '_rsaes_oaep_decrypt';
2958  }
2959 
2960  foreach ($ciphertext as $c) {
2961  $temp = $this->$decrypt($c);
2962  if ($temp === false) {
2963  return false;
2964  }
2965  $plaintext.= $temp;
2966  }
2967 
2968  return $plaintext;
2969  }
2970 
2979  function sign($message)
2980  {
2981  if (empty($this->modulus) || empty($this->exponent)) {
2982  return false;
2983  }
2984 
2985  switch ($this->signatureMode) {
2986  case self::SIGNATURE_PKCS1:
2987  return $this->_rsassa_pkcs1_v1_5_sign($message);
2988  //case self::SIGNATURE_PSS:
2989  default:
2990  return $this->_rsassa_pss_sign($message);
2991  }
2992  }
2993 
3003  function verify($message, $signature)
3004  {
3005  if (empty($this->modulus) || empty($this->exponent)) {
3006  return false;
3007  }
3008 
3009  switch ($this->signatureMode) {
3010  case self::SIGNATURE_PKCS1:
3011  return $this->_rsassa_pkcs1_v1_5_verify($message, $signature);
3012  //case self::SIGNATURE_PSS:
3013  default:
3014  return $this->_rsassa_pss_verify($message, $signature);
3015  }
3016  }
3017 
3025  function _extractBER($str)
3026  {
3027  /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them
3028  * above and beyond the ceritificate.
3029  * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line:
3030  *
3031  * Bag Attributes
3032  * localKeyID: 01 00 00 00
3033  * subject=/O=organization/OU=org unit/CN=common name
3034  * issuer=/O=organization/CN=common name
3035  */
3036  $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1);
3037  // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
3038  $temp = preg_replace('#-+[^-]+-+#', '', $temp);
3039  // remove new lines
3040  $temp = str_replace(array("\r", "\n", ' '), '', $temp);
3041  $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
3042  return $temp != false ? $temp : $str;
3043  }
3044 }
_convertPublicKey($n, $e)
Convert a public key to the appropriate format.
Definition: RSA.php:949
_decodeLength(&$string)
DER-decode the length.
Definition: RSA.php:1906
_equals($x, $y)
Performs blinded RSA equality testing.
Definition: RSA.php:2199
setSignatureMode($mode)
Set Signature Mode.
Definition: RSA.php:2852
const ASN1_OCTETSTRING
ASN1 Octet String.
Definition: RSA.php:142
const ENCRYPTION_PKCS1
Use PKCS#1 padding.
Definition: RSA.php:93
_rsavp1($s)
RSAVP1.
Definition: RSA.php:2276
_rsaes_oaep_encrypt($m, $l='')
RSAES-OAEP-ENCRYPT.
Definition: RSA.php:2320
$format
Definition: metadata.php:141
const PUBLIC_FORMAT_PKCS1_RAW
Definition: RSA.php:225
const PRIVATE_FORMAT_XML
XML formatted private key.
Definition: RSA.php:187
$config
Definition: bootstrap.php:15
$result
const ASN1_BITSTRING
ASN1 Bit String.
Definition: RSA.php:138
_rsaes_pkcs1_v1_5_decrypt($c)
RSAES-PKCS1-V1_5-DECRYPT.
Definition: RSA.php:2516
$type
const PUBLIC_FORMAT_PKCS8
PKCS#1 formatted public key (encapsulated)
Definition: RSA.php:249
const PUBLIC_FORMAT_RAW
#-
Definition: RSA.php:212
$h
const MODE_OPENSSL
To use the OpenSSL library.
Definition: RSA.php:166
_rsassa_pss_verify($m, $s)
RSASSA-PSS-VERIFY.
Definition: RSA.php:2675
const PRIVATE_FORMAT_PKCS8
PKCS#8 formatted private key.
Definition: RSA.php:191
getComment()
Get public key comment.
Definition: RSA.php:2874
setPublicKeyFormat($format)
Determines the public key format.
Definition: RSA.php:1973
sign($message)
Create a signature.
Definition: RSA.php:2979
createKey($bits=1024, $timeout=false, $partial=array())
Create public / private key pair.
Definition: RSA.php:540
decrypt($ciphertext)
Decryption.
Definition: RSA.php:2937
_rsadp($c)
RSADP.
Definition: RSA.php:2240
$s
Definition: pwgen.php:45
const ENCRYPTION_OAEP
#+ public
Definition: RSA.php:86
setEncryptionMode($mode)
Set Encryption Mode.
Definition: RSA.php:2839
$index
Definition: metadata.php:60
_start_element_handler($parser, $name, $attribs)
Start Element Handler.
Definition: RSA.php:1439
getPrivateKey($type=self::PUBLIC_FORMAT_PKCS1)
Returns the private key.
Definition: RSA.php:1804
const ASN1_SEQUENCE
ASN1 Sequence (with the constucted bit set)
Definition: RSA.php:150
setComment($comment)
Set public key comment.
Definition: RSA.php:2863
getPublicKeyFingerprint($algorithm='md5')
Returns the public key&#39;s fingerprint.
Definition: RSA.php:1770
const ASN1_OBJECT
ASN1 Object Identifier.
Definition: RSA.php:146
_encodeLength($length)
DER-encode the length.
Definition: RSA.php:1927
Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic...
Definition: AES.php:50
$start
Definition: bench.php:8
setPublicKey($key=false, $type=false)
Defines the public key.
Definition: RSA.php:1655
$base
Definition: index.php:4
const MODE_INTERNAL
#-
Definition: RSA.php:160
setHash($hash)
Determines which hashing function should be used.
Definition: RSA.php:1987
setSaltLength($sLen)
Determines the salt length.
Definition: RSA.php:2045
$r
Definition: example_031.php:79
catch(Exception $e) $message
_rsassa_pkcs1_v1_5_sign($m)
RSASSA-PKCS1-V1_5-SIGN.
Definition: RSA.php:2766
_parseKey($key, $type)
Break a public or private key down into its constituant components.
Definition: RSA.php:1029
$y
Definition: example_007.php:83
const PUBLIC_FORMAT_XML
XML formatted public key.
Definition: RSA.php:229
loadKey($key, $type=false)
Loads a public or private key.
Definition: RSA.php:1513
_mgf1($mgfSeed, $maskLen)
MGF1.
Definition: RSA.php:2295
comment()
Definition: comment.php:2
_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients)
Convert a private key to the appropriate format.
Definition: RSA.php:720
setPrivateKey($key=false, $type=false)
Defines the private key.
Definition: RSA.php:1715
Pure-PHP PKCS#1 compliant implementation of RSA.
setPrivateKeyFormat($format)
Determines the private key format.
Definition: RSA.php:1961
_os2ip($x)
Octet-String-to-Integer primitive.
Definition: RSA.php:2079
_rsassa_pss_sign($m)
RSASSA-PSS-SIGN.
Definition: RSA.php:2648
encrypt($plaintext)
Encryption.
Definition: RSA.php:2891
getSize()
Returns the key size.
Definition: RSA.php:1424
_string_shift(&$string, $index=1)
String Shift.
Definition: RSA.php:1947
setMGFHash($hash)
Determines which hashing function should be used for the mask generation function.
Definition: RSA.php:2016
_stop_element_handler($parser, $name)
Stop Element Handler.
Definition: RSA.php:1479
Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic...
$n
Definition: RandomTest.php:85
Pure-PHP implementation of Triple DES.
_emsa_pkcs1_v1_5_encode($m, $emLen)
EMSA-PKCS1-V1_5-ENCODE.
Definition: RSA.php:2715
_extractBER($str)
Extract raw BER from Base64 encoding.
Definition: RSA.php:3025
_exponentiate($x)
Exponentiate with or without Chinese Remainder Theorem.
Definition: RSA.php:2093
const SIGNATURE_PKCS1
Use the PKCS#1 scheme by default.
Definition: RSA.php:124
getPublicKey($type=self::PUBLIC_FORMAT_PKCS8)
Returns the public key.
Definition: RSA.php:1745
_rsasp1($m)
RSASP1.
Definition: RSA.php:2258
$parser
Definition: BPMN2Parser.php:23
setPassword($password=false)
Sets the password.
Definition: RSA.php:1629
global $l
Definition: afr.php:30
Pure-PHP arbitrary precision integer arithmetic library.
_rsassa_pkcs1_v1_5_verify($m, $s)
RSASSA-PKCS1-V1_5-VERIFY.
Definition: RSA.php:2796
_rsaes_oaep_decrypt($c, $l='')
RSAES-OAEP-DECRYPT.
Definition: RSA.php:2383
_rsaep($m)
RSAEP.
Definition: RSA.php:2222
$i
Definition: disco.tpl.php:19
_data_handler($parser, $data)
Data Handler.
Definition: RSA.php:1496
verify($message, $signature)
Verifies a signature.
Definition: RSA.php:3003
__clone()
__clone() magic method
Definition: RSA.php:1863
_emsa_pss_encode($m, $emBits)
EMSA-PSS-ENCODE.
Definition: RSA.php:2565
_getPrivatePublicKey($mode=self::PUBLIC_FORMAT_PKCS8)
Returns a minimalistic private key.
Definition: RSA.php:1828
const MODE_CFB
Encrypt / decrypt using the Cipher Feedback mode.
Definition: Base.php:80
const PRIVATE_FORMAT_PUTTY
PuTTY formatted private key.
Definition: RSA.php:183
static string($length)
Generate a random string.
Definition: Random.php:54
const PUBLIC_FORMAT_PKCS1
PKCS#1 formatted public key (raw)
Definition: RSA.php:224
$source
Definition: linkback.php:22
_rsaes_pkcs1_v1_5_encrypt($m)
RSAES-PKCS1-V1_5-ENCRYPT.
Definition: RSA.php:2457
Pure-PHP implementation of DES.
const ENCRYPTION_NONE
Do not use any padding.
Definition: RSA.php:100
Pure-PHP implementation of AES.
hash(StreamInterface $stream, $algo, $rawOutput=false)
Calculate a hash of a Stream.
Definition: functions.php:406
_generateMinMax($bits)
Generates the smallest and largest numbers requiring $bits bits.
Definition: RSA.php:1877
_raw_encrypt($m)
Raw Encryption / Decryption.
Definition: RSA.php:2441
$key
Definition: croninfo.php:18
_i2osp($x, $xLen)
Integer-to-Octet-String primitive.
Definition: RSA.php:2060
$x
Definition: complexTest.php:9
__toString()
__toString() magic method
Definition: RSA.php:1847
if(function_exists('posix_getuid') &&posix_getuid()===0) if(!array_key_exists('t', $options)) $tag
Definition: cron.php:35
for($i=6; $i< 13; $i++) for($i=1; $i< 13; $i++) $d
Definition: date.php:296
const PUBLIC_FORMAT_OPENSSH
OpenSSH formatted public key.
Definition: RSA.php:235
const PRIVATE_FORMAT_PKCS1
#-
Definition: RSA.php:179
_emsa_pss_verify($m, $em, $emBits)
EMSA-PSS-VERIFY.
Definition: RSA.php:2603
const SIGNATURE_PSS
#-
Definition: RSA.php:117
$data
Definition: bench.php:6
const ASN1_INTEGER
#-
Definition: RSA.php:134
_blind($x, $r, $i)
Performs RSA Blinding.
Definition: RSA.php:2173
__construct()
The constructor.
Definition: RSA.php:464