RSA: changes to OpenSSH and PuTTY plugins

This commit is contained in:
terrafrost 2015-10-24 01:00:46 -05:00
parent 7b1b7c22e2
commit 43165d976c
6 changed files with 176 additions and 37 deletions

View File

@ -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
);
}
} }
/** /**

View File

@ -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
* *

View File

@ -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;
}
} }

View File

@ -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);
*/ */

View File

@ -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);

View File

@ -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"));
}
} }