Decrypting string encrypted in C# with RijndaelManaged class using PHP - php

Here's some C# code (I've modified it slightly to modify some of the hard coded values in it):
public static string Decrypt(string InputFile)
{
string outstr = null;
if ((InputFile != null))
{
if (File.Exists(InputFile))
{
FileStream fsIn = null;
CryptoStream cstream = null;
try
{
byte[] _b = { 94, 120, 102, 204, 199, 246, 243, 104, 185, 115, 76, 48, 220, 182, 112, 101 };
fsIn = File.Open(InputFile, FileMode.Open, System.IO.FileAccess.Read);
SymmetricAlgorithm symm = new RijndaelManaged();
PasswordDeriveBytes Key = new PasswordDeriveBytes(System.Environment.MachineName, System.Text.Encoding.Default.GetBytes("G:MFX62rlABW:IUYAX(i"));
ICryptoTransform transform = symm.CreateDecryptor(Key.GetBytes(24), _b);
cstream = new CryptoStream(fsIn, transform, CryptoStreamMode.Read);
StreamReader sr = new StreamReader(cstream);
char[] buff = new char[1000];
sr.Read(buff, 0, 1000);
outstr = new string(buff);
}
finally
{
if (cstream != null)
{
cstream.Close();
}
if (fsIn != null)
{
fsIn.Close();
}
}
}
}
return outstr;
}
I need to come up with a function to do the same in PHP. Bear in mind, I did not write the C# code and I cannot modify it, so even if it's bad, I'm stuck with it. I've searched all over and have found bits and pieces around, but nothing that works so far. All examples I've found use mcrypt, which seems to be frowned upon these days, but I'm probably stuck using it. Next, I found the following post which has some useful info: Rewrite Rijndael 256 C# Encryption Code in PHP
So looks like the PasswordDeriveBytes class is the key to this. I created the following PHP code to try to decrypt:
function PBKDF1($pass,$salt,$count,$dklen) {
$t = $pass.$salt;
//echo 'S||P: '.bin2hex($t).'<br/>';
$t = sha1($t, true);
//echo 'T1:' . bin2hex($t) . '<br/>';
for($i=2; $i <= $count; $i++) {
$t = sha1($t, true);
//echo 'T'.$i.':' . bin2hex($t) . '<br/>';
}
$t = substr($t,0,$dklen);
return $t;
}
$input = 'Ry5WdjGS8rpA9eA+iQ3aPw==';
$key = "win7x64";
$salt = implode(unpack('C*', "G:MFX62rlABW:IUYAX(i"));
$salt = pack("H*", $salt);
$it = 1000;
$keyLen = 16;
$key = PBKDF1($key, $salt, $it, $keyLen);
$key = bin2hex(substr($key, 0, 8));
$iv = bin2hex(substr($key, 8, 8));
echo trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, base64_decode($input), MCRYPT_MODE_CBC, $iv));
You'll note that for what I believe to be System.Environment.MachineName, I put in a fixed value for now which is the computer name of the machine I'm on, so should be equivalent of what the C# code is doing. Other than that, I've noticed that using MCRYPT_RIJNDAEL_256 doesn't work, it throws the error "The IV parameter must be as long as the blocksize". If I use MCRYPT_RIJNDAEL_128, I don't get that error, but decryption still fails. I assume I'm missing the piece for the byte array _b that's used by the CreateDecryptor function, I have no idea where that's supposed to fit in. Any help is appreciated.
UPDATE
This is the solution, which was made possible by the answer marked correct. Note that the code for the PBKDF1 function is not mine, it was linked to in the answer.
function PBKDF1($pass, $salt, $count, $cb) {
static $base;
static $extra;
static $extracount= 0;
static $hashno;
static $state = 0;
if ($state == 0)
{
$hashno = 0;
$state = 1;
$key = $pass . $salt;
$base = sha1($key, true);
for($i = 2; $i < $count; $i++)
{
$base = sha1($base, true);
}
}
$result = "";
if ($extracount > 0)
{
$rlen = strlen($extra) - $extracount;
if ($rlen >= $cb)
{
$result = substr($extra, $extracount, $cb);
if ($rlen > $cb)
{
$extracount += $cb;
}
else
{
$extra = null;
$extracount = 0;
}
return $result;
}
$result = substr($extra, $rlen, $rlen);
}
$current = "";
$clen = 0;
$remain = $cb - strlen($result);
while ($remain > $clen)
{
if ($hashno == 0)
{
$current = sha1($base, true);
}
else if ($hashno < 1000)
{
$n = sprintf("%d", $hashno);
$tmp = $n . $base;
$current .= sha1($tmp, true);
}
$hashno++;
$clen = strlen($current);
}
// $current now holds at least as many bytes as we need
$result .= substr($current, 0, $remain);
// Save any left over bytes for any future requests
if ($clen > $remain)
{
$extra = $current;
$extracount = $remain;
}
return $result;
}
$input = 'base 64 encoded string to decrypt here';
$key = strtoupper(gethostname());
$salt = 'G:MFX62rlABW:IUYAX(i';
$it = 100;
$keyLen = 24;
$key = PBKDF1($key, $salt, $it, $keyLen);
$iv = implode(array_map('chr', [94, 120, 102, 204, 199, 246, 243, 104, 185, 115, 76, 48, 220, 182, 112, 101]));

_b is a static value that is used as the IV (CreateDecryptor takes a key and IV parameter). Since it is 16 bytes long, this means that you're using Rijndael-128 or more commonly known AES.
Key.GetBytes(24) suggests that a 24 byte key is derived and not a 16 byte key.
Make sure that
System.Text.Encoding.Default is equivalent with implode(unpack('C*', ...,
Default value for iterations of PasswordDeriveBytes is 1000,
Default value for hash of PasswordDeriveBytes is SHA-1
Security problems:
PBKDF1 is obsolete and PBKDF2 isn't that much better. Use up-to-date key derivation algorithms like Argon2 or scrypt.
The IV must be randomly chosen to achieve semantic security. It doesn't have to be secret, so it can be sent along with the ciphertext.
Stretching a key by encoding it to hex doesn't provide any security (don't use bin2hex).
The ciphertext is not authenticated, which means that you cannot detect (malicious) manipulation of encrypted messages. Employ encrypt-then-MAC.

Related

Working on a probability & provably fair chest opening function, am I thinking right?

So I'm working on a "chest opening simulator" with a client of mine and I have completed the whole system except for the actual probability part.
He sent me this calculation and algorithm for how the different rarities and items should function.
Now I got completely stuck here, but from looking at this logic, if I roll a dice that is 1-100, and it lands on 2, I hit the "Super Rare" tier correct?
I made a function for rolling a dice that is between 0.00-100.00 and which is provably fair (Using a users Client seed and a random server seed + nonce for each roll) which you can find below, how would I utilize this function to determine which of the tiers I hit and what item within the tier?
public function roll()
{
$client_seed = "client_seed";
$server_seed = "server_seed";
$nonce = 0;
$secret = $client_seed."-".$nonce;
$hash = hash_hmac('sha512', $secret, $server_seed); // Hash server_seed and secret
for($i = 0; $i < strlen($hash); $i += 5)
{
$sub = substr($hash, $i, 5); //Split it
if(strlen($sub) == 5)
{
$decimal_number = hexdec($sub); // Hex to decimal. At this point we have a random number
if($decimal_number < 1000000)
{
$decimal_fourc = bcmod($decimal_number, 10000); //Get the modulus
$final_decimal = bcdiv($decimal_fourc, 100, 2); //Divide the result by 100
$obj = new \stdClass();
$obj->seeds = new \stdClass();
$obj->seeds->server = $server_seed;
$obj->seeds->client = $client_seed;
$obj->result = new \stdClass();
$obj->result->nonce = $nonce;
$obj->result->lucky_number = number_format($final_decimal, 2);
}
} else {
break;
}
}
echo "<pre>";
print_r($obj);
echo "</pre>";
}

aes-256-cbc encryption decryption with initial vector in nodejs

I am trying to convert some existing php code into nodejs but node js code returns:
TypeError: Salt must be a buffer
I am using node version => v8.11.2
PHP Code :
class SecurityModel {
protected $key;
protected $method;
protected $data;
protected $iv;
function __construct($data, $key = 'testing', $method = 'AES-256-CBC',$InitialVector = "aw90rela942f65u2") {
$this->data = $data;
$this->key = $this->passwordDeriveBytes($key, null);
$this->method = $method;
$this->iv = $InitialVector;
}
function passwordDeriveBytes($password, $salt, $iterations = 100, $len = 32) {
$key = $password . $salt;
for($i = 0; $i < $iterations; $i++) {
$key = sha1($key, true);
}
if (strlen($key) < $len) {
$hx = $this->passwordDeriveBytes($password, $salt, $iterations - 1, 20);
$counter = 0;
while (strlen($key) < $len) {
$counter += 1;
$key .= sha1($counter . $hx, true);
}
}
return substr($key, 0, $len);
}
function encrypt(): string {
return openssl_encrypt($this->data, "aes-256-cbc", $this->key, 0, $this->iv);
}
function decrypt(): string {
return openssl_decrypt($this->data, "aes-256-cbc", $this->key, 0, $this->iv);
}
}
$objSecurityModel = new SecurityModel('437217');
$Encrypted = $objSecurityModel->encrypt();
echo "Encrypted :".$Encrypted ."<br>"; //returns-->C9xJGa03dRQx9ePm0nLnHg==
$objSecurityModel = new SecurityModel($Encrypted);
echo "Decrypted::".$objSecurityModel->decrypt(); //returns-->437217
I tried some what in nodejs
NodeJs Code :
const express = require('express');
const app = express();
var crypto = require('crypto');
key = 'testing'
plaintext = '437217'
iv = 'aw90rela942f65u2'
crypto.pbkdf2('testing', null, 100, 32, 'AES-256-CBC', (err, derivedKey) => {
if (err) throw err;
console.log(derivedKey.toString('hex')); // '3745e48...08d59ae'
key = derivedKey.toString('hex');
});
cipher = crypto.createCipheriv('aes-256-cbc', key,iv)
decipher = crypto.createDecipheriv('aes-256-cbc', key,iv);
var encryptedPassword = cipher.update(plaintext, 'utf8', 'base64');
encryptedPassword += cipher.final('base64')
var decryptedPassword = decipher.update(encryptedPassword, 'base64', 'utf8');
decryptedPassword += decipher.final('utf8');
console.log('original :', plaintext);
console.log('encrypted :', encryptedPassword);
console.log('decrypted :', decryptedPassword);
//PORT
const port = process.env.PORT || 3000;
app.listen(port,() => console.log(`Listening on port ${port}....`));
PBKDF2 is a great idea and is what the PHP code should have done in the first place. Unfortunately what happens inside passwordDeriveBytes() is nowhere near PBKDF2. You need to reproduce the looping like what happens inside passwordDeriveBytes() if you want to match it.
Oh and "Salt must be a buffer" is solved by converting the IV to a Buffer with Buffer.from(iv) (that is also a sign that a good IV should not be a string but random bytes).
const crypto = require('crypto');
function sha1(input) {
return crypto.createHash('sha1').update(input).digest();
}
function passwordDeriveBytes(password, salt, iterations, len) {
var key = Buffer.from(password + salt);
for(var i = 0; i < iterations; i++) {
key = sha1(key);
}
if (key.length < len) {
var hx = passwordDeriveBytes(password, salt, iterations - 1, 20);
for (var counter = 1; key.length < len; ++counter) {
key = Buffer.concat([key, sha1(Buffer.concat([Buffer.from(counter.toString()), hx]))]);
}
}
return Buffer.alloc(len, key);
}
var password = 'testing';
var plaintext = '437217';
var iv = 'aw90rela942f65u2';
//var key = crypto.pbkdf2Sync(password, '', 100, 32, 'sha1'); // How it should be
var key = passwordDeriveBytes(password, '', 100, 32); // How it is
console.log(key.toString('hex'));
var cipher = crypto.createCipheriv('aes-256-cbc', key, Buffer.from(iv));
var decipher = crypto.createDecipheriv('aes-256-cbc', key, Buffer.from(iv));
var part1 = cipher.update(plaintext, 'utf8');
var part2 = cipher.final();
var encrypted = Buffer.concat([part1, part2]).toString('base64');
var decrypted = decipher.update(encrypted, 'base64', 'utf8');
decrypted += decipher.final();
console.log('original :', plaintext);
console.log('encrypted :', encrypted);
console.log('decrypted :', decrypted);
Output:
df07df624db35d0bcf5fe7ff2dfdfffcef93f098939d750ca55595ae1b33925d
original : 437217
encrypted : C9xJGa03dRQx9ePm0nLnHg==
decrypted : 437217

Can't encrypt/decrypt on PHP equivalent NodeJS crypto

I'm having some trouble to get work an AES-256-CTR encrypt/decrypt in PHP, having a previosly encrypted string made with NodeJS crypto.
Here my JS code:
var crypto = require('crypto'),
algorithm = 'aes-256-ctr',
password = 'd6F3Efeq';
function encrypt(text){
var cipher = crypto.createCipher(algorithm,password)
var crypted = cipher.update(text,'utf8','hex')
crypted += cipher.final('hex');
return crypted;
}
function decrypt(text){
var decipher = crypto.createDecipher(algorithm,password)
var dec = decipher.update(text,'hex','utf8')
dec += decipher.final('utf8');
return dec;
}
Here my PHP code:
$text = "pereira";
echo bin2hex(openssl_encrypt($text, "aes-256-ctr", "d6F3Efeq",OPENSSL_RAW_DATA));
The JS's version return this on encrypt:
148bc695286379
The PHP's version return this on my encrypt test:
2f2ad5bb09fb56
Am I missing something here? Obviosly, I neither can decrypt correctly in PHP.
Thanks in advance.
You must set iv (initialization vector) in both side.
Node JS code:
var crypto = require('crypto'),
password = '1234567890abcdef1234567890abcdef',
iv = '1234567890abcdef',
text = "pereira";
function encrypt(iv, text, password){
var cipher = crypto.createCipheriv('aes-256-ctr', password, iv)
var crypted = cipher.update(text,'utf8','hex')
crypted += cipher.final('hex');
return crypted;
}
function decrypt(iv, text, password){
var decipher = crypto.createDecipheriv('aes-256-ctr', password, iv)
var dec = decipher.update(text,'hex','utf8')
dec += decipher.final('utf8');
return dec;
}
console.log(encrypt(iv, text, password));
And PHP code:
$text = 'pereira';
$algorithm = 'aes-256-ctr';
$password = '1234567890abcdef1234567890abcdef'; //node js required 32 byte length key
$iv = '1234567890abcdef'; //must be 16 byte length
echo bin2hex(openssl_encrypt($text, $algorithm, $password, OPENSSL_RAW_DATA, $iv));
Finally I got a solution for my question, thanks to #Alex K and #Artjom B.
As I mention in a comment, I must get a PHP encrypt/descrypt compatible with the NodeJS createCipher because the NodeJS application is a thrid party one.
So, the JS code is like this:
var crypto = require('crypto'),
algorithm = 'aes-256-ctr',
password = 'd6F3Efeq';
function encrypt(text){
var cipher = crypto.createCipher(algorithm,password)
var crypted = cipher.update(text,'utf8','hex')
crypted += cipher.final('hex');
return crypted;
}
function decrypt(text){
var decipher = crypto.createDecipher(algorithm,password)
var dec = decipher.update(text,'hex','utf8')
dec += decipher.final('utf8');
return dec;
}
I wrote a little PHP class to achieve this encrypt/decrypt job (Based on #Artjon's solution):
class Crypto
{
const METHOD = 'aes-256-ctr';
public function encrypt($plaintext, $password, $salt='', $encode = false)
{
$keyAndIV = self::evpKDF($password, $salt);
$ciphertext = openssl_encrypt(
$plaintext,
self::METHOD,
$keyAndIV["key"],
OPENSSL_RAW_DATA,
$keyAndIV["iv"]
);
$ciphertext = bin2hex($ciphertext);
if ($encode)
{
$ciphertext = base64_encode($ciphertext);
}
return $ciphertext;
}
public function decrypt($ciphertext, $password, $salt='', $encoded = false)
{
if ( $encoded )
{
$ciphertext = base64_decode($ciphertext, true);
if ($ciphertext === false)
{
throw new Exception('Encryption failure');
}
}
$ciphertext = hex2bin($ciphertext);
$keyAndIV = self::evpKDF($password, $salt);
$plaintext = openssl_decrypt(
$ciphertext,
self::METHOD,
$keyAndIV["key"],
OPENSSL_RAW_DATA,
$keyAndIV["iv"]
);
return $plaintext;
}
public function evpKDF($password, $salt, $keySize = 8, $ivSize = 4, $iterations = 1, $hashAlgorithm = "md5")
{
$targetKeySize = $keySize + $ivSize;
$derivedBytes = "";
$numberOfDerivedWords = 0;
$block = NULL;
$hasher = hash_init($hashAlgorithm);
while ($numberOfDerivedWords < $targetKeySize)
{
if ($block != NULL)
{
hash_update($hasher, $block);
}
hash_update($hasher, $password);
hash_update($hasher, $salt);
$block = hash_final($hasher, TRUE);
$hasher = hash_init($hashAlgorithm);
// Iterations
for ($i = 1; $i < $iterations; $i++)
{
hash_update($hasher, $block);
$block = hash_final($hasher, TRUE);
$hasher = hash_init($hashAlgorithm);
}
$derivedBytes .= substr($block, 0, min(strlen($block), ($targetKeySize - $numberOfDerivedWords) * 4));
$numberOfDerivedWords += strlen($block)/4;
}
return array(
"key" => substr($derivedBytes, 0, $keySize * 4),
"iv" => substr($derivedBytes, $keySize * 4, $ivSize * 4)
);
}
}
According to the NodeJS documentation, It derives the IV from the password with no salt, so I used an empty string as salt.
I'm using this PHP class in CodeIgniter like this:
$this->load->library("Crypto");
$plain_text = "pereira";
$password = "d6F3Efeq";
$enc_text = $this->crypto->encrypt($plain_text,$password);
$pla_text = $this->crypto->decrypt($enc_text,$password);
echo $enc_text."<br>";
echo $pla_text;
Both of them (NodeJS and PHP) return the same result for encrypt and decrypt function:
pereira = 148bc695286379
Regards.

PHP invalid hash tiger192 3

I am using a php code with php 5.4 and i am trying to hash a string with tiger192,3. I am not getting a correct hash after all..
$keyLength = 24;
$keyCharacters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$string = '';
$key = '';
$keyHash = '';
$first8Hash = '';
$uniqueKey = false;
while ($uniqueKey == false)
{
for ($p = 0; $p < $keyLength; $p++)
{
$string .= $keyCharacters[mt_rand(0, strlen($keyCharacters)-1)];
}
$key = $string;
$keyHash = hash('tiger192,3', $key);
$first8Hash = substr($keyHash, 0, 16);
$sql = "SELECT * FROM `penkeys` WHERE first8 = '" . $first8Hash . "'";
$result = $db->sql_query($sql);
while ($keyrow = $db->sql_fetchrow($result))
{
$uniqueKey = false;
}
$db->sql_freeresult($result);
$uniqueKey = true;
}
I am hashing the string nQ5GcLMsOlPIaUYJOMkmjo7f
I should be getting babcb7d489332aee9c554a7a654bb65b4dd892e5b80e0156 but i am getting ee2a3389d4b7bcba5bb64b657a4a559c56010eb8e592d84d.
Can you help me?
This is an interesting case where both answers (both the php answer and the timestampgenerator answer) are correct. In PHP >= 5.4, the tiger hashes use big-endian byte notation and in PHP < 5.4, it presumably used little-endian byte notation (noted in the changelog http://php.net/manual/en/function.hash.php). The function below is in the php docs and oldtiger gives babcb7d489332aee9c554a7a654bb65b4dd892e5b80e0156 as the result.
function old_tiger($data = "", $width=192, $rounds = 3) {
return substr(
implode(
array_map(
function ($h) {
return str_pad(bin2hex(strrev($h)), 16, "0");
},
str_split(hash("tiger192,$rounds", $data, true), 8)
)
),
0, 48-(192-$width)/4
);
}
echo hash('tiger192,3', 'a-string'), PHP_EOL;
echo old_tiger('a-string'), PHP_EOL;
If you notice, both babcb7d489332aee9c554a7a654bb65b4dd892e5b80e0156 and ee2a3389d4b7bcba5bb64b657a4a559c56010eb8e592d84d are anagrams of each other which is more than a coincidence. The endianness is changed between the two strings. Each set of 8 bytes is in byte-reversed order.
ee-2a-33-89-d4-b7-bc-ba becomes ba-bc-b7-d4-89-33-2a-ee. The byte-order endieness is switched for each 64-bit(8 byte) word.

Encrypt and Decrypt text with RSA in PHP

Is there any class for PHP 5.3 that provides RSA encryption/decryption without padding?
I've got private and public key, p,q, and modulus.
You can use phpseclib, a pure PHP RSA implementation:
<?php
include('Crypt/RSA.php');
$privatekey = file_get_contents('private.key');
$rsa = new Crypt_RSA();
$rsa->loadKey($privatekey);
$plaintext = new Math_BigInteger('aaaaaa');
echo $rsa->_exponentiate($plaintext)->toBytes();
?>
Security warning: This code snippet is vulnerable to Bleichenbacher's 1998 padding oracle attack. See this answer for better security.
class MyEncryption
{
public $pubkey = '...public key here...';
public $privkey = '...private key here...';
public function encrypt($data)
{
if (openssl_public_encrypt($data, $encrypted, $this->pubkey))
$data = base64_encode($encrypted);
else
throw new Exception('Unable to encrypt data. Perhaps it is bigger than the key size?');
return $data;
}
public function decrypt($data)
{
if (openssl_private_decrypt(base64_decode($data), $decrypted, $this->privkey))
$data = $decrypted;
else
$data = '';
return $data;
}
}
No application written in 2017 (or thereafter) that intends to incorporate serious cryptography should use RSA any more. There are better options for PHP public-key cryptography.
There are two big mistakes that people make when they decide to encrypt with RSA:
Developers choose the wrong padding mode.
Since RSA cannot, by itself, encrypt very long strings, developers will often break a string into small chunks and encrypt each chunk independently. Sort of like ECB mode.
The Best Alternative: sodium_crypto_box_seal() (libsodium)
$keypair = sodium_crypto_box_keypair();
$publicKey = sodium_crypto_box_publickey($keypair);
// ...
$encrypted = sodium_crypto_box_seal(
$plaintextMessage,
$publicKey
);
// ...
$decrypted = sodium_crypto_box_seal_open(
$encrypted,
$keypair
);
Simple and secure. Libsodium will be available in PHP 7.2, or through PECL for earlier versions of PHP. If you need a pure-PHP polyfill, get paragonie/sodium_compat.
Begrudgingly: Using RSA Properly
The only reason to use RSA in 2017 is, "I'm forbidden to install PECL extensions and therefore cannot use libsodium, and for some reason cannot use paragonie/sodium_compat either."
Your protocol should look something like this:
Generate a random AES key.
Encrypt your plaintext message with the AES key, using an AEAD encryption mode or, failing that, CBC then HMAC-SHA256.
Encrypt your AES key (step 1) with your RSA public key, using RSAES-OAEP + MGF1-SHA256
Concatenate your RSA-encrypted AES key (step 3) and AES-encrypted message (step 2).
Instead of implementing this yourself, check out EasyRSA.
Further reading: Doing RSA in PHP correctly.
If you are using PHP >= 7.2 consider using inbuilt sodium core extension for encrption.
It is modern and more secure. You can find more information here - http://php.net/manual/en/intro.sodium.php. and here - https://paragonie.com/book/pecl-libsodium/read/00-intro.md
Example PHP 7.2 sodium encryption class -
<?php
/**
* Simple sodium crypto class for PHP >= 7.2
* #author MRK
*/
class crypto {
/**
*
* #return type
*/
static public function create_encryption_key() {
return base64_encode(sodium_crypto_secretbox_keygen());
}
/**
* Encrypt a message
*
* #param string $message - message to encrypt
* #param string $key - encryption key created using create_encryption_key()
* #return string
*/
static function encrypt($message, $key) {
$key_decoded = base64_decode($key);
$nonce = random_bytes(
SODIUM_CRYPTO_SECRETBOX_NONCEBYTES
);
$cipher = base64_encode(
$nonce .
sodium_crypto_secretbox(
$message, $nonce, $key_decoded
)
);
sodium_memzero($message);
sodium_memzero($key_decoded);
return $cipher;
}
/**
* Decrypt a message
* #param string $encrypted - message encrypted with safeEncrypt()
* #param string $key - key used for encryption
* #return string
*/
static function decrypt($encrypted, $key) {
$decoded = base64_decode($encrypted);
$key_decoded = base64_decode($key);
if ($decoded === false) {
throw new Exception('Decryption error : the encoding failed');
}
if (mb_strlen($decoded, '8bit') < (SODIUM_CRYPTO_SECRETBOX_NONCEBYTES + SODIUM_CRYPTO_SECRETBOX_MACBYTES)) {
throw new Exception('Decryption error : the message was truncated');
}
$nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
$ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
$plain = sodium_crypto_secretbox_open(
$ciphertext, $nonce, $key_decoded
);
if ($plain === false) {
throw new Exception('Decryption error : the message was tampered with in transit');
}
sodium_memzero($ciphertext);
sodium_memzero($key_decoded);
return $plain;
}
}
Sample Usage -
<?php
$key = crypto::create_encryption_key();
$string = 'Sri Lanka is a beautiful country !';
echo $enc = crypto::encrypt($string, $key);
echo crypto::decrypt($enc, $key);
Yes. Look at http://jerrywickey.com/test/testJerrysLibrary.php
It gives sample code examples for RSA encryption and decryption in PHP as well as RSA encryption in javascript.
If you want to encrypt text instead of just base 10 numbers, you'll also need a base to base conversion. That is convert text to a very large number. Text is really just writing in base 63. 26 lowercase letters plus 26 uppercase + 10 numerals + space character. The code for that is below also.
The $GETn parameter is a file name that holds keys for the cryption functions. If you don't figure it out, ask. I'll help.
I actually posted this whole encryption library yesterday, but Brad Larson a mod, killed it and said this kind of stuff isn't really what Stack Overflow is about. But you can still find all the code examples and the whole function library to carry out client/server encryption decryption for AJAX at the link above.
function RSAencrypt( $num, $GETn){
if ( file_exists( 'temp/bigprimes'.hash( 'sha256', $GETn).'.php')){
$t= explode( '>,', file_get_contents('temp/bigprimes'.hash( 'sha256', $GETn).'.php'));
return JL_powmod( $num, $t[4], $t[10]);
}else{
return false;
}
}
function RSAdecrypt( $num, $GETn){
if ( file_exists( 'temp/bigprimes'.hash( 'sha256', $GETn).'.php')){
$t= explode( '>,', file_get_contents('temp/bigprimes'.hash( 'sha256', $GETn).'.php'));
return JL_powmod( $num, $t[8], $t[10]);
}else{
return false;
}
}
function JL_powmod( $num, $pow, $mod) {
if ( function_exists('bcpowmod')) {
return bcpowmod( $num, $pow, $mod);
}
$result= '1';
do {
if ( !bccomp( bcmod( $pow, '2'), '1')) {
$result = bcmod( bcmul( $result, $num), $mod);
}
$num = bcmod( bcpow( $num, '2'), $mod);
$pow = bcdiv( $pow, '2');
} while ( bccomp( $pow, '0'));
return $result;
}
function baseToBase ($message, $fromBase, $toBase){
$from= strlen( $fromBase);
$b[$from]= $fromBase;
$to= strlen( $toBase);
$b[$to]= $toBase;
$result= substr( $b[$to], 0, 1);
$f= substr( $b[$to], 1, 1);
$tf= digit( $from, $b[$to]);
for ($i=strlen($message)-1; $i>=0; $i--){
$result= badd( $result, bmul( digit( strpos( $b[$from], substr( $message, $i, 1)), $b[$to]), $f, $b[$to]), $b[$to]);
$f= bmul($f, $tf, $b[$to]);
}
return $result;
}
function digit( $from, $bto){
$to= strlen( $bto);
$b[$to]= $bto;
$t[0]= intval( $from);
$i= 0;
while ( $t[$i] >= intval( $to)){
if ( !isset( $t[$i+1])){
$t[$i+1]= 0;
}
while ( $t[$i] >= intval( $to)){
$t[$i]= $t[$i] - intval( $to);
$t[$i+1]++;
}
$i++;
}
$res= '';
for ( $i=count( $t)-1; $i>=0; $i--){
$res.= substr( $b[$to], $t[$i], 1);
}
return $res;
}
function badd( $n1, $n2, $nbase){
$base= strlen( $nbase);
$b[$base]= $nbase;
while ( strlen( $n1) < strlen( $n2)){
$n1= substr( $b[$base], 0, 1) . $n1;
}
while ( strlen( $n1) > strlen( $n2)){
$n2= substr( $b[$base], 0, 1) . $n2;
}
$n1= substr( $b[$base], 0, 1) . $n1;
$n2= substr( $b[$base], 0, 1) . $n2;
$m1= array();
for ( $i=0; $i<strlen( $n1); $i++){
$m1[$i]= strpos( $b[$base], substr( $n1, (strlen( $n1)-$i-1), 1));
}
$res= array();
$m2= array();
for ($i=0; $i<strlen( $n1); $i++){
$m2[$i]= strpos( $b[$base], substr( $n2, (strlen( $n1)-$i-1), 1));
$res[$i]= 0;
}
for ($i=0; $i<strlen( $n1) ; $i++){
$res[$i]= $m1[$i] + $m2[$i] + $res[$i];
if ($res[$i] >= $base){
$res[$i]= $res[$i] - $base;
$res[$i+1]++;
}
}
$o= '';
for ($i=0; $i<strlen( $n1); $i++){
$o= substr( $b[$base], $res[$i], 1).$o;
}
$t= false;
$o= '';
for ($i=strlen( $n1)-1; $i>=0; $i--){
if ($res[$i] > 0 || $t){
$o.= substr( $b[$base], $res[$i], 1);
$t= true;
}
}
return $o;
}
function bmul( $n1, $n2, $nbase){
$base= strlen( $nbase);
$b[$base]= $nbase;
$m1= array();
for ($i=0; $i<strlen( $n1); $i++){
$m1[$i]= strpos( $b[$base], substr($n1, (strlen( $n1)-$i-1), 1));
}
$m2= array();
for ($i=0; $i<strlen( $n2); $i++){
$m2[$i]= strpos( $b[$base], substr($n2, (strlen( $n2)-$i-1), 1));
}
$res= array();
for ($i=0; $i<strlen( $n1)+strlen( $n2)+2; $i++){
$res[$i]= 0;
}
for ($i=0; $i<strlen( $n1) ; $i++){
for ($j=0; $j<strlen( $n2) ; $j++){
$res[$i+$j]= ($m1[$i] * $m2[$j]) + $res[$i+$j];
while ( $res[$i+$j] >= $base){
$res[$i+$j]= $res[$i+$j] - $base;
$res[$i+$j+1]++;
}
}
}
$t= false;
$o= '';
for ($i=count( $res)-1; $i>=0; $i--){
if ($res[$i]>0 || $t){
$o.= substr( $b[$base], $res[$i], 1);
$t= true;
}
}
return $o;
}
I have difficulty in decrypting a long string that is encrypted in python. Here is the python encryption function:
def RSA_encrypt(public_key, msg, chunk_size=214):
"""
Encrypt the message by the provided RSA public key.
:param public_key: RSA public key in PEM format.
:type public_key: binary
:param msg: message that to be encrypted
:type msg: string
:param chunk_size: the chunk size used for PKCS1_OAEP decryption, it is determined by \
the private key length used in bytes - 42 bytes.
:type chunk_size: int
:return: Base 64 encryption of the encrypted message
:rtype: binray
"""
rsa_key = RSA.importKey(public_key)
rsa_key = PKCS1_OAEP.new(rsa_key)
encrypted = b''
offset = 0
end_loop = False
while not end_loop:
chunk = msg[offset:offset + chunk_size]
if len(chunk) % chunk_size != 0:
chunk += " " * (chunk_size - len(chunk))
end_loop = True
encrypted += rsa_key.encrypt(chunk.encode())
offset += chunk_size
return base64.b64encode(encrypted)
The decryption in PHP:
/**
* #param base64_encoded string holds the encrypted message.
* #param Resource your private key loaded using openssl_pkey_get_private
* #param integer Chunking by bytes to feed to the decryptor algorithm.
* #return String decrypted message.
*/
public function RSADecyrpt($encrypted_msg, $ppk, $chunk_size=256){
if(is_null($ppk))
throw new Exception("Returned message is encrypted while you did not provide private key!");
$encrypted_msg = base64_decode($encrypted_msg);
$offset = 0;
$chunk_size = 256;
$decrypted = "";
while($offset < strlen($encrypted_msg)){
$decrypted_chunk = "";
$chunk = substr($encrypted_msg, $offset, $chunk_size);
if(openssl_private_decrypt($chunk, $decrypted_chunk, $ppk, OPENSSL_PKCS1_OAEP_PADDING))
$decrypted .= $decrypted_chunk;
else
throw new exception("Problem decrypting the message");
$offset += $chunk_size;
}
return $decrypted;
}

Categories