PHP加密数据需要在React Native中解密

PHP encrypted data needs to be decrypted in ReactNative

我正在使用以下代码片段在 PHP 中对 CBC 256 模式进行 AES 加密。

$iv_real = "ahc/2u6F0Yvww12fyQiZWA==";
$decoded_iv = base64_decode($iv_real);
$plaintext_shared_secret = "9b8a3e600073de05e5d095b5d909043e50f5047ffcd0048c01c65ca690b7b4e981e51b59641d4ffd5a140c27f25a761ab0f99e601b59c5ae3427c751bfae9331";
echo "Shared secret: {$plaintext_shared_secret}\r\n";
$aes_key = hash("sha256", "s@keypact.appa62f1bed41166b2c455d82337222723b0287d920"); 
$encrypted_shared_secret = openssl_encrypt(
    $plaintext_shared_secret, 
    "aes-256-cbc", 
    $aes_key,
    OPENSSL_ZERO_PADDING,
    $decoded_iv //Binary data
);
//Base64 encoded, encryped shared secret
echo "\r\nEncrypted, base64_encoded, shared secret\r\n";
var_dump($encrypted_shared_secret);

我得到的加密响应与我在 React Native 中得到的完全不同。我需要在 ReactNative 中解密相同的数据。我试过了

  1. React-native-simple-crypto
  2. react-native-crypto-js

但是其中 none 似乎可以正常工作并提供解密数据甚至获得加密数据,就像我正在使用 PHP 一样。

填充支持不在任何反应模块中。

我选择了 ReactNative Framework,以为它是最先进的框架,但我已经浪费了很多天来解决这个问题,所以不确定如何解决这个问题。

以下是 React 代码

async DecryptSharedSecret() {
//    var plaintext_secret = "9b8a3e600073de05e5d095b5d909043e50f5047ffcd0048c01c65ca690b7b4e981e51b59641d4ffd5a140c27f25a761ab0f99e601b59c5ae3427c751bfae9331";
var strIV = 'ahc/2u6F0Yvww12fyQiZWA==';
var iv_bin = Base64.Decode(strIV, strIV.length);
// Decoded value IV is in Binary when converted to Hex its 6a173fdaee85d18bf0c35d9fc9089958

var shared_secret =
  'n4gIdzwY5UsOpzGtslpRlyNjLwLla7sJWfGkfH0GHadPFjtsOxNGJgqRe9WnjYkbNjCgwvvAvQtYV1kFHTpCIS4zDJa3h/2ADHrDgC7ym3HUyMeVIWgFKRCZYeVKz8SEHmullNxWG6dCjsWEbK6yqVmpNfwJSeh0XHHDFe0/Sak=';
var shared_secret_bin = Base64.Decode(shared_secret, shared_secret.length);
// Decoded Value shared_secret is in Binary when converted to HEx for printing, Value is
// '9f8808773c18e54b0ea731adb25a519723632f02e56bbb0959f1a47c7d061da74f163b6c3b1346260a917bd5a78d891b3630a0c2fbc0bd0b585759051d3a42212e330c96b787fd800c7ac3802ef29b71d4c8c79521680529109961e54acfc4841e6ba594dc561ba7428ec5846caeb2a959a935fc0949e8745c71c315ed3f49a9'

var strKey = 's@keypact.appa62f1bed41166b2c455d82337222723b0287d920';
var keysha256 = await RNSimpleCrypto.SHA.sha256(strKey);
console.log('Key SHA 256 ===== >> ', keysha256);
// var key =
// '074bf4849734a4c3a653fc213f2734d23f5dc63ce9d6244f386276097f032ad7';

let bytes = CryptoJS.AES.decrypt(
  shared_secret_bin,
  CryptoJS.enc.Hex.parse(keysha256),
  {
    iv: iv_bin,
    mode: CryptoJS.mode.CBC,
  },
);
var decrypted = bytes.toString(CryptoJS.enc.Utf8);
console.log('Decrypted Data ', decrypted);

} }

您的 PHP 代码的结果是:

n4gIdzwY5UsOpzGtslpRlyNjLwLla7sJWfGkfH0GHadPFjtsOxNGJgqRe9WnjYkbNjCgwvvAvQtYV1kFHTpCIS4zDJa3h/2ADHrDgC7ym3HUyMeVIWgFKRCZYeVKz8SEHmullNxWG6dCjsWEbK6yqVmpNfwJSeh0XHHDFe0/Sak=

以下 CryptoJS 代码给出相同的结果:

// IV
var iv_real = "ahc/2u6F0Yvww12fyQiZWA==";
var decoded_iv = CryptoJS.enc.Base64.parse(iv_real);

// Key
var hash = CryptoJS.SHA256("s@keypact.appa62f1bed41166b2c455d82337222723b0287d920");
var hashHex32 = hash.toString(CryptoJS.enc.Hex).substring(0,32);
var aes_key = CryptoJS.enc.Utf8.parse(hashHex32);

// Plaintext
var plaintext_shared_secret = "9b8a3e600073de05e5d095b5d909043e50f5047ffcd0048c01c65ca690b7b4e981e51b59641d4ffd5a140c27f25a761ab0f99e601b59c5ae3427c751bfae9331";

var encrypted = CryptoJS.AES.encrypt(
    plaintext_shared_secret, 
    aes_key, 
    {
        iv: decoded_iv,
        padding: CryptoJS.pad.NoPadding 
    });

console.log(encrypted.toString().replace(/(.{56})/g,'\n'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>

在 PHP 代码中,SHA256 哈希是 return 十六进制编码的,因此由 64 个字符或 64 个字节组成。 openssl_encrypt 仅将前 32 个字节用于 AES-256,因此在 CryptoJS 代码中必须相应地缩短密钥。 return 将散列作为二进制数据更有意义,因此它的长度为 32 个字节。此外,如果十六进制编码数据在一个环境中为大写而在另一个环境中为小写,则使用十六进制编码数据可能会导致跨平台问题。
另一点是填充在 PHP 代码中被禁用(因为 OPENSSL_ZERO_PADDING 标志),因此它也必须在 CryptoJS 代码中被禁用。这意味着只有长度为块大小(AES 为 16 字节)的整数倍的明文才能被加密,对于发布的明文(长度为 128 字节)也是如此。
由于明文在 PHP 代码中似乎是十六进制编码的,因此优化是在加密之前对明文进行十六进制解码,这将使数据量减半。
请注意,静态 IV 是不安全的。相反,每次加密都应使用随机生成的 IV。