0

0

Forge AES解密中的填充问题与解决方案

碧海醫心

碧海醫心

发布时间:2025-11-16 23:08:12

|

374人浏览过

|

来源于php中文网

原创

Forge AES解密中的填充问题与解决方案

本文深入探讨了在使用javascript `forge`库进行aes解密时,因默认填充机制导致文本截断的问题。核心解决方案是,当加密端未应用pkcs#7填充或使用了其他填充方式时,需在`forge`解密时通过`decipher.finish(() => true)`显式禁用默认的pkcs#7去填充操作,以确保完整恢复原始明文。文章还强调了填充一致性、ecb模式的安全隐患及密钥派生最佳实践。

1. 理解块密码与填充机制

AES(高级加密标准)是一种对称块密码,它以固定大小的“块”(Block)处理数据。对于AES,块大小固定为16字节。这意味着无论明文的实际长度是多少,它都必须被分割成16字节的块进行加密。

当明文数据的长度不是块大小(16字节)的整数倍时,就需要引入“填充”(Padding)机制。填充的作用是在明文末尾添加额外的数据,使其长度达到块大小的整数倍。PKCS#7是常用的一种填充标准,它会在数据末尾填充N个字节,每个字节的值都为N,其中N是需要填充的字节数。

在解密过程中,如果数据在加密时使用了填充,那么解密后也必须执行“去填充”(Unpadding)操作,将这些额外的填充字节移除,以恢复原始明文。

2. forge库的默认行为与问题根源

forge是一个功能强大的JavaScript加密库。在使用其forge.cipher.createDecipher创建解密器时,默认情况下,它会假定加密数据使用了PKCS#7填充,并在decipher.finish()方法中自动尝试执行去填充操作。

当加密端(例如,使用R语言的digest::AES库)在加密时没有使用PKCS#7填充,或者根本没有使用任何填充(这通常发生在明文长度恰好是块大小的整数倍时),forge的默认去填充行为就会导致问题。forge会错误地移除“它认为”是填充的数据,从而可能截断原始明文,导致解密结果不完整。

3. 解决方案:禁用forge的默认去填充

解决此问题的关键是显式地告诉forge在解密完成时不要执行默认的PKCS#7去填充操作。这可以通过修改decipher.finish()方法的调用方式来实现。

我秀秀淘宝客api源码
我秀秀淘宝客api源码

程序介绍:程序采用.net 2.0进行开发,全自动应用淘客api,自动采集信息,无需,手工更新,源码完全开放。(程序改进 无需填入阿里妈妈淘客API 您只要修改app_code文件下的config.cs文件中的id为你的淘客id即可)针对淘客3/300毫秒的查询限制,系统采用相应的解决方案,可以解决大部分因此限制带来的问题;程序采用全局异常,避免偶尔没考虑到的异常带来的问题;程序源码全部开放,请使

下载

将:

const result = decipher.finish();

替换为:

const result = decipher.finish(() => true); // 禁用去填充

decipher.finish()方法可以接受一个回调函数作为参数。当这个回调函数返回true时,forge将跳过其内部的去填充逻辑,直接返回解密后的原始字节序列。

4. 示例代码

以下是修正后的JavaScript解密函数,演示了如何禁用forge的默认去填充:

// 引入 forge 库,例如通过 CDN:
// 

seed = 'hi';
text = 'KQdciM892XEZXYC+jm4sWsijh/fQ4z/PRlpIHQG/+fM='; // Base64编码的密文

function decrypt(seed, text){
  // 1. 密钥派生:使用SHA256哈希种子生成32字节(256位)的密钥
  const md = forge.md.sha256.create();
  md.update(seed);
  const key = md.digest().getBytes(32); // 获取32字节的原始密钥

  // 2. 准备密文:将Base64编码的密文解码并转换为forge的缓冲区
  const cypher = forge.util.createBuffer(forge.util.decode64(text), 'raw');

  console.log('密文的十六进制表示:', cypher.toHex());

  // 3. 创建解密器:使用AES-ECB模式
  var decipher = forge.cipher.createDecipher('AES-ECB', key);

  // 4. 初始化解密器
  decipher.start();

  // 5. 更新解密数据
  decipher.update(cypher);

  // 6. 完成解密并禁用去填充
  // 通过传递一个返回 true 的回调函数,指示 forge 跳过默认的 PKCS#7 去填充
  const result = decipher.finish(() => true); 

  if(result){
    const out = decipher.output; // 获取解密后的输出缓冲区
    console.log('解密结果的十六进制表示:', out.toHex());

    // 7. 将解密后的字节序列解码为UTF-8字符串
    const dec = forge.util.encodeUtf8(out);
    console.log('解密后的明文:', dec);
  }else{
    // 注意:禁用去填充后,此处的 'Bad key.' 提示可能不再准确
    // 因为即使密钥错误,也可能不会触发填充错误。
    console.log('解密失败或密钥不正确。');
  }
}

decrypt(seed, text);

运行上述代码,将能够完整地解密出原始明文:

[1] 2



### 5. 注意事项与最佳实践

#### 5.1 填充的一致性
这是最关键的一点。加密和解密过程中的填充策略必须完全一致。
*   如果加密时使用了PKCS#7填充,那么解密时应允许`forge`执行默认的去填充(即使用`decipher.finish()`)。
*   如果加密时没有使用任何填充(例如,明文长度恰好是块大小的整数倍),或者使用了其他非PKCS#7的填充方式,那么解密时必须禁用`forge`的默认去填充(即使用`decipher.finish(() => true)`)。
*   如果加密时使用了自定义填充,解密时禁用`forge`的默认去填充后,你需要手动实现自定义的去填充逻辑。

#### 5.2 明文长度与块大小
当禁用填充时,请务必确保加密前的明文长度是块大小(AES为16字节)的整数倍。否则,解密结果将包含原始明文之外的额外字节,或者无法正确解码。

#### 5.3 AES-ECB模式的安全性
示例中使用了AES-ECB(Electronic Codebook)模式。**ECB是一种不安全的块密码模式,不推荐用于加密敏感数据。** 它的主要缺点是:
*   **模式泄露:** 相同的明文块会产生相同的密文块,这使得攻击者可以识别数据中的模式和结构,即使不知道密钥也能进行分析。
*   **不提供完整性保护:** 密文块可以被任意重排或篡改,而解密端无法检测到这些更改。

对于大多数应用场景,应优先考虑使用更安全的模式,例如:
*   **AES-CBC (Cipher Block Chaining):** 比ECB更安全,引入了初始化向量(IV)来消除模式泄露,但仍不提供完整性保护。
*   **AES-GCM (Galois/Counter Mode):** 推荐的认证加密模式。它不仅提供数据的机密性(加密),还提供数据的完整性(防止篡改)和认证(验证数据来源)。

#### 5.4 密钥派生
示例中直接使用`forge.md.sha256`对种子字符串进行哈希来生成密钥。这种简单的密钥派生方式存在安全风险,因为它容易受到字典攻击和暴力破解。

**推荐使用专门的密钥派生函数 (Key Derivation Function, KDF)**,例如:
*   **PBKDF2 (Password-Based Key Derivation Function 2)**
*   **scrypt**
*   **argon2**

这些KDF通过引入迭代次数、盐值(salt)和内存消耗等参数,大大增加了暴力破解的难度。

#### 5.5 错误检测与数据完整性
当禁用填充时,`forge`在解密失败(例如,使用了错误的密钥)时,可能不会像启用填充时那样抛出“Bad key”等异常。这是因为填充校验是许多库判断解密是否成功的一个辅助手段。

*   **非认证加密 (如AES-ECB, AES-CBC):** 如果密钥错误,解密会返回一串看似随机的字节序列。此时,你需要依赖后续的数据处理(例如,尝试UTF-8解码,如果失败则可能密钥错误)来判断解密是否成功。但这种方法并不可靠,因为随机字节序列也可能“偶然”符合某种格式。
*   **认证加密 (如AES-GCM):** GCM模式会在解密时验证一个认证标签(Authentication Tag)。如果标签验证失败,则表明密文被篡改或密钥不正确,此时解密会明确失败,提供可靠的错误检测。因此,强烈建议使用认证加密模式。

### 6. 总结

在使用`forge`库进行AES解密时,遇到解密文本不完整的问题,通常是由于加密端未进行PKCS#7填充,而`forge`默认尝试去填充所致。通过在`decipher.finish()`方法中传入一个返回`true`的回调函数,可以有效禁用`forge`的默认去填充行为,从而完整恢复原始明文。然而,在解决此问题的同时,务必注意加密填充的一致性、ECB模式的安全隐患、使用安全的密钥派生函数以及优先选择认证加密模式(如AES-GCM)来确保数据的机密性、完整性和认证性。

相关专题

更多
js获取数组长度的方法
js获取数组长度的方法

在js中,可以利用array对象的length属性来获取数组长度,该属性可设置或返回数组中元素的数目,只需要使用“array.length”语句即可返回表示数组对象的元素个数的数值,也就是长度值。php中文网还提供JavaScript数组的相关下载、相关课程等内容,供大家免费下载使用。

554

2023.06.20

js刷新当前页面
js刷新当前页面

js刷新当前页面的方法:1、reload方法,该方法强迫浏览器刷新当前页面,语法为“location.reload([bForceGet]) ”;2、replace方法,该方法通过指定URL替换当前缓存在历史里(客户端)的项目,因此当使用replace方法之后,不能通过“前进”和“后退”来访问已经被替换的URL,语法为“location.replace(URL) ”。php中文网为大家带来了js刷新当前页面的相关知识、以及相关文章等内容

374

2023.07.04

js四舍五入
js四舍五入

js四舍五入的方法:1、tofixed方法,可把 Number 四舍五入为指定小数位数的数字;2、round() 方法,可把一个数字舍入为最接近的整数。php中文网为大家带来了js四舍五入的相关知识、以及相关文章等内容

732

2023.07.04

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

477

2023.09.01

JavaScript转义字符
JavaScript转义字符

JavaScript中的转义字符是反斜杠和引号,可以在字符串中表示特殊字符或改变字符的含义。本专题为大家提供转义字符相关的文章、下载、课程内容,供大家免费下载体验。

394

2023.09.04

js生成随机数的方法
js生成随机数的方法

js生成随机数的方法有:1、使用random函数生成0-1之间的随机数;2、使用random函数和特定范围来生成随机整数;3、使用random函数和round函数生成0-99之间的随机整数;4、使用random函数和其他函数生成更复杂的随机数;5、使用random函数和其他函数生成范围内的随机小数;6、使用random函数和其他函数生成范围内的随机整数或小数。

991

2023.09.04

如何启用JavaScript
如何启用JavaScript

JavaScript启用方法有内联脚本、内部脚本、外部脚本和异步加载。详细介绍:1、内联脚本是将JavaScript代码直接嵌入到HTML标签中;2、内部脚本是将JavaScript代码放置在HTML文件的`<script>`标签中;3、外部脚本是将JavaScript代码放置在一个独立的文件;4、外部脚本是将JavaScript代码放置在一个独立的文件。

657

2023.09.12

Js中Symbol类详解
Js中Symbol类详解

javascript中的Symbol数据类型是一种基本数据类型,用于表示独一无二的值。Symbol的特点:1、独一无二,每个Symbol值都是唯一的,不会与其他任何值相等;2、不可变性,Symbol值一旦创建,就不能修改或者重新赋值;3、隐藏性,Symbol值不会被隐式转换为其他类型;4、无法枚举,Symbol值作为对象的属性名时,默认是不可枚举的。

551

2023.09.20

高德地图升级方法汇总
高德地图升级方法汇总

本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

27

2026.01.16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 3.7万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 2.3万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 2.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号