当前位置: 首页 > article >正文

关于js解密中遇到base64时的坑

直接贴图,某道翻译的aes解密,但是他先将文本进行base64解密后再进行的aes解密,采用crypto库解密是没有问题的,但是,采用crypto-js库进行解密会报错 Malformed UTF-8 data编码问题,一般这种情况都是密文或者key,iv的问题,所以核验密文,

发现密文中有_以及-这种字符,所以判断这个加密字符串进行了URL 安全 Base64 编码,因为在 URL 中,+ 和 / 具有特殊含义。+ 通常用于表示空格,而 / 用于路径分隔。因此,在 URL 中使用标准 Base64 编码会导致解析问题(chatgpt解释),所以我们在对加密字符串进行base64解码时需要先将_和-替换掉,才能进行最终的解密。可是这里又有个疑问,既然_和-是错误的字符,那为什么crypto能够解密成功呢,以下是chatgpt原话:        

  1. 内部处理机制

    • Node.js 的 Buffer 模块在解析 Base64 字符串时,会自动处理 URL 安全 Base64 编码中的字符 _ 和 -
    • 这意味着即使字符串中包含这些字符,Node.js 也会将其视为有效的 Base64 字符进行解码。
  2. 容错性

    • Buffer.from 方法在处理 Base64 字符串时,不会严格要求字符必须符合标准 Base64 字符集。它会尝试将字符串解析为二进制数据,即使字符串中包含 _ 和 - 也不会导致解码失败。

为什么 crypto-js 需要手动替换?

虽然 Buffer.from 可以直接解码 URL 安全 Base64 编码的字符串,但在使用 crypto-js 时,需要手动替换 _ 和 -,这是因为 crypto-js 的 Base64 解码实现遵循更严格的 Base64 字符集规范,不自动处理 URL 安全 Base64 编码中的字符。

看到这里就差不多能理解了,因为crypto采用的是buffer的解密,而buffer有一定的容错性,所以也能解密成功,而crypto-js在解析base64时是更为严格的,所以解码失败,要手动的进行替换_和-。

然后再来看python中

我们可以发现,即使对-和_进行了替换,base64解码时也还是会报错Incorrect padding,这是因为Python的base64编码要求输入字符串的长度必须是4的倍数,如果不是,则需要在末尾添加填充字符=。那我们回到刚才,那反过来,既然这个加密字符串的长度不是4的倍数,那为什么crypto和crypto-js能够解码成功呢,再问chatgpt:

测试一下:

可以发现crypto以及crypto-js对base64的解码并不会严格要求符合长度,即使少了=号也能够解码成功,而python里就不行。所以python要进行手动补全=号达到4的倍数。

下面附带三种解密的方式:

crypto:

function decodeData(t) {
  // 将输入文本从 Base64 解码为二进制数据
  const base64Decoded = Buffer.from(t, 'base64');
  // 计算 MD5 哈希并截取前 16 字节作为密钥和 IV
  const key = crypto.createHash('md5').update(o).digest();
  const iv = crypto.createHash('md5').update(n).digest();
  // 创建 decipher 实例
  const decipher = crypto.createDecipheriv('aes-128-cbc', key, iv);
  // 更新 decipher 并最终解密
  let decrypted = decipher.update(base64Decoded, 'binary', 'utf8');
  decrypted += decipher.final('utf8');
  return decrypted;
}

crypto-js:

function decodeData(t) {
  //需要手动的替换-以及_
  t = t.replace(/-/g, '+').replace(/_/g, '/');
  const key = CryptoJS.MD5(o).toString(CryptoJS.enc.Hex);
  const iv = CryptoJS.MD5(n).toString(CryptoJS.enc.Hex);
  const keyWordArray = CryptoJS.enc.Hex.parse(key);
  const ivWordArray = CryptoJS.enc.Hex.parse(iv);
  // 解密 AES
  const decrypted = CryptoJS.AES.decrypt(
    { ciphertext: CryptoJS.enc.Base64.parse(t) }, 
    keyWordArray,
    { iv: ivWordArray, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }
  );
  return decrypted.toString(CryptoJS.enc.Utf8);
}

Python:

def decodeData(t):
    # 一样的替换_和-
    t = t.replace("_", "/").replace("-", "+")
    # Python中需要验证base64的长度是不是4的倍数,如果不是需要手动的补全结尾的=号,不然也会解码失败
    missing_padding = 4 - len(t) % 4
    if missing_padding:
        t += "=" * missing_padding
    decodeiv = "ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4"
    # 秘钥
    decodekey = "ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl"
    # 先把密匙和偏移量进行md5加密 digest()是返回二进制的值
    key = hashlib.md5(decodekey.encode(encoding="utf-8")).digest()
    iv = hashlib.md5(decodeiv.encode(encoding="utf-8")).digest()
    # AES解密 CBC模式解密
    aes_en = AES.new(key, AES.MODE_CBC, iv)
    # 将已经加密的数据放进该方法
    data_new = base64.urlsafe_b64decode(t)
    # 参数准备完毕后,进行解密
    a = aes_en.decrypt(data_new).decode("utf-8").strip()
    return a

本篇文章是给自己做个记录,当然也欢迎大家学习交流。


http://www.kler.cn/a/419012.html

相关文章:

  • Java全栈:超市购物系统实现
  • Android -- 简易音乐播放器
  • unity中添加预制体及其基本设置
  • 牛客-尼科彻斯定理、整型数组合并
  • 十一、快速入门go语言之接口和反射
  • Spring Web MVC其他扩展(详解下)
  • 22智能 图
  • 【docker】8. 镜像仓库实战
  • oracle日期格式查询
  • ajax都有哪些优点和缺点?
  • Python实现有向图及查找
  • 深度学习中的迁移学习:应用与实践
  • 【Linux】gdb / cgdb 调试 + 进度条
  • kubernetes组件ETCD未授权访问
  • TransmittableThreadLocal维护Token中的userId
  • Hexo博客在多个设备同步
  • 数据库原理-期末复习基础知识第二弹
  • 【深度学习】四大图像分类网络之VGGNet
  • 【MySQL】数据库的基本认识和使用
  • 什么是sfp,onu,​为什么PON(​俗称“光猫”​)模块使用SC光纤接口
  • 数据同步、流计算全面强化,TDengine 3.3.4.3 版本正式发布
  • C++高阶算法[汇总]
  • How to monitor Spring Boot apps with the AppDynamics Java Agent
  • Android下载出现open failed: EPERM (Operation not permitted)
  • Vue3 子路由vue如何调用父路由vue中的方法?
  • android 项目多电脑共用github及github项目迁移