Skip to content

Instantly share code, notes, and snippets.

@shgysk8zer0
Last active September 8, 2020 14:49
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shgysk8zer0/b519edd5ecea9b009aaf1e59f335dba5 to your computer and use it in GitHub Desktop.
Save shgysk8zer0/b519edd5ecea9b009aaf1e59f335dba5 to your computer and use it in GitHub Desktop.
RSA public key cryptography using PHP >= 7
<?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;
}
<?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;
}
}
}
<?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