C++/PHP下AES加密解密结果不一致

MapleElyse 2015-03-01 11:23:01
最近客户端和服务端加密通信遇到一个困难,我发现同样的KEY和IV下,C++加密结果和PHP加密结果不同,同样也导致服务端返回的加密数据无法被C++客户端解密


以下是C++实现部分

void Cryptology::OpenSSLAESEncodeByMapleArrayForPHP(MapleByteArray &source, MapleByteArray &key, MapleByteArray &iv, MapleByteArray &res)
{

//16B对齐
int buqi_len = ((source.GetLength() + 15) / 16) * 16;
for (int i = 0; i < buqi_len; i++)
{
if (i < source.GetLength())
{
}
else
{
source.AddByte((char)0x00);
}
}
////////
EVP_CIPHER_CTX en;
EVP_CIPHER_CTX_init(&en);
EVP_EncryptInit_ex(&en, EVP_aes_256_cbc(), NULL, key.data_ptr(), iv.data_ptr());

int c_len = source.GetLength() + AES_BLOCK_SIZE, f_len = 0;
unsigned char *ciphertext = (unsigned char *)malloc(c_len);
ZeroMemory(ciphertext, c_len);

EVP_EncryptInit_ex(&en, NULL, NULL, NULL, NULL);
EVP_EncryptUpdate(&en, ciphertext, &c_len, (unsigned char *)source.data_ptr(), source.GetLength());
EVP_EncryptFinal_ex(&en, ciphertext + c_len, &f_len);

EVP_CIPHER_CTX_cleanup(&en);

res.fromByte((BYTE*)ciphertext, c_len);
ZeroMemory(ciphertext, c_len);

free(ciphertext);
}



以下是PHP实现部分:


//Encrtption.php
<?php

namespace phpaes;

interface Encryption {

/**
* Encrypt $data
*
* @param string $data
*
* @return string
*/
public function encrypt($data);

/**
* Decrypt $ciphertext
*
* @param string $ciphertext
*
* @return string
*/
public function decrypt($ciphertext);

}



//AES.PHP
<?php

namespace phpaes;

abstract class AES implements Encryption {

/** @var string */
private $key;

/** @var string */
private $iv;

/**
* @param string $iv
* @throws \InvalidArgumentException
*/
public function setIv($iv) {
if (!is_string($iv)) {
throw new \InvalidArgumentException("IV must be a string");
}
if (strlen($iv) != 16) {
throw new \InvalidArgumentException("IV length must be 16 bytes");
}
$this->iv = $iv;
}

/**
* @param string $key
* @throws \InvalidArgumentException
*/
public function setKey($key) {
if (!is_string($key)) {
throw new \InvalidArgumentException("Key must be a string");
}
if (!in_array(strlen($key), array(16,24,32))) {
throw new \InvalidArgumentException("Key length must be 16, 24, or 32 bytes");
}
$this->key = $key;
}

/**
* @throws \LogicException
* @return string
*/
public function getIv() {
if (!isset($this->iv)) {
throw new \LogicException('The iv is not set, call setIv() prior to usage');
}
return $this->iv;
}

/**
* @throws \LogicException
* @return string
*/
public function getKey() {
if (!isset($this->key)) {
throw new \LogicException('The key is not set, call setKey() prior to usage');
}
return $this->key;
}

}
//AES_CBC_OpenSSL.PHP

<?php

namespace phpaes;

/**
* You'll note that this class doesn't do it's own padding.
*
* openssl uses pkcs#7 padding by default and as I generally recommend
* that, I've not provided an option to do any other kind of padding.
*
* There isn't anything that stops you, of course, from pre-padding
* your data with whatever method you like before pushing it into
* these methods.
*/
class AES_CBC_OpenSSL extends AES {

/** @var string */
private $aesmode = '';

private $rawoption;

public function setKey($key) {
parent::setKey($key);
// Transform the key into the bit size and set the openssl mode string
$this->aesmode = 'aes-256-cbc';
// in 5.3 the 3rd option to these calls was a boolean for raw/not raw, but became a bitmask in 5.4
// pick the right variant like this:
$this->rawoption = OPENSSL_ZERO_PADDING;
}

/** @inheritdoc */
public function encrypt($text) {
return openssl_encrypt($text, $this->aesmode, $this->getKey(), $this->rawoption, $this->getIv());
}

/** @inheritdoc */
public function decrypt($cipherText) {
return openssl_decrypt($cipherText, $this->aesmode, $this->getKey(), $this->rawoption, $this->getIv());
}

}

//test.php


<?php

//require __DIR__ . '/../vendor/autoload.php';
//require __DIR__ . '/shared-data.php';

require_once("D:\code\phpaes-master\phpaes-master\src\Encryption.php");
require_once("D:\code\phpaes-master\phpaes-master\src\AES.php");
require_once("D:\code\phpaes-master\phpaes-master\src\AES_CBC_OpenSSL.php");
require_once("D:\code\phpaes-master\phpaes-master\src\Util.php");


$aescbc = new phpaes\AES_CBC_OpenSSL();
$util = new phpaes\Util();
$key = "12345678901234567890123456789012";
$iv = "1234567890123456";
$plainText = "1234567890123456";

$aescbc->setKey($key);
$aescbc->setIv($iv);
$cipherText = $aescbc->encrypt($plainText);
$decodedText = $aescbc->decrypt($cipherText);
echo "-------------------------------------------------------<br>";
echo " Key: $key<br>";
echo " Plaintext: $plainText<br>";
echo " Plaintext (bytes): ". bin2hex($plainText) ."<br>";
echo "Ciphertext (bytes): ". bin2hex($cipherText) ."<br>";
echo " Ciphertext (len): ". strlen($cipherText) ."<br>";
echo " Decryptedtext: $decodedText<br>";



测试时,为了避免可能是补齐导致的问题,所以要加密的字符串刚好是16字节不多不少,这时候发现如果Padding为OPENSSL_ZERO_PADDING时,PHP结果(HEX)为6535526e6e6c4a6b763451476e47686b4d77667667413d3d (24字节)
而当Padding为OPENSSL_RAW_DATA时,结果为7b94679e5264bf84069c68643307ef800a9e195e288c42965e89921500425701(32字节)

在C++下,我没有试pkcs#7填充,而是用补0的方式补齐,结果为7b94679e5264bf84069c68643307ef80(16字节)

比较发现,C++加密结果的16字节和PHP的OPENSSL_RAW_DATA补齐时前16个字节完全吻合,但是PHP不知道为什么后面又多了16字节,整个加密结果是32字节,而OPENSSL_ZERO_PADDING下加密结果就完全不一致了,而我测试时的$plainText = "1234567890123456"; 一共刚好16个字符,按理来说没有补齐的需要,十分费解为什么会出现这种问题,希望大大帮忙解答一下
...全文
324 1 打赏 收藏 转发到动态 举报
写回复
用AI写文章
1 条回复
切换为时间正序
请发表友善的回复…
发表回复
tottyandbaty 2015-03-02
  • 打赏
  • 举报
回复
这里是一个PHP的AES加密和解密,之前和他们做JAVA的对接程序用到了

function encrypt($str, $key) {

		$block = mcrypt_get_block_size('des', 'ecb');
		$pad = $block - (strlen($str) % $block);
		$str .= str_repeat(chr($pad), $pad);
		return base64_encode(mcrypt_encrypt(MCRYPT_DES, $key, $str, MCRYPT_MODE_CBC, IV));
	}

	function decrypt($str, $key) {
		$str = base64_decode($str);
		$str = mcrypt_decrypt(MCRYPT_DES, $key, $str, MCRYPT_MODE_CBC, IV);
		$block = mcrypt_get_block_size('des', 'ecb');
		$pad = ord($str[($len = strlen($str)) - 1]);
		return substr($str, 0, strlen($str) - $pad);
	}

21,886

社区成员

发帖
与我相关
我的任务
社区描述
从PHP安装配置,PHP入门,PHP基础到PHP应用
社区管理员
  • 基础编程社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧