I am just wondering is it possible to convert PHP encryption function to Python? I am using PHP function to encrypt USER ID and store it in Database and now I need to decrypt USER ID in Python, I using this PHP function:
function decrypt($id) {
$cryptKey = '123';
$decoded = rtrim( mcrypt_decrypt( MCRYPT_RIJNDAEL_256, md5( $cryptKey ), base64_decode( $id ), MCRYPT_MODE_CBC, md5( md5( $cryptKey ) ) ), "\0");
return( $decoded );
}
Here is your function 'translated' to python
from Crypto.Cipher import AES
from hashlib import md5
def decrypt(id):
cryptKey = '123'
cipher = AES.new(key=md5(cryptKey).hexdigest(), mode=AES.MODE_CBC, IV=md5(md5(cryptKey).hexdigest()).hexdigest()[:16])
decoded = cipher.decrypt(id.decode('base64')).rstrip('\0')
return decoded
A fiew suggestions
1. Use a random iv
2. Use a more complex key
3. Don't hardcode the key
4. Use openssl_decrypt , mcrypt_decrypt is deprecated
Note
This will not work with MCRYPT_RIJNDAEL_256 because it uses 32 byte blocks .
But you could use MCRYPT_RIJNDAEL_128 or openssl
Here is an example with openssl AES CBC in PHP :
function encrypt($data, $key) {
$method = "aes-" . strlen($key) * 8 . "-cbc";
$iv = openssl_random_pseudo_bytes(16);
$encoded = base64_encode($iv . openssl_encrypt($data, $method, $key, TRUE, $iv));
return $encoded;
}
function decrypt($data, $key) {
$method = "aes-" . strlen($key) * 8 . "-cbc";
$iv = substr(base64_decode($data), 0, 16);
$decoded = openssl_decrypt(substr(base64_decode($data), 16), $method, $key, TRUE, $iv);
return $decoded;
}
Python code :
from Crypto.Cipher import AES
from Crypto import Random
import base64
def encrypt(data, key):
pkcs7pad = lambda data: data + chr(16-(len(data)%16)).encode() * (16-(len(data)%16))
iv = Random.new().read(16)
cipher = AES.new(key=key, mode=AES.MODE_CBC, IV=iv)
encoded = base64.b64encode(iv + cipher.encrypt(pkcs7pad(data)))
return encoded
def decrypt(data, key):
pkcs7unpad = lambda data: data[:-ord(data[-1:])]
cipher = AES.new(key=key, mode=AES.MODE_CBC, IV=base64.b64decode(data)[:16])
decoded = cipher.decrypt(base64.b64decode(data)[16:])
return pkcs7unpad(decoded)
With the above functions you can encrypt in PHP - decrypt in python , and vice versa
Assuming that the key has a valid size (16, 24 or 32 bytes)
Related
I'm trying to write logic with Elixir and PHP to encrypt and decrypt text. My goal is to decrypt data which is encrypted in Elixir and vice versa. Both algorithms works fine, but the only problem I'm encountering is separating Initialization Vector from cipher text and conversion of binary to string in PHP and also decrypted text in Elixir.
Elixir Implementation:
defmodule Crypto.AES do
#block_size 16
#secret_key "something secret here"
def encrypt(text) do
secret_key_hash = make_hash(#secret_key, 32)
IO.inspect secret_key_hash
# create random Initialisation Vector
iv = :crypto.strong_rand_bytes(#block_size)
text = pad_pkcs7(text, #block_size)
encrypted_text = :crypto.crypto_one_time(:aes_256_cbc, secret_key_hash, iv, text, true )
encrypted_text = ( iv <> <<"::">> <> encrypted_text )
Base.encode64(encrypted_text)
end
def decrypt(ciphertext) do
secret_key_hash = make_hash(#secret_key, 32)
{:ok, ciphertext} = Base.decode64(ciphertext)
<<iv::binary-16, rp::binary-2, ciphertext::binary>> = ciphertext
decrypted_text = :crypto.crypto_one_time(:aes_256_cbc, secret_key_hash, iv, ciphertext, false)
unpad_pkcs7(decrypted_text)
end
#doc """
Pad the `message` by extending it to the nearest `blocksize` boundary,
appending the number of bytes of padding to the end of the block.
If the original `message` is a multiple of `blocksize`, an additional block
of bytes with value `blocksize` is added.
"""
def pad_pkcs7(message, blocksize) do
pad = blocksize - rem(byte_size(message), blocksize)
message <> to_string(List.duplicate(pad, pad))
end
#doc """
Remove the PKCS#7 padding from the end of `data`.
"""
def unpad_pkcs7(data) do
<<pad>> = binary_part(data, byte_size(data), -1)
binary_part(data, 0, byte_size(data) - pad)
end
def make_hash(text, length) do
:crypto.hash(:sha512, text)
|> Base.encode16
|> String.downcase
|> String.slice(0, length)
end
end
PHP Implementation:
<?php
$ENCRYPTION_KEY = 'something secret here';
$ENCRYPTION_ALGORITHM = 'AES-256-CBC';
function encrypt($plain_text) {
global $ENCRYPTION_KEY;
global $ENCRYPTION_ALGORITHM;
$EncryptionKey = make_hash($ENCRYPTION_KEY, 32);
// create random Initialisation Vector
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($ENCRYPTION_ALGORITHM));
$encrypted_text = openssl_encrypt(
$plain_text,
$ENCRYPTION_ALGORITHM,
$EncryptionKey,
0,
$iv
);
return base64_encode($encrypted_text . '::' . $iv);
}
function decrypt($ciphertext) {
global $ENCRYPTION_KEY;
global $ENCRYPTION_ALGORITHM;
$EncryptionKey = make_hash($ENCRYPTION_KEY, 32);
$split = explode('::', base64_decode($ciphertext), 2);
list($iv, $encrypted_text) = array_pad($split, 2, null);
$plain_text = openssl_decrypt(
$encrypted_text,
$ENCRYPTION_ALGORITHM,
$EncryptionKey,
0,
$iv
);
return $plain_text;
}
function make_hash($text, $length) {
$hash_key = hash("sha512", $text, false);
return substr($hash_key,0,$length);
}
$ct = encrypt("hello");
// echo $ct."\n";
echo decrypt("Sr4nMnMdDHhUQcnW6RwZ2Do6rhBh/ytW1W/x7Xx2/Xrv3A==")."\n";
?>
Please suggest some possible solutions. Thanks
The two codes are incompatible for two reasons:
The PHP code returns the ciphertext Base64 encoded by default during encryption. This must be disabled because the Base64 encoding is performed explicitly after the concatenation of IV and ciphertext. Disabling is possible by passing OPENSSL_RAW_DATA as 4th parameter in openssl_encrypt(). The same applies for decryption:
$encrypted_text = openssl_encrypt(
$plain_text,
$ENCRYPTION_ALGORITHM,
$EncryptionKey,
OPENSSL_RAW_DATA, // Fix: Don't Base64 encode the ciphertext
$iv
);
...
$plain_text = openssl_decrypt(
$encrted_text,
$ENCRYPTION_ALGORITHM,
$EncryptionKey,
OPENSSL_RAW_DATA, // Fix: Don't Base64 decode the ciphertext
$iv
);
As already noted in the comment, both codes assume a different order of IV and ciphertext when separating during decryption. Menwhile you have adapted the separation during decryption in the PHP code to that of the Elixir code.
But also for encryption, both codes use a different order of IV and ciphertext. The modification of the PHP code concerning the encryption is still missing:
...
return base64_encode($iv . '::' . $encrypted_text); // Reverse order
With these changes, the two codes are functionally identical and a ciphertext generated with the PHP code can be decrypted with the Elixir code and vice versa.
A note regarding concatenation: Both the IV and ciphertext can contain the separator :: with a certain probability, which can cause problems. It would be better to concatenate the data without a separator and use the known length of the IV for the separation.
Also, using a hash function for key derivation is insecure, better apply a reliable key derivation function like PBKDF2. Furthermore, taking the hex encoded key instead of the binary data reduces security.
With suggestions (except PBKDF2, will add later) from #Topaco, here's complete solution.
Elixir - git-link
defmodule Crypto.AES do
#block_size 16
#secret_key "put something secret here"
def encrypt(plain_text) do
secret_key_hash = make_hash(#secret_key, 32)
# create Initialisation Vector
iv = :crypto.strong_rand_bytes(#block_size)
padded_text = pad_pkcs7(plain_text, #block_size)
encrypted_text = :crypto.crypto_one_time(:aes_256_cbc, secret_key_hash, iv, padded_text, true )
# concatenate IV for decryption
encrypted_text = ( iv <> encrypted_text )
Base.encode64(encrypted_text)
end
def decrypt(ciphertext) do
secret_key_hash = make_hash(#secret_key, 32)
{:ok, ciphertext} = Base.decode64(ciphertext)
<<iv::binary-16, ciphertext::binary>> = ciphertext
decrypted_text = :crypto.crypto_one_time(:aes_256_cbc, secret_key_hash, iv, ciphertext, false)
unpad_pkcs7(decrypted_text)
end
defp pad_pkcs7(message, blocksize) do
pad = blocksize - rem(byte_size(message), blocksize)
message <> to_string(List.duplicate(pad, pad))
end
defp unpad_pkcs7(data) do
<<pad>> = binary_part(data, byte_size(data), -1)
binary_part(data, 0, byte_size(data) - pad)
end
defp make_hash(text, length) do
:crypto.hash(:sha512, text)
|> Base.encode16
|> String.downcase
|> String.slice(0, length)
end
end
PHP - gist-link
<?php
$ENCRYPTION_KEY = "put something secret here";
$ENCRYPTION_ALGORITHM = 'AES-256-CBC';
function encrypt($plain_text) {
global $ENCRYPTION_KEY;
global $ENCRYPTION_ALGORITHM;
$EncryptionKey = make_hash($ENCRYPTION_KEY, 32);
// create random Initialization Vector
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($ENCRYPTION_ALGORITHM));
$encrypted_text = openssl_encrypt(
$plain_text,
$ENCRYPTION_ALGORITHM,
$EncryptionKey,
OPENSSL_RAW_DATA,
$iv
);
# concatenate the IV for decryption
return base64_encode($iv . $encrypted_text);
}
function decrypt($ciphertext) {
global $ENCRYPTION_KEY;
global $ENCRYPTION_ALGORITHM;
$EncryptionKey = make_hash($ENCRYPTION_KEY, 32);
$ciphertext = base64_decode($ciphertext);
// get Initialization Vector part (16 bytes long)
$iv = substr($ciphertext, 0, 16);
// rest is actual cipher text
$ciphertext = substr($ciphertext, 16);
$decrypted_text = openssl_decrypt(
$ciphertext,
$ENCRYPTION_ALGORITHM,
$EncryptionKey,
OPENSSL_RAW_DATA,
$iv
);
return $decrypted_text;
}
function make_hash($text, $length) {
$hash_key = hash("sha512", $text, false);
return substr($hash_key,0,$length);
}
// $ct = encrypt("code");
// $dt = decrypt($ct);
// echo $ct."\n";
// echo $dt."\n";
?>
I have updated my php version to 7.1.
I had functions where i encrypt data using mcrypt.
Now this function is deprecated.
How can i decrypt the data anyway withoud going back to older versions of php.
This is the code i used:
public function encrypt($plaintext) {
$ivSize = mcrypt_get_iv_size(self::CIPHER, self::MODE);
$iv = mcrypt_create_iv($ivSize, MCRYPT_DEV_URANDOM);
$ciphertext = mcrypt_encrypt(self::CIPHER, $this->key, $plaintext, self::MODE, $iv);
return base64_encode($iv.$ciphertext);
}
public function decrypt($ciphertext) {
$ciphertext = base64_decode($ciphertext);
$ivSize = mcrypt_get_iv_size(self::CIPHER, self::MODE);
if (strlen($ciphertext) < $ivSize) {
throw new Exception('Missing initialization vector');
}
$iv = substr($ciphertext, 0, $ivSize);
$ciphertext = substr($ciphertext, $ivSize);
$plaintext = mcrypt_decrypt(self::CIPHER, $this->key, $ciphertext, self::MODE, $iv);
return rtrim($plaintext, "\0");
}
With Constants:
const CIPHER = MCRYPT_RIJNDAEL_128; // Rijndael-128 is AES
const MODE = MCRYPT_MODE_CBC;
I saw that it was recommended to use OpenSSL. That is what i will use from now on. But how can i decrypt the older data using this method?
Thanks
Edit:
I know i can use OpenSSL as alternative.
Thats what i am doing for the content from now on.
But i need to decrypt my mcrypted code from my old contents.
*Edit request #symcbean
Tried to decrypt with OpenSSL like this:
public function decrypt($ciphertext) {
$ciphertext = base64_decode($ciphertext);
if (!function_exists("openssl_decrypt")) {
throw new Exception("aesDecrypt needs openssl php module.");
}
$key = $this->key;
$method = 'AES-256-CBC';
$ivSize = openssl_cipher_iv_length($method);
$iv = substr($ciphertext,0,$ivSize);
$data = substr($ciphertext,$ivSize);
$clear = openssl_decrypt ($data, $method, $key, 'OPENSSL_RAW_DATA'|'OPENSSL_ZERO_PADDING', $iv);
return $clear;
}
Important thing to note is that mcrypt_encrypt zero-pads input data if it's not a multiple of the blocksize. This leads to ambiguous results if the data itself has trailing zeroes.
openssl_decrypt doesn't remove the zero-padding automatically, so you're left only with the possibility of trimming the trailing nulls.
Here's a trivial example:
$data = "Lorem ipsum";
$key = "1234567890abcdef";
$iv = "1234567890abcdef";
$encrypted = mcrypt_encrypt(
MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv);
echo bin2hex($encrypted) . "\n";
$decrypted = openssl_decrypt(
$encrypted, "AES-128-CBC", $key,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
echo var_export($decrypted, true) . "\n";
$result = rtrim($decrypted, "\0");
echo var_export($result, true) . "\n";
Output:
70168f2d5751b3d3bf36b7e6b8ec5843
'Lorem ipsum' . "\0" . '' . "\0" . '' . "\0" . '' . "\0" . '' . "\0" . ''
'Lorem ipsum'
I solved it.
Don't know if its the right way (guess not)
But connected remotely on a server with a lower php version.
Decrypted all the content and encrypted with OpenSSL.
Thanks for the suggestions!
I also had some problems decrypting data encrypted with mcrypt_encrypt with openssl_decrypt. The following small test encrypts a string with mcrypt and openssl (with added zero padding and without) and decrypts all strings with both methods. This example uses ECB mode but you can easily change this to CBC by adding an IV if needed.
// Setup key and test data
$key = hash("sha256", 'test', true);
$data = 'Hello World';
$enc = $dec = [];
// Encrypt with MCRYPT_RIJNDAEL_128 method
$enc['RIJ'] = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_ECB));
// Encrypt with OpenSSL equivalent AES-256
$enc['AES'] = base64_encode(openssl_encrypt($data, 'aes-256-ecb', $key, OPENSSL_RAW_DATA));
// Encrypt with OpenSSL equivalent AES-256 and added zero padding
if (strlen($data) % 8) $data = str_pad($data, strlen($data) + 8 - strlen($data) % 8, "\0");
$enc['AES0'] = base64_encode(openssl_encrypt($data, 'aes-256-ecb', $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING));
// Decrypt all strings with MCRYPT_RIJNDAEL_128
$dec['mRIJ'] = bin2hex(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($enc['RIJ']), MCRYPT_MODE_ECB));
$dec['mAES'] = bin2hex(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($enc['AES']), MCRYPT_MODE_ECB));
$dec['mAES0'] = bin2hex(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($enc['AES0']), MCRYPT_MODE_ECB));
// Decrypt all strings with OpenSSL equivalent AES-256
$dec['oRIJ'] = bin2hex(openssl_decrypt(base64_decode($enc['RIJ']), 'aes-256-ecb', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING));
$dec['oAES'] = bin2hex(openssl_decrypt(base64_decode($enc['AES']), 'aes-256-ecb', $key, OPENSSL_RAW_DATA));
$dec['oAES0'] = bin2hex(openssl_decrypt(base64_decode($enc['AES0']), 'aes-256-ecb', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING));
// Print results
print_r($enc);
var_dump($dec);
The print_r and var_dump output is the following:
Array
(
[RIJ] => YcvcTwAMLUMBCZXu5XqoEw==
[AES] => +AXMBwkWlgM1YDieGgekSg==
[AES0] => YcvcTwAMLUMBCZXu5XqoEw==
)
array(6) {
["mRIJ"]=>
string(32) "48656c6c6f20576f726c640000000000"
["mAES"]=>
string(32) "48656c6c6f20576f726c640505050505"
["mAES0"]=>
string(32) "48656c6c6f20576f726c640000000000"
["oRIJ"]=>
string(32) "48656c6c6f20576f726c640000000000"
["oAES"]=>
string(22) "48656c6c6f20576f726c64"
["oAES0"]=>
string(32) "48656c6c6f20576f726c640000000000"
}
If you need the same encrypted string with the openssl methods as you had with mcrypt, you'll have add the zero padding manually to the string (AES0 in the example). This way you'll get the exact same encrypted and decrypted strings as before. For some additional information about the zero padding, you should look at Joe's answer here: php: mcrypt_encrypt to openssl_encrypt, and OPENSSL_ZERO_PADDING problems
If you don't want to manually add the zero padding to all new messages, you'll need different flags for decrypting the old mcrypt-encrypted messages and the new messages encrypted with openssl. For the old messages you'll have to use the OPENSSL_ZERO_PADDING flag ($dec['oRIJ'] in the example), whereas you must not use it for the openssl encrypted messages ($dec['oAES'] in the example). In my case I used this approach, because the default behaviour of openssl seems more correct to me as the mcrypt one - if you encrypt a string with 11 bytes you get a string with 11 bytes back after decrypting it. As you can see in the example, this is not the case with mcrypt or with openssl and the added zero padding. In these cases you would have to remove the trailing zeros manually to get the original data back.
I am working on a project where PHP is used for decrypt AES-256-CBC messages
<?php
class CryptService{
private static $encryptMethod = 'AES-256-CBC';
private $key;
private $iv;
public function __construct(){
$this->key = hash('sha256', 'c7b35827805788e77e41c50df44441491098be42');
$this->iv = substr(hash('sha256', 'c09f6a9e157d253d0b2f0bcd81d338298950f246'), 0, 16);
}
public function decrypt($string){
$string = base64_decode($string);
return openssl_decrypt($string, self::$encryptMethod, $this->key, 0, $this->iv);
}
public function encrypt($string){
$output = openssl_encrypt($string, self::$encryptMethod, $this->key, 0, $this->iv);
$output = base64_encode($output);
return $output;
}
}
$a = new CryptService;
echo $a->encrypt('secret');
echo "\n";
echo $a->decrypt('S1NaeUFaUHdqc20rQWM1L2ZVMDJudz09');
echo "\n";
ouutput
>>> S1NaeUFaUHdqc20rQWM1L2ZVMDJudz09
>>> secret
Now I have to write Python 3 code for encrypting data.
I've tried use PyCrypto but without success. My code:
import base64
import hashlib
from Crypto.Cipher import AES
class AESCipher:
def __init__(self, key, iv):
self.key = hashlib.sha256(key.encode('utf-8')).digest()
self.iv = hashlib.sha256(iv.encode('utf-8')).digest()[:16]
__pad = lambda self,s: s + (AES.block_size - len(s) % AES.block_size) * chr(AES.block_size - len(s) % AES.block_size)
__unpad = lambda self,s: s[0:-ord(s[-1])]
def encrypt( self, raw ):
raw = self.__pad(raw)
cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
return base64.b64encode(cipher.encrypt(raw))
def decrypt( self, enc ):
enc = base64.b64decode(enc)
cipher = AES.new(self.key, AES.MODE_CBC, self.iv )
return self.__unpad(cipher.decrypt(enc).decode("utf-8"))
cipher = AESCipher('c7b35827805788e77e41c50df44441491098be42', 'c09f6a9e157d253d0b2f0bcd81d338298950f246')
enc_str = cipher.encrypt("secret")
print(enc_str)
output
>>> b'tnF87LsVAkzkvs+gwpCRMg=='
But I need output S1NaeUFaUHdqc20rQWM1L2ZVMDJudz09 which will PHP decrypt to secret. How to modify Python code to get expected output?
PHP's hash outputs a Hex-encoded string by default, but Python's .digest() returns bytes. You probably wanted to use .hexdigest():
def __init__(self, key, iv):
self.key = hashlib.sha256(key.encode('utf-8')).hexdigest()[:32].encode("utf-8")
self.iv = hashlib.sha256(iv.encode('utf-8')).hexdigest()[:16].encode("utf-8")
The idea of the initialization vector (IV) is to provide randomization for the encryption with the same key. If you use the same IV, an attacker may be able to deduce that you send the same message twice. This can be considered as a broken protocol.
The IV is not supposed to be secret, so you can simply send it along with the ciphertext. It is common to prepend it to the ciphertext during encryption and slice it off before decryption.
I need to communicate with a asp platform that uses the aspEncrypt from persits.
Can anyone provide an example how to decode a string with PHP and mcrypt that was created via the aspEncrypt routines.
An example page of aspEncrypt is available at this link:
http://support.persits.com/encrypt/demo_text.asp
So if I use the text "Test" and the key "test" it provides an base64 encoded string. I need a php example that convert this encoded string back to the text "Test" with usage of key "test".
This is how i finally solved it:
Expectation:
Key is known
IV is known (in my case, first 32 characters of encoded data)
Encrypted Text is known
In my special case all received data hex encoded.
This means IV and encrypted text.
function decrypt($sString, $sIv, $sKey, $iCipherAlg) {
$sDecrypted = mcrypt_decrypt($iCipherAlg, $sKey, $sString, MCRYPT_MODE_CBC, $sIv);
return trim($sDecrypted);
}
function hex2bin($sData) {
$iLen = strlen($sData);
$sNewData = '';
for($iCount=0;$iCount<$iLen;$iCount+=2) {
$sNewData .= pack("C",hexdec(substr($sData,$iCount,2)));
}
return $sNewData;
}
$sKey = 'this is my key';
// first 32 chars are IV
$sIv = hex2bin(substr($sEncodedData, 0, 32));
$sEncodedData = substr($sEncodedData, 32);
$sEncodedRaw = hex2bin($sEncodedData);
$sDecrypted = decrypt($sEncodedRaw, $sIv, $sKey, MCRYPT_RIJNDAEL_128);
A corresponding encryption works like that:
$sIv = mcrypt_create_iv(mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND);
$sKey = 'this is my key';
$sContent = 'a lot of content';
$sEncrypted = bin2hex(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $sKey, $sContent, MCRYPT_MODE_CBC, $sIv));
$sFullEncodedText = bin2hex($sIv) . $sEncrypted;
I encountered an old VBScript project which was encrypting strings with AspEncrypt like this:
Function EncryptString(data, base64Iv)
Set CM = Server.CreateObject("Persits.CryptoManager")
Set Context = CM.OpenContextEx("Microsoft Enhanced RSA and AES Cryptographic Provider", "", True)
Set Key = Context.GenerateKeyFromPassword("secret encryption password", calgSHA512, calgAES256)
Set IVblob = CM.CreateBlob
IVblob.Base64 = base64Iv
Key.SetIV IVblob
Set Blob = Key.EncryptText(data)
EncryptString = Blob.Base64 & ":" & base64Iv
End Function
Based on the arguments to GenerateKeyFromPassword, a binary key is created by hashing the password with SHA-512, and data is encrypted with the aes-256-cbc algorithm. The random Base64-encoded initialization vector is appended to the encrypted value after a colon.
This can be replicated in PHP using the OpenSSL extension:
class Aes256Cbc
{
private string $algo = 'aes-256-cbc';
private string $key;
private int $ivLen;
public function __construct(string $password)
{
$this->key = hash('sha512', $password, true);
$this->ivLen = openssl_cipher_iv_length($this->algo);
}
public function encrypt(string $data): string
{
$iv = random_bytes($this->ivLen);
$ciphertext = openssl_encrypt($data, $this->algo, $this->key, OPENSSL_RAW_DATA, $iv);
return base64_encode($ciphertext) . ':' . base64_encode($iv);
}
public function decrypt(string $encrypted): string
{
[$ctPart, $ivPart] = explode(':', $encrypted);
$iv = base64_decode($ivPart);
$ciphertext = base64_decode($ctPart);
return openssl_decrypt($ciphertext, $this->algo, $this->key, OPENSSL_RAW_DATA, $iv);
}
}
Example usage:
$aes = new Aes256Cbc("secret encryption password");
$decrypted = $aes->decrypt($someValue);
Note: if AspEncrypt was used without setting an initialization vector, the IV will be sequence of null bytes. This fixed IV could be generated in the above PHP class as follows:
$iv = str_repeat("\0", $this->ivLen);
It depends on which cipher it uses, take a look at mcrypt as long as you know the cipher and key it should be easy to decrypt.
If you know the cipher and mode used by the encryption, the function mcrypt_decrypt can decrypt it.
http://uk3.php.net/manual/en/function.mcrypt-decrypt.php
I have some encryption code that has been written in Perl (also a code snippet in PHP) - but I just can't get a version written in VB.NET to work with the third party.
Example in Perl
package Sitemaker::API::Crypt;
use warnings;
use strict;
use base qw( Exporter );
use Crypt::CBC;
use MIME::Base64;
our #EXPORT = qw(
encrypt
decrypt
);
=head2 encrypt( $key, $iv, $arg1[,$arg2][,...] )
This encrypts the argumenst (which must all be values, not references) with
the key (which must be 16 characters long)
=cut
sub encrypt {
my ( $key, $iv, #args ) = #_;
my $args = join( '|', #args );
my $cipher = Crypt::CBC->new(
-cipher => 'Rijndael',
-blocksize => 16,
-keysize => 16,
-header => 'none',
-padding => 'standard',
-iv => $iv,
-literal_key => 1,
-key => $key,
);
my $binary_token = $cipher->encrypt( $args );
return encode_base64( $iv . $binary_token );
}
1;
Example in PHP
/**
* #param string $plaintext
* #return string encrypted token
*
* Relies on the Mcrypt module
*/
function encrypt( $plaintext, $apiKey)
{
// Initialize mcrypt module (AES, 128-bit key, CBC)
$handle = mcrypt_module_open( MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
$iv = mcrypt_create_iv( mcrypt_enc_get_iv_size( $handle ), MCRYPT_DEV_URANDOM );
// PKCS#5 padding
$blocksize = mcrypt_enc_get_block_size( $handle );
$padsize = $blocksize - ( strlen( $plaintext ) % $blocksize );
$plaintext .= str_repeat( chr( $padsize ), $padsize );
// encrypt
mcrypt_generic_init( $handle, $apiKey, $iv );
$ciphertext = mcrypt_generic( $handle, $plaintext );
// clean up
mcrypt_generic_deinit( $handle );
mcrypt_module_close( $handle );
return base64_encode( $iv. $ciphertext );
}
So I tried to recreate the same in VB.NET, but I don't think it's working, as I post to the service and just get errors back. The encryption method in VB.NET is...
Public Function EncrpytIt(ByVal Key As String, ByVal IV As String, ByVal arrParams As String()) As String
Dim plainTextBytes As Byte()
plainTextBytes = Encoding.ASCII.GetBytes(Join(arrParams, "~"))
Dim outputBytes As Byte()
Dim symmetricKey As New System.Security.Cryptography.RijndaelManaged()
With symmetricKey
.Key = Encoding.ASCII.GetBytes(Key)
.IV = Encoding.ASCII.GetBytes(IV)
.Mode = CipherMode.CBC
.BlockSize = 128
.KeySize = 128
.Padding = PaddingMode.PKCS7
End With
Dim encryptor As ICryptoTransform = symmetricKey.CreateEncryptor(symmetricKey.Key, symmetricKey.IV)
Using msEncrypt As New MemoryStream()
Using csEncrypt As New CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)
csEncrypt.Write(plainTextBytes, 0, plainTextBytes.Length)
csEncrypt.FlushFinalBlock()
End Using
outputBytes = msEncrypt.ToArray
End Using
Return IV & Convert.ToBase64String(outputBytes)
End Function
Is that behaving in the same way, or do I have to alter the settings in the vb.net code?
The first thing that I see is that in the cases of PHP and Perl, you base 64 encode the concatenated IV string with the encrypted data, whereas in VB you return the concatenated string of the IV and the base 64 encoded output of the encryption.
You need to change the return statement to this:
Return Convert.ToBase64String(Encoding.ASCII.GetBytes(IV) & outputBytes)
Also, there seems to be a bit of inconsistency between the padding schemes used - at least between the PHP and the VB.NET version. Based on the comments in the PHP sample - that is using PKCS #5 and your Visual Basic code is using PKCS #7. I'm not sure what 'standard' means in the context of the Perl.