You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义Key和IV的Perl AES加密:与JS结果不一致问题

JavaScript AES加密与Perl实现不匹配的问题解决

问题场景

我有一段JavaScript的AES加密代码,但在Perl中始终无法得到匹配的加密结果。以下是具体代码及输出:


JavaScript代码

'use strict';
const CryptoJS = require('crypto-js');

const message = 's3cret';
const aesPassword = 'MyPassword';
const salt = CryptoJS.lib.WordArray.random(16);
const iv = CryptoJS.lib.WordArray.random(16);
const key = CryptoJS.PBKDF2(aesPassword, salt, {keySize: 128/32, iterations: 1000});
const ciphertext = CryptoJS.AES.encrypt(message, key, {iv: iv}).ciphertext.toString(CryptoJS.enc.Base64);
const encryptedMessage = Buffer.from(iv + '::' + salt + '::' + ciphertext).toString('base64');
console.log("encryptedMessage:", encryptedMessage);

JavaScript输出

encryptedMessage: NDNjOWNjYmE2MzBmZTFjZDYxZmMyYmRiOTAxMjFjNmY6OjVjNzg4YTQxNTg1MWU5MDlkOWM3OTUxNzE3NzE0MjA0OjpxVDFWdG9kN2loUzJ3dnR3bWxuUG93PT0=

(Base64编码前为92字节。)


Perl原代码

use strict;
use warnings;
use Crypt::CBC;
use Crypt::PBKDF2;
use MIME::Base64;

my $message = 's3cret';
my $aesPassword = 'MyPassword';
my $salt = Crypt::CBC->random_bytes(16);
my $iv = Crypt::CBC->random_bytes(16);
my $pbkdf2 = Crypt::PBKDF2->new(output_len=>128/32, iterations => 1000);
my $key = $pbkdf2->PBKDF2($aesPassword, $salt);
my $cipher = Crypt::CBC->new(-cipher=>'Cipher::AES', -pbkdf=>'pbkdf2', -key=>$key, -iv=>$iv, -header=>'none');
my $ciphertext = encode_base64( $cipher->encrypt($message), "" );
my $encryptedMessage = encode_base64( join('::', $iv, $salt, $ciphertext), "" );
print "encryptedMessage: $encryptedMessage";

Perl原输出

encryptedMessage: tO/jlJ3NkuJG0ZA58EDR0Do6wUf4xHb/MwWm3iyT+ejYWDo6SllMSHB0S0ZuMDVYWDRzS3ZCWlRiZz09Cg==

(Base64编码前为61字节。)


问题分析与修正

1. PBKDF2密钥派生参数错误

CryptoJS中keySize: 128/32表示生成4个32位字的密钥(即128位/16字节),但Perl的Crypt::PBKDF2的output_len参数是字节数,原代码设为128/32(即4)会生成4字节的错误密钥,需改为16。同时显式指定HMAC-SHA1哈希算法,与CryptoJS默认逻辑对齐。

2. Crypt::CBC初始化错误

原代码指定了-pbkdf=>'pbkdf2',会导致Crypt::CBC将传入的-key当作密码再次派生密钥,而非直接使用已生成的密钥。需去掉该参数,并添加-literal_key => 1明确告知直接使用传入的密钥;同时指定PKCS7填充、CBC模式,与CryptoJS默认加密配置匹配。

3. IV与Salt的字符串处理错误

JavaScript中iv + '::' + salt会自动将二进制的IV、Salt转换为十六进制字符串(每个字节对应2个十六进制字符),但原Perl代码直接使用二进制数据拼接,导致格式完全不一致。需将Perl中的二进制IV、Salt转为十六进制字符串后再拼接。


修正后的Perl代码

use strict;
use warnings;
use Crypt::CBC;
use Crypt::PBKDF2;
use MIME::Base64;

my $message = 's3cret';
my $aesPassword = 'MyPassword';
my $salt = Crypt::CBC->random_bytes(16);
my $iv = Crypt::CBC->random_bytes(16);

# 修正PBKDF2参数:生成16字节(128位)密钥,显式指定哈希算法
my $pbkdf2 = Crypt::PBKDF2->new(
    output_len => 16,
    iterations => 1000,
    hash_class => 'HMACSHA1'
);
my $key = $pbkdf2->PBKDF2($aesPassword, $salt);

# 修正Crypt::CBC配置:使用字面密钥、PKCS7填充、CBC模式
my $cipher = Crypt::CBC->new(
    -cipher      => 'OpenSSL::AES',
    -key         => $key,
    -iv          => $iv,
    -header      => 'none',
    -literal_key => 1,
    -padding     => 'pkcs7',
    -mode        => 'cbc'
);

my $ciphertext = encode_base64($cipher->encrypt($message), "");

# 将二进制IV、Salt转为十六进制字符串,与JS逻辑对齐
my $iv_hex = unpack('H*', $iv);
my $salt_hex = unpack('H*', $salt);
my $combined = join('::', $iv_hex, $salt_hex, $ciphertext);

my $encryptedMessage = encode_base64($combined, "");
print "encryptedMessage: $encryptedMessage\n";

验证说明

修正后的代码会生成与JavaScript格式、内容完全匹配的加密结果:

  • IV和Salt以十六进制字符串形式拼接,长度与JS一致
  • 密钥派生、加密模式、填充逻辑完全对齐
  • 最终Base64编码前的内容长度为92字节,与JS输出一致

内容的提问来源于stack exchange,提问作者h q

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 06:16:03