CryptoJs AES Decryption Error: Malformed UTF-8 data in Nativescript-Angular

CryptoJs AES Decryption Error: Malformed UTF-8 data in Nativescript-Angular

解密加密数据时出错。我的目标是在 PHP 中加密 pdf 文件并在 nativescript 中解密以在应用程序中本地显示 pdf。我将此作为最后的手段发布,因为我已经阅读过关于我的问题的类似帖子,但 none 对我有用。数据在 PHP 中使用 openssl and will be decrypted in nativescript using cryptoJs for AES decryption and jsrsasign 进行加密,用于密钥和 IV 的 RSA 解密。我非常感谢任何能帮助我的东西。我的做法可能有问题,但我不确定是什么。这是我目前的进度。

PHP 中的加密 (RSA+AES)。

Already tested in decrypting the data successfully in PHP. So it's safe to assume that it can be decrypted.

$data = // Is a pdf file
//get public key
$publicKey = // API CALL to get the public key
// get mime type of file
$dataFormat['docMimeType'] = "application/json";
// compute the hash of the file before encryption
$dataFormat['hash'] = hash('sha256', $data);
// generate random two 16 bytes long encryption key
$secretKey1 = openssl_random_pseudo_bytes(16);
$secretKey2 = openssl_random_pseudo_bytes(16);
// concatenate the two key to make the 32 bytes long AES encryption key
$secretKey = $secretKey1.$secretKey2;

// RSA Encryption of the keys and IV
// encrypt the aes encryption key with the public key
openssl_public_encrypt($secretKey1, $dataFormat['key1'], $publicKey, OPENSSL_PKCS1_OAEP_PADDING);
openssl_public_encrypt($secretKey2, $dataFormat['key2'], $publicKey, OPENSSL_PKCS1_OAEP_PADDING);
// Encoding the encrypted keys to base64
$dataFormat['key1'] = base64_encode($dataFormat['key1']);
$dataFormat['key2'] = base64_encode($dataFormat['key2']);

// AES Encryption
// generate random iv
$iv = openssl_random_pseudo_bytes(16);
// encrypt iv with the public key
openssl_public_encrypt($iv, $dataFormat['iv'], $publicKey, OPENSSL_PKCS1_OAEP_PADDING);
$dataFormat['iv'] = base64_encode($dataFormat['iv']);
// Manually doing the padding - Zero padding
$pad = 32 - (strlen($data) % 32);
$clear = $data . str_repeat(chr(0), $pad);

// encrypt the data with the aes encryption key
$dataFormat['doc'] = openssl_encrypt($clear, 'AES-256-CBC', $secretKey, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv);
$dataFormat['doc'] = base64_encode($dataFormat['doc']);

// save file to the cloud which will then be fetch by the app in nativescript for decryption
// save cipher
file_put_contents($cipherfile, json_encode($dataFormat));
// $cipherfile is the .enc file that will be uploaded to the cloud

pm 格式的 pkcs#8 私钥示例

-----BEGIN PRIVATE KEY-----
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
LFzA9Qx4EmEK73S8UTxk/9eDLFMhfA==
-----END PRIVATE KEY-----

示例输出(用于处理):文件内容如下所示。

{
  "docMimeType": "application/json",
  "hash": "253cd9d4f849084f276c649a5155512c5b0bb68c8f7cbde33cc529f68c672168",
  "key1": "dxUYOfiIEchLeha/Hu3dUd6MzucwABf8/Q9eUPgJAhEhmjrsf1MXnXOtcPCNGE/8bXEU6Iv3XIcLjImhaFBF8aYoDbS6GjnhPMeGpRQN8+raXrfm/5J9rlRjUImSyZUUnwgJ7vkHP22F9I8rrZywA5QgHiK97vyp7y2cv1H9y2tT5kukrDxhvWWBHoii+V0pA0SCEviPrlWjopyL/SpiaUfoG/L97Rdg7k181c1pHR5S+dWRAy190aU/IFo7VZXQ6FuqWd0TyoOEeBoAwrxTXG+03OXs7qqZwlFdHtCZ6ehrUl8Kkcbbv7O2PBOFsHnla5c+7aSDQ69xCHXmzoxB+dTbu5OY0E8TFWLD6V8QVe2Y5q79Nybf9r56YZDX6sqKK5uZqJDQB9wQUWHhM5hE7mzP7Ll1IVC4sAml+k2qrwJqOaDDN0eUidR+X31LXDEO9+y3g6NrlsNTROttiF0jC1oKyN5y+OOwxx0OEntXYUsM1Z8iQ9QQBHL418EF/FoEwmTVcg/7Lg20Qdgp21mv0ihMmYssB0EmopzH9yQ6d4eEp4Wh5krO2Ph73zJx6RvjVu1JfkoMmZ8fxwlHs+Xe09WE828NCWTW2WheP63V+fQZ07nm9c/BcITCmiQ9376wYh3+Hj+o6/N+et1nf0vO9zbooUI+V3Hd/m0wUnBxm+s=",
  "key2": "stkiCTyRmKfU7OhGfsqQyvi2UZ2syRjAdD4Sk0N/x+JMYX2Dr5rlwV+EH5TG388uvoPH7NSalPrprF+Nlm+YEVhYS0E5SBXJiIEFixT5AIQ9rhAHD/WFfxjvHainhiHhoLcX5rQ6C0zGCNUZaH0X4I4v8r575rbklTqSgCs3ueGDX9mAQKj2q5eJMIfRh+mAwQq7lhTk4TbGxL6LAyWxYL+Zw4R5ljUl6uK0o5Vwpn7I2xdAIQs6J7jW6jDfMT+5mbk06nrba09ysKdJILe8je97SyBRR5y+OHfJF5EArTSKwnLrAp/DXE40DP8BHOheL/PNxoKzIEkOX0Cu3aAXmHR6SLJxtwKGEHn5tzUvBzx54h2K5iEh2C8Wvkqqi7m+DcmxnrjYKEjmP7jzUvSMIob7EBcJ+wHvE9s5aPCK1MbKqK1UbHHkHRHsk8tgZsv/PC8A0+wdlMKI30GL1B9wcck7Fottgnc1eGzdFjbnEirLHeihaJPJOjkjiz1RtkOQHgd6l9bzUTMUnCq0melgSlut68akgvGM7kTtHhEY27sHj8UGF5zjTS+KPYzpi17Wp/9TgWpETxXsewvtBP9oCl/L+ZhhUrRihZOctSgTzFTZpIyrlx9ZDg0A0MU/Q2AyhBU0r9dzowyApDUu/TWPdj2Z9p/DX7c0i5y4IK7TOX0=",
  "iv": "Y2RdcAtnid52Ueu2MDdh2bY1aJBysk2UTstPdCAILU+RqcZCtgp+f5kLlRweG7Kgst88oZrJy6B/33F2WMqQ1qx1AqK+WthG+gVg15YC50OGaKGNuBAFSdFCJli2MLIE4s8ItXH2VmgxkGGE3ETuz3b7fheUJOUpUKcc3Kz0HmZiyo5eXsB4lNSXsaTP5Z8kaQcHDfacu9UORF9RUM2tfUH4IzAkU3ENPEM4cTP27HFRsFAeuXAcNAAYUQws9HVQFtg9pXAyuN0GUzD4GksL/3M0Y8CvITewLQDRHSBSljspPtF+lFqS3ZQTxAbtqxjEf6/Q1dCP1CIeiXEi2it8GEKZzWhH7385qW383JIBk1b9HjT7+26w2s7AqpLSevK5iOis+4sHzqEix9MHsOUp4OEcQ+BAYFkYiFlmPeo4tNgg9sRuIrgQwaEvCgHG2IoboURqCRs6iRnS4j8dHyvG5acavSEm0ozouqt4KBVoZt/chq8hpxH123kRIeuZH1qTsmzfSInAJLKpW2ky+nLifjOrSCA1D7ZDpfykACa1PpP+I6LgjMkWoQ/zkd70hz28fD+tpDWBJPngDcxnCz8UbZRma9psVEsw/4IOWQr3yy+V4Y9+xllLzZLy1YbEcdewsh/FXjLkhWqic3uPPyNlLWqzpHqBqTvgpPwU31zyJks=",
  "doc": // data is too large to be included in the post. If you want to see the full version of the sample output I created a gist in github https://gist.github.com/ketuvin/cba7e7215dbdd67ea8b10050ecbbcd3f
}

在 nativescript 中,此文件将被提取并从 JSON 处理到数组。然后会被解密。

在 Nativescript 中解密

import * as cryptoJs from "crypto-js"
import { KJUR, KEYUTIL } from "jsrsasign"

/**
  @params encryptedFile - the file to be decrypted
  @params pem - the pkcs#8 private key (cipher=aes-256-cbc)
*/
decryptFile(encryptedFile, pem) {
    // load key
    let privateKey = KEYUTIL.getKey(pem);

    // Base64 Decode using cryptoJs
    let key1 = cryptoJs.enc.Base64.parse(encryptedFile['key1']).toString();
    let key2 = cryptoJs.enc.Base64.parse(encryptedFile['key2']).toString();
    let iv = cryptoJs.enc.Base64.parse(encryptedFile['iv']).toString();
    let doc = cryptoJs.enc.Base64.parse(encryptedFile['doc']).toString();

    console.log("Base64 Decoded Key1: "+key1);
    console.log("Base64 Decoded Key2: "+key2);
    console.log("Base64 Decoded IV: "+iv);

    // RSA Decryption of the keys and IV using jsrsasign
    let decryptedKey1 = KJUR.crypto.Cipher.decrypt(key1, privateKey, "RSAOAEP");
    let decryptedKey2 = KJUR.crypto.Cipher.decrypt(key2, privateKey, "RSAOAEP");
    let decryptedIV = KJUR.crypto.Cipher.decrypt(iv, privateKey, "RSAOAEP");

    console.log("RSA Decrypted Key1: "+decryptedKey1);
    console.log("RSA Decrypted Key2: "+decryptedKey2);
    console.log("RSA Decrypted IV: "+decryptedIV);

    // Concatenate the keys to make up the AES key
    let decryptedKey = decryptedKey1 + decryptedKey2;

    console.log("AES KEY (Key1+Key2): "+decryptedKey);

    // Convert to wordarray
    let parsedkey = cryptoJs.enc.Utf8.parse(decryptedKey);
    let parsedIV = cryptoJs.enc.Utf8.parse(decryptedIV);
    
    // AES Decryption using crypto-js
    let decrypted = cryptoJs.AES.decrypt(doc, parsedkey, {
        iv: parsedIV,
        mode: cryptoJs.mode.CBC,
        padding: cryptoJs.pad.ZeroPadding
    }).toString(cryptoJs.enc.Utf8);  // Error: Malformed Utf-8 data

    return decrypted;
}

在控制台中, See actual log here

Base64 Decoded Key1: 0a5b90147517916c338fb8aa359d3f53a542eed1d482e42dd93eea0735a5d5a64dc219cdc703ba0a88cbf10b7feae82d2d4fe5a389f296cba8452b5e3371166c903aae2f64223f561821bb61f7be1ea41d96ac64d0e47da601041aef88ab9ded35c40240f7615e001f624b22966107539f98c499607e4b2760a162a963a854dffbfe955afec959b069101a49d1f765e729ff7035f0beb8ce06a1b3776a62f8c7db5b3efcfb1b0c1231f654e6aa075667621e99abbb6ba78a6fd81061c59de1024da81d2a8cc42dc84fa5af61c3d5b1a009cca2b4543e66974dce60f6631c490564be8494d0324fb1d7a8e7067feba5dd38653c936895247557e380e31e118af7a1886063e867acf58a3f63ab98bfa619b6a18518be68675a072d5a5217bc7ca4c5dba601ab7560e42dce1aadeca5a7fe0eba50f5d1c997eaf25d8dbbfded2cbea6402b121df8691f1673c243d1a8195e0af2a3b938fa271ecda93984ba9eef24912169ccabfd7a8f9fd2c4aa710e174f10d04914d7107c2a4fa54b463d002cc2347d4429604b2e1bb536a74f754f1f931362c90168e841ee05717e23323d627a938b582af62fb64c2d55442932517194eedf89d265523fe6f6ccdcd9c90fe5c76f9b3220c4a4fecfb4e84075167092b3971d52cfafba904f5b00ea14878df6b47bfbae29a97d1a4fa7caa995baddb6280f4939091fe2531d39b80759971d55ee
VM38 bundle.ad8287f8f3cbaa7f8ee5.hot-update.js:109 Base64 Decoded Key2: 2ba192411fb0a2ceeca359d389b4cacfa0f87f7a3a0c7168ad1e2e92d4952b4ab6465b7ad8975e2b735bd6c9489d4c34462df636ef3b4d7fdc6ed56cdb9cd3bab96539a420ca630721cb378df95371f1f7b10cf2dc9f7f149816b920be2a2dd0006052176ec10fee093672526e0b3eb8488a5ad1dee25a34e754c5c4b0a99478173501609bcb35dd5e99355b660485eee8f3fed8b3fcbc4873e760a303682ccd0f0aceea1c9da419bfec5d78619cbe5d22bfd34cc055e3ce7c3c596029b9c393b2039823e29b465ca4581e80015052c4ac4c63d9ffdafa995c0b1c2bc0cb10f194a99989467a7958956d29cea1efa271a6239110316fa9ea9f4bfe2f8a509db86e68245ae930f27b327e49f799757fc868985cfb93a24e7686b2d55966365ba506acd9e63e64b63ccf98024fcd472b2d69cee7a20904ba07556ac8ba6f05d8ec60c18e958dfb100b29d6409efb164caec3d35dfaf39f83ef59c3db1fa21fe684c6c13a403c52cec4b593b153e6ad79607f7f5e4dbfcb4c64ec058846278e06d0cdf72afee431a10bb12a2955ca78ea105da0607942d8af226e64fd2803310090f7e69c96fd634b82e946213bf073952bef8dcbdab11be4b197c144bacd10f6231c043795f855fd2042e2370bc64cdbefdbc9180e48b3d8aff957e8c998b92b8c5c2e8a2cc25f79c40f7633842b839313dbf6547b34d4d8f7974c09f54f9a20d8
VM38 bundle.ad8287f8f3cbaa7f8ee5.hot-update.js:110 Base64 Decoded IV: 4bba6438c3360e5d18080afc3edde5f3bcb81f3690cb152758b15ebf5377b6f4fe6b8008fce35fb7f966251b7405916626c3fe2caa61c9e183f87ced6b3f5cee8e336c32d23f0646c18c38aa2ce98dcfb9bfb3f32a02bdf9f3d16e36ef501d564316300203dc42b416c512530161952ddeef7d016b7af9b5c757ed6a43646efe34615db7674ec9446d1bf3aa8e8832a81b601bde8b2281c6fbe441d7bb2e8c3a7f26204e51078857b13d31b117df82850e20c6bf38110b7a52d62f78fd571204a50ae9ac3c11300eef05b48d43d7bd64532e50e9b03add4c826f9bb2804f8c5362f5f6ec5526219c2519f9e04742b92b52db45d7d717fd0fe43a2c31f59634a958811e07e095c3f9e4f8562ae999ef0761411d7de71751a80e0332d6aaa298d090c23790ee1439e8f5b2b9d8d80bf5834306a9be47a476f31dd9ec71c9e254eb48dce2139cc4170ca42f385ef408a2e8425a60b443ef41d8725a0399a81cfa53117c931c7c066f25614182117b18b9a1e5302a0bcc866560f2309d5c9d2dedcbed2782b1a3535d0b2532d0d7f7ff9d89296054fa12a00b54401fc4b098f2011506f25165618eae95bfcd83d097f1ca897e83528a22dfaa11c7978daa9fbca33b246f395417376162967275ecbdad7007c93b9b3e40377814c632926a01ff93b62dd583cb9ae4b5e5cb2fa338de4e3acaeb22be7622bfb6bbfbbf56dafb07e093
VM38 bundle.ad8287f8f3cbaa7f8ee5.hot-update.js:115 RSA Decrypted Key1: g.œ'Æ‹wvüXã2¸¶
VM38 bundle.ad8287f8f3cbaa7f8ee5.hot-update.js:116 RSA Decrypted Key2: lÖÑCò®|f.üØÁ}]
VM38 bundle.ad8287f8f3cbaa7f8ee5.hot-update.js:117 RSA Decrypted IV: KN¿N8ª1Ê°YëLr”e
VM38 bundle.ad8287f8f3cbaa7f8ee5.hot-update.js:119 AES KEY (Key1+Key2): g.œ'Æ‹wvüXã2¸¶lÖÑCò®|f.üØÁ}]
VM9 vendor.js:20948 ERROR Error: Malformed UTF-8 data

我解决了问题! (从技术上讲,虽然我问错了问题)。我意识到这是因为我使用 UTF-8 编码解析密钥和 IV,而实际上我应该使用 Latin1。我检查了数据的编码,它是我最近了解到的ISO-8859-1,也称为Latin1。所以我更改了这段代码

let parsedkey = cryptoJs.enc.Utf8.parse(decryptedKey);
let parsedIV = cryptoJs.enc.Utf8.parse(decryptedIV);

let parsedkey = cryptoJs.enc.Latin1.parse(decryptedKey);
let parsedIV = cryptoJs.enc.Latin1.parse(decryptedIV);

我的aes解密现在是这样的

let decrypted = cryptoJs.AES.decrypt(doc, parsedkey, {
    iv: parsedIV,
    mode: cryptoJs.mode.CBC,
    padding: cryptoJs.pad.ZeroPadding
}).toString(cryptoJs.enc.Latin1);

现在我可以处理 pdf 并将其本地显示在我的应用程序中。希望这对和我有类似情况的其他人有所帮助。