mirror of
https://github.com/phpseclib/phpseclib.git
synced 2025-02-03 20:38:29 +00:00
RSA: changes to OpenSSH and PuTTY plugins
This commit is contained in:
parent
7b1b7c22e2
commit
43165d976c
@ -62,46 +62,40 @@ class OpenSSH
|
|||||||
|
|
||||||
$parts = explode(' ', $key, 3);
|
$parts = explode(' ', $key, 3);
|
||||||
|
|
||||||
$key = isset($parts[1]) ? base64_decode($parts[1]) : false;
|
$key = isset($parts[1]) ? base64_decode($parts[1]) : base64_decode($parts[0]);
|
||||||
if ($key === false) {
|
if ($key === false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$comment = isset($parts[2]) ? $parts[2] : false;
|
$comment = isset($parts[2]) ? $parts[2] : false;
|
||||||
|
|
||||||
$cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa";
|
if (substr($key, 0, 11) != "\0\0\0\7ssh-rsa") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
self::_string_shift($key, 11);
|
||||||
if (strlen($key) <= 4) {
|
if (strlen($key) <= 4) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
extract(unpack('Nlength', self::_string_shift($key, 4)));
|
extract(unpack('Nlength', self::_string_shift($key, 4)));
|
||||||
|
if (strlen($key) <= $length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
$publicExponent = new BigInteger(self::_string_shift($key, $length), -256);
|
$publicExponent = new BigInteger(self::_string_shift($key, $length), -256);
|
||||||
if (strlen($key) <= 4) {
|
if (strlen($key) <= 4) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
extract(unpack('Nlength', self::_string_shift($key, 4)));
|
extract(unpack('Nlength', self::_string_shift($key, 4)));
|
||||||
|
if (strlen($key) != $length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
$modulus = new BigInteger(self::_string_shift($key, $length), -256);
|
$modulus = new BigInteger(self::_string_shift($key, $length), -256);
|
||||||
|
|
||||||
if ($cleanup && strlen($key)) {
|
return array(
|
||||||
if (strlen($key) <= 4) {
|
'isPublicKey' => true,
|
||||||
return false;
|
'modulus' => $modulus,
|
||||||
}
|
'publicExponent' => $publicExponent,
|
||||||
extract(unpack('Nlength', self::_string_shift($key, 4)));
|
'comment' => $comment
|
||||||
$realModulus = new BigInteger(self::_string_shift($key, $length), -256);
|
);
|
||||||
return strlen($key) ? false : array(
|
|
||||||
'isPublicKey' => true,
|
|
||||||
'modulus' => $realModulus,
|
|
||||||
'publicExponent' => $modulus,
|
|
||||||
'comment' => $comment
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return strlen($key) ? false : array(
|
|
||||||
'isPublicKey' => true,
|
|
||||||
'modulus' => $modulus,
|
|
||||||
'publicExponent' => $publicExponent,
|
|
||||||
'comment' => $comment
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -32,29 +32,54 @@ abstract class PKCS
|
|||||||
/**#@+
|
/**#@+
|
||||||
* @access private
|
* @access private
|
||||||
* @see \phpseclib\Crypt\RSA::createKey()
|
* @see \phpseclib\Crypt\RSA::createKey()
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* ASN1 Integer
|
* ASN1 Integer
|
||||||
*/
|
*/
|
||||||
const ASN1_INTEGER = 2;
|
const ASN1_INTEGER = 2;
|
||||||
/**
|
/**
|
||||||
* ASN1 Bit String
|
* ASN1 Bit String
|
||||||
*/
|
*/
|
||||||
const ASN1_BITSTRING = 3;
|
const ASN1_BITSTRING = 3;
|
||||||
/**
|
/**
|
||||||
* ASN1 Octet String
|
* ASN1 Octet String
|
||||||
*/
|
*/
|
||||||
const ASN1_OCTETSTRING = 4;
|
const ASN1_OCTETSTRING = 4;
|
||||||
/**
|
/**
|
||||||
* ASN1 Object Identifier
|
* ASN1 Object Identifier
|
||||||
*/
|
*/
|
||||||
const ASN1_OBJECT = 6;
|
const ASN1_OBJECT = 6;
|
||||||
/**
|
/**
|
||||||
* ASN1 Sequence (with the constucted bit set)
|
* ASN1 Sequence (with the constucted bit set)
|
||||||
*/
|
*/
|
||||||
const ASN1_SEQUENCE = 48;
|
const ASN1_SEQUENCE = 48;
|
||||||
/**#@-*/
|
/**#@-*/
|
||||||
|
|
||||||
|
/**#@+
|
||||||
|
* @access private
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Auto-detect the format
|
||||||
|
*/
|
||||||
|
const MODE_ANY = 0;
|
||||||
|
/**
|
||||||
|
* Require base64-encoded PEM's be supplied
|
||||||
|
*/
|
||||||
|
const MODE_PEM = 1;
|
||||||
|
/**
|
||||||
|
* Require raw DER's be supplied
|
||||||
|
*/
|
||||||
|
const MODE_DER = 2;
|
||||||
|
/**#@-*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the key a base-64 encoded PEM, DER or should it be auto-detected?
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @param int
|
||||||
|
*/
|
||||||
|
static $format = self::MODE_ANY;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the mode constant corresponding to the mode string
|
* Returns the mode constant corresponding to the mode string
|
||||||
*
|
*
|
||||||
@ -166,13 +191,19 @@ abstract class PKCS
|
|||||||
$crypto = self::getEncryptionObject($matches[1]);
|
$crypto = self::getEncryptionObject($matches[1]);
|
||||||
$crypto->setKey(self::generateSymmetricKey($password, $iv, $crypto->getKeyLength() >> 3));
|
$crypto->setKey(self::generateSymmetricKey($password, $iv, $crypto->getKeyLength() >> 3));
|
||||||
$crypto->setIV($iv);
|
$crypto->setIV($iv);
|
||||||
$decoded = $crypto->decrypt($ciphertext);
|
$key = $crypto->decrypt($ciphertext);
|
||||||
|
if ($key === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$decoded = self::_extractBER($key);
|
if (self::$format != self::MODE_DER) {
|
||||||
}
|
$decoded = self::_extractBER($key);
|
||||||
|
if ($decoded !== false) {
|
||||||
if ($decoded !== false) {
|
$key = $decoded;
|
||||||
$key = $decoded;
|
} elseif (self::$format == self::MODE_PEM) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ord(self::_string_shift($key)) != self::ASN1_SEQUENCE) {
|
if (ord(self::_string_shift($key)) != self::ASN1_SEQUENCE) {
|
||||||
@ -332,6 +363,41 @@ abstract class PKCS
|
|||||||
return $components;
|
return $components;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Require base64-encoded PEM's be supplied
|
||||||
|
*
|
||||||
|
* @see self::load()
|
||||||
|
* @access public
|
||||||
|
*/
|
||||||
|
static function requirePEM()
|
||||||
|
{
|
||||||
|
self::$format = self::MODE_PEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Require raw DER's be supplied
|
||||||
|
*
|
||||||
|
* @see self::load()
|
||||||
|
* @access public
|
||||||
|
*/
|
||||||
|
static function requireDER()
|
||||||
|
{
|
||||||
|
self::$format = self::MODE_DER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accept any format and auto detect the format
|
||||||
|
*
|
||||||
|
* This is the default setting
|
||||||
|
*
|
||||||
|
* @see self::load()
|
||||||
|
* @access public
|
||||||
|
*/
|
||||||
|
static function requireAny()
|
||||||
|
{
|
||||||
|
self::$format = self::MODE_ANY;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DER-decode the length
|
* DER-decode the length
|
||||||
*
|
*
|
||||||
|
@ -17,6 +17,7 @@ namespace phpseclib\Crypt\RSA;
|
|||||||
use phpseclib\Math\BigInteger;
|
use phpseclib\Math\BigInteger;
|
||||||
use phpseclib\Crypt\AES;
|
use phpseclib\Crypt\AES;
|
||||||
use phpseclib\Crypt\Hash;
|
use phpseclib\Crypt\Hash;
|
||||||
|
use phpseclib\Crypt\RSA\OpenSSH;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PuTTY Formatted RSA Key Handler
|
* PuTTY Formatted RSA Key Handler
|
||||||
@ -85,6 +86,24 @@ class PuTTY
|
|||||||
$one = new BigInteger(1);
|
$one = new BigInteger(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strpos($key, 'BEGIN SSH2 PUBLIC KEY')) {
|
||||||
|
$data = preg_split('#[\r\n]+#', $key);
|
||||||
|
$data = array_splice($data, 2, -1);
|
||||||
|
$data = implode('', $data);
|
||||||
|
|
||||||
|
$components = OpenSSH::load($data);
|
||||||
|
if ($components === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!preg_match('#Comment: "(.+)"#', $key, $matches)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$components['comment'] = str_replace(array('\\\\', '\"'), array('\\', '"'), $matches[1]);
|
||||||
|
|
||||||
|
return $components;
|
||||||
|
}
|
||||||
|
|
||||||
$components = array('isPublicKey' => false);
|
$components = array('isPublicKey' => false);
|
||||||
$key = preg_split('#\r\n|\r|\n#', $key);
|
$key = preg_split('#\r\n|\r|\n#', $key);
|
||||||
$type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0]));
|
$type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0]));
|
||||||
@ -258,4 +277,34 @@ class PuTTY
|
|||||||
|
|
||||||
return $key;
|
return $key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a public key to the appropriate format
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param \phpseclib\Math\BigInteger $n
|
||||||
|
* @param \phpseclib\Math\BigInteger $e
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
static function savePublicKey(BigInteger $n, BigInteger $e)
|
||||||
|
{
|
||||||
|
$n = $n->toBytes(true);
|
||||||
|
$e = $e->toBytes(true);
|
||||||
|
|
||||||
|
$key = pack(
|
||||||
|
'Na*Na*Na*',
|
||||||
|
strlen('ssh-rsa'),
|
||||||
|
'ssh-rsa',
|
||||||
|
strlen($e),
|
||||||
|
$e,
|
||||||
|
strlen($n),
|
||||||
|
$n
|
||||||
|
);
|
||||||
|
$key = "---- BEGIN SSH2 PUBLIC KEY ----\r\n" .
|
||||||
|
'Comment: "' . str_replace(array('\\', '"'), array('\\\\', '\"'), self::$comment) . "\"\r\n";
|
||||||
|
chunk_split(base64_encode($key), 64) .
|
||||||
|
'---- END SSH2 PUBLIC KEY ----';
|
||||||
|
|
||||||
|
return $key;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1301,7 +1301,7 @@ class SSH1
|
|||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
$rsa = new RSA();
|
$rsa = new RSA();
|
||||||
$rsa->load($key, RSA::PUBLIC_FORMAT_RAW);
|
$rsa->load($key, 'raw');
|
||||||
$rsa->setEncryptionMode(RSA::ENCRYPTION_PKCS1);
|
$rsa->setEncryptionMode(RSA::ENCRYPTION_PKCS1);
|
||||||
return $rsa->encrypt($m);
|
return $rsa->encrypt($m);
|
||||||
*/
|
*/
|
||||||
|
@ -4040,7 +4040,7 @@ class SSH2
|
|||||||
|
|
||||||
$rsa = new RSA();
|
$rsa = new RSA();
|
||||||
$rsa->setSignatureMode(RSA::SIGNATURE_PKCS1);
|
$rsa->setSignatureMode(RSA::SIGNATURE_PKCS1);
|
||||||
$rsa->load(array('e' => $e, 'n' => $n), 'Raw');
|
$rsa->load(array('e' => $e, 'n' => $n), 'raw');
|
||||||
if (!$rsa->verify($this->exchange_hash, $signature)) {
|
if (!$rsa->verify($this->exchange_hash, $signature)) {
|
||||||
//user_error('Bad server signature');
|
//user_error('Bad server signature');
|
||||||
return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
|
return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
|
||||||
|
@ -441,4 +441,34 @@ Private-MAC: 35134b7434bf828b21404099861d455e660e8740';
|
|||||||
|
|
||||||
$this->assertSame($privKey->decrypt($ciphertext), $plaintext);
|
$this->assertSame($privKey->decrypt($ciphertext), $plaintext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testNakedOpenSSHKey()
|
||||||
|
{
|
||||||
|
$key = 'AAAAB3NzaC1yc2EAAAABIwAAAIEA/NcGSQFZ0ZgN1EbDusV6LLwLnQjs05ljKcVVP7Z6aKIJUyhUDHE30uJa5XfwPPBsZ3L3Q7S0yycVcuuHjdauugmpn9xx+gyoYs7UiV5G5rvxNcA/Tc+MofGhAMiTmNicorNAs5mv6fRoVbkpIONRXPz6WK0kjx/X04EV42Vm9Qk=';
|
||||||
|
|
||||||
|
$rsa = new RSA();
|
||||||
|
$rsa->load($key);
|
||||||
|
|
||||||
|
$this->assertSame($rsa->getLoadedFormat(), 'OpenSSH');
|
||||||
|
|
||||||
|
$this->assertGreaterThanOrEqual(1, strlen("$rsa"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPuttyPublicKey()
|
||||||
|
{
|
||||||
|
$key = '---- BEGIN SSH2 PUBLIC KEY ----
|
||||||
|
Comment: "rsa-key-20151023"
|
||||||
|
AAAAB3NzaC1yc2EAAAABJQAAAIEAhC/CSqJ+8vgeQ4H7fJru29h/McqAC9zdGzw0
|
||||||
|
9QsifLQ7s5MvXCavhjUPYIfV0KsdLQydNPLJcbKpXmpVD9azo61zLXwsYr8d1eHr
|
||||||
|
C/EwUYl8b0fAwEsEF3myb+ryzgA9ihY08Zs9NZdmt1Maa+I7lQcLX9F/65YdcAch
|
||||||
|
ILaEujU=
|
||||||
|
---- END SSH2 PUBLIC KEY ----';
|
||||||
|
|
||||||
|
$rsa = new RSA();
|
||||||
|
$rsa->load($key);
|
||||||
|
|
||||||
|
$this->assertSame($rsa->getLoadedFormat(), 'PuTTY');
|
||||||
|
|
||||||
|
$this->assertGreaterThanOrEqual(1, strlen("$rsa"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user