如何从dart:encrypt包和crypto-js中获得相同的结果?

huangapple go评论100阅读模式
英文:

How to get same results from dart:encrypt package and crypto-js?

问题

以下是已翻译的部分:

我在Flutter中使用dart > encrypt包。
我想在Flutter Web项目中的Web Worker中执行一些加密解密逻辑。我尝试使用crypto-js包和其纯JavaScript版本https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js。

我想要验证结果是否使用相同的密钥生成相同的加密和解密值。

我也在StackOverflow上查找了一些解决方案,但未找到适合我的工作方式,可以为我提供相同的生成值。

以下是Dart代码及其结果:

import 'package:encrypt/encrypt.dart';
.
.
final key = Key.fromUtf8("VishnuTestKeyIsHereLongValid32Ch");
final encrypter = Encrypter(AES(key));
final iv = IV.fromLength(16);
var encrypted = encrypter.encrypt("Vishnu", iv: iv);
print(encrypted.base64); // 打印结果为 8beisXeeEE055QEPq+kumw==

我期望从Crypto-js中解密相同的值。https://jsfiddle.net/vcgupta/grkexuwz/4/

//html:
 <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>

//js:

var base64Decrypted = '8beisXeeEE055QEPq+kumw=='

var myKey = "VishnuTestKeyIsHereLongValid32Ch"
var result = CryptoJS.AES.decrypt(base64Decrypted, myKey).toString(CryptoJS.enc.Utf8)

console.log(result);

我希望交替执行相同的操作。在JavaScript中加密,然后在Dart中解密。

有谁能帮助我找出上述解决方案中的问题?

英文:

I am using dart > encrypt package in flutter.
I want to do some encryption-decryption logic in web-worker in flutter-web project. I have tried to use crypto-js package and its vanilla JS https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js.

I want to verify the results if same encryption and decryption values appear with same key.

I have gone through some solutions in stackoverflow as well but couldn't find a suitable working which can provide me same generated values.

Following is dart code and its result:

    import 'package:encrypt/encrypt.dart';
    .
    .
    final key = Key.fromUtf8("VishnuTestKeyIsHereLongValid32Ch");
    final encrypter = Encrypter(AES(key));
    final iv = IV.fromLength(16);
    var encrypted = encrypter.encrypt("Vishnu", iv: iv);
    print(encrypted.base64); // prints 8beisXeeEE055QEPq+kumw==

I am expecting same value to be decrypted from Crypto-js. https://jsfiddle.net/vcgupta/grkexuwz/4/

//html:
 <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>

//js:

var base64Decrypted = '8beisXeeEE055QEPq+kumw=='

var myKey = "VishnuTestKeyIsHereLongValid32Ch"
var result = CryptoJS.AES.decrypt(base64Decrypted, myKey).toString(CryptoJS.enc.Utf8)

console.log(result);

I want to same operations alternatively. Encrypt in javascript and decrypt in dart.

Can anybody help me what I am missing in above solution?

答案1

得分: 2

Dart/encrypt 默认使用CTR模式(也称为SIC)和PKCS7填充(即对于流密码模式,填充不会被隐式禁用)。由于您在CryptoJS代码中未指定模式,因此将应用默认模式,即CBC(请参阅此处)。因此,这两个代码是不兼容的。

另外,在CryptoJS代码中,密钥必须以WordArray形式传递(如果以字符串形式传递,则会使用内置密钥派生)。此外,IV必须显式传递(以WordArray形式)。在Dart代码中,应用了零IV(16个0x00值)。

此外,您应该使用较新版本的CryptoJS(您正在使用3.1.2版本,当前版本为4.1.1)。

总的来说,使用CryptoJS进行解密的代码如下:

var base64Decrypted = '8beisXeeEE055QEPq+kumw==';
var myKey = "VishnuTestKeyIsHereLongValid32Ch";
var result = CryptoJS.AES.decrypt(
                 base64Decrypted, 
                 CryptoJS.enc.Utf8.parse(myKey), 
                 {
                     iv: CryptoJS.enc.Utf8.parse("
var base64Decrypted = '8beisXeeEE055QEPq+kumw==';
var myKey = "VishnuTestKeyIsHereLongValid32Ch";
var result = CryptoJS.AES.decrypt(
                 base64Decrypted, 
                 CryptoJS.enc.Utf8.parse(myKey), 
                 {
                     iv: CryptoJS.enc.Utf8.parse("\0".repeat(16)), // 传递IV
                     mode: CryptoJS.mode.CTR,                      // 应用CTR(又名SIC)
                     padding: CryptoJS.pad.Pkcs7                   // 应用PKCS#7(CryptoJS默认)
                 }
             );

console.log(result.toString());                   // 566973686e75    // 去除填充
console.log(result.toString(CryptoJS.enc.Utf8));  // Vishnu           // Utf-8解码
"
.repeat(16)), // 传递IV
mode: CryptoJS.mode.CTR, // 应用CTR(又名SIC) padding: CryptoJS.pad.Pkcs7 // 应用PKCS#7(CryptoJS默认) } ); console.log(result.toString()); // 566973686e75 // 去除填充 console.log(result.toString(CryptoJS.enc.Utf8)); // Vishnu // Utf-8解码
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>

请记住,静态IV(如零IV)是一个漏洞。相反,应为每次加密使用随机IV,并将其与密文一起传递给解密方,通常是串联的(IV不是秘密的)。

编辑: 关于评论中提到的对于大于1个块(16字节)的纯文本出现的错误:已修复。另外,为大于1个块的纯文本添加了以下测试:

var base64Decrypted = '87a0+WiecyRYz2l3zpRKsWHZyfUbnZ5Ar3nzYokfuD3zN7iV0M2TefRJe4pr4hh+' // 来自Dart
var myKey = 'VishnuTestKeyIsHereLongValid32Ch';
var result = CryptoJS.AES.decrypt(
                 base64Decrypted, 
                 CryptoJS.enc.Utf8.parse(myKey), 
                 {
                     iv: CryptoJS.enc.Utf8.parse('
var base64Decrypted = '87a0+WiecyRYz2l3zpRKsWHZyfUbnZ5Ar3nzYokfuD3zN7iV0M2TefRJe4pr4hh+' // 来自Dart
var myKey = 'VishnuTestKeyIsHereLongValid32Ch';
var result = CryptoJS.AES.decrypt(
                 base64Decrypted, 
                 CryptoJS.enc.Utf8.parse(myKey), 
                 {
                     iv: CryptoJS.enc.Utf8.parse('\0'.repeat(16)), // 传递IV
                     mode: CryptoJS.mode.CTR,                      // 应用CTR(又名SIC)
                     padding: CryptoJS.pad.Pkcs7                   // 应用PKCS#7(CryptoJS默认)
                 }
             );

console.log(result.toString());                   // 54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67   // 去除填充
console.log(result.toString(CryptoJS.enc.Utf8));  // The quick brown fox jumps over the lazy dog                                              // Utf-8解码
'
.repeat(16)), // 传递IV
mode: CryptoJS.mode.CTR, // 应用CTR(又名SIC) padding: CryptoJS.pad.Pkcs7 // 应用PKCS#7(CryptoJS默认) } ); console.log(result.toString()); // 54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67 // 去除填充 console.log(result.toString(CryptoJS.enc.Utf8)); // The quick brown fox jumps over the lazy dog // Utf-8解码
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
英文:

Dart/encrypt uses CTR mode (aka SIC) and PKCS7 padding by default (i.e. the padding is not implicitly disabled for stream cipher modes). Since you do not specify a mode in the CryptoJS code, the default mode is applied, i.e. CBC (s. here). Thus both codes are incompatible.

Also, in the CryptoJS code, the key must be passed as WordArray (if it is passed as string, a built-in key derivation is used). Additionally, the IV must be passed explicitly (as WordArray). In the Dart code a zero IV (16 0x00 values) is applied.

Furthermore, you should use a newer version of CryptoJS (you are running 3.1.2, the current version is 4.1.1).

Overall, decryption with CryptoJS is:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

var base64Decrypted = &#39;8beisXeeEE055QEPq+kumw==&#39;
var myKey = &quot;VishnuTestKeyIsHereLongValid32Ch&quot;
var result = CryptoJS.AES.decrypt(
                 base64Decrypted, 
                 CryptoJS.enc.Utf8.parse(myKey), 
                 {
                     iv: CryptoJS.enc.Utf8.parse(&quot;
var base64Decrypted = &#39;8beisXeeEE055QEPq+kumw==&#39;
var myKey = &quot;VishnuTestKeyIsHereLongValid32Ch&quot;
var result = CryptoJS.AES.decrypt(
base64Decrypted, 
CryptoJS.enc.Utf8.parse(myKey), 
{
iv: CryptoJS.enc.Utf8.parse(&quot;\0&quot;.repeat(16)), // pass IV
mode: CryptoJS.mode.CTR,                      // apply CTR (aka SIC)
padding: CryptoJS.pad.Pkcs7                   // apply PKCS#7 (CryptoJS default)
}
);
console.log(result.toString());                   // 566973686e75	// padding removed
console.log(result.toString(CryptoJS.enc.Utf8));  // Vishnu 		// Utf-8 decoded
&quot;.repeat(16)), // pass IV mode: CryptoJS.mode.CTR, // apply CTR (aka SIC) padding: CryptoJS.pad.Pkcs7 // apply PKCS#7 (CryptoJS default) } ); console.log(result.toString()); // 566973686e75 // padding removed console.log(result.toString(CryptoJS.enc.Utf8)); // Vishnu // Utf-8 decoded

<!-- language: lang-html -->

&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js&quot;&gt;&lt;/script&gt;

<!-- end snippet -->

Keep in mind that a static IV (like a zero IV) is a vulnerability. Instead, a random IV should be used for each encryption, which is passed along with the ciphertext to the decrypting side, usually concatenated (the IV is not secret).


Edit: Regarding the bug mentioned in the comment which occurs for plaintexts larger than 1 block (16 bytes): Fixed. Additionally added the following test for plaintexts longer than 1 block:

<!-- begin snippet: js hide: true console: true babel: false -->

<!-- language: lang-js -->

var base64Decrypted = &#39;87a0+WiecyRYz2l3zpRKsWHZyfUbnZ5Ar3nzYokfuD3zN7iV0M2TefRJe4pr4hh+&#39; // from Dart
var myKey = &#39;VishnuTestKeyIsHereLongValid32Ch&#39;
var result = CryptoJS.AES.decrypt(
                 base64Decrypted, 
                 CryptoJS.enc.Utf8.parse(myKey), 
                 {
                     iv: CryptoJS.enc.Utf8.parse(&#39;
var base64Decrypted = &#39;87a0+WiecyRYz2l3zpRKsWHZyfUbnZ5Ar3nzYokfuD3zN7iV0M2TefRJe4pr4hh+&#39; // from Dart
var myKey = &#39;VishnuTestKeyIsHereLongValid32Ch&#39;
var result = CryptoJS.AES.decrypt(
base64Decrypted, 
CryptoJS.enc.Utf8.parse(myKey), 
{
iv: CryptoJS.enc.Utf8.parse(&#39;\0&#39;.repeat(16)), // pass IV
mode: CryptoJS.mode.CTR,                      // apply CTR (aka SIC)
padding: CryptoJS.pad.Pkcs7                   // apply PKCS#7 (CryptoJS default)
}
);
console.log(result.toString());                   // 54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67   // padding removed
console.log(result.toString(CryptoJS.enc.Utf8));  // The quick brown fox jumps over the lazy dog                                              // Utf-8 decoded
&#39;.repeat(16)), // pass IV mode: CryptoJS.mode.CTR, // apply CTR (aka SIC) padding: CryptoJS.pad.Pkcs7 // apply PKCS#7 (CryptoJS default) } ); console.log(result.toString()); // 54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67 // padding removed console.log(result.toString(CryptoJS.enc.Utf8)); // The quick brown fox jumps over the lazy dog // Utf-8 decoded

<!-- language: lang-html -->

&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js&quot;&gt;&lt;/script&gt;

<!-- end snippet -->

答案2

得分: 0

感谢 @Topaco,他的解决方案非常有效。

以下是一个兼容 javascript <-> dart 的加密解密解决方案,希望对其他人有帮助。

JavaScript代码:

var textToDecrypt = 'Some long test here';

var myKey = "VishnuTestKeyIsHereLongValid32Ch"
var result = CryptoJS.AES.encrypt(
                 textToDecrypt, 
                 CryptoJS.enc.Utf8.parse(myKey), 
                 {
                     iv: CryptoJS.enc.Utf8.parse("
var textToDecrypt = 'Some long test here';

var myKey = "VishnuTestKeyIsHereLongValid32Ch"
var result = CryptoJS.AES.encrypt(
                 textToDecrypt, 
                 CryptoJS.enc.Utf8.parse(myKey), 
                 {
                     iv: CryptoJS.enc.Utf8.parse("\0".repeat(16)),
                     mode: CryptoJS.mode.CTR,
                     padding: CryptoJS.pad.Pkcs7
                 }
             );
var encryptedString = result.toString();
console.log(encryptedString);

result = CryptoJS.AES.decrypt(
                 encryptedString, 
                 CryptoJS.enc.Utf8.parse(myKey), 
                 {
                     iv: CryptoJS.enc.Utf8.parse("\0".repeat(16)),
                     mode: CryptoJS.mode.CTR,
                     padding: CryptoJS.pad.Pkcs7
                 }
             );
var decryptedString = result.toString(CryptoJS.enc.Utf8);
console.log(decryptedString); 
console.log(decryptedString == textToDecrypt);
"
.repeat(16)),
mode: CryptoJS.mode.CTR, padding: CryptoJS.pad.Pkcs7 } ); var encryptedString = result.toString(); console.log(encryptedString); result = CryptoJS.AES.decrypt( encryptedString, CryptoJS.enc.Utf8.parse(myKey), { iv: CryptoJS.enc.Utf8.parse("
var textToDecrypt = 'Some long test here';

var myKey = "VishnuTestKeyIsHereLongValid32Ch"
var result = CryptoJS.AES.encrypt(
                 textToDecrypt, 
                 CryptoJS.enc.Utf8.parse(myKey), 
                 {
                     iv: CryptoJS.enc.Utf8.parse("\0".repeat(16)),
                     mode: CryptoJS.mode.CTR,
                     padding: CryptoJS.pad.Pkcs7
                 }
             );
var encryptedString = result.toString();
console.log(encryptedString);

result = CryptoJS.AES.decrypt(
                 encryptedString, 
                 CryptoJS.enc.Utf8.parse(myKey), 
                 {
                     iv: CryptoJS.enc.Utf8.parse("\0".repeat(16)),
                     mode: CryptoJS.mode.CTR,
                     padding: CryptoJS.pad.Pkcs7
                 }
             );
var decryptedString = result.toString(CryptoJS.enc.Utf8);
console.log(decryptedString); 
console.log(decryptedString == textToDecrypt);
"
.repeat(16)),
mode: CryptoJS.mode.CTR, padding: CryptoJS.pad.Pkcs7 } ); var decryptedString = result.toString(CryptoJS.enc.Utf8); console.log(decryptedString); console.log(decryptedString == textToDecrypt);

Dart代码:

var textToDecrypt = 'Some long test here';

var myKey = "VishnuTestKeyIsHereLongValid32Ch";

final encrypter = Encrypter(AES(Key.fromUtf8(myKey)));
final iv = IV.fromLength(16);
var encrypted = encrypter.encrypt(textToDecrypt, iv: iv);
var encryptedString = encrypted.base64;
print(encryptedString);

var decrypted = encrypter.decrypt64(encryptedString, iv: iv);
var decryptedString = decrypted.toString();
print(decryptedString == textToDecrypt);

以上代码在JavaScript中可以完美地进行加密和解密,在Dart中也可以单独进行加密和解密。

英文:

Thanks to @Topaco, his solution worked great.

Here is an encryption-decryption solution compatible to javascript &lt;-&gt; dart. Hope it will be helpful for others.

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

var textToDecrypt = &#39;Some long test here&#39;;

var myKey = &quot;VishnuTestKeyIsHereLongValid32Ch&quot;
var result = CryptoJS.AES.encrypt(
                 textToDecrypt, 
                 CryptoJS.enc.Utf8.parse(myKey), 
                 {
                     iv: CryptoJS.enc. Utf8.parse(&quot;
var textToDecrypt = &#39;Some long test here&#39;;
var myKey = &quot;VishnuTestKeyIsHereLongValid32Ch&quot;
var result = CryptoJS.AES.encrypt(
textToDecrypt, 
CryptoJS.enc.Utf8.parse(myKey), 
{
iv: CryptoJS.enc. Utf8.parse(&quot;\0&quot;.repeat(16)),
mode: CryptoJS.mode.CTR,
padding: CryptoJS.pad.Pkcs7
}
);
var encryptedString = result.toString();
console.log(encryptedString);
result = CryptoJS.AES.decrypt(
encryptedString, 
CryptoJS.enc.Utf8.parse(myKey), 
{
iv: CryptoJS.enc. Utf8.parse(&quot;\0&quot;.repeat(16)),
mode: CryptoJS.mode.CTR,
padding: CryptoJS.pad.Pkcs7
}
);
var decryptedString = result.toString(CryptoJS.enc.Utf8);
console.log(decryptedString); 
console.log(decryptedString == textToDecrypt);
&quot;.repeat(16)), mode: CryptoJS.mode.CTR, padding: CryptoJS.pad.Pkcs7 } ); var encryptedString = result.toString(); console.log(encryptedString); result = CryptoJS.AES.decrypt( encryptedString, CryptoJS.enc.Utf8.parse(myKey), { iv: CryptoJS.enc. Utf8.parse(&quot;
var textToDecrypt = &#39;Some long test here&#39;;
var myKey = &quot;VishnuTestKeyIsHereLongValid32Ch&quot;
var result = CryptoJS.AES.encrypt(
textToDecrypt, 
CryptoJS.enc.Utf8.parse(myKey), 
{
iv: CryptoJS.enc. Utf8.parse(&quot;\0&quot;.repeat(16)),
mode: CryptoJS.mode.CTR,
padding: CryptoJS.pad.Pkcs7
}
);
var encryptedString = result.toString();
console.log(encryptedString);
result = CryptoJS.AES.decrypt(
encryptedString, 
CryptoJS.enc.Utf8.parse(myKey), 
{
iv: CryptoJS.enc. Utf8.parse(&quot;\0&quot;.repeat(16)),
mode: CryptoJS.mode.CTR,
padding: CryptoJS.pad.Pkcs7
}
);
var decryptedString = result.toString(CryptoJS.enc.Utf8);
console.log(decryptedString); 
console.log(decryptedString == textToDecrypt);
&quot;.repeat(16)), mode: CryptoJS.mode.CTR, padding: CryptoJS.pad.Pkcs7 } ); var decryptedString = result.toString(CryptoJS.enc.Utf8); console.log(decryptedString); console.log(decryptedString == textToDecrypt);

<!-- language: lang-html -->

&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js&quot;&gt;&lt;/script&gt;

<!-- end snippet -->

Above code encrypt and decrypt perfectly in JS. Below code for same purpose in dart:

 var textToDecrypt = &#39;Some long test here&#39;;

    var myKey = &quot;VishnuTestKeyIsHereLongValid32Ch&quot;;

    final encrypter = Encrypter(AES(Key.fromUtf8(myKey)));
    final iv = IV.fromLength(16);
    var encrypted = encrypter.encrypt(textToDecrypt, iv: iv);
    var encryptedString = encrypted.base64;
    print(encryptedString);

    var decrypted = encrypter.decrypt64(encryptedString, iv: iv);
    var decryptedString = decrypted.toString();
    print(decryptedString == ContentType.text);

The dart code does separately encrypt and decrypts fine. Also it can be done partially in either language.

huangapple
  • 本文由 发表于 2023年8月10日 19:19:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/76875242.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定