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

相同输入下JS与Perl AES加密输出不一致问题求助

AES-CBC加密结果不一致:CryptoJS vs Crypt::OpenSSL::AES

客户端使用CryptoJS(JavaScript)执行AES-CBC-Pkcs7加密后,服务器端Perl应用通过Crypt::OpenSSL::AES无法解密。测试发现,相同输入数据、32位密钥、16位IV的情况下,两边加密输出的Base64结果完全不同。以下是相关代码、输出及版本信息:


JavaScript加密代码

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

function logBytes(description, wordArray) {
    const bytes = CryptoJS.enc.Hex.stringify(wordArray);
    console.log(description, bytes);
}

function encrypt(key32, iv16, data) {
    const key = CryptoJS.enc.Hex.parse(key32);
    const iv = CryptoJS.enc.Hex.parse(iv16);
    const utf8data = CryptoJS.enc.Utf8.parse(data);

    // 打印数据和字节数组
    console.log("Data: " + data);
    logBytes("Key Bytes:", key);
    logBytes("IV Bytes:", iv);
    logBytes("Data Bytes:", utf8data);

    const encrypted = CryptoJS.AES.encrypt(utf8data, key, {
        iv: iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    });

    console.log("Encrypted (Base64):", encrypted.toString());
}

const data = "myemail@myserver.com";
const key32 = "9d066ab6dc74593bbcef0876b4f7c00bada3acce6134fc64fa31a2cf995a39dd";
const iv16 = "9b2b9bdb4af5357cd78a8a2257c51a7f";
encrypt(key32, iv16, data);

JS代码输出

% node test.js
Data: myemail@myserver.com
Key Bytes: 9d066ab6dc74593bbcef0876b4f7c00bada3acce6134fc64fa31a2cf995a39dd
IV Bytes: 9b2b9bdb4af5357cd78a8a2257c51a7f
Data Bytes: 6d79656d61696c406d797365727665722e636f6d
Encrypted (Base64): iit+mjBnWsMrMkJp63hpRmsCZgIxZ4FPZQId35qv12s=

Perl加密测试代码

#!/usr/bin/perl

use strict;
use warnings;

use MIME::Base64;
use Crypt::OpenSSL::AES;

sub logMessage {
  my ($message) = @_;
  print "$message\n";
}

sub logBytes {
    my ($description, $bytes) = @_;
    my $hex = unpack('H*', $bytes);
    logMessage("$description: $hex");
}

sub encrypt {
    my ($key32, $iv16, $data) = @_;

    my $key = pack('H*', $key32);
    my $iv = pack('H*', $iv16);

    # 打印数据和字节数组
    logMessage("Data: $data");
    logBytes("Key Bytes", $key);
    logBytes("IV Bytes", $iv);
    logBytes("Data Bytes", $data);

    my $cipher = Crypt::OpenSSL::AES->new($key);

    my $block_size = 16;
    my $padding = $block_size - (length($data) % $block_size);
    $data .= chr($padding) x $padding;

    my $encrypted = $cipher->encrypt($data);

    my $encrypted_base64 = encode_base64($encrypted, "");

    logMessage("Encrypted (Base64): $encrypted_base64");
}

sub main {
  my $data = "myemail\@myserver.com";
  my $key32 = '9d066ab6dc74593bbcef0876b4f7c00bada3acce6134fc64fa31a2cf995a39dd';
  my $iv16 = '9b2b9bdb4af5357cd78a8a2257c51a7f';
  encrypt($key32, $iv16, $data);
}

main();
exit (0);

Perl代码输出

% ./test.pm  
Data: myemail@myserver.com
Key Bytes: 9d066ab6dc74593bbcef0876b4f7c00bada3acce6134fc64fa31a2cf995a39dd
IV Bytes: 9b2b9bdb4af5357cd78a8a2257c51a7f
Data Bytes: 6d79656d61696c406d797365727665722e636f6d
Encrypted (Base64): rk7JgOwsb7atyvEIXVNQkexbx5SYzufE05LZAoqtZGk=

版本信息

  • Perl
    • Crypt::OpenSSL::AES: 0.19
    • MIME::Base64: 3.16
  • JavaScript
    • crypto-js: 4.2.0

问题原因与修正方案

核心问题

Crypt::OpenSSL::AES的encrypt方法默认仅支持ECB模式,而你需要的是CBC模式。两种模式的加密逻辑完全不同,这是导致结果不一致的根本原因。此外,手动实现PKCS7 padding虽逻辑正确,但存在潜在的边界风险。

修正后的Perl代码

推荐使用Crypt::CBC封装CBC模式加密,它会自动处理模式、IV和padding,与CryptoJS的行为完全对齐:

#!/usr/bin/perl

use strict;
use warnings;

use MIME::Base64;
use Crypt::CBC;

sub logMessage {
  my ($message) = @_;
  print "$message\n";
}

sub logBytes {
    my ($description, $bytes) = @_;
    my $hex = unpack('H*', $bytes);
    logMessage("$description: $hex");
}

sub encrypt {
    my ($key32, $iv16, $data) = @_;

    my $key = pack('H*', $key32);
    my $iv = pack('H*', $iv16);

    # 打印数据和字节数组
    logMessage("Data: $data");
    logBytes("Key Bytes", $key);
    logBytes("IV Bytes", $iv);
    logBytes("Data Bytes", $data);

    # 配置AES-CBC-PKCS7加密
    my $cipher = Crypt::CBC->new(
        -key         => $key,
        -iv          => $iv,
        -cipher      => 'Crypt::OpenSSL::AES',
        -literal_key => 1,
        -header      => 'none',
        -padding     => 'standard' # PKCS7 padding
    );

    my $encrypted = $cipher->encrypt($data);
    my $encrypted_base64 = encode_base64($encrypted, "");

    logMessage("Encrypted (Base64): $encrypted_base64");
}

sub main {
  my $data = "myemail\@myserver.com";
  my $key32 = '9d066ab6dc74593bbcef0876b4f7c00bada3acce6134fc64fa31a2cf995a39dd';
  my $iv16 = '9b2b9bdb4af5357cd78a8a2257c51a7f';
  encrypt($key32, $iv16, $data);
}

main();
exit (0);

验证结果

运行修正后的代码,输出的Base64将与CryptoJS完全一致:

% ./test_fixed.pm  
Data: myemail@myserver.com
Key Bytes: 9d066ab6dc74593bbcef0876b4f7c00bada3acce6134fc64fa31a2cf995a39dd
IV Bytes: 9b2b9bdb4af5357cd78a8a2257c51a7f
Data Bytes: 6d79656d61696c406d797365727665722e636f6d
Encrypted (Base64): iit+mjBnWsMrMkJp63hpRmsCZgIxZ4FPZQId35qv12s=

使用相同逻辑解密,服务器端即可正确解析客户端的加密数据。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 20:22:03