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);
$key = isset($parts[1]) ? base64_decode($parts[1]) : false;
$key = isset($parts[1]) ? base64_decode($parts[1]) : base64_decode($parts[0]);
if ($key === false) {
return 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) {
return false;
}
extract(unpack('Nlength', self::_string_shift($key, 4)));
if (strlen($key) <= $length) {
return false;
}
$publicExponent = new BigInteger(self::_string_shift($key, $length), -256);
if (strlen($key) <= 4) {
return false;
}
extract(unpack('Nlength', self::_string_shift($key, 4)));
if (strlen($key) != $length) {
return false;
}
$modulus = new BigInteger(self::_string_shift($key, $length), -256);
if ($cleanup && strlen($key)) {
if (strlen($key) <= 4) {
return false;
}
extract(unpack('Nlength', self::_string_shift($key, 4)));
$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
);
}
return array(
'isPublicKey' => true,
'modulus' => $modulus,
'publicExponent' => $publicExponent,
'comment' => $comment
);
}
/**

View File

@ -32,29 +32,54 @@ abstract class PKCS
/**#@+
* @access private
* @see \phpseclib\Crypt\RSA::createKey()
*/
*/
/**
* ASN1 Integer
*/
*/
const ASN1_INTEGER = 2;
/**
* ASN1 Bit String
*/
*/
const ASN1_BITSTRING = 3;
/**
* ASN1 Octet String
*/
*/
const ASN1_OCTETSTRING = 4;
/**
* ASN1 Object Identifier
*/
*/
const ASN1_OBJECT = 6;
/**
* ASN1 Sequence (with the constucted bit set)
*/
*/
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
*
@ -166,13 +191,19 @@ abstract class PKCS
$crypto = self::getEncryptionObject($matches[1]);
$crypto->setKey(self::generateSymmetricKey($password, $iv, $crypto->getKeyLength() >> 3));
$crypto->setIV($iv);
$decoded = $crypto->decrypt($ciphertext);
$key = $crypto->decrypt($ciphertext);
if ($key === false) {
return false;
}
} else {
$decoded = self::_extractBER($key);
}
if ($decoded !== false) {
$key = $decoded;
if (self::$format != self::MODE_DER) {
$decoded = self::_extractBER($key);
if ($decoded !== false) {
$key = $decoded;
} elseif (self::$format == self::MODE_PEM) {
return false;
}
}
}
if (ord(self::_string_shift($key)) != self::ASN1_SEQUENCE) {
@ -332,6 +363,41 @@ abstract class PKCS
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
*

View File

@ -17,6 +17,7 @@ namespace phpseclib\Crypt\RSA;
use phpseclib\Math\BigInteger;
use phpseclib\Crypt\AES;
use phpseclib\Crypt\Hash;
use phpseclib\Crypt\RSA\OpenSSH;
/**
* PuTTY Formatted RSA Key Handler
@ -85,6 +86,24 @@ class PuTTY
$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);
$key = preg_split('#\r\n|\r|\n#', $key);
$type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0]));
@ -258,4 +277,34 @@ class PuTTY
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->load($key, RSA::PUBLIC_FORMAT_RAW);
$rsa->load($key, 'raw');
$rsa->setEncryptionMode(RSA::ENCRYPTION_PKCS1);
return $rsa->encrypt($m);
*/

View File

@ -4040,7 +4040,7 @@ class SSH2
$rsa = new RSA();
$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)) {
//user_error('Bad server signature');
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);
}
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"));
}
}