常见加密

常见加密算法

1 base64 编码

1.1 base64 特点

  1. 密文内容:base64 的密文一般包含 0-9a-zA-Z+/=,而 = 一般用于末尾填充。

  2. 明文密文长度关系:⌈字节数 ÷ 3⌉ × 4(含填充,也就是密文大概是明文的 4/3)。+

    例如:“123456” 转换成 base64,值为 MTIzNDU2,长度为 8

1.1.2 base64 的实现

我们做逆向接触最多的就是 JavaScript,其次就是 python 调用,所以这里会给出这两种方式来实现对应的代码:

  • js 的实现

    const BASE64 = {
        // base64 编码方法
        "encode": function (data) {
            // 使用Buffer的toString方法进行Base64编码
            return Buffer.from(data).toString('base64');
        },
        // base64 解码方法
        "decode": function (encodedData) {
            return Buffer.from(encodedData, 'base64').toString('utf-8');
        }
    }
    
    1. Buffer.from("123456"):创建一个 Buffer 对象,表示将括号中的内容转换成二进制数据。
    2. .toString('base64'):将 Buffer 中的二进制数据转换为字符串,而字符串的编码方式则是 base64。
  • python 的实现

    import base64
    
    
    class Base64:
        '''base64编码方法'''
        @staticmethod
        def encode(data):
            return base64.b64encode(data.encode('utf-8')).decode()
    
        '''base64解码方法'''
        @staticmethod
        def decode(data):
            return base64.b64decode(data.encode('utf-8')).decode()
    

1.2 案例 - 深交所投资者服务通行证

1)需求

网址:https://owssso.szse.cn/sso/login?service=https://www.szse.cn/application/passCenter/pass/&locale=zh

接口:https://owssso.szse.cn/sso/login?service=https://www.szse.cn/application/passCenter/pass/&locale=zh

参数:

password MTIzNDU2

2)逆向

  1. 定位

    使用全局搜索的方式搜索 password = ,定位到:

    image-20250623144528726

    可以看到 config.elements.passwordInput.val() 的值是明文,经过了 config.elements.submitPasswordInput.val(encode64(password)) 变成密文,所以加密的地方就是config.elements.submitPasswordInput.val(encode64(password))

  2. 加密

    查看加密的代码,实际起到加密作用的代码是 encode64(password),这里加密的是 123456 字符串,而结果和我们前面用 base64 加密得到的很像。

    多次加密明文与网页的代码对比看看,发现结果相同,说明这里使用的是标准的 base64 实现。

2 哈希算法

哈希算法又称为消息摘要算法、散列算法,是一种将任意长度输入数据转换为固定长度输出值(哈希值)的密码学函数,具备高效性、确定性和抗碰撞性等核心特性。

2.1 特点

  1. 固定长度输出
    无论输入数据大小,输出哈希值长度固定。
  2. 不可逆性
    无法从哈希值反推原始数据,常用于密码存储。
  3. 高敏感性
    输入数据细微变化(如修改 1 个字符)会导致输出值截然不同。
  4. 抗碰撞性
    不同输入产生相同哈希值的概率极低。

2.2 MD5

MD5 是一种广泛使用的密码散列函数,用于生成128位(16字节)的哈希值,通常表示为32位十六进制字符串。

2.2.1 MD5 特点

  1. 长度:md5 加密的结果一般有 1632 和 40,40 的一般很少见。
  2. 内容:md5 加密的密文包含 0-9a-f,可以是大写。
  3. 关键字:如果加密方式是 md5 加密,可以通过关键 md5 字搜索。

2.2.2 MD5 实现

实现加密算法,前端使用的比较多的是 crypto-js 库。后面实现的时候,我们也多会使用这些前端常用的,方便我们在分析网页代码的时候,能更好的了解当前加密算法是什么算法。

使用 crypto-js 库,首先需要安装:

npm install crypto-js

这个库在后面还会用上,后面就不重复说明了。

  • js 实现

    // 引用 crypto-js 加密模块
    var CryptoJS = require('crypto-js')
    
    function md5(text) {
        // 调用库方法生成哈希并转为字符串
        return CryptoJS.MD5(text).toString()
    }
    
    console.log(md5("123456"))
    
  • python 实现

    # 加载Python内置的哈希算法库
    import hashlib
    
    
    def md5(text):
        # 创建MD5哈希对象
        md5 = hashlib.md5()
        # 将输入字符串编码为UTF-8字节并更新哈希计算
        md5.update(text.encode('utf-8'))
        # 返回16进制格式的哈希值
        return md5.hexdigest()
    
    
    if __name__ == '__main__':
        print(md5("123456"))
    

2.2.3 案例 - 科凡网

1)需求

网址:https://i.fkw.com/?_ta=0

接口:https://i.fkw.com/ajax/login_h.jsp?dogSrc=3

逆向 - 表单:pwd e10adc3949ba59abbe56e057f20f883e

2)逆向
  1. 定位加密位置

    这里可以通过全局搜索的方式定位:

    pwd=
    

    image-20250624165457303

    打上断点,看看是否能断住。

  2. 查看明文与密文

    image-20250624165604153

    可以看到这里刚好就是对应的值。

3.1 SHA

SHA 是由美国国家安全局(NSA)设计、NIST 发布的加密哈希函数家族。没错,它是一个家族,比如 sha1,sha256,sha512等。

3.1.1 SHA 特点

  1. 长度

    不同的 sha 加密后的密文长度是不一样的,常见的如下:

    • SHA-1:160 位(20 字节)
    • SHA-256:256 位(32 字节)
    • SHA-512:512 位(64 字节)
  2. 内容:一般以十六进制形式表示,也就是加密的密文包含 0-9a-f,可以是大写。

  3. 关键字:sha

3.1.2 SHA 实现

  • js 实现

    // 引用 crypto-js 加密模块
    var CryptoJS = require('crypto-js')
    
    function SHA1(text) {
        // 对输入字符串进行哈希计算,并将结果转换为十六进制字符串
        return CryptoJS.SHA1(text).toString();
    }
    
    console.log(SHA1("123456"))
    
  • python 实现

    import hashlib
    
    
    def sha1(text):
        # 创建 sha1 对象
        sha1 = hashlib.sha1()
        # 将输入字符串编码为UTF-8字节并更新哈希计算
        sha1.update(text.encode('utf-8'))
        # 返回16进制格式的哈希值
        return sha1.hexdigest()
    
    
    if __name__ == '__main__':
        print(sha1('123456'))
    

3.1.3 案例 - 六间房

1)需求

网址:https://v.6.cn/channel/index.php

接口:https://passport.6.cn/sso/login.php?username=13800138000&domain=v.6.cn

逆向:password 1199a3e08d94e5f59d099c6e369774737fd3da1c

2)逆向
  1. 定位加密位置

    通过全局搜索的方式定位,搜索:

    password:
    

    结果:

    image-20250625144848346

    这个就是。

  2. 断点调试

    在该位置打断点,再次登录:

    image-20250625145112636

    断点断住,查看参数和加密结果,确定就是这里了。查看加密方法。

  3. n.encode 方法

    由于这里代码是被压缩的,并不太好调试分析,所以在分析之前先格式化一下代码,点击左下角的大括号:

    image-20250625145330966

    进入到加密方法:

    image-20250625145439370

4 对称算法

对称加密算法是一种加密技术,其核心特点是加密和解密使用相同的密钥,即发送方用密钥加密明文,接收方用相同密钥解密密文。这类算法速度快、效率高,适合加密大量数据。

image-20250625153825963

常见的对称加密算法有:

  • DES(Data Encryption Standard),翻译为中文称为 数据加密标准
  • 3DES(Triple DES):对数据执行3次DES加密,相对于 DES 更加安全。
  • AES(Advanced Encryption Standard):翻译为中文为 高级加密标准,是 DES 的替代算法。

我们主要讲解 DES 和 AES 加密算法。

4.1 DES

4.1.1 DES 实现

  • js 实现

    // 引用 crypto-js 加密模块
    var CryptoJS = require('crypto-js');
    
    // 创建 DES 对象
    var DES = {
        // 加密方法
        encrypt: function (data, key, iv) {
            // 转为 UTF-8 格式的 WordArray 对象供 DES 算法使用
            var key = CryptoJS.enc.Utf8.parse(key),
                iv = CryptoJS.enc.Utf8.parse(iv),
                srcs = CryptoJS.enc.Utf8.parse(data),
                // 使用 DES 加密
                encrypted = CryptoJS.DES.encrypt(srcs, key, {
                    iv: iv,
                    // CBC 加密模式,需添加初始向量(IV)增强安全性
                    mode: CryptoJS.mode.CBC,
                    // Pkcs7 填充方式,自动补足数据块至 8 字节倍数
                    padding: CryptoJS.pad.Pkcs7
                });
            // 输出为 Base64 编码的密文字符串
            return encrypted.toString();
        },
        // 解密方法
        decrypt: function (data, key, iv) {
            var key = CryptoJS.enc.Utf8.parse(key),
                iv = CryptoJS.enc.Utf8.parse(iv),
                decrypted = CryptoJS.DES.decrypt(data, key, {
                    iv: iv,
                    mode: CryptoJS.mode.CBC,
                    padding: CryptoJS.pad.Pkcs7
                });
            // 转换为明文字符串
            return decrypted.toString(CryptoJS.enc.Utf8);
        },
    }
    
    
    // 密钥
    var desKey = "12345678";
    // 初始向量
    var desIv = "12345678";
    // 明文内容
    var text = "123456";
    
    // 加密
    var encryptedData = DES.encrypt(text, desKey, desIv);
    console.log("加密字符串: ", encryptedData);
    // 解密
    var decryptedData = DES.decrypt(encryptedData, desKey, desIv);
    console.log("解密字符串: ", decryptedData);
    
  • python 实现

    python 想要实现 DES 加密算法,需要安装第三方库:pip install pycryptodome

    # 提供 DES 加密算法
    from Crypto.Cipher import DES
    # 提供 PKCS#7 填充
    from Crypto.Util.Padding import pad, unpad
    import base64
    
    
    class DESCrypto:
        def __init__(self, key, iv):
            # 接收字节密钥和IV,自动转换为bytes格式
            self.key = key if isinstance(key, bytes) else key.encode('utf-8')
            self.iv = iv if isinstance(iv, bytes) else iv.encode('utf-8')
    
        def encrypt(self, plaintext):
            """加密明文并返回Base64编码结果"""
            # 创建 DES 对象,使用 CBC 模式
            cipher = DES.new(self.key, DES.MODE_CBC, self.iv)
            # 对明文的处理,使用 PKCS#7 方式填充,确保数据长度是 8 字节的倍数
            padded_data = pad(plaintext.encode('utf-8'), DES.block_size)
            # 加密
            encrypted = cipher.encrypt(padded_data)
            # 返回 Base64 字符串
            return base64.b64encode(encrypted).decode('utf-8')
    
        def decrypt(self, ciphertext):
            """解密Base64编码的密文"""
            # 创建 DES 对象,使用 CBC 模式
            cipher = DES.new(self.key, DES.MODE_CBC, self.iv)
            # 对明文使用 Base64 解码
            encrypted_data = base64.b64decode(ciphertext)
            # 解密
            decrypted = cipher.decrypt(encrypted_data)
            # 移除 PKCS#7 填充并解码为字符串
            return unpad(decrypted, DES.block_size).decode('utf-8')
    
    
    if __name__ == '__main__':
        des = DESCrypto("12345678", "12345678")
        print(des.encrypt('123456'))
        print(des.decrypt('HUX+7VtHgb0='))
    

不论是 JavaScript 代码实现,还是 python 代码实现,我们都可以看到几个关键词:

  1. key(密钥)
  2. iv(初始化向量)
  3. 加密模式
  4. 填充模式

这些是在 DES 和 AES 加密中都存在,也是对称加密算法的特点之一,下面我们来讲讲这些关键词。

温馨提示:

​ 关于这些关键词,主要以了解为主,知道这些分别是用来干什么的就行。

4.1.2 DES 特点

1)iv

但是当我们使用 CBC 模式实现加密的时候,需要提供一个初始化向量 iv,iv 必须是 8 字节。

什么是 iv,为什么需要有一个 iv?

这就需要说一下 DES 的加密模式了。

2)加密模式

在使用 DES 加密的时候,常用的有两种加密模式,分别是 ECB 和 CBC。

ECB

ECB 全称 Electronic Codebook,译为电子密码本模式,每个数据块独立进行加密与解密。

ECB 原理就是将明文分组,对每一组分别单独加密,加密后的每组密文之间没有联系,再将每一组加密的结果进行拼接。

image-20250625170321699

这种模式下,只需要密钥即可,解密也是这样的流程。

CBC

CBC 全称 Cipher Block Chaining,译为密文分组链接模式。这种模式的核心思想是每一个明文分组在被加密之前要与前一个的密文分组进行异或运算,即每一组的加密结果会参与下一个分组的加密。最后将每个密文分组按顺序合并起来就得到加密结果。

image-20250625190318419

3)填充模式

由于明文是需要被分组的,分组的每一段内容都需要和其他部分的进行运算,所以每段的长度需要一样。但是我们不能保证每次的明文都是刚好都分成一样的长度,那最后不够的部分怎么处理呢?答案就是填充了。

4)其他特点
  1. 内容:DES 是一种加密算法,其结果的表现形式可以是十六进制也可以是 base64,一般 base64 格式较多。
  2. 关键字:des、key、iv、CBC、ECB、encrypt、decrypt。

4.1.3 案例 - 长安

1)需求

网址:https://bqcm0.cavip1.com/

接口:https://bqcm0.cavip1.com/wps/session/login

逆向 :表单参数

2)逆向

由于参数整个就是一个密文,所以这里不太好通过全局搜索定位:

image-20250625211328264

这里考虑用调用堆栈调试查看。

  1. 调用堆栈

    image-20250625211424738

    从调用堆栈看函数名称,可以看到 login,这个可以猜测是实现登录的,直接看这个。

  2. login

    image-20250625212034062

    在图中断点处打上断点,再次运行程序,可以断下来。从字面看,可能是 des 加密,查看数据:

    • a 的值:

      {username: 'z13800138000', password: '123456', captcha: '6'}
      
    • i 的值:

      'U5Te0KJO6qOJH9Mq'
      
  3. utils.desEncrypt 函数

    desEncrypt: function(e, t) {
        var n = CryptoJS.enc.Utf8.parse(t);
        return CryptoJS.DES.encrypt(e, n, {
            mode: CryptoJS.mode.ECB,
            padding: CryptoJS.pad.Pkcs7
        }).toString()
    },
    

    从这里就可以看出来确实是 DES 加密算法了,并且是 ECB 模式加密的。

4.2 AES

4.2.1 AES 特点

AES 相对于 DES 来说,更加安全,但是不论在代码实现还是在实现原理方面来说,都比较类似。与 DES 相比,AES 的密钥长度是可选的,并非是固定的长度,其密钥长度可以是:

  • 128位(16字节)
  • 192位(24字节)
  • 256位(32字节)

而 iv 也比 DES 的要长一些,固定为 128 位( 16 字节)。

4.2.2 AES 实现

  • js 实现

    // 引用 crypto-js 加密模块
    var CryptoJS = require('crypto-js');
    
    var AES = {
        encrypt: function (data, key, iv) {
            // 转为 UTF-8 格式的 WordArray 对象供 AES 算法使用
            var key = CryptoJS.enc.Utf8.parse(key),
                iv = CryptoJS.enc.Utf8.parse(iv),
                srcs = CryptoJS.enc.Utf8.parse(data),
                // 使用 AES 加密
                encrypted = CryptoJS.AES.encrypt(srcs, key, {
                    iv: iv,
                    // CBC 加密模式,需添加初始向量(IV)增强安全性
                    mode: CryptoJS.mode.CBC,
                    // Pkcs7 填充方式,自动补足数据块至 8 字节倍数
                    padding: CryptoJS.pad.Pkcs7
                });
            // 输出为 Base64 编码的密文字符串
            return encrypted.toString();
        },
        decrypt: function (data, key, iv) {
            var key = CryptoJS.enc.Utf8.parse(key),
                iv = CryptoJS.enc.Utf8.parse(iv),
                decrypted = CryptoJS.AES.decrypt(data, key, {
                    iv: iv,
                    mode: CryptoJS.mode.CBC,
                    padding: CryptoJS.pad.Pkcs7
                });
            // 转换为明文字符串
            return decrypted.toString(CryptoJS.enc.Utf8);
        },
    }
    
    
    // 密钥
    var aesKey = "0123456789abcdef";
    // 初始向量
    var aesIv = "0123456789abcdef";
    // 明文内容
    var text = "123456";
    
    // 加密
    var encryptedData = AES.encrypt(text, aesKey, aesIv);
    console.log("加密字符串: ", encryptedData);
    // 解密
    var decryptedData = AES.decrypt(encryptedData, aesKey, aesIv);
    console.log("解密字符串: ", decryptedData);
    
  • python 实现

    # 提供 AES 加密算法
    
      from Crypto.Cipher import AES
    
      # 提供 PKCS#7 填充
    
      from Crypto.Util.Padding import pad, unpad
      import base64
    
    
      class AESCrypto:
          def __init__(self, key, iv):
              # 接收字节密钥和IV,自动转换为bytes格式
              self.key = key if isinstance(key, bytes) else key.encode('utf-8')
              self.iv = iv if isinstance(iv, bytes) else iv.encode('utf-8')
                
         def encrypt(self, plaintext):
          """加密明文并返回Base64编码结果"""
          # 创建 AES 对象,使用 CBC 模式
          cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
          # 对明文的处理,使用 PKCS#7 方式填充,确保数据长度是 8 字节的倍数
          padded_data = pad(plaintext.encode('utf-8'), AES.block_size)
          # 加密
          encrypted = cipher.encrypt(padded_data)
          # 返回 Base64 字符串
          return base64.b64encode(encrypted).decode('utf-8')
      
      def decrypt(self, ciphertext):
          """解密Base64编码的密文"""
          # 创建 AES 对象,使用 CBC 模式
          cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
          # 对明文使用 Base64 解码
          encrypted_data = base64.b64decode(ciphertext)
          # 解密
          decrypted = cipher.decrypt(encrypted_data)
          # 移除 PKCS#7 填充并解码为字符串
          return unpad(decrypted, AES.block_size).decode('utf-8')
    
     
    if __name__ == '__main__':
          aes = AESCrypto("0123456789abcdef", "0123456789abcdef")
          print(aes.encrypt('123456'))
          print(aes.decrypt('AL5riQzgwmMhXZX+5MtU0A=='))
    

4.2.3 案例 - 学习通

1)需求

网址:https://passport2.chaoxing.com/login

接口:https://passport2.chaoxing.com/fanyalogin

逆向:password 0QgUbMUJj2usHikiqtb8HQ==

2)逆向
  1. 定位加密位置

    通过全局搜索的方式定位 password 加密的位置:

    'password': 
    
  2. 加密位置

    image-20250625214158763

    从单词来看,像是 AES 加密。

  3. encryptByAES 函数

    function encryptByAES(message, key) {
        let CBCOptions = {
            iv: CryptoJS.enc.Utf8.parse(key),
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        };
        let aeskey = CryptoJS.enc.Utf8.parse(key);
        let secretData = CryptoJS.enc.Utf8.parse(message);
        let encrypted = CryptoJS.AES.encrypt(
            secretData,
            aeskey,
            CBCOptions
        );
        return CryptoJS.enc.Base64.stringify(encrypted.ciphertext);
    }
    

    从代码来看,确实是 AES 加密算法,只是相对于我们自己实现的,稍微有一点格式上的区别。

5 非对称算法 - RSA

非对称加密,使用 一对密钥(公钥和私钥)进行加密和解密。与对称加密不同,非对称加密的加密和解密使用不同的密钥,解决了密钥分发问题,但计算速度较慢,所以一般不会用非对称加密算法对大量数据进行加密。

image-20250625193848048

5.1 RSA 特点

  1. 长度:rsa 的密文与密钥的长度有关,密钥长度越长,密文长度越长。

    rsa 的密钥长度可以是 1024位、2048位 和 4096位。

  2. 内容:对相同的明文加密,密文都会不同。

  3. 关键字:key、new RSA、setPublic、encrypt、decrypt。

5.2 RSA 实现

  • js 实现

    crypto-js 模块并没有实现 rsa,需要安装另一个库:npm install node-jsencrypt

    const fs = require('fs');
    const JSEncrypt = require('node-jsencrypt');
    
    var RSA = {
        encrypt: function (data, key) {
            // 创建 JSEncrypt 对象
            const encryptor = new JSEncrypt();
            // 设置公钥
            encryptor.setPublicKey(key);
            // 加密数据
            return encryptor.encrypt(data);
        },
        decrypt: function (data, key) {
            // 创建 JSEncrypt 对象
            const decryptor = new JSEncrypt();
            // 设置私钥
            decryptor.setPrivateKey(key);
            // 解密数据
            return decryptor.decrypt(data);
        }
    }
    
    
    // 加密
    const publicKey = fs.readFileSync('public_key.pem', 'utf8');
    var encryptedData = RSA.encrypt("123456", publicKey);
    console.log(encryptedData);
    
    // 解密
    const privateKey = fs.readFileSync('private_key.pem', 'utf8');
    var decryptedData = RSA.decrypt(encryptedData, privateKey);
    console.log(decryptedData);
    
  • python 实现

    需要安装第三方库:pip install rsa

    import rsa
    import base64
    
    
    class RSA:
        def __init__(self, publicKey, private_key):
            self.publicKey = publicKey
            self.private_key = private_key
    
        def encrypt(self, t):
            """公钥加密"""
            encrypted = rsa.encrypt(t.encode("utf-8"), self.publicKey)
            return base64.b64encode(encrypted).decode("utf-8")
    
        def decrypt(self, t):
            """私钥解密"""
            return rsa.decrypt(base64.b64decode(t), self.private_key).decode("utf-8")
    
    
    if __name__ == "__main__":
        # 生成公钥、私钥
        public_key, private_key = rsa.newkeys(512)
        print('公钥:', public_key)
        print('私钥:', private_key)
    
        # 创建 rsa 对象
        rsa_ = RSA(public_key, private_key)
        # 待加密字符串
        text = '123456'
        encrypted_str = rsa_.encrypt(text)
        print('加密字符串:', encrypted_str)
        decrypted_str = rsa_.decrypt(encrypted_str)
        print('解密字符串:', decrypted_str)
    

5.3 案例 - 新华保险

1)需求

网址:https://tms.newchinalife.com/ex//app/login/login.jsp

接口:https://tms.newchinalife.com/ex/j_spring_security_check

逆向 :j_password

n4NUSQ479nvK9xjCq5W2MesLXPWfJ/mGdTENn8ptJr666++5C0w5pO1forQOTXOkFdwC1/TdoxQLJXhwGGDZwQg331AIPrjdLJZjTjiWjKVTRUKua8eOIVX4u2421PSjNbXILrALrRxhwKE4LjLwt8VCeFXF2yGA8lMOXOKPNNelbZOUo/eSNSBdIAHEqWZxBQiWCX6s7FAp5T5DH0a/yd+LtgbfRCaprKvH3G7oreW8HzJKJkp3cKrF2o9ntgaws2UJlF+3rTapAa5f8Jo4KJmDHXYH7bY2fSKvjSBZxv26/ZZaPEM4llrCcm3y6+/Kuks+EElCIYf1Sjtg8HQr1A==

2)逆向

  1. 定位加密位置

    用全局搜索的方式定位,搜索:

    j_password
    

    由于直接用 j_password 搜索出来的结果并不多,并且能找到数据,可以直接用它搜索。

  2. 定位位置

    image-20250625220936234

    再次发送请求,确定是否断住,结果是可以断下来的。

  3. 数据查看

    查看上面的 j_pwd 参数,结果是密码的明文值,所以这里大概率就是了。

    然后在上面的代码中有看到 RSAClient 字样,那么很有可能是 rsa 加密算法进行的加密。

    但是这里只看到了创建 rsa 对象,并没有找到设置密钥的地方,所以暂时还不能确定。

  4. rasClient.encrypt 函数

    进入到 rasClient.encrypt 函数:

    image-20250625221430376

    在这个页面可以看到 setPublicKey 方法的调用,并且刚好是 RSAClient 函数的。也就是说在创建 rsa 对象的时候,就已经设置了公钥,而公钥的值是 PUBLICKEY

posted @ 2025-09-17 20:39  笔锋微凉~~  阅读(839)  评论(0)    收藏  举报