DES / 3DES

DES(Data Encryption Standard)和 3DES(Triple DES)是比 AES 更老的对称加密算法。目前在新系统中已基本被 AES 取代,但在一些老接口逆向中仍会遇到。 搜索关键字:CryptoJS.DES、CryptoJS.TripleDES、des-cbc、TripleDES、3DES 特征:块大小为 8 字节(填充后密文长度是 8 的倍数);密钥长度为 8 / 16 / 24 字节。 块大小是关键区分指标:DES/3DES 块大小为 8 字节,AES 为 16 字节。若解密后密文长度是 8 的倍数而非 16,优先怀疑 D

分享

官方文档:https://csrc.nist.gov/publications/detail/sp/800/67/rev2/final
适用场景:JS 逆向中识别和还原老接口中的 DES/3DES 加密参数

DES(Data Encryption Standard)和 3DES(Triple DES)是比 AES 更老的对称加密算法。目前在新系统中已基本被 AES 取代,但在一些老接口逆向中仍会遇到。


对比

算法 密钥长度 块大小 安全性
DES 64 位(有效 56 位,8 字节) 8 字节 已不安全
3DES 128 / 192 位(16 / 24 字节) 8 字节 较弱,不推荐
AES 128 / 192 / 256 位 16 字节 安全,推荐

Python 实现

from Crypto.Cipher import DES, DES3
from Crypto.Util.Padding import pad, unpad
import base64

# ── DES ──────────────────────────────────────────────────────

def des_cbc_encrypt(plaintext: str, key: str, iv: str) -> str:
    """DES-CBC 加密,key 和 iv 均为 8 字节"""
    key_b  = key.encode('utf-8')  # 必须是 8 字节
    iv_b   = iv.encode('utf-8')   # 必须是 8 字节
    cipher = DES.new(key_b, DES.MODE_CBC, iv_b)
    return base64.b64encode(cipher.encrypt(pad(plaintext.encode(), DES.block_size))).decode()

def des_cbc_decrypt(ciphertext_b64: str, key: str, iv: str) -> str:
    key_b  = key.encode('utf-8')
    iv_b   = iv.encode('utf-8')
    data   = base64.b64decode(ciphertext_b64)
    cipher = DES.new(key_b, DES.MODE_CBC, iv_b)
    return unpad(cipher.decrypt(data), DES.block_size).decode('utf-8')

def des_ecb_encrypt(plaintext: str, key: str) -> str:
    key_b  = key.encode('utf-8')
    cipher = DES.new(key_b, DES.MODE_ECB)
    return base64.b64encode(cipher.encrypt(pad(plaintext.encode(), DES.block_size))).decode()

def des_ecb_decrypt(ciphertext_b64: str, key: str) -> str:
    key_b  = key.encode('utf-8')
    cipher = DES.new(key_b, DES.MODE_ECB)
    return unpad(cipher.decrypt(base64.b64decode(ciphertext_b64)), DES.block_size).decode()

# ── 3DES ─────────────────────────────────────────────────────

def des3_cbc_encrypt(plaintext: str, key: str, iv: str) -> str:
    """3DES-CBC 加密,key 为 16 或 24 字节,iv 为 8 字节"""
    key_b  = key.encode('utf-8')  # 16 或 24 字节
    iv_b   = iv.encode('utf-8')   # 8 字节
    cipher = DES3.new(key_b, DES3.MODE_CBC, iv_b)
    return base64.b64encode(cipher.encrypt(pad(plaintext.encode(), DES3.block_size))).decode()

def des3_cbc_decrypt(ciphertext_b64: str, key: str, iv: str) -> str:
    key_b  = key.encode('utf-8')
    iv_b   = iv.encode('utf-8')
    data   = base64.b64decode(ciphertext_b64)
    cipher = DES3.new(key_b, DES3.MODE_CBC, iv_b)
    return unpad(cipher.decrypt(data), DES3.block_size).decode('utf-8')

def des3_ecb_encrypt(plaintext: str, key: str) -> str:
    key_b  = key.encode('utf-8')
    cipher = DES3.new(key_b, DES3.MODE_ECB)
    return base64.b64encode(cipher.encrypt(pad(plaintext.encode(), DES3.block_size))).decode()

# 使用示例
key8  = '12345678'          # 8 字节 DES key
iv8   = '87654321'          # 8 字节 IV
key24 = '123456789012345678901234'  # 24 字节 3DES key

ct  = des_cbc_encrypt('hello world', key8, iv8)
pt  = des_cbc_decrypt(ct, key8, iv8)
ct3 = des3_cbc_encrypt('hello world', key24, iv8)
pt3 = des3_cbc_decrypt(ct3, key24, iv8)

JavaScript 实现

使用 CryptoJS

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

// ── DES-CBC ──────────────────────────────────────────────────

function desEncrypt(plaintext, key, iv) {
    const keyWA = CryptoJS.enc.Utf8.parse(key);  // 8 字节
    const ivWA  = CryptoJS.enc.Utf8.parse(iv);   // 8 字节
    return CryptoJS.DES.encrypt(plaintext, keyWA, {
        iv:      ivWA,
        mode:    CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    }).toString();
}

function desDecrypt(ciphertext, key, iv) {
    const keyWA = CryptoJS.enc.Utf8.parse(key);
    const ivWA  = CryptoJS.enc.Utf8.parse(iv);
    return CryptoJS.DES.decrypt(ciphertext, keyWA, {
        iv:      ivWA,
        mode:    CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    }).toString(CryptoJS.enc.Utf8);
}

// ── DES-ECB ──────────────────────────────────────────────────

function desEcbEncrypt(plaintext, key) {
    const keyWA = CryptoJS.enc.Utf8.parse(key);
    return CryptoJS.DES.encrypt(plaintext, keyWA, {
        mode:    CryptoJS.mode.ECB,
        padding: CryptoJS.pad.Pkcs7
    }).toString();
}

// ── 3DES-CBC ─────────────────────────────────────────────────

function tripleDesEncrypt(plaintext, key, iv) {
    const keyWA = CryptoJS.enc.Utf8.parse(key);  // 16 或 24 字节
    const ivWA  = CryptoJS.enc.Utf8.parse(iv);   // 8 字节
    return CryptoJS.TripleDES.encrypt(plaintext, keyWA, {
        iv:      ivWA,
        mode:    CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    }).toString();
}

function tripleDesDecrypt(ciphertext, key, iv) {
    const keyWA = CryptoJS.enc.Utf8.parse(key);
    const ivWA  = CryptoJS.enc.Utf8.parse(iv);
    return CryptoJS.TripleDES.decrypt(ciphertext, keyWA, {
        iv:      ivWA,
        mode:    CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    }).toString(CryptoJS.enc.Utf8);
}

// 使用示例
const key8  = '12345678';
const iv8   = '87654321';
const key16 = '1234567890abcdef';

console.log(desEncrypt('hello', key8, iv8));
console.log(tripleDesEncrypt('hello', key16, iv8));

使用 Node.js 内置 crypto

const crypto = require('crypto');

function desEncrypt(plaintext, key, iv) {
    const cipher  = crypto.createCipheriv('des-cbc',
        Buffer.from(key, 'utf8'),
        Buffer.from(iv,  'utf8')
    );
    return cipher.update(plaintext, 'utf8', 'base64') + cipher.final('base64');
}

function des3Encrypt(plaintext, key, iv) {
    // Node.js 中 3DES 算法名:des-ede3-cbc(24字节key)或 des-ede-cbc(16字节key)
    const algo   = key.length === 24 ? 'des-ede3-cbc' : 'des-ede-cbc';
    const cipher = crypto.createCipheriv(algo,
        Buffer.from(key, 'utf8'),
        Buffer.from(iv,  'utf8')
    );
    return cipher.update(plaintext, 'utf8', 'base64') + cipher.final('base64');
}

// Node.js 支持的 DES 算法名
// des-cbc       : DES CBC
// des-ecb       : DES ECB
// des-ede3-cbc  : 3DES CBC(24字节key)
// des-ede-cbc   : 3DES CBC(16字节key,k1=k3)

识别特征

搜索关键字:CryptoJS.DESCryptoJS.TripleDESdes-cbcTripleDES3DES
特征:块大小为 8 字节(填充后密文长度是 8 的倍数);密钥长度为 8 / 16 / 24 字节。


最佳实践

块大小是关键区分指标:DES/3DES 块大小为 8 字节,AES 为 16 字节。若解密后密文长度是 8 的倍数而非 16,优先怀疑 DES/3DES;再配合密钥长度(8/16/24 字节)确认。

3DES 的三种密钥模式:3DES 密钥长度决定安全强度:24 字节(3 个独立密钥)= 最安全;16 字节(K1=K3)= 最常用;8 字节(K1=K2=K3)= 退化为 DES。逆向时要准确提取密钥长度。

Python 还原用 pycryptodome 而非 pyDespyDes 库老旧且速度慢,推荐 from Crypto.Cipher import DES, DES3,API 与 AES 一致,迁移成本低。

from Crypto.Cipher import DES3
from Crypto.Util.Padding import unpad

cipher = DES3.new(key, DES3.MODE_CBC, iv)
plaintext = unpad(cipher.decrypt(ciphertext), DES3.block_size)

JS 侧的 CryptoJS.DES 默认 CBC + PKCS7:还原时先假设 CBC 模式,若结果不对再尝试 ECB(无 IV)。

3DES EDE 与 EEE 模式:JS 逆向中几乎全是 EDE(加-解-加)模式;EEE(加-加-加)极少见,若 EDE 不对可尝试 EEE。


常见陷阱

陷阱:3DES 密钥长度判断错误

现象: 用提取的 24 字节密钥解密失败,改为 16 字节正常。
原因: 代码中密钥字符串长度为 24 字符,但每个字符是十六进制(2 字符 = 1 字节),实际 12 字节;或密钥是 Base64 编码,需先解码。
解决: 确认密钥的编码格式(原始字节 / Hex / Base64)后再计算实际长度;key.length 是字符数不是字节数。

陷阱:ECB 和 CBC 模式混淆

现象: 提取了 IV 用 CBC 解密失败;去掉 IV 用 ECB 也不对。
原因: 部分实现虽然代码里写了 MODE_CBC 但 IV 全为零字节(\x00 * 8),与不用 IV 的 ECB 行为不同。
解决: 检查 JS 中 IV 的实际值;若 IV 是空字符串或空 WordArray,CryptoJS 可能默认使用全零 IV,Python 侧对应 iv = b'\x00' * 8

陷阱:DES 密钥奇偶校验位被修改

现象: 复制了 8 字节密钥但解密失败,用其他工具解密正常。
原因: DES 规范要求密钥每字节最低位为奇偶校验位,部分实现会自动调整这些位,导致实际密钥与提取值有细微差异。
解决: pycryptodome 默认忽略校验位;若使用 pyDes,需注意其是否校验密钥合法性。


参见

魔法数字速查
Base64与编码
代码片段大全

阅读更多

Web 安全基础

1. HTML 转义(服务端渲染必须): 2. CSP(Content Security Policy): 3. HttpOnly Cookie:防止 JS 读取会话 Cookie: 4. 前端框架防护: 攻击者在第三方网站构造一个表单,诱导已登录用户提交,浏览器会自动携带目标站的 Cookie。 触发条件: 1. 用户已登录目标网站(Cookie 有效) 2. 目标 API 仅凭 Cookie 识别用户身份 3. 请求来源未验证 1. CSRF Token(推荐): 2. SameSite Cookie: 3. 验证 Origin/Referer 头:

By yellowdog

HTTP 协议深度指南

HTTP(HyperText Transfer Protocol)是 Web 的基础传输协议,基于 TCP/IP,采用请求/响应模型。 相关文档:Web安全基础(/web-an-quan-ji-chu/) FastAPI完全指南(/fastapi-wan-quan-zhi-nan/) Nginx完全指南(/nginx-wan-quan-zhi-nan/) 幂等性:多次执行相同请求,服务器状态结果相同。PUT /users/1 多次执行结果一致;POST /users 每次创建新资源,非幂等。 浏览器直接从本地缓存读取,不向服务器发送请求。 缓存命中时,状

By yellowdog

系统设计基础

SLA 对照表: 选择建议:无状态服务(Web 层、API 层)优先水平扩展;数据库初期垂直扩展,达到瓶颈后考虑分库分表或读写分离。 缓存穿透(查询不存在的 key,每次都打到 DB): 缓存击穿(热点 key 过期,瞬间大量请求打到 DB): 缓存雪崩(大量 key 同时过期,或缓存服务宕机): 令牌桶 Python 实现: Redis 实现分布式限流(滑动窗口): URL 命名规则: Cursor 分页响应格式: 雪花算法结构(64 bit): 定义:分布式系统不能同时满足以下三个特性: 在分布式环境中 P 是必须保证的,所以实际是 CP vs AP

By yellowdog

算法思路与模板

二分查找要求序列有序,每次将搜索范围缩减一半,时间复杂度 O(log n)。 两个指针从两端向中间收缩,常用于有序数组。 滑动窗口维护一个满足条件的区间 left, right,right 不断向右扩张,条件不满足时收缩 left。 滑动窗口通用框架: 1. 确定"子问题":原问题可以分解为哪些规模更小的同类问题 2. 定义 dpi 或 dpij 的含义,要足够清晰 3. 推导状态转移方程 4. 确定初始状态(边界条件) 5. 确定计算顺序(确保依赖的子问题先计算) 每件物品最多选一次。dpj = 容量为 j 时的最大价值,逆序遍历容量防止重复选取。 每

By yellowdog