Last active
September 8, 2020 14:49
-
-
Save shgysk8zer0/b519edd5ecea9b009aaf1e59f335dba5 to your computer and use it in GitHub Desktop.
RSA public key cryptography using PHP >= 7
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
//namespace shgysk8zer0\PHPCrypt; | |
const PUBLIC_KEY = './pub.pem'; | |
const PRIVATE_KEY = './priv.pem'; | |
const PUB2 = './pub2.pem'; | |
const PRIV2 = './priv2.pem'; | |
const PASSWORD = 'dgbdkfjg'; | |
const PASSWORD2 = 'dfgnduyho'; | |
const MESSAGE = 'Hello world!'; | |
set_error_handler(function(...$args) | |
{ | |
print_r($args); | |
}); | |
set_exception_handler('print_r'); | |
set_include_path(__DIR__); | |
spl_autoload_register('spl_autoload'); | |
if ( | |
! @ file_exists(PUBLIC_KEY) | |
or ! @file_exists(PRIVATE_KEY) | |
or ! @ file_exists(PUB2) | |
or ! @file_exists(PRIV2) | |
) { | |
$keys = RSA::genKeys(PASSWORD); | |
$keys_other = RSA::genKeys(PASSWORD2); | |
file_put_contents(PRIVATE_KEY, $keys['private']); | |
file_put_contents(PUBLIC_KEY, $keys['public']); | |
file_put_contents(PRIV2, $keys_other['private']); | |
file_put_contents(PUB2, $keys_other['public']); | |
unset($keys, $keys_other); | |
} | |
$pkey = new RSA(PUBLIC_KEY, PRIVATE_KEY, PASSWORD); | |
$other = new RSA(PUB2, PRIV2, PASSWORD2); | |
$encrypted = $pkey->publicEncrypt(MESSAGE, PUB2); | |
$decrypted = $other->privateDecrypt($encrypted); | |
$sig = $pkey->sign($encrypted); | |
$valid = $other->verify($encrypted, $sig, PUBLIC_KEY); | |
if ($valid) { | |
echo 'Valid signature.' . PHP_EOL; | |
} else { | |
echo 'Invalid signature.' . PHP_EOL; | |
} | |
if ($decrypted === MESSAGE) { | |
echo $decrypted . PHP_EOL; | |
} elseif (!is_string($decrypted)) { | |
echo 'Failed to decrypt message.' . PHP_EOL; | |
} else { | |
echo 'Decrypted message does not match original message.' . PHP_EOL; | |
echo '[Original]: ' . MESSAGE . PHP_EOL; | |
echo '[Decrypted]: ' . $decrypted . PHP_EOL; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* @author Chris Zuber | |
* @package shgysk8zer0\PHPCrypt | |
* @subpackage Traits | |
* @version 1.0.0 | |
* @copyright 2017, Chris Zuber | |
* @license http://opensource.org/licenses/GPL-3.0 GNU General Public License, version 3 (GPL-3.0) | |
* This program is free software; you can redistribute it and/or | |
* modify it under the terms of the GNU General Public License | |
* as published by the Free Software Foundation, either version 3 | |
* of the License, or (at your option) any later version. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
* GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |
*/ | |
//namespace shgysk8zer0\PHPCrypt\Traits; | |
/** | |
* Provides easy, object-oriented methods for public key cryptography | |
* @example: | |
* if (! @ file_exists(PUBLIC_KEY) or ! @file_exists(PRIVATE_KEY)) { | |
* $keys = PKey::genKeys(PASSWORD); | |
* file_put_contents(PRIVATE_KEY, $keys['private']); | |
* file_put_contents(PUBLIC_KEY, $keys['public']); | |
* unset($keys); | |
* } | |
* $pkey = new PKey(); | |
* $pkey->setPrivateKey(PRIVATE_KEY[, PASSWORD]); | |
* $pkey->setPublicKey(PUBLIC_KEY); | |
* | |
* $encrypted = $pkey->publicEncrypt(MESSAGE); | |
* $decrypted = $pkey->privateDecrypt($encrypted); | |
* $sig = $pkey->sign($encrypted); | |
* $valid = $pkey->verify($encrypted, $sig); | |
* if ($valid) { | |
* echo 'Valid signature.' . PHP_EOL; | |
* } else { | |
* echo 'Invalid signature.' . PHP_EOL; | |
* } | |
* if ($decrypted === MESSAGE) { | |
* echo $decrypted . PHP_EOL; | |
* } else { | |
* echo 'Decrypted message does not match original message.' . PHP_EOL; | |
* } | |
*/ | |
trait Pkey | |
{ | |
/** | |
* The private key | |
* @var Resource | |
*/ | |
private $_private_key; | |
/** | |
* The public key | |
* @var Resource | |
*/ | |
private $_public_key; | |
/** | |
* Imports and sets private key | |
* @param String $key File to obtain private key from | |
* @param string $password Optional password to decrypt the key | |
* @return Bool Whether or not the key was successfully imported | |
*/ | |
final public function setPrivateKey(String $key, String $password = null) : Bool | |
{ | |
if ($this->_private_key = $this->_importPrivateKey($key, $password)) { | |
return true; | |
} else { | |
return false; | |
} | |
} | |
/** | |
* Imports and sets public key | |
* @param String $key File to obtain the public key from | |
* @return Bool Whether or not the key was successfully imported | |
*/ | |
final public function setPublicKey(String $key) : Bool | |
{ | |
if ($this->_public_key = $this->_importPublicKey($key)) { | |
return true; | |
} else { | |
return false; | |
} | |
} | |
/** | |
* Encrypt $data using public key | |
* @param String $data The data to encrypt using public key | |
* @param String $public_key Optional public key to check against. Defaults to $_public_key | |
* @param integer $padding Padding contant <https://php.net/manual/en/openssl.padding.php> | |
* @return String Data encrypted using public key | |
* @see https://php.net/manual/en/function.openssl-public-encrypt.php | |
*/ | |
final public function publicEncrypt(String $data, $public_key = null, Int $padding = OPENSSL_PKCS1_OAEP_PADDING) : String | |
{ | |
if (is_null($public_key)) { | |
if (!isset($this->_public_key)) { | |
throw new \Exception('Attempting to encrypt using unset public key.'); | |
} else { | |
$public_key = $this->_public_key; | |
} | |
} else { | |
$public_key = $this->_importPublicKey($public_key); | |
} | |
if (openssl_public_encrypt($data, $crypted, $public_key, $padding)) { | |
return $crypted; | |
} else { | |
trigger_error('Failed to encrypt data.'); | |
return ''; | |
} | |
} | |
/** | |
* Decrypt data using public key | |
* @param String $data The encrypted data | |
* @param String $public_key Optional public key to check against. Defaults to $_public_key | |
* @param integer $padding Padding contant <https://php.net/manual/en/openssl.padding.php> | |
* @return String Data decrypted using public key | |
* @see https://php.net/manual/en/function.openssl-public-decrypt.php | |
*/ | |
final public function publicDecrypt(String $data, String $public_key = null, Int $padding = OPENSSL_PKCS1_OAEP_PADDING): String | |
{ | |
if (is_null($public_key)) { | |
if (!isset($this->_public_key)) { | |
throw new \Exception('Attempting to decrypt using unset public key.'); | |
} else { | |
$public_key = $this->_public_key; | |
} | |
} else { | |
$public_key = $this->_importPublicKey($public_key); | |
} | |
if (openssl_private_decrypt($data, $decrypted, $public_key, $padding)) { | |
return $decrypted; | |
} else { | |
trigger_error('Failed to decrypt data'); | |
return ''; | |
} | |
} | |
/** | |
* Encrypt data using private key | |
* @param String $data The data to encrypt using private key | |
* @param integer $padding Padding contant <https://php.net/manual/en/openssl.padding.php> | |
* @return String Data encryped with private key | |
* @see https://php.net/manual/en/function.openssl-private-encrypt.php | |
*/ | |
final public function privateEncrypt(String $data, Int $padding = OPENSSL_PKCS1_OAEP_PADDING): String | |
{ | |
if (!isset($this->_private_key)) { | |
throw new \Exception('Attempting to encrypt using unset private key.'); | |
} | |
if (openssl_private_encrypt($data, $crypted, $this->_private_key, $padding)) { | |
return $crypted; | |
} else { | |
trigger_error('Failed to encrypt data'); | |
return ''; | |
} | |
} | |
/** | |
* Decrypt data using private key | |
* @param String $data The data to encrypt with the private key | |
* @param integer $padding Padding contant <https://php.net/manual/en/openssl.padding.php> | |
* @return String Data encrypted with private key | |
* @see https://php.net/manual/en/function.openssl-private-decrypt.php | |
*/ | |
final public function privateDecrypt(String $data, Int $padding = OPENSSL_PKCS1_OAEP_PADDING) : String | |
{ | |
if (!isset($this->_private_key)) { | |
throw new \Exception('Attempting to decrypt using unset private key.'); | |
} | |
if (openssl_private_decrypt($data, $decrypted, $this->_private_key, $padding)) { | |
return $decrypted; | |
} else { | |
trigger_error('Failed to decrypt data'); | |
return ''; | |
} | |
} | |
/** | |
* Sign $data using private key | |
* @param String $data The data to sign using private key | |
* @param integer $algo Signing algorithm constant <https://secure.php.net/manual/en/openssl.signature-algos.php> | |
* @return String Signature created using private key | |
* @see https://php.net/manual/en/function.openssl-sign.php | |
*/ | |
final public function sign(String $data, Int $algo = OPENSSL_ALGO_SHA512) : String | |
{ | |
if (!isset($this->_private_key)) { | |
throw new \Exception('Attempting to sign using unset private key.'); | |
} | |
if (openssl_sign($data, $sig, $this->_private_key, $algo)) { | |
return $sig; | |
} else { | |
trigger_error('Failed to sign data.'); | |
return ''; | |
} | |
} | |
/** | |
* Verify a signature using a public key | |
* @param String $data The original data | |
* @param String $sig The signature | |
* @param String $public_key Optional public key to check against. Defaults to $_public_key | |
* @param integer $algo Signing algorithm constant <https://secure.php.net/manual/en/openssl.signature-algos.php> | |
* @return Bool Whether or not the signature is valid | |
* @see https://php.net/manual/en/function.openssl-sign.php | |
*/ | |
final public function verify( | |
String $data, | |
String $sig, | |
String $public_key = null, | |
Int $algo = OPENSSL_ALGO_SHA512 | |
): Bool | |
{ | |
if (is_null($public_key)) { | |
if (!isset($this->_public_key)) { | |
throw new \Exception('Attempting to verify signature using unset public key.'); | |
} else { | |
$public_key = $this->_public_key; | |
} | |
} else { | |
$public_key = $this->_importPublicKey($public_key); | |
} | |
$valid = openssl_verify($data, $sig, $public_key, $algo); | |
if ($valid === 1) { | |
return true; | |
} elseif ($valid === 0) { | |
return false; | |
} else { | |
trigger_error('Error validating signature.'); | |
return false; | |
} | |
} | |
/** | |
* Generates a new private/public key pair | |
* @param string $password Optional password to encrypt the private key | |
* @param string $digest Hashing method to use | |
* @param integer $length Size of key | |
* @param integer $keytype Key type constant <https://secure.php.net/manual/en/openssl.key-types.php> | |
* @param integer $cipher Cipher to use when encrypting private key usign $password <https://secure.php.net/manual/en/openssl.ciphers.php> | |
* @return Array ['private' => $private_key, 'public' => $pubic_key] | |
* @see https://secure.php.net/manual/en/function.openssl-pkey-new.php | |
*/ | |
final public static function genKeys( | |
String $password = null, | |
String $digest = 'sha512', | |
Int $length = 4096, | |
Int $keytype = OPENSSL_KEYTYPE_RSA, | |
Int $cipher = OPENSSL_CIPHER_AES_256_CBC | |
) : Array | |
{ | |
$configargs = [ | |
'digest_alg' => $digest, | |
'private_key_bits' => $length, | |
'private_key_type' => $keytype, | |
]; | |
if (is_string($password)) { | |
$configargs['encrypt_key'] = true; | |
$configargs['encrypt_key_cipher'] = $cipher; | |
} | |
$res = openssl_pkey_new($configargs); | |
if (!$res) { | |
throw new \Exception('Error generating key pair.'); | |
} | |
$public = openssl_pkey_get_details($res); | |
openssl_pkey_export($res, $private, $password, $configargs); | |
return [ | |
'private' => $private, | |
'public' => $public['key'] | |
]; | |
} | |
/** | |
* Import public key from file or a PEM formatted public key string | |
* @param String $key '/path/to/key.pem' or '-----BEGIN PUBLIC KEY-----' ... | |
* @return Resource A positive key resource identifier | |
* @see https://php.net/manual/en/function.openssl-pkey-get-public.php | |
*/ | |
final private function _importPublicKey(String $key) | |
{ | |
if (@file_exists($key)) { | |
$key = 'file://' . realpath($key); | |
} | |
if ($key = @openssl_pkey_get_public($key)) { | |
return $key; | |
} else { | |
throw new \InvalidArgumentException('Failed to import public key.'); | |
return false; | |
} | |
} | |
/** | |
* Import private key from file or a PEM formatted private key string | |
* @param String $key '/path/to/key.pem' or '-----BEGIN [ENCRYPTED ]PRIVATE KEY-----' ... | |
* @param String $password Optional password to unlock an encrypted private key | |
* @return Resource A positive key resource identifier | |
* @see https://secure.php.net/manual/en/function.openssl-pkey-get-private.php | |
*/ | |
final private function _importPrivateKey(String $key, String $password = null) | |
{ | |
if (@file_exists($key)) { | |
$key = 'file://' . realpath($key); | |
} | |
if ($key = @openssl_pkey_get_private($key, $password)) { | |
return $key; | |
} else { | |
throw new \InvalidArgumentException('Failed to import private key.'); | |
return false; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* @author Chris Zuber | |
* @package shgysk8zer0\PHPCrypt | |
* @version 1.0.0 | |
* @copyright 2017, Chris Zuber | |
* @license http://opensource.org/licenses/GPL-3.0 GNU General Public License, version 3 (GPL-3.0) | |
* This program is free software; you can redistribute it and/or | |
* modify it under the terms of the GNU General Public License | |
* as published by the Free Software Foundation, either version 3 | |
* of the License, or (at your option) any later version. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
* GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |
*/ | |
//namespace shgysk8zer0\PHPCrypt; | |
final class RSA | |
{ | |
use Traits\PKey; | |
/** | |
* Loads private and public keys, unlocking private key with optional password | |
* @param String $public_key '/path/to/key.pem' or '-----BEGIN PUBLIC KEY-----' ... | |
* @param String $private_key '/path/to/key.pem' or '-----BEGIN [ENCRYPTED ]PRIVATE KEY-----' ... | |
* @param String $password Optional password to unlock private key | |
*/ | |
public function __construct( | |
String $public_key, | |
String $private_key, | |
String $password = null | |
) | |
{ | |
if ( | |
! $this->setPrivateKey($private_key, $password) | |
or ! $this->setPublicKey($public_key) | |
) { | |
throw new \InvalidArgumentException('Given files are not valid keys.'); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment