mirror of
https://github.com/danog/phpseclib.git
synced 2024-12-02 17:52:59 +01:00
RSA: add support for loading PuTTY v3 keys
This commit is contained in:
parent
f05091ca95
commit
45d2ddcbc4
@ -1487,11 +1487,18 @@ class Crypt_RSA
|
||||
unset($xml);
|
||||
|
||||
return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false;
|
||||
// from PuTTY's SSHPUBK.C
|
||||
// see PuTTY's SSHPUBK.C and https://tartarus.org/~simon/putty-snapshots/htmldoc/AppendixC.html
|
||||
case CRYPT_RSA_PRIVATE_FORMAT_PUTTY:
|
||||
$components = array();
|
||||
$key = preg_split('#\r\n|\r|\n#', $key);
|
||||
$type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0]));
|
||||
if ($this->_string_shift($key[0], strlen('PuTTY-User-Key-File-')) != 'PuTTY-User-Key-File-') {
|
||||
return false;
|
||||
}
|
||||
$version = (int) $this->_string_shift($key[0], 3); // should be either "2: " or "3: 0" prior to int casting
|
||||
if ($version != 2 && $version != 3) {
|
||||
return false;
|
||||
}
|
||||
$type = rtrim($key[0]);
|
||||
if ($type != 'ssh-rsa') {
|
||||
return false;
|
||||
}
|
||||
@ -1506,14 +1513,41 @@ class Crypt_RSA
|
||||
extract(unpack('Nlength', $this->_string_shift($public, 4)));
|
||||
$components['modulus'] = new Math_BigInteger($this->_string_shift($public, $length), -256);
|
||||
|
||||
$privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$publicLength + 4]));
|
||||
$private = base64_decode(implode('', array_map('trim', array_slice($key, $publicLength + 5, $privateLength))));
|
||||
|
||||
$offset = $publicLength + 4;
|
||||
switch ($encryption) {
|
||||
case 'aes256-cbc':
|
||||
if (!class_exists('Crypt_AES')) {
|
||||
include_once 'Crypt/AES.php';
|
||||
}
|
||||
$crypto = new Crypt_AES();
|
||||
switch ($version) {
|
||||
case 3:
|
||||
if (!function_exists('sodium_crypto_pwhash')) {
|
||||
return false;
|
||||
}
|
||||
$flavour = trim(preg_replace('#Key-Derivation: (.*)#', '$1', $key[$offset++]));
|
||||
switch ($flavour) {
|
||||
case 'Argon2i':
|
||||
$flavour = SODIUM_CRYPTO_PWHASH_ALG_ARGON2I13;
|
||||
break;
|
||||
case 'Argon2id':
|
||||
$flavour = SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
$memory = trim(preg_replace('#Argon2-Memory: (\d+)#', '$1', $key[$offset++]));
|
||||
$passes = trim(preg_replace('#Argon2-Passes: (\d+)#', '$1', $key[$offset++]));
|
||||
$parallelism = trim(preg_replace('#Argon2-Parallelism: (\d+)#', '$1', $key[$offset++]));
|
||||
$salt = pack('H*', trim(preg_replace('#Argon2-Salt: ([0-9a-f]+)#', '$1', $key[$offset++])));
|
||||
|
||||
$length = 80; // keylen + ivlen + mac_keylen
|
||||
$temp = sodium_crypto_pwhash($length, $this->password, $salt, $passes, $memory << 10, $flavour);
|
||||
|
||||
$symkey = substr($temp, 0, 32);
|
||||
$symiv = substr($temp, 32, 16);
|
||||
break;
|
||||
case 2:
|
||||
$symkey = '';
|
||||
$sequence = 0;
|
||||
while (strlen($symkey) < 32) {
|
||||
@ -1521,11 +1555,16 @@ class Crypt_RSA
|
||||
$symkey.= pack('H*', sha1($temp));
|
||||
}
|
||||
$symkey = substr($symkey, 0, 32);
|
||||
$crypto = new Crypt_AES();
|
||||
$symiv = str_repeat("\0", 16);
|
||||
}
|
||||
}
|
||||
|
||||
$privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$offset++]));
|
||||
$private = base64_decode(implode('', array_map('trim', array_slice($key, $offset, $privateLength))));
|
||||
|
||||
if ($encryption != 'none') {
|
||||
$crypto->setKey($symkey);
|
||||
$crypto->setIV($symiv);
|
||||
$crypto->disablePadding();
|
||||
$private = $crypto->decrypt($private);
|
||||
if ($private === false) {
|
||||
|
@ -535,4 +535,85 @@ Vyaqr/WTPzxdXJAAAADHJvb3RAdmFncmFudAECAwQFBg==
|
||||
|
||||
$this->assertTrue($rsa->verify('zzz', $sig));
|
||||
}
|
||||
|
||||
public function testPuTTYV3NoPW()
|
||||
{
|
||||
$key = 'PuTTY-User-Key-File-3: ssh-rsa
|
||||
Encryption: none
|
||||
Comment: rsa-key-20220216
|
||||
Public-Lines: 6
|
||||
AAAAB3NzaC1yc2EAAAADAQABAAABAQCJ39DLYw81oZmBMeRze+Plu0p8+kJezer4
|
||||
mRpltRoqpZ0yRnyb5k0FtXrDeYL9IyCceOTsse/qks3CtVWQ2q7C2tqyezmk8mDf
|
||||
aKXqnaSG3hHZo7vJcy76J7NNB6Mz2BxF9RGvb+sylEKdWOJdgmYC6dzyvpg/0qs6
|
||||
yNPQGA5QOOzy2AstxnsujDl16I0GGsjw7ybc5844Hq4VhIQaft2Yd35UqGt5G1hs
|
||||
nIZu1cLO/F+8xs+0xEY04FvJRNAoJGlVc8oPx7slU7vF5m22AmBqrhkljbid72OR
|
||||
oXpI+4c7zc0dYZBIMoAEIJKTbliQE1WV0lYiXkS9RY3UjUyPLho9
|
||||
Private-Lines: 14
|
||||
AAABAQCI4IliEeMcpGVILOcXe2yCO1E1CCLyCc53pU/en0/t/OM18WJuR9I5k7Tf
|
||||
8XeIpeIPVbo3/mMn5zydS/c5ytDrI+kwfkN5LSPdSABIDt8zAa6I+hNJaK+/q8BG
|
||||
/gkZRDi1fxpiqGLAoQ4NNhvtJ7Lsu44d8/gkjJpvzsbx9Z/oJVK8ID10Wiiz9R7u
|
||||
WPCOJbrETGU1LaY4N0hwhbqD28xtX4ypBh+HQ9umCqOMopeqVhebMolAZ62K5V+N
|
||||
SbdN1JFk2FPQxMv3v4ApDW48AcJ1dNgO6euncaySLaQv3tnxYVjKVaf3JO0ALzoq
|
||||
zsR2uj5bJUvhSapj9uWdDJTurGzFAAAAgQDY5t/F2Ruoa5wtF/XTiIxFpb//xQ+2
|
||||
JhQOWd1fZZ+oMclqNS5E45E11TWnKthgr5NN4UB6TH4rtUETjsypD3w2PYZamTD1
|
||||
QzeoOS0xRxjKfQu08ApDV94mx9LfX6Xi2IqTW0pC+IbBx8AUnK7J7scva8TYn7Qu
|
||||
1QLSY4/tn3BBBwAAAIEAorolHJnR+w5FajTc8VeqN5E9bfc39Mr+2lQcqtARJGAM
|
||||
2jLhN3ZWGIboG3Ttqcbfuicv/WzFe+gGRA8awvMS4v2C5/knZl4Vq859KCP7JOeW
|
||||
63+5mLw5OKZOzWkguMu8+IfkUtIMv1JFuCU2eRL5elUthKlK6WFcMejuygNTrZsA
|
||||
AACARP7yi23FNxAqHcgbx5MlrLYbMSjxp5yT+1XeNVTSpM/dvDVsy+8ETi/c1870
|
||||
UfAzuIHQl2fu6NdtPBQUoqWKgBRtp46J/BoWF3Ty6klz+FAP2of4gojYvqa87H+6
|
||||
dW7G8+QXxXM704cxjbBQAApItfVw3upWPrYP9FDy7xvtYRY=
|
||||
Private-MAC: 7979eb6f604fb3e0bd191295479517f641598649167835402c6cbfde6cbf21ef';
|
||||
|
||||
$rsa = new Crypt_RSA();
|
||||
$this->assertTrue($rsa->loadKey($key));
|
||||
$this->assertIsString($rsa->getPublicKey());
|
||||
$this->assertIsString($rsa->getPrivateKey());
|
||||
}
|
||||
|
||||
public function testPuTTYV3PW()
|
||||
{
|
||||
if (!function_exists('sodium_crypto_pwhash')) {
|
||||
self::markTestSkipped('sodium_crypto_pwhash() function is not available.');
|
||||
}
|
||||
|
||||
$key = 'PuTTY-User-Key-File-3: ssh-rsa
|
||||
Encryption: aes256-cbc
|
||||
Comment: rsa-key-20220216
|
||||
Public-Lines: 6
|
||||
AAAAB3NzaC1yc2EAAAADAQABAAABAQCJ39DLYw81oZmBMeRze+Plu0p8+kJezer4
|
||||
mRpltRoqpZ0yRnyb5k0FtXrDeYL9IyCceOTsse/qks3CtVWQ2q7C2tqyezmk8mDf
|
||||
aKXqnaSG3hHZo7vJcy76J7NNB6Mz2BxF9RGvb+sylEKdWOJdgmYC6dzyvpg/0qs6
|
||||
yNPQGA5QOOzy2AstxnsujDl16I0GGsjw7ybc5844Hq4VhIQaft2Yd35UqGt5G1hs
|
||||
nIZu1cLO/F+8xs+0xEY04FvJRNAoJGlVc8oPx7slU7vF5m22AmBqrhkljbid72OR
|
||||
oXpI+4c7zc0dYZBIMoAEIJKTbliQE1WV0lYiXkS9RY3UjUyPLho9
|
||||
Key-Derivation: Argon2id
|
||||
Argon2-Memory: 8192
|
||||
Argon2-Passes: 13
|
||||
Argon2-Parallelism: 1
|
||||
Argon2-Salt: d9bfa07d14a450a26ada4eb5d30c4dae
|
||||
Private-Lines: 14
|
||||
L3TUmo97jnxJVYIScxzPIaq19/yNQ5HDQKGSTz4vqUrQR3wXQEyhzxlN2mm5zZtT
|
||||
pst7K61P0awtjs4kHUfsKxXh/upv7ndS9u9G7cnnBfP5mjs0wAE2VaghbP4UXprH
|
||||
/MQC9Dr13Iuydv5Oih+PLpkvM3DbY5t+nrIWy/29yDLYe/QjLvy346Gz3pnLmCfb
|
||||
hbEFfjefdppa+6QZ+qU6ai/NMAM/Q5OxjRlIo1brrKJNvMrbzP7irZ4+Ao2Or/hX
|
||||
nb2ZZLY0eUotD8iFuOk2EjjqP9iakag1OHdvdy6EcPzkIObN5YeZGz9/hRDFr9Ml
|
||||
xNxdaw5c1BhqU5pm0B0HUDqW5kmYTiugUKQiGr0+1ckliUt6jsb7YImnqJIgL7PS
|
||||
vKcqNvz95u4on77gHPl2JdsXxuz6jOkDwc9jvsJCtIMJ8qhAVXGS7WaH2aF9ty7B
|
||||
4E+f2yIbsRr0RFCZoTTjTmhtYsVd7DYo0Jftya3Sh/lVO1MLo1z8em0MFJdR683N
|
||||
tRDA2lbRPOdKYaiKdyp5bAsl4fqPR1e2GR9ybalPn/XSFDRtDfdMr7hyQboBR7uC
|
||||
X3nYsh5OiXakUSr2ST41pP27s8F48590M6xWb9LGFJA+JqmAZ5rxPTxFYjkz27y9
|
||||
Yvlq6lvM+XsUREPrxhWrHya4Jyp4WtyVtJXDg626hoZBSEtcOY/mbPfwVFnoU9vz
|
||||
V8TI/YU837mUceEJlEQEbT+bFJfh0W5jzAYx2xX6uPnDkodBMK2p6QS3ZKib0NJ7
|
||||
W+jQr9TT40H0agZhtAmPKaLGxtgdpUps1CDPV+8Y/pBf28CsI2DjFaOYopZXcW9s
|
||||
vCIjXopt4wAKbXiLyb5JXzFfB7CVron48NHB7wzuwvnUoYa/4dbjeEos+1y72xoP
|
||||
Private-MAC: d26baf87446604974287b682ed9e0c00ce54e460e1cb719953a81291147b3c59
|
||||
';
|
||||
|
||||
$rsa = new Crypt_RSA();
|
||||
$rsa->setPassword('demo');
|
||||
$this->assertTrue($rsa->loadKey($key));
|
||||
$this->assertIsString($rsa->getPublicKey());
|
||||
$this->assertIsString($rsa->getPrivateKey());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user