466 $this->configFile = dirname(__FILE__) .
'/../openssl.cnf';
468 if (!defined(
'CRYPT_RSA_MODE')) {
473 case defined(
'MATH_BIGINTEGER_OPENSSL_DISABLE'):
474 define(
'CRYPT_RSA_MODE', self::MODE_INTERNAL);
476 case extension_loaded(
'openssl') && file_exists($this->configFile):
480 $content = ob_get_contents();
483 preg_match_all(
'#OpenSSL (Header|Library) Version(.*)#im', $content, $matches);
486 if (!empty($matches[1])) {
487 for (
$i = 0;
$i < count($matches[1]);
$i++) {
488 $fullVersion = trim(str_replace(
'=>',
'', strip_tags($matches[2][
$i])));
491 if (!preg_match(
'/(\d+\.\d+\.\d+)/i', $fullVersion,
$m)) {
492 $versions[$matches[1][
$i]] = $fullVersion;
494 $versions[$matches[1][
$i]] =
$m[0];
501 case !isset($versions[
'Header']):
502 case !isset($versions[
'Library']):
503 case $versions[
'Header'] == $versions[
'Library']:
504 define(
'CRYPT_RSA_MODE', self::MODE_OPENSSL);
507 define(
'CRYPT_RSA_MODE', self::MODE_INTERNAL);
508 define(
'MATH_BIGINTEGER_OPENSSL_DISABLE',
true);
512 define(
'CRYPT_RSA_MODE', self::MODE_INTERNAL);
520 $this->hLen = $this->
hash->getLength();
521 $this->hashName =
'sha1';
522 $this->mgfHash =
new Hash(
'sha1');
523 $this->mgfHLen = $this->mgfHash->getLength();
540 function createKey($bits = 1024, $timeout =
false, $partial = array())
542 if (!defined(
'CRYPT_RSA_EXPONENT')) {
544 define(
'CRYPT_RSA_EXPONENT',
'65537');
552 if (!defined(
'CRYPT_RSA_SMALLEST_PRIME')) {
553 define(
'CRYPT_RSA_SMALLEST_PRIME', 4096);
557 if (CRYPT_RSA_MODE == self::MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) {
559 if (isset($this->configFile)) {
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'];
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)));
571 while (openssl_error_string() !==
false) {
575 'privatekey' => $privatekey,
576 'publickey' => $publickey,
577 'partialkey' =>
false 589 if ($temp > CRYPT_RSA_SMALLEST_PRIME) {
590 $num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME);
591 $temp = CRYPT_RSA_SMALLEST_PRIME;
601 $n = $this->one->copy();
602 if (!empty($partial)) {
603 extract(unserialize($partial));
605 $exponents = $coefficients = $primes = array();
607 'top' => $this->one->copy(),
613 $i0 = count($primes) + 1;
616 for (
$i = $i0;
$i <= $num_primes;
$i++) {
617 if ($timeout !==
false) {
618 $timeout-= time() -
$start;
624 'partialkey' => serialize(array(
626 'coefficients' => $coefficients,
628 'exponents' => $exponents
634 if (
$i == $num_primes) {
635 list($min, $temp) = $absoluteMin->divide(
$n);
636 if (!$temp->equals($this->zero)) {
637 $min = $min->add($this->one);
639 $primes[
$i] = $generator->randomPrime($min, $finalMax, $timeout);
641 $primes[
$i] = $generator->randomPrime($min, $max, $timeout);
644 if ($primes[
$i] ===
false) {
645 if (count($primes) > 1) {
649 $partialkey = serialize(array(
651 'coefficients' => $coefficients,
653 'exponents' => $exponents
660 'partialkey' => $partialkey
667 $coefficients[
$i] =
$n->modInverse($primes[
$i]);
670 $n =
$n->multiply($primes[
$i]);
672 $temp = $primes[
$i]->subtract($this->one);
676 $lcm[
'top'] = $lcm[
'top']->multiply($temp);
677 $lcm[
'bottom'] = $lcm[
'bottom'] ===
false ? $temp : $lcm[
'bottom']->gcd($temp);
679 $exponents[
$i] = $e->modInverse($temp);
682 list($temp) = $lcm[
'top']->divide($lcm[
'bottom']);
683 $gcd = $temp->gcd($e);
685 }
while (!$gcd->equals($this->one));
687 $d = $e->modInverse($temp);
689 $coefficients[2] = $primes[2]->modInverse($primes[1]);
708 'partialkey' =>
false 722 $signed = $this->privateKeyFormat != self::PRIVATE_FORMAT_XML;
723 $num_primes = count($primes);
725 'version' => $num_primes == 2 ? chr(0) : chr(1),
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)
738 switch ($this->privateKeyFormat) {
739 case self::PRIVATE_FORMAT_XML:
740 if ($num_primes != 2) {
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" .
754 case self::PRIVATE_FORMAT_PUTTY:
755 if ($num_primes != 2) {
758 $key =
"PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: ";
759 $encryption = (!empty($this->password) || is_string($this->password)) ?
'aes256-cbc' :
'none';
766 strlen($raw[
'publicExponent']),
767 $raw[
'publicExponent'],
768 strlen($raw[
'modulus']),
782 $public = base64_encode($public);
783 $key.=
"Public-Lines: " . ((strlen($public) + 63) >> 6) .
"\r\n";
784 $key.= chunk_split($public, 64);
787 strlen($raw[
'privateExponent']),
788 $raw[
'privateExponent'],
789 strlen($raw[
'prime1']),
791 strlen($raw[
'prime2']),
793 strlen($raw[
'coefficient']),
796 if (empty($this->password) && !is_string($this->password)) {
797 $source.= pack(
'Na*', strlen($private), $private);
798 $hashkey =
'putty-private-key-file-mac-key';
801 $source.= pack(
'Na*', strlen($private), $private);
804 while (strlen($symkey) < 32) {
805 $temp = pack(
'Na*', $sequence++, $this->password);
806 $symkey.= pack(
'H*', sha1($temp));
808 $symkey = substr($symkey, 0, 32);
811 $crypto->setKey($symkey);
812 $crypto->disablePadding();
813 $private = $crypto->encrypt($private);
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";
826 $components = array();
827 foreach ($raw as
$name => $value) {
828 $components[
$name] = pack(
'Ca*a*', self::ASN1_INTEGER, $this->
_encodeLength(strlen($value)), $value);
831 $RSAPrivateKey = implode(
'', $components);
833 if ($num_primes > 2) {
834 $OtherPrimeInfos =
'';
835 for (
$i = 3;
$i <= $num_primes;
$i++) {
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);
848 $RSAPrivateKey.= pack(
'Ca*a*', self::ASN1_SEQUENCE, $this->
_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos);
851 $RSAPrivateKey = pack(
'Ca*a*', self::ASN1_SEQUENCE, $this->
_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
853 if ($this->privateKeyFormat == self::PRIVATE_FORMAT_PKCS8) {
854 $rsaOID = pack(
'H*',
'300d06092a864886f70d0101010500');
855 $RSAPrivateKey = pack(
864 $RSAPrivateKey = pack(
'Ca*a*', self::ASN1_SEQUENCE, $this->
_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
865 if (!empty($this->password) || is_string($this->password)) {
867 $iterationCount = 2048;
870 $crypto->setPassword($this->password,
'pbkdf1',
'md5', $salt, $iterationCount);
871 $RSAPrivateKey = $crypto->encrypt($RSAPrivateKey);
875 self::ASN1_OCTETSTRING,
882 $pbeWithMD5AndDES_CBC =
"\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03";
884 $encryptionAlgorithm = pack(
888 $pbeWithMD5AndDES_CBC,
894 $RSAPrivateKey = pack(
898 $encryptionAlgorithm,
899 self::ASN1_OCTETSTRING,
904 $RSAPrivateKey = pack(
'Ca*a*', self::ASN1_SEQUENCE, $this->
_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
906 $RSAPrivateKey =
"-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" .
907 chunk_split(base64_encode($RSAPrivateKey), 64) .
908 '-----END ENCRYPTED PRIVATE KEY-----';
910 $RSAPrivateKey =
"-----BEGIN PRIVATE KEY-----\r\n" .
911 chunk_split(base64_encode($RSAPrivateKey), 64) .
912 '-----END PRIVATE KEY-----';
914 return $RSAPrivateKey;
917 if (!empty($this->password) || is_string($this->password)) {
919 $symkey = pack(
'H*', md5($this->password . $iv));
920 $symkey.= substr(pack(
'H*', md5($symkey . $this->password . $iv)), 0, 8);
922 $des->setKey($symkey);
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" .
929 chunk_split(base64_encode($des->encrypt($RSAPrivateKey)), 64) .
930 '-----END RSA PRIVATE KEY-----';
932 $RSAPrivateKey =
"-----BEGIN RSA PRIVATE KEY-----\r\n" .
933 chunk_split(base64_encode($RSAPrivateKey), 64) .
934 '-----END RSA PRIVATE KEY-----';
937 return $RSAPrivateKey;
951 $signed = $this->publicKeyFormat != self::PUBLIC_FORMAT_XML;
953 $modulus =
$n->toBytes($signed);
954 $publicExponent = $e->toBytes($signed);
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" .
965 case self::PUBLIC_FORMAT_OPENSSH:
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;
973 return $RSAPublicKey;
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)
985 $RSAPublicKey = pack(
988 $this->
_encodeLength(strlen($components[
'modulus']) + strlen($components[
'publicExponent'])),
989 $components[
'modulus'],
990 $components[
'publicExponent']
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-----';
999 $rsaOID = pack(
'H*',
'300d06092a864886f70d0101010500');
1000 $RSAPublicKey = chr(0) . $RSAPublicKey;
1001 $RSAPublicKey = chr(3) . $this->
_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;
1003 $RSAPublicKey = pack(
1005 self::ASN1_SEQUENCE,
1007 $rsaOID . $RSAPublicKey
1010 $RSAPublicKey =
"-----BEGIN PUBLIC KEY-----\r\n" .
1011 chunk_split(base64_encode($RSAPublicKey), 64) .
1012 '-----END PUBLIC KEY-----';
1015 return $RSAPublicKey;
1031 if (
$type != self::PUBLIC_FORMAT_RAW && !is_string(
$key)) {
1036 case self::PUBLIC_FORMAT_RAW:
1037 if (!is_array(
$key)) {
1040 $components = array();
1042 case isset(
$key[
'e']):
1043 $components[
'publicExponent'] =
$key[
'e']->copy();
1045 case isset(
$key[
'exponent']):
1046 $components[
'publicExponent'] =
$key[
'exponent']->copy();
1048 case isset(
$key[
'publicExponent']):
1049 $components[
'publicExponent'] =
$key[
'publicExponent']->copy();
1051 case isset(
$key[0]):
1052 $components[
'publicExponent'] =
$key[0]->copy();
1055 case isset(
$key[
'n']):
1056 $components[
'modulus'] =
$key[
'n']->copy();
1058 case isset(
$key[
'modulo']):
1059 $components[
'modulus'] =
$key[
'modulo']->copy();
1061 case isset(
$key[
'modulus']):
1062 $components[
'modulus'] =
$key[
'modulus']->copy();
1064 case isset(
$key[1]):
1065 $components[
'modulus'] =
$key[1]->copy();
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:
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)));
1089 $symkey.= pack(
'H*', md5($symkey . $this->password . substr($iv, 0, 8)));
1091 $key = preg_replace(
'#^(?:Proc-Type|DEK-Info): .*#m',
'',
$key);
1093 if ($ciphertext ===
false) {
1096 switch ($matches[1]) {
1098 $crypto =
new AES();
1101 $symkey = substr($symkey, 0, 16);
1102 $crypto =
new AES();
1104 case 'DES-EDE3-CFB':
1107 case 'DES-EDE3-CBC':
1108 $symkey = substr($symkey, 0, 24);
1112 $crypto =
new DES();
1117 $crypto->setKey($symkey);
1118 $crypto->setIV($iv);
1119 $decoded = $crypto->decrypt($ciphertext);
1124 if ($decoded !==
false) {
1128 $components = array();
1149 if (
$tag == self::ASN1_INTEGER && substr(
$key, 0, 3) ==
"\x01\x00\x30") {
1151 $tag = self::ASN1_SEQUENCE;
1154 if (
$tag == self::ASN1_SEQUENCE) {
1156 if (ord($this->
_string_shift($temp)) != self::ASN1_OBJECT) {
1161 case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01":
1163 case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03":
1169 if (ord($this->
_string_shift($temp)) != self::ASN1_SEQUENCE) {
1177 if (ord($this->
_string_shift($temp)) != self::ASN1_INTEGER) {
1181 list(, $iterationCount) = unpack(
'N', str_pad($temp, 4, chr(0), STR_PAD_LEFT));
1184 if (strlen(
$key) != $length) {
1188 $crypto =
new DES();
1189 $crypto->setPassword($this->password,
'pbkdf1',
'md5', $salt, $iterationCount);
1191 if (
$key ===
false) {
1210 if (
$tag == self::ASN1_BITSTRING) {
1221 if (
$tag != self::ASN1_INTEGER) {
1227 if (strlen($temp) != 1 || ord($temp) > 2) {
1228 $components[
'modulus'] =
new BigInteger($temp, 256);
1267 while (!empty(
$key)) {
1285 case self::PUBLIC_FORMAT_OPENSSH:
1286 $parts = explode(
' ',
$key, 3);
1288 $key = isset($parts[1]) ? base64_decode($parts[1]) :
false;
1289 if (
$key ===
false) {
1293 $comment = isset($parts[2]) ? $parts[2] :
false;
1295 $cleanup = substr(
$key, 0, 11) ==
"\0\0\0\7ssh-rsa";
1297 if (strlen(
$key) <= 4) {
1302 if (strlen(
$key) <= 4) {
1308 if ($cleanup && strlen(
$key)) {
1309 if (strlen(
$key) <= 4) {
1314 return strlen(
$key) ? false : array(
1315 'modulus' => $realModulus,
1316 'publicExponent' => $modulus,
1317 'comment' => $comment
1320 return strlen(
$key) ? false : array(
1321 'modulus' => $modulus,
1322 'publicExponent' => $publicExponent,
1323 'comment' => $comment
1328 case self::PRIVATE_FORMAT_XML:
1329 case self::PUBLIC_FORMAT_XML:
1330 $this->components = array();
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');
1337 if (!xml_parse(
$xml,
'<xml>' .
$key .
'</xml>')) {
1341 return isset($this->components[
'modulus']) && isset($this->components[
'publicExponent']) ? $this->components :
false;
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') {
1350 $encryption = trim(preg_replace(
'#Encryption: (.+)#',
'$1',
$key[1]));
1351 $comment = trim(preg_replace(
'#Comment: (.+)#',
'$1',
$key[2]));
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)));
1358 extract(unpack(
'Nlength', $this->
_string_shift($public, 4)));
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))));
1364 switch ($encryption) {
1368 while (strlen($symkey) < 32) {
1369 $temp = pack(
'Na*', $sequence++, $this->password);
1370 $symkey.= pack(
'H*', sha1($temp));
1372 $symkey = substr($symkey, 0, 32);
1373 $crypto =
new AES();
1376 if ($encryption !=
'none') {
1377 $crypto->setKey($symkey);
1378 $crypto->disablePadding();
1379 $private = $crypto->decrypt($private);
1380 if ($private ===
false) {
1385 extract(unpack(
'Nlength', $this->
_string_shift($private, 4)));
1386 if (strlen($private) < $length) {
1390 extract(unpack(
'Nlength', $this->
_string_shift($private, 4)));
1391 if (strlen($private) < $length) {
1395 extract(unpack(
'Nlength', $this->
_string_shift($private, 4)));
1396 if (strlen($private) < $length) {
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);
1406 extract(unpack(
'Nlength', $this->
_string_shift($private, 4)));
1407 if (strlen($private) < $length) {
1426 return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits());
1444 $this->current = &$this->components[
'modulus'];
1447 $this->current = &$this->components[
'publicExponent'];
1450 $this->current = &$this->components[
'primes'][1];
1453 $this->current = &$this->components[
'primes'][2];
1456 $this->current = &$this->components[
'exponents'][1];
1459 $this->current = &$this->components[
'exponents'][2];
1462 $this->current = &$this->components[
'coefficients'][2];
1465 $this->current = &$this->components[
'privateExponent'];
1467 $this->current =
'';
1481 if (isset($this->current)) {
1482 $this->current =
new BigInteger(base64_decode($this->current), 256);
1483 unset($this->current);
1498 if (!isset($this->current) || is_object($this->current)) {
1501 $this->current.= trim(
$data);
1516 $this->privateKeyFormat =
$key->privateKeyFormat;
1517 $this->publicKeyFormat =
$key->publicKeyFormat;
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;
1528 if (is_object(
$key->hash)) {
1531 if (is_object(
$key->mgfHash)) {
1532 $this->mgfHash =
new Hash(
$key->mgfHash->getHash());
1535 if (is_object(
$key->modulus)) {
1536 $this->modulus =
$key->modulus->copy();
1538 if (is_object(
$key->exponent)) {
1539 $this->exponent =
$key->exponent->copy();
1541 if (is_object(
$key->publicExponent)) {
1542 $this->publicExponent =
$key->publicExponent->copy();
1545 $this->primes = array();
1546 $this->exponents = array();
1547 $this->coefficients = array();
1549 foreach ($this->primes as $prime) {
1550 $this->primes[] = $prime->copy();
1552 foreach ($this->exponents as $exponent) {
1553 $this->exponents[] = $exponent->copy();
1555 foreach ($this->coefficients as $coefficient) {
1556 $this->coefficients[] = $coefficient->copy();
1562 if (
$type ===
false) {
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
1570 foreach ($types as
$type) {
1572 if ($components !==
false) {
1580 if ($components ===
false) {
1584 if (isset($components[
'comment']) && $components[
'comment'] !==
false) {
1585 $this->
comment = $components[
'comment'];
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'];
1596 $this->primes = array();
1597 $this->exponents = array();
1598 $this->coefficients = array();
1599 $this->publicExponent =
false;
1603 case self::PUBLIC_FORMAT_OPENSSH:
1604 case self::PUBLIC_FORMAT_RAW:
1607 case self::PRIVATE_FORMAT_PKCS1:
1609 case strpos(
$key,
'-BEGIN PUBLIC KEY-') !==
false:
1610 case strpos(
$key,
'-BEGIN RSA PUBLIC KEY-') !==
false:
1658 if (!empty($this->publicExponent)) {
1662 if (
$key ===
false && !empty($this->modulus)) {
1667 if (
$type ===
false) {
1669 self::PUBLIC_FORMAT_RAW,
1670 self::PUBLIC_FORMAT_PKCS1,
1671 self::PUBLIC_FORMAT_XML,
1672 self::PUBLIC_FORMAT_OPENSSH
1674 foreach ($types as
$type) {
1676 if ($components !==
false) {
1684 if ($components ===
false) {
1688 if (empty($this->modulus) || !$this->modulus->equals($components[
'modulus'])) {
1689 $this->modulus = $components[
'modulus'];
1690 $this->exponent = $this->publicExponent = $components[
'publicExponent'];
1694 $this->publicExponent = $components[
'publicExponent'];
1717 if (
$key ===
false && !empty($this->publicExponent)) {
1718 unset($this->publicExponent);
1726 unset($rsa->publicExponent);
1747 if (empty($this->modulus) || empty($this->publicExponent)) {
1752 $this->publicKeyFormat =
$type;
1754 $this->publicKeyFormat = $oldFormat;
1772 if (empty($this->modulus) || empty($this->publicExponent)) {
1776 $modulus = $this->modulus->toBytes(
true);
1777 $publicExponent = $this->publicExponent->toBytes(
true);
1779 $RSAPublicKey = pack(
'Na*Na*Na*', strlen(
'ssh-rsa'),
'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);
1781 switch ($algorithm) {
1783 $hash =
new Hash(
'sha256');
1784 $base = base64_encode($hash->hash($RSAPublicKey));
1787 return substr(chunk_split(md5($RSAPublicKey), 2,
':'), 0, -1);
1806 if (empty($this->primes)) {
1811 $this->privateKeyFormat =
$type;
1812 $temp = $this->
_convertPrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients);
1813 $this->privateKeyFormat = $oldFormat;
1830 if (empty($this->modulus) || empty($this->exponent)) {
1835 $this->publicKeyFormat = $mode;
1837 $this->publicKeyFormat = $oldFormat;
1850 if (
$key !==
false) {
1866 $key->loadKey($this);
1879 $bytes = $bits >> 3;
1880 $min = str_repeat(chr(0), $bytes);
1881 $max = str_repeat(chr(0xFF), $bytes);
1884 $min = chr(1 << ($msb - 1)) . $min;
1885 $max = chr((1 << $msb) - 1) . $max;
1887 $min[0] = chr(0x80);
1909 if ($length & 0x80) {
1912 list(, $length) = unpack(
'N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4));
1929 if ($length <= 0x7F) {
1930 return chr($length);
1933 $temp = ltrim(pack(
'N', $length), chr(0));
1934 return pack(
'Ca*', 0x80 | strlen($temp), $temp);
1949 $substr = substr($string, 0,
$index);
1950 $string = substr($string,
$index);
1963 $this->privateKeyFormat =
$format;
1975 $this->publicKeyFormat =
$format;
1998 $this->hashName =
$hash;
2002 $this->hashName =
'sha1';
2004 $this->hLen = $this->
hash->getLength();
2026 $this->mgfHash =
new Hash($hash);
2029 $this->mgfHash =
new Hash(
'sha1');
2031 $this->mgfHLen = $this->mgfHash->getLength();
2047 $this->sLen =
$sLen;
2063 if (strlen(
$x) > $xLen) {
2064 user_error(
'Integer too large');
2067 return str_pad(
$x, $xLen, chr(0), STR_PAD_LEFT);
2095 if (empty($this->primes) || empty($this->coefficients) || empty($this->exponents)) {
2096 return $x->modPow($this->exponent, $this->modulus);
2099 $num_primes = count($this->primes);
2101 if (defined(
'CRYPT_RSA_DISABLE_BLINDING')) {
2103 1 =>
$x->modPow($this->exponents[1], $this->primes[1]),
2104 2 =>
$x->modPow($this->exponents[2], $this->primes[2])
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]));
2111 $r = $this->primes[1];
2112 for (
$i = 3;
$i <= $num_primes;
$i++) {
2113 $m_i =
$x->modPow($this->exponents[
$i], $this->primes[$i]);
2115 $r =
$r->multiply($this->primes[$i - 1]);
2117 $h = $m_i->subtract(
$m);
2118 $h = $h->multiply($this->coefficients[$i]);
2119 list(, $h) = $h->divide($this->primes[$i]);
2121 $m =
$m->add(
$r->multiply($h));
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];
2133 $r = $one->random($one, $smallest->subtract($one));
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]));
2144 $r = $this->primes[1];
2145 for (
$i = 3;
$i <= $num_primes;
$i++) {
2148 $r =
$r->multiply($this->primes[
$i - 1]);
2150 $h = $m_i->subtract(
$m);
2151 $h =
$h->multiply($this->coefficients[
$i]);
2152 list(,
$h) =
$h->divide($this->primes[$i]);
2175 $x =
$x->multiply(
$r->modPow($this->publicExponent, $this->primes[
$i]));
2176 $x =
$x->modPow($this->exponents[
$i], $this->primes[$i]);
2178 $r =
$r->modInverse($this->primes[$i]);
2180 list(,
$x) =
$x->divide($this->primes[$i]);
2201 if (strlen(
$x) != strlen(
$y)) {
2206 for (
$i = 0;
$i < strlen(
$x);
$i++) {
2224 if (
$m->compare($this->zero) < 0 ||
$m->compare($this->modulus) > 0) {
2225 user_error(
'Message representative out of range');
2242 if (
$c->compare($this->zero) < 0 ||
$c->compare($this->modulus) > 0) {
2243 user_error(
'Ciphertext representative out of range');
2260 if (
$m->compare($this->zero) < 0 ||
$m->compare($this->modulus) > 0) {
2261 user_error(
'Message representative out of range');
2278 if (
$s->compare($this->zero) < 0 ||
$s->compare($this->modulus) > 0) {
2279 user_error(
'Signature representative out of range');
2300 $count = ceil($maskLen / $this->mgfHLen);
2301 for (
$i = 0;
$i < $count;
$i++) {
2303 $t.= $this->mgfHash->hash($mgfSeed .
$c);
2306 return substr(
$t, 0, $maskLen);
2329 if ($mLen > $this->k - 2 * $this->hLen - 2) {
2330 user_error(
'Message too long');
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;
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;
2390 if (strlen(
$c) != $this->k || $this->k < 2 * $this->hLen + 2) {
2391 user_error(
'Decryption error');
2400 user_error(
'Decryption error');
2407 $lHash = $this->
hash->hash(
$l);
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');
2421 $m = ltrim(
$m, chr(0));
2422 if (ord(
$m[0]) != 1) {
2423 user_error(
'Decryption error');
2429 return substr(
$m, 1);
2444 $temp = $this->
_rsaep($temp);
2445 return $this->
_i2osp($temp, $this->k);
2463 if ($mLen > $this->k - 11) {
2464 user_error(
'Message too long');
2470 $psLen = $this->k - $mLen - 3;
2472 while (strlen($ps) != $psLen) {
2474 $temp = str_replace(
"\x00",
'', $temp);
2479 if (defined(
'CRYPT_RSA_PKCS15_COMPAT') && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) {
2482 $ps = str_repeat(
"\xFF", $psLen);
2484 $em = chr(0) . chr(
$type) . $ps . chr(0) .
$m;
2520 if (strlen(
$c) != $this->k) {
2521 user_error(
'Decryption error');
2531 user_error(
'Decryption error');
2538 if (ord($em[0]) != 0 || ord($em[1]) > 2) {
2539 user_error(
'Decryption error');
2543 $ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
2544 $m = substr($em, strlen($ps) + 3);
2546 if (strlen($ps) < 8) {
2547 user_error(
'Decryption error');
2570 $emLen = ($emBits + 1) >> 3;
2573 $mHash = $this->
hash->hash(
$m);
2574 if ($emLen < $this->hLen + $sLen + 2) {
2575 user_error(
'Encoding error');
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);
2608 $emLen = ($emBits + 1) >> 3;
2611 $mHash = $this->
hash->hash(
$m);
2612 if ($emLen < $this->hLen + $sLen + 2) {
2616 if ($em[strlen($em) - 1] != chr(0xBC)) {
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) {
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) {
2633 $salt = substr($db, $temp + 1);
2634 $m2 =
"\0\0\0\0\0\0\0\0" . $mHash . $salt;
2635 $h2 = $this->
hash->hash($m2);
2679 if (strlen(
$s) != $this->k) {
2680 user_error(
'Invalid signature');
2690 if ($m2 ===
false) {
2691 user_error(
'Invalid signature');
2694 $em = $this->
_i2osp($m2, $modBits >> 3);
2695 if ($em ===
false) {
2696 user_error(
'Invalid signature');
2723 switch ($this->hashName) {
2725 $t = pack(
'H*',
'3020300c06082a864886f70d020205000410');
2728 $t = pack(
'H*',
'3020300c06082a864886f70d020505000410');
2731 $t = pack(
'H*',
'3021300906052b0e03021a05000414');
2734 $t = pack(
'H*',
'3031300d060960864801650304020105000420');
2737 $t = pack(
'H*',
'3041300d060960864801650304020205000430');
2740 $t = pack(
'H*',
'3051300d060960864801650304020305000440');
2745 if ($emLen < $tLen + 11) {
2746 user_error(
'Intended encoded message length too short');
2750 $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
2752 $em =
"\0\1$ps\0$t";
2771 if ($em ===
false) {
2772 user_error(
'RSA modulus too short');
2800 if (strlen(
$s) != $this->k) {
2801 user_error(
'Invalid signature');
2809 if ($m2 ===
false) {
2810 user_error(
'Invalid signature');
2813 $em = $this->
_i2osp($m2, $this->k);
2814 if ($em ===
false) {
2815 user_error(
'Invalid signature');
2822 if ($em2 ===
false) {
2823 user_error(
'RSA modulus too short');
2828 return $this->
_equals($em, $em2);
2841 $this->encryptionMode = $mode;
2854 $this->signatureMode = $mode;
2893 switch ($this->encryptionMode) {
2894 case self::ENCRYPTION_NONE:
2895 $plaintext = str_split($plaintext, $this->k);
2897 foreach ($plaintext as
$m) {
2901 case self::ENCRYPTION_PKCS1:
2902 $length = $this->k - 11;
2907 $plaintext = str_split($plaintext, $length);
2909 foreach ($plaintext as $m) {
2915 $length = $this->k - 2 * $this->hLen - 2;
2920 $plaintext = str_split($plaintext, $length);
2922 foreach ($plaintext as $m) {
2939 if ($this->k <= 0) {
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);
2948 switch ($this->encryptionMode) {
2949 case self::ENCRYPTION_NONE:
2950 $decrypt =
'_raw_encrypt';
2952 case self::ENCRYPTION_PKCS1:
2953 $decrypt =
'_rsaes_pkcs1_v1_5_decrypt';
2957 $decrypt =
'_rsaes_oaep_decrypt';
2960 foreach ($ciphertext as
$c) {
2961 $temp = $this->$decrypt($c);
2962 if ($temp ===
false) {
2981 if (empty($this->modulus) || empty($this->exponent)) {
2985 switch ($this->signatureMode) {
2986 case self::SIGNATURE_PKCS1:
3005 if (empty($this->modulus) || empty($this->exponent)) {
3009 switch ($this->signatureMode) {
3010 case self::SIGNATURE_PKCS1:
3036 $temp = preg_replace(
'#.*?^-+[^-]+-+[\r\n ]*$#ms',
'', $str, 1);
3038 $temp = preg_replace(
'#-+[^-]+-+#',
'', $temp);
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;
_convertPublicKey($n, $e)
Convert a public key to the appropriate format.
_decodeLength(&$string)
DER-decode the length.
_equals($x, $y)
Performs blinded RSA equality testing.
setSignatureMode($mode)
Set Signature Mode.
const ASN1_OCTETSTRING
ASN1 Octet String.
const ENCRYPTION_PKCS1
Use PKCS#1 padding.
_rsaes_oaep_encrypt($m, $l='')
RSAES-OAEP-ENCRYPT.
const PUBLIC_FORMAT_PKCS1_RAW
const PRIVATE_FORMAT_XML
XML formatted private key.
const ASN1_BITSTRING
ASN1 Bit String.
_rsaes_pkcs1_v1_5_decrypt($c)
RSAES-PKCS1-V1_5-DECRYPT.
const PUBLIC_FORMAT_PKCS8
PKCS#1 formatted public key (encapsulated)
const PUBLIC_FORMAT_RAW
#-
const MODE_OPENSSL
To use the OpenSSL library.
_rsassa_pss_verify($m, $s)
RSASSA-PSS-VERIFY.
const PRIVATE_FORMAT_PKCS8
PKCS#8 formatted private key.
getComment()
Get public key comment.
setPublicKeyFormat($format)
Determines the public key format.
sign($message)
Create a signature.
createKey($bits=1024, $timeout=false, $partial=array())
Create public / private key pair.
decrypt($ciphertext)
Decryption.
const ENCRYPTION_OAEP
#+ public
setEncryptionMode($mode)
Set Encryption Mode.
_start_element_handler($parser, $name, $attribs)
Start Element Handler.
getPrivateKey($type=self::PUBLIC_FORMAT_PKCS1)
Returns the private key.
const ASN1_SEQUENCE
ASN1 Sequence (with the constucted bit set)
setComment($comment)
Set public key comment.
getPublicKeyFingerprint($algorithm='md5')
Returns the public key's fingerprint.
const ASN1_OBJECT
ASN1 Object Identifier.
_encodeLength($length)
DER-encode the length.
Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic...
setPublicKey($key=false, $type=false)
Defines the public key.
setHash($hash)
Determines which hashing function should be used.
setSaltLength($sLen)
Determines the salt length.
catch(Exception $e) $message
_rsassa_pkcs1_v1_5_sign($m)
RSASSA-PKCS1-V1_5-SIGN.
_parseKey($key, $type)
Break a public or private key down into its constituant components.
const PUBLIC_FORMAT_XML
XML formatted public key.
loadKey($key, $type=false)
Loads a public or private key.
_mgf1($mgfSeed, $maskLen)
MGF1.
_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients)
Convert a private key to the appropriate format.
setPrivateKey($key=false, $type=false)
Defines the private key.
Pure-PHP PKCS#1 compliant implementation of RSA.
setPrivateKeyFormat($format)
Determines the private key format.
_os2ip($x)
Octet-String-to-Integer primitive.
_rsassa_pss_sign($m)
RSASSA-PSS-SIGN.
encrypt($plaintext)
Encryption.
getSize()
Returns the key size.
_string_shift(&$string, $index=1)
String Shift.
setMGFHash($hash)
Determines which hashing function should be used for the mask generation function.
_stop_element_handler($parser, $name)
Stop Element Handler.
Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic...
Pure-PHP implementation of Triple DES.
_emsa_pkcs1_v1_5_encode($m, $emLen)
EMSA-PKCS1-V1_5-ENCODE.
_extractBER($str)
Extract raw BER from Base64 encoding.
_exponentiate($x)
Exponentiate with or without Chinese Remainder Theorem.
const SIGNATURE_PKCS1
Use the PKCS#1 scheme by default.
getPublicKey($type=self::PUBLIC_FORMAT_PKCS8)
Returns the public key.
setPassword($password=false)
Sets the password.
Pure-PHP arbitrary precision integer arithmetic library.
_rsassa_pkcs1_v1_5_verify($m, $s)
RSASSA-PKCS1-V1_5-VERIFY.
_rsaes_oaep_decrypt($c, $l='')
RSAES-OAEP-DECRYPT.
_data_handler($parser, $data)
Data Handler.
verify($message, $signature)
Verifies a signature.
__clone()
__clone() magic method
_emsa_pss_encode($m, $emBits)
EMSA-PSS-ENCODE.
_getPrivatePublicKey($mode=self::PUBLIC_FORMAT_PKCS8)
Returns a minimalistic private key.
const MODE_CFB
Encrypt / decrypt using the Cipher Feedback mode.
const PRIVATE_FORMAT_PUTTY
PuTTY formatted private key.
static string($length)
Generate a random string.
const PUBLIC_FORMAT_PKCS1
PKCS#1 formatted public key (raw)
_rsaes_pkcs1_v1_5_encrypt($m)
RSAES-PKCS1-V1_5-ENCRYPT.
Pure-PHP implementation of DES.
const ENCRYPTION_NONE
Do not use any padding.
Pure-PHP implementation of AES.
hash(StreamInterface $stream, $algo, $rawOutput=false)
Calculate a hash of a Stream.
_generateMinMax($bits)
Generates the smallest and largest numbers requiring $bits bits.
_raw_encrypt($m)
Raw Encryption / Decryption.
_i2osp($x, $xLen)
Integer-to-Octet-String primitive.
__toString()
__toString() magic method
if(function_exists('posix_getuid') &&posix_getuid()===0) if(!array_key_exists('t', $options)) $tag
for($i=6; $i< 13; $i++) for($i=1; $i< 13; $i++) $d
const PUBLIC_FORMAT_OPENSSH
OpenSSH formatted public key.
const PRIVATE_FORMAT_PKCS1
#-
_emsa_pss_verify($m, $em, $emBits)
EMSA-PSS-VERIFY.
_blind($x, $r, $i)
Performs RSA Blinding.
__construct()
The constructor.