From 317efaf28a02f380a292068443f47dd0ef660fd1 Mon Sep 17 00:00:00 2001 From: terrafrost Date: Thu, 17 Apr 2014 10:30:32 -0500 Subject: [PATCH] RSA: auto-detect public keys vs private keys --- phpseclib/Crypt/RSA.php | 53 +++++++++++++++++++++++- tests/Crypt/RSA/LoadKeyTest.php | 71 ++++++++++++++++++++++++++++++--- 2 files changed, 117 insertions(+), 7 deletions(-) diff --git a/phpseclib/Crypt/RSA.php b/phpseclib/Crypt/RSA.php index b6015178..d53d0e2f 100644 --- a/phpseclib/Crypt/RSA.php +++ b/phpseclib/Crypt/RSA.php @@ -477,7 +477,7 @@ class Crypt_RSA case extension_loaded('openssl') && version_compare(PHP_VERSION, '4.2.0', '>=') && file_exists($this->configFile): // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work ob_start(); - phpinfo(); + @phpinfo(); $content = ob_get_contents(); ob_end_clean(); @@ -1478,6 +1478,19 @@ class Crypt_RSA $this->publicExponent = false; } + switch ($type) { + case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH: + case CRYPT_RSA_PUBLIC_FORMAT_RAW: + $this->setPublicKey(); + break; + case CRYPT_RSA_PRIVATE_FORMAT_PKCS1: + switch (true) { + case strpos($key, '-BEGIN PUBLIC KEY-') !== false: + case strpos($key, '-BEGIN RSA PUBLIC KEY-') !== false: + $this->setPublicKey(); + } + } + return true; } @@ -1504,7 +1517,9 @@ class Crypt_RSA * used in certain contexts. For example, in SSH-2, RSA authentication works by sending the public key along with a * message signed by the private key to the server. The SSH-2 server looks the public key up in an index of public keys * and if it's present then proceeds to verify the signature. Problem is, if your private key doesn't include the public - * exponent this won't work unless you manually add the public exponent. + * exponent this won't work unless you manually add the public exponent. phpseclib tries to guess if the key being used + * is the public key but in the event that it guesses incorrectly you might still want to explicitly set the key as being + * public. * * Do note that when a new key is loaded the index will be cleared. * @@ -1560,6 +1575,40 @@ class Crypt_RSA return true; } + /** + * Defines the private key + * + * If phpseclib guessed a private key was a public key and loaded it as such it might be desirable to force + * phpseclib to treat the key as a private key. This function will do that. + * + * Do note that when a new key is loaded the index will be cleared. + * + * Returns true on success, false on failure + * + * @see getPublicKey() + * @access public + * @param String $key optional + * @param Integer $type optional + * @return Boolean + */ + function setPrivateKey($key = false, $type = false) + { + if ($key === false && !empty($this->publicExponent)) { + unset($this->publicExponent); + return true; + } + + $rsa = new Crypt_RSA(); + if (!$rsa->loadKey($key, $type)) { + return false; + } + unset($rsa->publicExponent); + + // don't overwrite the old key if the new key is invalid + $this->loadKey($rsa); + return true; + } + /** * Returns the public key * diff --git a/tests/Crypt/RSA/LoadKeyTest.php b/tests/Crypt/RSA/LoadKeyTest.php index 95239d84..23594c01 100644 --- a/tests/Crypt/RSA/LoadKeyTest.php +++ b/tests/Crypt/RSA/LoadKeyTest.php @@ -36,7 +36,7 @@ U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ 37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0= -----END RSA PRIVATE KEY-----'; - $this->assertTrue($rsa->loadKey($key)); + $this->assertTrue($rsa->loadKey($key) && is_string($rsa->getPrivateKey())); } public function testPKCS1SpacesKey() @@ -58,7 +58,7 @@ U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ -----END RSA PRIVATE KEY-----'; $key = str_replace(array("\r", "\n", "\r\n"), ' ', $key); - $this->assertTrue($rsa->loadKey($key)); + $this->assertTrue($rsa->loadKey($key) && is_string($rsa->getPrivateKey())); } public function testPKCS1NoHeaderKey() @@ -77,7 +77,7 @@ X6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ 37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0='; - $this->assertTrue($rsa->loadKey($key)); + $this->assertTrue($rsa->loadKey($key) && is_string($rsa->getPrivateKey())); } public function testPKCS1NoWhitespaceNoHeaderKey() @@ -95,7 +95,7 @@ U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ 'X6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl' . 'U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ' . '37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0='; - $this->assertTrue($rsa->loadKey($key)); + $this->assertTrue($rsa->loadKey($key) && is_string($rsa->getPrivateKey())); } public function testRawPKCS1Key() @@ -115,6 +115,67 @@ U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ '37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0='; $key = base64_decode($key); - $this->assertTrue($rsa->loadKey($key)); + $this->assertTrue($rsa->loadKey($key) && is_string($rsa->getPrivateKey())); + } + + public function testPubKey1() + { + $rsa = new Crypt_RSA(); + + $key = '-----BEGIN RSA PUBLIC KEY----- +MIIBCgKCAQEA61BjmfXGEvWmegnBGSuS+rU9soUg2FnODva32D1AqhwdziwHINFa +D1MVlcrYG6XRKfkcxnaXGfFDWHLEvNBSEVCgJjtHAGZIm5GL/KA86KDp/CwDFMSw +luowcXwDwoyinmeOY9eKyh6aY72xJh7noLBBq1N0bWi1e2i+83txOCg4yV2oVXhB +o8pYEJ8LT3el6Smxol3C1oFMVdwPgc0vTl25XucMcG/ALE/KNY6pqC2AQ6R2ERlV +gPiUWOPatVkt7+Bs3h5Ramxh7XjBOXeulmCpGSynXNcpZ/06+vofGi/2MlpQZNhH +Ao8eayMp6FcvNucIpUndo1X8dKMv3Y26ZQIDAQAB +-----END RSA PUBLIC KEY-----'; + + $this->assertTrue($rsa->loadKey($key) && is_string($rsa->getPublicKey()) && $rsa->getPrivateKey() === false); + } + + public function testPubKey2() + { + $rsa = new Crypt_RSA(); + + $key = '-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA61BjmfXGEvWmegnBGSuS ++rU9soUg2FnODva32D1AqhwdziwHINFaD1MVlcrYG6XRKfkcxnaXGfFDWHLEvNBS +EVCgJjtHAGZIm5GL/KA86KDp/CwDFMSwluowcXwDwoyinmeOY9eKyh6aY72xJh7n +oLBBq1N0bWi1e2i+83txOCg4yV2oVXhBo8pYEJ8LT3el6Smxol3C1oFMVdwPgc0v +Tl25XucMcG/ALE/KNY6pqC2AQ6R2ERlVgPiUWOPatVkt7+Bs3h5Ramxh7XjBOXeu +lmCpGSynXNcpZ/06+vofGi/2MlpQZNhHAo8eayMp6FcvNucIpUndo1X8dKMv3Y26 +ZQIDAQAB +-----END PUBLIC KEY-----'; + + $this->assertTrue($rsa->loadKey($key) && is_string($rsa->getPublicKey()) && $rsa->getPrivateKey() === false); + } + + public function testPubKey3() + { + $rsa = new Crypt_RSA(); + + $key = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4e' . + 'CZ0FPqri0cb2JZfXJ/DgYSF6vUpwmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMS' . + 'GkVb1/3j+skZ6UtW+5u09lHNsj6tQ51s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZw== ' . + 'phpseclib-generated-key'; + + $this->assertTrue($rsa->loadKey($key) && is_string($rsa->getPublicKey()) && $rsa->getPrivateKey() === false); + } + + public function testSetPrivate() + { + $rsa = new Crypt_RSA(); + + $key = '-----BEGIN RSA PUBLIC KEY----- +MIIBCgKCAQEA61BjmfXGEvWmegnBGSuS+rU9soUg2FnODva32D1AqhwdziwHINFa +D1MVlcrYG6XRKfkcxnaXGfFDWHLEvNBSEVCgJjtHAGZIm5GL/KA86KDp/CwDFMSw +luowcXwDwoyinmeOY9eKyh6aY72xJh7noLBBq1N0bWi1e2i+83txOCg4yV2oVXhB +o8pYEJ8LT3el6Smxol3C1oFMVdwPgc0vTl25XucMcG/ALE/KNY6pqC2AQ6R2ERlV +gPiUWOPatVkt7+Bs3h5Ramxh7XjBOXeulmCpGSynXNcpZ/06+vofGi/2MlpQZNhH +Ao8eayMp6FcvNucIpUndo1X8dKMv3Y26ZQIDAQAB +-----END RSA PUBLIC KEY-----'; + + $this->assertTrue($rsa->loadKey($key) && $rsa->setPrivateKey() && is_string("$rsa") && $rsa->getPublicKey() === false); } }