百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

Python爬虫学习笔记之对称加密算法剖析

toyiye 2024-06-06 22:12 14 浏览 0 评论

声明:本文章所有演示内容仅供学习交流使用,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系作者立即删除!

提示:演示内容相关敏感数据已做脱敏处理!

对称加密算法有哪些?

对称加密(加密解密密钥相同):DES、3DES、AES、RC4

简介

对称式加密就是加密和解密使用同一个密钥。信息接收双方都需事先知道密匙和加解密算法且其密匙是相同的,之后便是对数据进行加解密了。对称加密算法用来对敏感数据等信息进行加密。

原理-1

常见算法归纳

DES:56位密钥,由于密钥太短,被逐渐被弃用。

AES:有128位、192位、256位密钥,现在比较流行。密钥长、可以增加破解的难度和成本。

加盐模式归纳

  • ECB模式 全称Electronic Codebook模式,译为电子密码本模式
  • CBC模式 全称Cipher Block Chaining模式,译为密文分组链接模式
  • CFB模式 全称Cipher FeedBack模式,译为密文反馈模式
  • OFB模式 全称Output Feedback模式,译为输出反馈模式。
  • CTR模式 全称Counter模式,译为计数器模式。

原理-2

1. DES算法

简介:DES是一种分组加密算法,他以64位为分组对数据加密。64位一组的明文从算法的一端 输入,64位的密文从另一端输出。DES是一个对称算法:加密和解密用的是同一个算法(除 密钥编排不同以外)。

密钥的长度为56位(密钥通常表示为64位的数,但每个第8位都用作奇偶检验,可以忽 略)。密钥可以是任意的56位数,且可以在任意的时候改变。

DES算法的入口参数有3个:Key,Data,Mode。其中Key为8个字节共64位,是DES算法 的工作密钥;Data也为8个字节64位,是要被加密或解密的数据:Mode为DES的工作方式,有 两种:加密或解密。

DES算法的工作过程:若Mode为加密,则用Key对数据Data进行加密,生成Data的密码 形式(64位)作为DES的输出结果;若Mode为解密,则用Key对密码形式的数据Data解密,还 原为Data的明码形式(64位)作为DES的输出结果。

简单地说,算法只不过是加密的一种基本技术,DES基本组建分组是这些技术的一种组合 ,他基于密钥作用于明文,这是众所周知的轮(round)。DES有16轮,这意味着要在明文分 组上16次实施相同的组合技术。

  • mode 支持:CBC,CFB,CTR,CTRGladman,ECB,OFB 等。
  • padding 支持:ZeroPadding,NoPadding,AnsiX923,Iso10126,Iso97971,Pkcs7 等。

1.1 JavaScript 实现

DES算法的入口参数有3个

  • key、DATA、Mode、padding
  • key为7个字节共56位,是DES算法的工作密钥
  • Data为8个字节64位,是要被加密或被解密的数据
  • Mode为DES的工作方式
  • padding为填充模式,如果加密后密文长度如果达不到指定整数倍(8个字节,16个字节),填充
// 引用 crypto-js 加密模块
var CryptoJS = require('crypto-js')

function desEncrypt() {
    var key = CryptoJS.enc.Utf8.parse(desKey),
        iv = CryptoJS.enc.Utf8.parse(desIv),
        srcs = CryptoJS.enc.Utf8.parse(text),
        // CBC 加密模式,Pkcs7 填充方式
        encrypted = CryptoJS.DES.encrypt(srcs, key, {
            iv: iv,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        });
    return encrypted.toString();
}

function desDecrypt() {
    var key = CryptoJS.enc.Utf8.parse(desKey),
        iv = CryptoJS.enc.Utf8.parse(desIv),
        srcs = encryptedData,
        // CBC 加密模式,Pkcs7 填充方式
        decrypted = CryptoJS.DES.decrypt(srcs, key, {
            iv: iv,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        });
    return decrypted.toString(CryptoJS.enc.Utf8);
}

var text = "i am ziyupython"       // 待加密对象
var desKey = "123456"    // 密钥
var desIv = "ertyhcdsfbdssfwtuew"    // 初始向量

var encryptedData = desEncrypt()
var decryptedData = desDecrypt()

console.log("加密字符串: ", encryptedData)
console.log("解密字符串: ", decryptedData)

// 加密字符串:  3emnE4fUWAIvhTg4h38VxA==
// 解密字符串:  i am ziyupython

案例片段分享:

var CryptoJS = require('crypto-js')

 o = {
            keyHex: CryptoJS.enc.Utf8.parse(Object({
                NODE_ENV: "production",
                VUE_APP_BASE_API: "/pro-api",
                VUE_APP_CONSTRUCTION_API: "/pro-api-construction",
                VUE_APP_DEV_FILE_PREVIEW: "/lyjcdFileView/onlinePreview",
                VUE_APP_FILE_ALL_PATH: "http://www.nuyl.cn:8089",
                VUE_APP_FILE_PREFIX: "/mygroup",
                VUE_APP_LAND_API: "/pro-api-land",
                VUE_APP_PREVIEW_PREFIX: "/lyjcdFileView",
                VUE_APP_PROCUREMENT_API: "/pro-api-procurement",
                VUE_APP_WINDOW_TITLE: "",
                BASE_URL: "/"
            }).VUE_APP_CUSTOM_KEY || ""),
            ivHex: CryptoJS.enc.Utf8.parse(Object({
                NODE_ENV: "production",
                VUE_APP_BASE_API: "/pro-api",
                VUE_APP_CONSTRUCTION_API: "/pro-api-construction",
                VUE_APP_DEV_FILE_PREVIEW: "/lyjcdFileView/onlinePreview",
                VUE_APP_FILE_ALL_PATH: "http://www.nuyl.cn:8089",
                VUE_APP_FILE_PREFIX: "/mygroup",
                VUE_APP_LAND_API: "/pro-api-land",
                VUE_APP_PREVIEW_PREFIX: "/lyjcdFileView",
                VUE_APP_PROCUREMENT_API: "/pro-api-procurement",
                VUE_APP_WINDOW_TITLE: "",
                BASE_URL: "/"
            }).VUE_APP_CUSTOM_IV || "")
        };

function c(t) {
    return CryptoJS.DES.encrypt(t, o.keyHex, {
        iv: o.ivHex,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    }).ciphertext.toString()
}


console.log(c("245"));

1.2 Python 实现

PythonDES模块安装:

pip install pyDes
import binascii
# 加密模式 CBC,填充方式 PAD_PKCS5
from pyDes import des, CBC, PAD_PKCS5

def des_encrypt(key, text, iv):
    k = des(key, CBC, iv, pad=None, padmode=PAD_PKCS5)
    en = k.encrypt(text, padmode=PAD_PKCS5)
    return binascii.b2a_hex(en)

def des_decrypt(key, text, iv):
    k = des(key, CBC, iv, pad=None, padmode=PAD_PKCS5)
    de = k.decrypt(binascii.a2b_hex(text), padmode=PAD_PKCS5)
    return de

if __name__ == '__main__':
    secret_key = ''   # 密钥
    text = ''   # 加密对象
    iv = secret_key           # 偏移量
    secret_str = des_encrypt(secret_key, text, iv)
    print('加密字符串:', secret_str)
    clear_str = des_decrypt(secret_key, secret_str, iv)
    print('解密字符串:', clear_str)

1.3 实际案例

1.3.1 逆向目标

首页:aHR0cHM6Ly93d3cuZW5kYXRhLmNvbS5jbi9Cb3hPZmZpY2UvQk8vTW9udGgvb25lTW9udGguaHRtbA==

数据:aHR0cHM6Ly93d3cuZW5kYXRhLmNvbS5jbi9BUEkvR2V0RGF0YS5hc2h4

1.3.2 逆向分析

  • 使用xhr断点数据地址,进行单步调试

XHR断点

  • 调试对应的数据

数据调试

总结:可以看到该站点是对数据用webInstace.shell进行了解密

2. AES算法

环境安装

pip install pycryptodome

2.1 算法简介

简介:全称高级加密标准(英文名称:Advanced Encryption Standard),在密码学中又称 Rijndael 加密法,由美国国家标准与技术研究院 (NIST)于 2001 年发布,并在 2002 年成为有效的标准,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的 DES,已经被多方分析且广为全世界所使用,它本身只有一个密钥,即用来实现加密,也用于解密。

  • mode 支持:CBC,CFB,CTR,CTRGladman,ECB,OFB 等。
  • padding 支持:ZeroPadding,NoPadding,AnsiX923,Iso10126,Iso97971,Pkcs7 等。

参数定义:

  • key length(密钥位数,密码长度)AES128,AES192,AES256(128 位、192 位或 256 位)
  • key (密钥,密码)key指的就是密码了,AES128就是128位的,如果位数不够,某些库可能会自动填充到128。
  • IV (向量)IV称为初始向量,不同的IV加密后的字符串是不同的,加密和解密需要相同的IV。
  • mode (加密模式)AES分为几种模式,比如ECB,CBC,CFB等等,这些模式除了ECB由于没有使用IV而不太安全,其他模式差别并没有太明显。
  • padding (填充方式)对于加密解密两端需要使用同一的PADDING模式,大部分PADDING模式为PKCS5, PKCS7, NOPADDING。

加密原理:

AES加密算法采用分组密码体制,每个分组数据的长度为128位16个字节,密钥长度可以是128位16个字节、192位或256位,一共有四种加密模式,我们通常采用需要初始向量IV的CBC模式,初始向量的长度也是128位16个字节。

2.2 JavaScript 实现

类似网站:aHR0cHM6Ly93d3cuZG5zLmNvbS9sb2dpbi5odG1s

// 引用 crypto-js 加密模块
var CryptoJS = require('crypto-js')

function tripleAesEncrypt() {
    var key = CryptoJS.enc.Utf8.parse(aesKey),
        iv = CryptoJS.enc.Utf8.parse(aesIv),
        srcs = CryptoJS.enc.Utf8.parse(text),
        // CBC 加密方式,Pkcs7 填充方式
        encrypted = CryptoJS.AES.encrypt(srcs, key, {
            iv: iv,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        });
    return encrypted.toString();
}

function tripleAesDecrypt() {
    var key = CryptoJS.enc.Utf8.parse(aesKey),
        iv = CryptoJS.enc.Utf8.parse(aesIv),
        srcs = encryptedData,
        // CBC 加密方式,Pkcs7 填充方式
        decrypted = CryptoJS.AES.decrypt(srcs, key, {
            iv: iv,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        });
    return decrypted.toString(CryptoJS.enc.Utf8);
}

var text = ""       // 待加密对象
var aesKey = ""   // 密钥,16 倍数
var aesIv = ""    // 偏移量,16 倍数

var encryptedData = tripleAesEncrypt()
var decryptedData = tripleAesDecrypt()

console.log("加密字符串: ", encryptedData)
console.log("解密字符串: ", decryptedData)

2.3 Python 实现

import base64
from Crypto.Cipher import AES

# 需要补位,str不是16的倍数那就补足为16的倍数
def add_to_16(value):
    while len(value) % 16 != 0:
        value += '\0'
    return str.encode(value)


# 加密方法
def aes_encrypt(key, t, iv):
    aes = AES.new(add_to_16(key), AES.MODE_CBC, add_to_16(iv))  # 初始化加密器
    encrypt_aes = aes.encrypt(add_to_16(t))                    # 先进行 aes 加密
    encrypted_text = str(base64.encodebytes(encrypt_aes), encoding='utf-8')  # 执行加密并转码返回 bytes
    return encrypted_text


# 解密方法
def aes_decrypt(key, t, iv):
    aes = AES.new(add_to_16(key), AES.MODE_CBC, add_to_16(iv))         # 初始化加密器
    base64_decrypted = base64.decodebytes(t.encode(encoding='utf-8'))  # 优先逆向解密 base64 成 bytes
    decrypted_text = str(aes.decrypt(base64_decrypted), encoding='utf-8').replace('\0', '')  # 执行解密密并转码返回str
    return decrypted_text


if __name__ == '__main__':
    secret_key = ''   # 密钥
    text = ''   # 加密对象
    iv = secret_key           # 初始向量
    encrypted_str = aes_encrypt(secret_key, text, iv)
    print('加密字符串:', encrypted_str)
    decrypted_str = aes_decrypt(secret_key, encrypted_str, iv)
    print('解密字符串:', decrypted_str)

注意:明文加密要求是16的整数倍

2.4 实际案例

2.4.1 逆向目标

  • 接口:aHR0cHM6Ly9nYXRld2F5LmV3dDM2MC5jb20vYXBpL2F1dGhjZW50ZXIvdjIvb2F1dGgvbG9naW4vYWNjb3VudA==
  • 逆向参数:
    • sign: 3976F10977FC65F9CB967AEF79E508BD
    • password: "A7428361DEF118911783F446A129FFCE"

2.4.2 抓包分析

来到某网通的登录页面,随便输入一个账号密码登陆,抓包定位到登录接口为 aHR0cHM6Ly9nYXRld2F5LmV3dDM2MC5jb20vYXBpL2F1dGhjZW50ZXIvdjIvb2F1dGgvbG9naW4vYWNjb3VudA==,请求头里,有一个 sign,Payload 里,密码 password 被加密处理了。

抓包分析

2.4.3 参数逆向

1 sign签名处理

首先来看一下请求头的 sign,尝试直接搜索一下,发现并不是经过某些请求返回的数据,观察一下其他请求,可以发现同样有 sign,而且每次请求的值都不一样:

关键字搜索

由此可以初步判断这个值应该是通过 JS 生成的,全局搜索关键字 sign:,可以分别在 request.js、request.ts 两个文件里面看到疑似 sign 赋值的地方,埋下断点调试,成功断下,原理也很简单,时间戳加上一串固定的字符,经过 MD5 加密后再转大写即可。

定位调试

使用 Python 实现:

import time
import hashlib

timestamp = str(int(time.time() * 1000))
sign = hashlib.md5((timestamp + 'bdc739ff2dcf').encode(encoding='utf-8')).hexdigest().upper()
print(sign)

2 password处理

password 是明文密码经过加密后得到的值,如果尝试直接去搜索的话,会发现出来的值非常非常多,要想找到准确的值难度巨大:

可以看到这条请求是 XHR 请求,本次我们使用 XHR 断点的方法来定位具体的加密位置,通过本次案例,我们来学习一下具体是如何跟进调用栈、如何通过上下文来定位具体的加密位置。

切换到 Network 选项卡,找到登陆请求,鼠标移动到 Initiator 选项卡下的 JS 上,可以看到其调用栈,如果站点的加密方式比较简单,没有太多混淆的话,调用栈里面就可以看到login、send、post、encrypt 等等之类的关键词,这种情况下就可以直接点进去,比较容易找到加密的地方,但是大多数站点对于函数名、变量名都做了混淆,和本案例一样,调用栈里面显示的都是一些单个或者多个无规则的字母的函数,无法直接定位,此时就需要我们从最后一个函数往前慢慢找。

跟栈-1

点击进入最后一个函数,即 Y 函数,它位于调用栈的最顶层,表示经过此函数后,浏览器就会发送登录的请求,密码的加密过程已经处理完毕。在此函数埋下断点,可以在右侧的Call Stack 看到调用栈,从下到上,表示的是点击登陆后,先后调用的函数的执行过程:

根据这种思路,一步一步往下跟进调用栈,可以看到在 utils.ts 里面执行了一个匿名函数,其中调用了一个 passwordEncrypt 函数,通过函数名就可以看出基本上就是密码加密的函数了


总结:

在此处埋下断点进行调试,传进来的是明文密码,passwordEncrypt 实际上是调用的 encode.ts 中的 O 函数,跟进 O 函数,引用了 crypto-js 加密模块,很明显的 AES 加密,本地改写一下就OK。

JavaScript 加密代码

CryptoJS = require("crypto-js")

const key = CryptoJS.enc.Utf8.parse("20987878990967789009786788978");
const iv = CryptoJS.enc.Utf8.parse('20987878990967789009786788978'); //十六位十六进制数作为密钥偏移量

function getEncryptedPassword(word) {
    let srcs = CryptoJS.enc.Utf8.parse(word);
    let encrypted = CryptoJS.AES.encrypt(srcs, key, {
            iv: iv,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        });
    return encrypted.ciphertext.toString().toUpperCase();
}

// 测试样例
// console.log(getEncryptedPassword("123457"))

python模拟登录

import time
import hashlib

import execjs
import requests


login_url = 'aHR0cHM6Ly9nYXRld2F5LmV3dDM2MC5jb20vYXBpL2F1dGhjZW50ZXIvdjIvb2F1dGgvbG9naW4vYWNjb3VudA=='
session = requests.session()


def get_sign():
    timestamp = str(int(time.time()*1000))
    sign = hashlib.md5((timestamp + 'bdc739ff2dcf').encode(encoding='utf-8')).hexdigest().upper()
    return sign


def get_encrypted_parameter(password):
    with open('encrypt.js', 'r', encoding='utf-8') as f:
        ewt360_js = f.read()
    encrypted_password = execjs.compile(ewt360_js).call('getEncryptedPassword', password)
    return encrypted_password


def login(sign, username, encrypted_password):
    headers = {
        'sign': sign,
        'timestamp': str(int(time.time()*1000)),
        'sec-ch-ua': '" Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
    }
    data = {
        'autoLogin': True,
        'password': encrypted_password,
        'platform': 1,
        'userName': username
    }
    response = session.post(url=login_url, headers=headers, json=data)
    print(response.json())


def main():
    username = input('请输入登录账号: ')
    password = input('请输入登录密码: ')
    sign = get_sign()
    encrypted_password = get_encrypted_parameter(password)
    login(sign, username, encrypted_password)


if __name__ == '__main__':
    main()


参考资料:

  • RFC 4772:https://datatracker.ietf.org/doc/rfc4772/
  • DES 维基百科:https://en.wikipedia.org/wiki/Data_Encryption_Standard
  • RFC 3268:https://datatracker.ietf.org/doc/rfc3268/
  • AES 维基百科:https://en.wikipedia.org/wiki/Advanced_Encryption_Standard

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码