We have php code:
define('myaesKey', 'znwoq8fq0jf2qjve8laper9f'); // 192 bits and 25 ch.
function encode($CodeTo) {
$Type = 'rijndael-128';
$Mode = 'ecb';
$IV = "1234567890123450";
$Object = mcrypt_module_open($Type, '', $Mode, '');
mcrypt_generic_init($Object , myaesKey, $IV);
$Enc2Code = mcrypt_generic($Object , $CodeTo);
mcrypt_generic_deinit($Object);
mcrypt_module_close($Object);
return bin2hex($secEncCode);
}
Length of $CodeTo is 5, CodeTo is readable symbols of English Alphabet, function send somethind like this
1e49651ba23801907e1d67c5a7c18e06
aefdc02bbcb8ed8e8209a935aa62be53
I tried to decode by diff. ways, one of this :
const
KeySize = 24; // 32 bytes = 256 bits 24 - 192
BlockSize = 16; // 16 bytes = 128 bits
function Decrypt(AText:AnsiString):String;
var
Cipher : TDCP_rijndael; i:Integer;
Data, Key, IV,NewStr : ansistring;
begin
// Pad Key and IV with zeros as appropriate
Key := PadWithZeros(ansistring('znwoq8fq0jf2qjve8laper9f'),KeySize);
IV := PadWithZeros(ansistring('1234567890123450'),BlockSize);
// Decode the Base64 encoded string
NewStr:='';
for i:=1 to (Length(AText) div 2) do
NewStr:=NewStr+chr(byte(StrToInt('$'+Copy(AText,(i-1)*2+1,2))));
Data := NewStr;
// Create the cipher and initialise according to the key length
Cipher := TDCP_rijndael.Create(nil);
if Length(ansistring('znwoq8fq0jf2qjve8laper9f')) <= 16 then
Cipher.Init(Key[1],128,#IV[1])
else if Length(ansistring('znwoq8fq0jf2qjve8laper9f')) <= 24 then
Cipher.Init(Key[1],192,#IV[1])
else
Cipher.Init(Key[1],256,#IV[1]);
// Decrypt the data
// Cipher.DecryptCBC(Data[1],Data[1],Length(Data));
Cipher.DecryptECB(Data[1],Data[1]);
// Free the cipher and clear sensitive information
Cipher.Free;
FillChar(Key[1],Length(Key),0);
// Display the result
result:= Data;
end;
but the decoded text is wrong
6d309aab9887deed8da964cca8818eb4
µ€ц‰ъиTDHQ ЮB№еП
Why?
Can someone help? Easy to decode it by http://www.tools4noobs.com/online_tools/decrypt/ withot IV ...
Try to use this
function AESDecrypt(AData, AKey: String): string;
var
KeyByte,Data,Dest:TBytes;
KeyBlock:integer;
Cipher:TDCP_rijndael;
begin
KeyByte:=TEncoding.UTF8.GetBytes(AKey);
while (Length(KeyByte) mod 16 <> 0) do begin
SetLength(KeyByte,Length(KeyByte)+1);
KeyByte[Length(KeyByte)-1]:=0;
end;
SetLength(Data,Length(AData) div 2);
SetLEngth(Dest,Length(AData) div 2);
Data:=GetBytesFromHex(AData);
Cipher:= TDCP_rijndael.Create(nil);
KeyBlock:=192; //by PHP code comment
Cipher.Init(KeyByte[0],KeyBlock,nil); //for ECB method IV is optional
try
for i := 1 to (Length(AData) div 16) do
begin
Cipher.DecryptECB(Data[(i-1)*16],Dest[(i-1)*16]);
end;
finally
Cipher.Burn;
end;
AData:=TEncoding.UTF8.GetString(Dest);
Result:=AData;
end;
Related
Can't figure out how does PHP openssl_encrypt work, and cannot reproduce its output in GoLang and NodeJs, here's a simplified code in PHP - outputs hvAB:
<?php
$string = 'aaa';
$cipher = "AES-128-CTR";
$options = 0;
$encryption_iv = '1234567890123456';
$encryption_key = 'bc7316929fe1545bf0b98d114ee3ecb8';
$encryption = openssl_encrypt($string, $cipher, $encryption_key, $options, $encryption_iv);
echo $encryption; // hvAB
In GoLang, assuming key must be hex decoded to get the desired length of 16 so that AES 128 will be used - outputs PQ5k:
package main
import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"encoding/hex"
"fmt"
)
func main() {
plainText := "aaa"
fmt.Println(encryption(plainText)) // PQ5k
}
func encryption(plainText string) string {
bytes := []byte(plainText)
blockCipher := createCipher()
stream := cipher.NewCTR(blockCipher, []byte("1234567890123456"))
stream.XORKeyStream(bytes, bytes)
return base64.StdEncoding.EncodeToString(bytes)
}
func createCipher() cipher.Block {
key, _ := hex.DecodeString("bc7316929fe1545bf0b98d114ee3ecb8")
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
return block
}
And in NodeJs - outputs PQ5k:
var crypto = require('crypto');
var algorithm = 'aes-128-ctr';
function encrypt(text, password) {
const key = Buffer.from(password, "hex").slice(0, 16);
const ivBuffer = Buffer.from("1234567890123456");
const cipher = crypto.createCipheriv(algorithm, key, ivBuffer);
let encrypted = cipher.update(text,'utf8','base64') + cipher.final('base64')
console.log(encrypted) // PQ5k
}
encrypt('aaa', 'bc7316929fe1545bf0b98d114ee3ecb8');
Thought it's an encoding issue at first, but I think that's correct - openssl_encrypt will return base64 value. I need to translate the PHP variant into GoLang, but an example in (almost) any other language will be much appreciated.
In the PHP code, the key is not hex decoded, but is a binary string that is 32 bytes in size and thus too large for AES-128.
PHP/OpenSSL implicitly truncates the key by considering only the first 16 bytes.
In Go simply use key := []byte("bc7316929fe1545b")
In NodeJS: const key = Buffer.from("bc7316929fe1545b", "utf8") to get the PHP result.
Conversely, in the PHP code the key can also be hex decoded using hex2bin().
Here c/c++ version, but output is not yet the same with golang/nodejs.
#include <openssl/aes.h>
#include <stdio.h>
#include <string.h>
int main() {
AES_KEY aes_key;
unsigned char key[AES_BLOCK_SIZE] = {0xbc, 0x73, 0x16, 0x92, 0x9f, 0xe1, 0x54, 0x5b, 0xf0, 0xb9, 0x8d, 0x11, 0x4e, 0xe3, 0xec, 0xb8 };
unsigned char iv[AES_BLOCK_SIZE] = {1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6};
unsigned char ecount[AES_BLOCK_SIZE]; memset( ecount, 0, 16 );
unsigned int num = 0 ;
const char *x = "aaa";
unsigned char out[16];
AES_set_encrypt_key(key, 128, &aes_key);
AES_ctr128_encrypt( (const unsigned char *) x, out, AES_BLOCK_SIZE, &aes_key, iv, ecount, &num);
for (int k = 0; k < 3; k++) printf("%02x", out[k]); // c9aa18
printf( "\n");
return 0;
}
// g++ -o aes-simple aes-simple.cpp -I/usr/local/ssl/include -L/usr/local/ssl/lib -g -lcrypto
I'd like to encrypt a string in PHP and then decrypt it in C. I'm stuck on the decryption part.
(PHP) I first encrypt the string:
function encrypt($plaintext, $key) {
$iv = 'aaaaaaaaaaaaaaaa';
$ciphertext = openssl_encrypt($plaintext, "AES-256-CBC", $key, OPENSSL_RAW_DATA, $iv);
return $ciphertext;
}
echo encrypt('This is a test', 'test');
// output: 7q�7h_��8� ��L
(C) Then I want to decrypt it, I use tiny-AES-c library for the functions:
int test_decrypt_cbc(void) {
uint8_t key[] = "test";
uint8_t iv[] = "aaaaaaaaaaaaaaaa";
uint8_t str[] = "7q�7h_��8� ��L";
printf("%s", str);
printf("\n Decrypted buffer\n");
struct AES_ctx ctx;
AES_init_ctx_iv(&ctx, key, iv);
AES_CBC_decrypt_buffer(&ctx, str, sizeof(str));
printf("%s", str);
printf("\n");
return 1;
}
This outputs:
7q�7h_��8� ��L
Decrypted buffer
?L??Ɵ??m??Dˍ?'?&??c?W
It should instead output "This is a test".
How can I fix this?
In the PHP code, AES-256 is used. tiny-AES-c only supports AES-128 by default. In order for AES-256 to be supported, the corresponding constant must be defined in aes.h, i.e. the line //#define AES256 1 must be commented in, here.
PHP uses PKCS7 padding by default. The padding should be removed in the C code.
PHP implicitly pads too short keys with zero values to the specified length. Since AES-256-CBC was specified in the PHP code, the key test is extended as follows:
test\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
In the C code this extended key must be used (see also the comment of #r3mainer).
For the transfer of the ciphertext between the two codes a suitable encoding must be used, e.g. Base64 or hexadecimal (see also the comment of #Ôrel). For the latter, bin2hex can be applied to the ciphertext in the PHP code. An example of a hex decoding in C can be found here.
A possible C-implementation is:
// Pad the key with zero values
uint8_t key[] = "test\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
uint8_t iv[] = "aaaaaaaaaaaaaaaa";
uint8_t ciphertextHex[] = "3771e837685ff5d4173801900de6e14c";
// Hex decode (e.g. https://stackoverflow.com/a/3409211/9014097)
uint8_t ciphertext[sizeof(ciphertextHex) / 2], * pos = ciphertextHex;
for (size_t count = 0; count < sizeof ciphertext / sizeof * ciphertext; count++) {
sscanf((const char*)pos, "%2hhx", &ciphertext[count]);
pos += 2;
}
// Decrypt
struct AES_ctx ctx;
AES_init_ctx_iv(&ctx, key, iv);
AES_CBC_decrypt_buffer(&ctx, ciphertext, sizeof(ciphertext));
// Remove the PKCS7 padding
uint8_t ciphertextLength = sizeof(ciphertext);
uint8_t numberOfPaddingBytes = ciphertext[ciphertextLength - 1];
ciphertext[ciphertextLength - numberOfPaddingBytes] = 0;
printf("%s", ciphertext);
I am encrypting like:
plaintextstr := "0000000000000thankustackoverflow"
plaintext := []byte(plaintextstr)
key := []byte("abcdefghijklmnop")
block, _ := aes.NewCipher(key)
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
iv := ciphertext[:aes.BlockSize]
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
fmt.Printf("%x\n", ciphertext)
and the output is:
00000000000000000000000000000000d77aa6646bb541808ed23c88d4b06d30f42b01d6e806a02b29086bc82892334f
but the output from another version of this code writen in PHP is:
d77aa6646bb541808ed23c88d4b06d30f42b01d6e806a02b29086bc82892334fbf21ea861abbc3d72e44731978bb76c2
Notice the 00000000000000000000000000000000 is the exact length of the missing data at the end. And it's 32, the size of the orig plaintextstr. Any idea how to left shift all that in golang and get the missing data?
PHP:
<?php
include('Crypt/AES.php');
$aes = new Crypt_AES();
$aes->setKey('abcdefghijklmnop');
echo bin2hex($aes->encrypt("0000000000000thankustackoverflow"));
https://github.com/andrewarrow/phpseclib1/blob/master/Crypt/AES.php
https://github.com/andrewarrow/phpseclib1/blob/master/Crypt/Rijndael.php
Debug php output:
00000000000000000000000000000000 <--- IV
30303030303030303030303030746861 block 0
d77aa6646bb541808ed23c88d4b06d30 crypted 0
6e6b75737461636b6f766572666c6f77 block 16
f42b01d6e806a02b29086bc82892334f crypted 16
10101010101010101010101010101010 block 32
bf21ea861abbc3d72e44731978bb76c2 crypted 32
It is not a shifting problem, it is an output buffer size problem due to the prefixing of the iv to the encrypted data.
The IV is a block of nulls (0x00) since it is never set to a value. It is prepended to the encrypted data, that is common. A block of padding needs to be added since the input data is an exact multiple of the block size.
This the output buffer needs to be the size of the iv + the size of the data + the size of the padding block. (16 + 32 + 16 = 64)
Add another block size to the output buffer:
ciphertext := make([]byte, aes.BlockSize + len(plaintext) + aes.BlockSize)
The PHP version was encoding an extra 16 bytes of an "empty" string that was filled with 16s!
p1 := "0000000000000tha"
p2 := "nkustackoverflow"
p1b := []byte(p1)
p2b := []byte(p2)
p3b, _ := hex.DecodeString("10101010101010101010101010101010")
fmt.Println(p3b)
key := []byte("abcdefghijklmnop")
block, _ := aes.NewCipher(key)
ciphertext := make([]byte, aes.BlockSize+16)
iv := ciphertext[:aes.BlockSize]
mode := cipher.NewCBCEncrypter(block, iv)
i := 0
for {
if i == 0 {
mode.CryptBlocks(ciphertext[aes.BlockSize:], p1b)
} else if i == 1 {
mode.CryptBlocks(ciphertext[aes.BlockSize:], p2b)
} else if i == 2 {
mode.CryptBlocks(ciphertext[aes.BlockSize:], p3b)
}
fmt.Printf("%x\n", ciphertext[16:])
i += 1
if i > 2 {
break
}
}
This code prints the identical stuff in golang that php is.
I am trying to encrypt / decrypt a string using Rijndael from php to Delphi and back.
If I decrypt a PHP string from Delphi ... works fine.
If I encrypt a string with Delphi the result string is ok but shorter
for test I used a string with 62 character. the encrypted string with delphi is long 4 char less of PHP
these the strings ... last characters:
PHP: GyLWj1anBJRmE8mBsaO5cvTrcbvvA==
Delphi: GyLWj1anBJRmE8mBsaO5cvTrcbv
thanks for any advices
I use this source code example:
PHP:
function encrypt ($key, $value)
{
$padSize = 16 - (strlen ($value) % 16) ;
$value = $value . str_repeat (chr ($padSize), $padSize) ;
$output = mcrypt_encrypt (MCRYPT_RIJNDAEL_128, $key, $value, MCRYPT_MODE_CBC, 'xxxxxxx') ;
return base64_encode ($output) ;
}
Delphi encrypt:
function EncryptData3(Data: string; AKey: AnsiString; AIv: AnsiString): string;
var
cipher: TDCP_rijndael;
key, iv, src, dest, b64: TBytes;
index, slen, bsize, pad: integer;
begin
//key := Base64DecodeBytes(TEncoding.UTF8.GetBytes(AKey));
//iv := Base64DecodeBytes(TEncoding.UTF8.GetBytes(AIv));
key := TEncoding.ASCII.GetBytes(AKey);
iv := TEncoding.ASCII.GetBytes(AIv);
src := TEncoding.ascii.GetBytes(Data);
cipher := TDCP_rijndael.Create(nil);
try
cipher.CipherMode := cmCBC;
// Add padding.
// Resize the Value array to make it a multiple of the block length.
// If it's already an exact multiple then add a full block of padding.
slen := Length(src);
bsize := (cipher.BlockSize div 8);
pad := bsize - (slen mod bsize);
Inc(slen, pad);
SetLength(src, slen);
for index := pad downto 1 do
begin
src[slen - index] := pad;
end;
SetLength(dest, slen);
cipher.Init(key[0], 256, #iv[0]); // DCP uses key size in BITS not BYTES
cipher.Encrypt(src[0], dest[0], slen);
b64 := Base64EncodeBytes(dest);
result := TEncoding.Default.GetString(b64);
finally
cipher.Free;
end;
end;
Delphi decrypt ... not works:
function DecryptData3(Data: string; AKey: AnsiString; AIv: AnsiString): string;
var
key, iv, src, dest: TBytes;
cipher: TDCP_rijndael;
slen, pad: integer;
begin
//key := Base64DecodeBytes(TEncoding.UTF8.GetBytes(AKey));
//iv := Base64DecodeBytes(TEncoding.UTF8.GetBytes(AIv));
key := TEncoding.ASCII.GetBytes(AKey);
iv := TEncoding.ASCII.GetBytes(AIv);
src := Base64DecodeBytes(TEncoding.UTF8.GetBytes(Data));
cipher := TDCP_rijndael.Create(nil);
try
cipher.CipherMode := cmCBC;
slen := Length(src);
SetLength(dest, slen);
cipher.Init(key[0], 256, #iv[0]); // DCP uses key size in BITS not BYTES
cipher.Decrypt(src[0], dest[0], slen);
// Remove the padding. Get the numerical value of the last byte and remove
// that number of bytes
pad := dest[slen - 1];
SetLength(dest, slen - pad);
// Base64 encode it
result := TEncoding.Default.GetString(dest);
finally
cipher.Free;
end;
end;
I don't know if I used a correct way ... but if I convert the Bytes Value in String and I use this Base64Encode in this link:
Simple code to encrypt an .INI file string using a password
now I encrypt correctly. this is the example:
SetString(stringValue, PAnsiChar(#dest[0]), slen);
result := Base64Encode2(stringValue);
I have problems with decrypting strings sent from PHP to Delphi using the rijndael cipher.
I'm using mcrypt on the PHP side and DCP_rijndael on the Delphi side.
At the moment I have the below code.
PHP:
function encRJ($key, $iv, $data)
{
$r = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $data, MCRYPT_MODE_CBC, $iv);
$r = base64_encode($r);
return $r;
}
And in Delphi:
function decRJ(Data: string; Key: string; IV: string): string;
var ciph: TDCP_rijndael;
begin
Data := Base64DecodeStr(Data);
ciph:= TDCP_rijndael.Create(Self);
ciph.Init(Key[1], 256, #IV[1]);
ciph.DecryptCBC(Data[1], Data[1], Length(Data));
ciph.Free;
Result := Data;
end;
I have tried using several Units on the Internet implementing the cipher, and found out most people are saying about the DCP components. Even so, I haven't managed to make it correctly decrypt. I've tried using Byte arrays for the parameters, AnsiStrings, WideStrings, etc, but unfortunately no luck.
Excuse me if I'm missing something really obvious here, as my mind isn't in good shape atm, after hours of searching for the matter.
I seem to have spent too long on this but...
Your problem is the block size. TDCP_rijndael is equivalent to MCRYPT_RIJNDAEL_128 (not _256). The '256' value in ciph.Init(...) call is still correct though. Other than that it looks pretty much ok. That is, assuming you're using ansistrings for key/iv or you're using non-unicode Delphi.
For unicode Delphi versions I'd be inclined to use TBytes and key[0] / iv[0].
Padding may still be an issue. If so, then here's what I've mangled up based on the PHP manual pages and some trial and error.
PHP:
function Encrypt($src, $key, $iv)
{
$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, 'cbc');
//echo "Block size: " . $block . "\r\n";
$pad = $block - (strlen($src) % $block);
$src .= str_repeat(chr($pad), $pad);
$enc = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $src, MCRYPT_MODE_CBC, $iv);
$r = base64_encode($enc);
return $r;
}
function Decrypt($src, $key, $iv)
{
$enc = base64_decode($src);
$dec = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $enc, MCRYPT_MODE_CBC, $iv);
$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, 'cbc');
$pad = ord($dec[($len = strlen($dec)) - 1]);
return substr($dec, 0, strlen($dec) - $pad);
}
Delphi:
function DecryptData(Data: string; AKey: AnsiString; AIv: AnsiString): string;
var
key, iv, src, dest: TBytes;
cipher: TDCP_rijndael;
slen, pad: integer;
begin
//key := Base64DecodeBytes(TEncoding.UTF8.GetBytes(AKey));
//iv := Base64DecodeBytes(TEncoding.UTF8.GetBytes(AIv));
key := TEncoding.ASCII.GetBytes(AKey);
iv := TEncoding.ASCII.GetBytes(AIv);
src := Base64DecodeBytes(TEncoding.UTF8.GetBytes(Data));
cipher := TDCP_rijndael.Create(nil);
try
cipher.CipherMode := cmCBC;
slen := Length(src);
SetLength(dest, slen);
cipher.Init(key[0], 256, #iv[0]); // DCP uses key size in BITS not BYTES
cipher.Decrypt(src[0], dest[0], slen);
// Remove the padding. Get the numerical value of the last byte and remove
// that number of bytes
pad := dest[slen - 1];
SetLength(dest, slen - pad);
// Base64 encode it
result := TEncoding.Default.GetString(dest);
finally
cipher.Free;
end;
end;
function EncryptData(Data: string; AKey: AnsiString; AIv: AnsiString): string;
var
cipher: TDCP_rijndael;
key, iv, src, dest, b64: TBytes;
index, slen, bsize, pad: integer;
begin
//key := Base64DecodeBytes(TEncoding.UTF8.GetBytes(AKey));
//iv := Base64DecodeBytes(TEncoding.UTF8.GetBytes(AIv));
key := TEncoding.ASCII.GetBytes(AKey);
iv := TEncoding.ASCII.GetBytes(AIv);
src := TEncoding.UTF8.GetBytes(Data);
cipher := TDCP_rijndael.Create(nil);
try
cipher.CipherMode := cmCBC;
// Add padding.
// Resize the Value array to make it a multiple of the block length.
// If it's already an exact multiple then add a full block of padding.
slen := Length(src);
bsize := (cipher.BlockSize div 8);
pad := bsize - (slen mod bsize);
Inc(slen, pad);
SetLength(src, slen);
for index := pad downto 1 do
begin
src[slen - index] := pad;
end;
SetLength(dest, slen);
cipher.Init(key[0], 256, #iv[0]); // DCP uses key size in BITS not BYTES
cipher.Encrypt(src[0], dest[0], slen);
b64 := Base64EncodeBytes(dest);
result := TEncoding.Default.GetString(b64);
finally
cipher.Free;
end;
end;
The PHP and Delphi functions now give me the same answer.
EDIT
Base64DecodeBytes was a bit of code I added to the DCP Base64 unit:
function Base64DecodeBytes(Input: TBytes): TBytes;
var
ilen, rlen: integer;
begin
ilen := Length(Input);
SetLength(result, (ilen div 4) * 3);
rlen := Base64Decode(#Input[0], #result[0], ilen);
// Adjust the length of the output buffer according to the number of valid
// b64 characters
SetLength(result, rlen);
end;
EDIT 2018 (Raising the dead...):
As requested, here is the encoding method, unchecked and pulled straight from an old source file I found.
DISCLAIMER: It is many years old and untested in recent memory and not used since Delphi 2010. There are probably many better alternatives now. Use at your own risk.
function Base64EncodeBytes(Input: TBytes): TBytes;
var
ilen: integer;
begin
ilen := Length(Input);
SetLength(result, ((ilen + 2) div 3) * 4);
Base64Encode(#Input[0], #result[0], ilen);
end;
Neither your PHP nor your Delphi methods appear to specify any padding. If the default paddings are different then you will get problems. Explicitly specify PKCS7 (or PKCS5) for both.
GregS' comment about the result of decoding Base64 is correct. You are supplying encrypted cyphertext to your decRJ() method. That will be random appearing bytes. Attempting to convert it to UTF-8 will mangle it enough that it cannot be decrypted. The incoming cyphertext must be converted from Base64 direct to a byte array. Cyphertext is not a character string, which is why it needs to be converted to Base64 to be transmitted as text. It will only be text again after it has been decrypted back to plaintext.