From 2b28c3814b0680d6338cfc778baad615ba6c705e Mon Sep 17 00:00:00 2001 From: terrafrost Date: Sun, 19 Jan 2020 02:09:39 -0600 Subject: [PATCH] Keys/PuTTY: better support RFC4716 keys --- .../Crypt/Common/Formats/Keys/OpenSSH.php | 2 +- phpseclib/Crypt/Common/Formats/Keys/PuTTY.php | 39 +++++++++++++++---- tests/Unit/Crypt/RSA/LoadKeyTest.php | 21 ++++++++++ 3 files changed, 53 insertions(+), 9 deletions(-) diff --git a/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php b/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php index 96a434c0..fc4e2906 100644 --- a/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php +++ b/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php @@ -186,7 +186,7 @@ abstract class OpenSSH private static function checkType($candidate) { if (!in_array($candidate, static::$types)) { - throw new \RuntimeException('The key type is not equal to: ' . implode(',', static::$types)); + throw new \RuntimeException("The key type ($candidate) is not equal to: " . implode(',', static::$types)); } } diff --git a/phpseclib/Crypt/Common/Formats/Keys/PuTTY.php b/phpseclib/Crypt/Common/Formats/Keys/PuTTY.php index 5d97c4a7..fb2ffa77 100644 --- a/phpseclib/Crypt/Common/Formats/Keys/PuTTY.php +++ b/phpseclib/Crypt/Common/Formats/Keys/PuTTY.php @@ -85,19 +85,42 @@ abstract class PuTTY } if (strpos($key, 'BEGIN SSH2 PUBLIC KEY') !== false) { - $data = preg_split('#[\r\n]+#', $key); - $data = array_splice($data, 2, -1); - $data = implode('', $data); + $lines = preg_split('#[\r\n]+#', $key); + switch (true) { + case $lines[0] != '---- BEGIN SSH2 PUBLIC KEY ----': + throw new \UnexpectedValueException('Key doesn\'t start with ---- BEGIN SSH2 PUBLIC KEY ----'); + case $lines[count($lines) - 1] != '---- END SSH2 PUBLIC KEY ----': + throw new \UnexpectedValueException('Key doesn\'t end with ---- END SSH2 PUBLIC KEY ----'); + } + $lines = array_splice($lines, 1, -1); + $lines = array_map(function ($line) { + return rtrim($line, "\r\n"); + }, $lines); + $data = $current = ''; + $values = []; + $in_value = false; + foreach ($lines as $line) { + switch (true) { + case preg_match('#^(.*?): (.*)#', $line, $match): + $in_value = $line[strlen($line) - 1] == '\\'; + $current = strtolower($match[1]); + $values[$current] = $in_value ? substr($match[2], 0, -1) : $match[2]; + break; + case $in_value: + $in_value = $line[strlen($line) - 1] == '\\'; + $values[$current].= $in_value ? substr($line, 0, -1) : $line; + break; + default: + $data.= $line; + } + } $components = call_user_func([static::PUBLIC_HANDLER, 'load'], $data); if ($components === false) { throw new \UnexpectedValueException('Unable to decode public key'); } - - if (!preg_match('#Comment: "(.+)"#', $key, $matches)) { - throw new \UnexpectedValueException('Key is missing a comment'); - } - $components['comment'] = str_replace(['\\\\', '\"'], ['\\', '"'], $matches[1]); + $components+= $values; + $components['comment'] = str_replace(['\\\\', '\"'], ['\\', '"'], $values['comment']); return $components; } diff --git a/tests/Unit/Crypt/RSA/LoadKeyTest.php b/tests/Unit/Crypt/RSA/LoadKeyTest.php index 9c9173d4..a82d9155 100644 --- a/tests/Unit/Crypt/RSA/LoadKeyTest.php +++ b/tests/Unit/Crypt/RSA/LoadKeyTest.php @@ -993,5 +993,26 @@ AAAAB3NzaC1yc2EAAAADAQABAAAAQQCo9+BpMRYQ/dL3DS2CyJxRF+j6ctbT3/Qp $key = PublicKeyLoader::load($orig); $this->assertSame($orig, $key->toString('PuTTY')); + + $key = '---- BEGIN SSH2 PUBLIC KEY ---- +Subject: me +Comment: 1024-bit rsa, created by me@example.com Mon Jan 15 \ +08:31:24 2001 +AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 +596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 +soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0= +---- END SSH2 PUBLIC KEY ----'; + $key = PublicKeyLoader::load($key); + $this->assertInstanceOf(PublicKey::class, $key); + + $key = '---- BEGIN SSH2 PUBLIC KEY ---- +Comment: "1024-bit RSA, converted from OpenSSH by me@example.com" +x-command: /home/me/bin/lock-in-guest.sh +AAAAB3NzaC1yc2EAAAABIwAAAIEA1on8gxCGJJWSRT4uOrR13mUaUk0hRf4RzxSZ1zRb +YYFw8pfGesIFoEuVth4HKyF8k1y4mRUnYHP1XNMNMJl1JcEArC2asV8sHf6zSPVffozZ +5TT4SfsUu/iKy9lUcCfXzwre4WWZSXXcPff+EHtWshahu3WzBdnGxm5Xoi89zcE= +---- END SSH2 PUBLIC KEY ----'; + $key = PublicKeyLoader::load($key); + $this->assertInstanceOf(PublicKey::class, $key); } }