RSA: make it so you can't sign w/ public key, decrypt w/ private

This commit is contained in:
terrafrost 2018-02-15 05:29:14 -06:00
parent 63b6df29a8
commit 55384afdac
6 changed files with 190 additions and 10 deletions

View File

@ -52,6 +52,8 @@ use phpseclib\Common\Functions\Strings;
use phpseclib\File\ASN1\Maps\DigestInfo; use phpseclib\File\ASN1\Maps\DigestInfo;
use phpseclib\Crypt\Common\AsymmetricKey; use phpseclib\Crypt\Common\AsymmetricKey;
use phpseclib\Exception\UnsupportedAlgorithmException; use phpseclib\Exception\UnsupportedAlgorithmException;
use phpseclib\Exception\UnsupportedOperationException;
use phpseclib\Exception\NoKeyLoadedException;
/** /**
* Pure-PHP PKCS#1 compliant implementation of RSA. * Pure-PHP PKCS#1 compliant implementation of RSA.
@ -201,7 +203,6 @@ class RSA extends AsymmetricKey
*/ */
private $sLen; private $sLen;
/** /**
* Comment * Comment
* *
@ -243,6 +244,14 @@ class RSA extends AsymmetricKey
*/ */
private static $defaultExponent = 65537; private static $defaultExponent = 65537;
/**
* Is the loaded key a public key?
*
* @var bool
* @access private
*/
private $isPublic = false;
/** /**
* Smallest Prime * Smallest Prime
* *
@ -450,6 +459,7 @@ class RSA extends AsymmetricKey
$publickey->k = $bits >> 3; $publickey->k = $bits >> 3;
$publickey->exponent = $e; $publickey->exponent = $e;
$publickey->publicExponent = $e; $publickey->publicExponent = $e;
$publickey->isPublic = true;
return compact('privatekey', 'publickey'); return compact('privatekey', 'publickey');
} }
@ -475,6 +485,7 @@ class RSA extends AsymmetricKey
$this->sLen = $key->sLen; $this->sLen = $key->sLen;
$this->mgfHLen = $key->mgfHLen; $this->mgfHLen = $key->mgfHLen;
$this->password = $key->password; $this->password = $key->password;
$this->isPublic = $key->isPublic;
if (is_object($key->hash)) { if (is_object($key->hash)) {
$this->hash = new Hash($key->hash->getHash()); $this->hash = new Hash($key->hash->getHash());
@ -524,6 +535,7 @@ class RSA extends AsymmetricKey
return false; return false;
} }
$this->isPublic = false;
$this->modulus = $components['modulus']; $this->modulus = $components['modulus'];
$this->k = $this->modulus->getLengthInBytes(); $this->k = $this->modulus->getLengthInBytes();
$this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent']; $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent'];
@ -669,6 +681,7 @@ class RSA extends AsymmetricKey
} }
if ($key === false && !empty($this->modulus)) { if ($key === false && !empty($this->modulus)) {
$this->isPublic = true;
$this->publicExponent = $this->exponent; $this->publicExponent = $this->exponent;
return true; return true;
} }
@ -681,6 +694,7 @@ class RSA extends AsymmetricKey
if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) { if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) {
$this->modulus = $components['modulus']; $this->modulus = $components['modulus'];
$this->exponent = $this->publicExponent = $components['publicExponent']; $this->exponent = $this->publicExponent = $components['publicExponent'];
$this->isPublic = true;
return true; return true;
} }
@ -689,6 +703,18 @@ class RSA extends AsymmetricKey
return true; return true;
} }
/**
* Does the key self-identify as being a public key or not?
*
* @see self::isPublicKey()
* @access public
* @return bool
*/
public function isPublicKey()
{
return $this->isPublic();
}
/** /**
* Defines the private key * Defines the private key
* *
@ -1729,6 +1755,14 @@ class RSA extends AsymmetricKey
*/ */
public function encrypt($plaintext, $padding = self::PADDING_OAEP) public function encrypt($plaintext, $padding = self::PADDING_OAEP)
{ {
if (empty($this->modulus) || empty($this->exponent)) {
throw new NoKeyLoadedException('No key has been loaded');
}
if (!$this->isPublic) {
throw new UnsupportedOperationException('phpseclib does not allow the use of private keys to encrypt data');
}
switch ($padding) { switch ($padding) {
case self::PADDING_NONE: case self::PADDING_NONE:
return $this->raw_encrypt($plaintext); return $this->raw_encrypt($plaintext);
@ -1752,6 +1786,14 @@ class RSA extends AsymmetricKey
*/ */
public function decrypt($ciphertext, $padding = self::PADDING_OAEP) public function decrypt($ciphertext, $padding = self::PADDING_OAEP)
{ {
if (empty($this->modulus) || empty($this->exponent)) {
throw new NoKeyLoadedException('No key has been loaded');
}
if ($this->isPublic) {
throw new UnsupportedOperationException('phpseclib does not allow the use of public keys to decrypt data');
}
switch ($padding) { switch ($padding) {
case self::PADDING_NONE: case self::PADDING_NONE:
return $this->raw_encrypt($ciphertext); return $this->raw_encrypt($ciphertext);
@ -1775,7 +1817,11 @@ class RSA extends AsymmetricKey
public function sign($message, $padding = self::PADDING_PSS) public function sign($message, $padding = self::PADDING_PSS)
{ {
if (empty($this->modulus) || empty($this->exponent)) { if (empty($this->modulus) || empty($this->exponent)) {
return false; throw new NoKeyLoadedException('No key has been loaded');
}
if ($this->isPublic) {
throw new UnsupportedOperationException('phpseclib does not allow the use of public keys to sign data');
} }
switch ($padding) { switch ($padding) {
@ -1801,7 +1847,11 @@ class RSA extends AsymmetricKey
public function verify($message, $signature, $padding = self::PADDING_PSS) public function verify($message, $signature, $padding = self::PADDING_PSS)
{ {
if (empty($this->modulus) || empty($this->exponent)) { if (empty($this->modulus) || empty($this->exponent)) {
return false; throw new NoKeyLoadedException('No key has been loaded');
}
if (!$this->isPublic) {
throw new UnsupportedOperationException('phpseclib does not allow the use of private keys to verify data');
} }
switch ($padding) { switch ($padding) {

View File

@ -0,0 +1,26 @@
<?php
/**
* NoKeyLoadedException
*
* PHP version 5
*
* @category Exception
* @package NoKeyLoadedException
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Exception;
/**
* NoKeyLoadedException
*
* @package NoKeyLoadedException
* @author Jim Wigginton <terrafrost@php.net>
*/
class NoKeyLoadedException extends \RuntimeException
{
}

View File

@ -0,0 +1,26 @@
<?php
/**
* UnsupportedOperationException
*
* PHP version 5
*
* @category Exception
* @package UnsupportedOperationException
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Exception;
/**
* UnsupportedOperationException
*
* @package UnsupportedOperationException
* @author Jim Wigginton <terrafrost@php.net>
*/
class UnsupportedOperationException extends \RuntimeException
{
}

View File

@ -518,7 +518,7 @@ class X509
switch ($algorithm) { switch ($algorithm) {
case 'rsaEncryption': case 'rsaEncryption':
$cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'] $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']
= Base64::encode("\0" . Base64::decode(preg_replace('#-.+-|[\r\n]#', '', $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']))); = "\0" . Base64::decode(preg_replace('#-.+-|[\r\n]#', '', $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']));
/* "[For RSA keys] the parameters field MUST have ASN.1 type NULL for this algorithm identifier." /* "[For RSA keys] the parameters field MUST have ASN.1 type NULL for this algorithm identifier."
-- https://tools.ietf.org/html/rfc3279#section-2.3.1 -- https://tools.ietf.org/html/rfc3279#section-2.3.1
@ -1235,6 +1235,8 @@ class X509
case 'rsaEncryption': case 'rsaEncryption':
$rsa = new RSA(); $rsa = new RSA();
$rsa->load($publicKey); $rsa->load($publicKey);
//zzzzz
$rsa->setPublicKey();
switch ($signatureAlgorithm) { switch ($signatureAlgorithm) {
case 'md2WithRSAEncryption': case 'md2WithRSAEncryption':
@ -2036,7 +2038,7 @@ class X509
switch ($algorithm) { switch ($algorithm) {
case 'rsaEncryption': case 'rsaEncryption':
$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'] $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']
= Base64::encode("\0" . Base64::decode(preg_replace('#-.+-|[\r\n]#', '', $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']))); = "\0" . Base64::decode(preg_replace('#-.+-|[\r\n]#', '', $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']));
$csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['parameters'] = null; $csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['parameters'] = null;
$csr['signatureAlgorithm']['parameters'] = null; $csr['signatureAlgorithm']['parameters'] = null;
$csr['certificationRequestInfo']['signature']['parameters'] = null; $csr['certificationRequestInfo']['signature']['parameters'] = null;
@ -2122,7 +2124,6 @@ class X509
case 'rsaEncryption': case 'rsaEncryption':
$this->publicKey = new RSA(); $this->publicKey = new RSA();
$this->publicKey->load($key); $this->publicKey->load($key);
$this->publicKey->setPublicKey();
break; break;
default: default:
$this->publicKey = null; $this->publicKey = null;
@ -2157,7 +2158,7 @@ class X509
switch ($algorithm) { switch ($algorithm) {
case 'rsaEncryption': case 'rsaEncryption':
$spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'] $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']
= Base64::encode("\0" . Base64::decode(preg_replace('#-.+-|[\r\n]#', '', $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']))); = "\0" . Base64::decode(preg_replace('#-.+-|[\r\n]#', '', $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']));
} }
} }

View File

@ -132,4 +132,82 @@ U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
$rsa->load($rsa->getPublicKey()); $rsa->load($rsa->getPublicKey());
$this->assertTrue($rsa->verify($plaintext, $sig)); $this->assertTrue($rsa->verify($plaintext, $sig));
} }
/**
* @expectedException \phpseclib\Exception\UnsupportedOperationException
*/
public function testPrivateEncrypt()
{
$rsa = new RSA();
$privatekey = '-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp
wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5
1s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh
3tx4VgMtrQ+WEgCjhoTwo23KMBAuJGSYnRmoBZM3lMfTKevIkAidPExvYCdm5dYq3XToLkkLv5L2
pIIVOFMDG+KESnAFV7l2c+cnzRMW0+b6f8mR1CJzZuxVLL6Q02fvLi55/mbSYxECQQDeAw6fiIQX
GukBI4eMZZt4nscy2o12KyYner3VpoeE+Np2q+Z3pvAMd/aNzQ/W9WaI+NRfcxUJrmfPwIGm63il
AkEAxCL5HQb2bQr4ByorcMWm/hEP2MZzROV73yF41hPsRC9m66KrheO9HPTJuo3/9s5p+sqGxOlF
L0NDt4SkosjgGwJAFklyR1uZ/wPJjj611cdBcztlPdqoxssQGnh85BzCj/u3WqBpE2vjvyyvyI5k
X6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl
U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0=
-----END RSA PRIVATE KEY-----';
$rsa->load($privatekey);
$rsa->encrypt('hello, world!');
}
/**
* @expectedException \phpseclib\Exception\UnsupportedOperationException
*/
public function testPublicSign()
{
$rsa = new RSA();
$rsa->load('-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBAMuqkz8ij+ESAaNvgocVGmapjlrIldmhRo4h2NX4e6IXiCLTSxASQtY4
iqRnmyxqQSfaan2okTfQ6sP95bl8Qz8lgneW3ClC6RXG/wpJgsx7TXQ2kodlcKBF
m4k72G75QXhZ+I40ZG7cjBf1/9egakR0a0X0MpeOrKCzMBLv9+mpAgMBAAE=
-----END RSA PUBLIC KEY-----');
$rsa->sign('hello, world!');
}
/**
* @expectedException \phpseclib\Exception\UnsupportedOperationException
*/
public function testPublicDecrypt()
{
$rsa = new RSA();
$rsa->load('-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBAMuqkz8ij+ESAaNvgocVGmapjlrIldmhRo4h2NX4e6IXiCLTSxASQtY4
iqRnmyxqQSfaan2okTfQ6sP95bl8Qz8lgneW3ClC6RXG/wpJgsx7TXQ2kodlcKBF
m4k72G75QXhZ+I40ZG7cjBf1/9egakR0a0X0MpeOrKCzMBLv9+mpAgMBAAE=
-----END RSA PUBLIC KEY-----');
$rsa->decrypt('zzz');
}
/**
* @expectedException \phpseclib\Exception\UnsupportedOperationException
*/
public function testPrivateVerify()
{
$rsa = new RSA();
$privatekey = '-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp
wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5
1s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh
3tx4VgMtrQ+WEgCjhoTwo23KMBAuJGSYnRmoBZM3lMfTKevIkAidPExvYCdm5dYq3XToLkkLv5L2
pIIVOFMDG+KESnAFV7l2c+cnzRMW0+b6f8mR1CJzZuxVLL6Q02fvLi55/mbSYxECQQDeAw6fiIQX
GukBI4eMZZt4nscy2o12KyYner3VpoeE+Np2q+Z3pvAMd/aNzQ/W9WaI+NRfcxUJrmfPwIGm63il
AkEAxCL5HQb2bQr4ByorcMWm/hEP2MZzROV73yF41hPsRC9m66KrheO9HPTJuo3/9s5p+sqGxOlF
L0NDt4SkosjgGwJAFklyR1uZ/wPJjj611cdBcztlPdqoxssQGnh85BzCj/u3WqBpE2vjvyyvyI5k
X6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl
U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0=
-----END RSA PRIVATE KEY-----';
$rsa->load($privatekey);
$rsa->verify('hello, world!', 'dummysignature');
}
} }

View File

@ -46,8 +46,7 @@ class Unit_File_X509_SPKACTest extends PhpseclibTestCase
public function testSaveSPKAC() public function testSaveSPKAC()
{ {
$privKey = new RSA(); extract(RSA::createKey());
extract($privKey->createKey());
$x509 = new X509(); $x509 = new X509();
$x509->setPrivateKey($privatekey); $x509->setPrivateKey($privatekey);
@ -59,7 +58,7 @@ class Unit_File_X509_SPKACTest extends PhpseclibTestCase
$this->assertInternalType('string', $x509->saveSPKAC($spkac)); $this->assertInternalType('string', $x509->saveSPKAC($spkac));
$x509 = new X509(); $x509 = new X509();
$x509->setPrivateKey($privKey); $x509->setPrivateKey($privatekey);
$spkac = $x509->signSPKAC(); $spkac = $x509->signSPKAC();
$this->assertInternalType('array', $spkac); $this->assertInternalType('array', $spkac);