From:https://mp.weixin.qq.com/s/Yezf-ueRi7PxggZtah8K5g
CTF-Tools:https://github.com/qianxiao996/CTF-Tools
ToolsFx:https://github.com/Leon406/ToolsFx
cyberchef
关键字:加密 解密 编码 解码
爬虫工程师在做加密参数逆向的时候,经常会遇到各种各样的加密算法、编码、混淆,每个算法都有其对应的特征,对于一些较小的网站,往往直接引用这些官方算法,没有进行魔改等其他操作,这种情况下,如果我们能熟悉常见算法的特征,通过密文就能猜测出使用的哪种算法、编码、混淆,将会大大提高工作效率!在 CTF 中通常也会有密码类的题目,掌握一些常见密文特征也是 CTFer 们必备的技能!
将介绍以下编码和加密算法的特征:
Base64 是我们最常见的编码,除此之外,其实还有 Base16、Base32、Base58、Base85、Base100 等,他们之间最明显的区别就是使用了不同数量的可打印字符对任意字节数据进行编码,比如 Base64 使用了 64 个可打印字符(A-Z、a-z、0-9、+、/),Base16 使用了 16 个可打印字符(A-F、0-9),这里主要讲怎么快速识别,其具体原理可自行百度,Base 系列主要特征如下:
示例:
编码类型 | 示例一 | 示例二 |
明文 | 01234567890 | administrators |
Base16 | 3031323334353637383930 |
61646D696E6973747261746F7273 |
Base32 | GAYTEMZUGU3DOOBZGA====== |
MFSG22LONFZXI4TBORXXE4Y= |
Base58 | cX8j8pvGzppMKVb |
BNF5dFLUTN5XwM1yLoF |
Base64 | MDEyMzQ1Njc4OTA= |
YWRtaW5pc3RyYXRvcnM= |
Base85 | 0JP==1c70M3&rY |
@:X4hDJ=06Eaa'.EcV |
Base100 |
|
|
Base64 原理:将要编码的内容按3字节为一组进行分组,最后一组不够3位的则补0(显然最多补两个0)。每组中每字节最高2位改成0不使用,原先各位的内容保持原有顺序往后移;最后在上一步中补了几个0就加几个等号以供解码时按等号个数删除0(经此操作原先3节字就变成了只使用低6位的4字节)
用途:一是SMTP中要以BASE64形式传输二进制文件,二是常用于将二进制数据转成可打印的ASCII码字符进行存储(下文各加密算法的密钥一般使用十六进制字符串形式存储,但也有以base64形式存储)。
其他:本质上讲Base64只能算是一种编码不能算是一种加密算法,PyCryptodome库也不支持。但从”Base64让人一下看不懂原本内容是什么“的角度讲你也不能说他完全不算加密,平时也经常用,我们就顺道讲一讲如何实现。
import base64
test_str = "12345abcd"print(f"Base64 编码 ---> {base64.b64encode(b'12345abcd')}")
print(f"Base64 解码 ---> {base64.b64decode(b'MTIzNDVhYmNk')}")
Unicode 又称为统一码、万国码、单一码,是一种在计算机上使用的字符编码。Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。其主要特征如下:
以 \u
、 或
开头,后面是数字加字母组合
PS:\u
开头和 开头是一样的,都是 16 进制 Unicode 字符的不同写法,
则是 Unicode 字符 10 进制的写法,此外,
和
开头的,也称为 HTML 字符实体转换,字符实体是用一个编号写入 HTML 代码中来代替一个字符,在 HTML 中,某些字符是预留的,如果希望正确地显示预留字符,就必须在 HTML 源代码中使用字符实体。
示例:
编码类型 | 示例一 | 示例二 |
明文 | 12345 | admin |
Unicode | \u0031\u0032\u0033\u0034\u0035 |
\u0061\u0064\u006d\u0069\u006e |
Escape 编码又叫 %u 编码,Escape 编码就是字符对应 UTF-16BE 表示方式前面加 %u,Escape 不会对 ASCII 字母和数字进行编码,也不会对下面这些 ASCII 标点符号进行编码: * @ - _ + . /
,其他所有的字符都会被转义序列替换。其主要特征如下:
以 %u
开头,后面是数字加字母组合
示例:
编码类型 | 示例一 | 示例二 |
明文 | K 哥爬虫 | 我爱 Python |
Escape | K%u54E5%u722C%u866B |
%u6211%u7231Python |
URL 和 Hex 编码的结果是一样的,不同的是当你用 URL 编码网址时是不会把 http
、https
关键字和 /
、?
、&
、=
等连接符进行编码的,而 Hex 编码则全部转化了,其主要特征如下:
以 %
开头,后面是数字加字母组合
编码类型 | 示例 |
明文 | https://www.kuaidaili.com/ |
Unicode | https://%77%77%77%2E%6B%75%61%69%64%61%69%6C%69%2E%63%6F%6D/ |
Hex | %68%74%74%70%73%3a%2f%2f%77%77%77%2e%6b%75%61%69%64%61%69%6c%69%2e%63%6f%6d%2f |
单向加密是指只能对明文数据进行加密,而不能解密数据。
例子:每个人都有不同的指纹,看到这个人,可以得出他的指纹等信息,并且唯一对应,但你只看一个指纹,是不可能看到或读到这个人的长相或身份等信息。常用的算法实现:
- MD5:128bits
- SHA:SHA1(160bits), SHA224, SHA256, SHA384
特点:
- - 不可逆:无法根据数据指纹/特征码还原原来的数据。
- - 容易计算:从原数据计算出MD5值很容易。
- - 抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。
- - 定长输出:任意长度的数据,算出的MD5值长度都是固定的。
一般来说标准库就挺好用时我们一般就直接用标准库,python的标准库就能容易地实现单向加密算法,所以单向加密我们使用标准库实现。
python中hash类算法使用hashlib库实现,hmac类算法使用hmac库实现。
import hmac
import hashlib# 注意输出结果中各算法使用大写和小写写了两遍
# 同时也是说到后边的书写中算法名大写或小写都是可以的
# hashlib.algorithms_available # 标准库支持的单向加密算法
# 实例化方法一,直接使用算法名;这种形式不完全支持hashlib.algorithms_available中的算法
# m = hashlib.md5()
# 实例化方法二,使用new方法;所有支持的算法名看上边hashlib.algorithms_available
m = hashlib.new("md5") # 使用hashlib实现hash算法(以md5为例)
# update内是要加密的内容
# update使用+=,即连续多次update表示在原先内容上追加而不是替换
m.update(b"123456")
# 以十六进制字符串形式输出
m.hexdigest()# 实例化原形是hmac.new(key, msg=None, digestmod=None)
# key--加密用的盐值
# msg--要加密码的内容,可为空而后用update方法设置
# digestmod--加密算法,默认为md5,所有支持的算法名看上边hashlib.algorithms_available
m = hmac.new(b"123", digestmod="sha1") # 使用hmac实现hmac算法(以sha1为例)
# update使用+=,即连续多次update表示在原先内容上追加而不是替换
m.update(b"123456")
# 以十六进制字符串形式输出
m.hexdigest()
MD5 实质是一种消息摘要算法,一个数据的 MD5 值是唯一的,同一个数据不可能计算出多个不同的 MD5 值,但是,不同数据计算出来的 MD5 值是有可能一样的,知道一个 MD5 值,理论上是无法还原出它的原始数据的,MD5 是最容易辨别的,主要特征如下:
密文一般为 16 位或者 32 位,其中 16 位是取的 32 位第 9~25 位的值;
组成方式为字母(a-f)和数字(0-9)混合,字母可以全部是大写或者小写。
除了通过密文特征来判断以外,我们还可以搜索源代码,标准 MD5 的源码里是包含了一些特定的值的,没有这些特定值,就无法实现 MD5:
0123456789ABCDEF
、0123456789abcdef
1732584193
、-271733879
、-1732584194
、271733878
PS:某些特殊情况下,密文的长度也有可能不止 16 位或者 32 位,有可能是在官方算法上有一些魔改,通常也是在 16 位的基础上,左右填充了一些随机字符串。
示例:
编码类型 | 示例一 | 示例二 |
明文 | 123456 | admin |
MD5 (16 位小写) |
49ba59abbe56e057 |
7a57a5a743894a0e |
MD5 (16 位大写) |
49BA59ABBE56E057 |
7A57A5A743894A0E |
MD5 (32 位小写) |
e10adc3949ba59abbe56e057f20f883e |
21232f297a57a5a743894a0e4a801fc3 |
MD5 (32 位大写) |
E10ADC3949BA59ABBE56E057F20F883E |
21232F297A57A5A743894A0E4A801FC3 |
SHA 是比 MD5 更安全一点的摘要算法,SHA 通常指 SHA 家族算法,分别是 SHA-1、SHA-2、SHA-3,其中 SHA-2 是 SHA-224、SHA-256、SHA-384、SHA-512 的并称,SHA-3 是 SHA3-224、SHA3-256、SHA3-384、SHA3-512、SHAKE128、SHAKE256 的并称,其名字的后缀的数字就代表了结果的大小(bit),注意,SHAKE 算法结果的大小并不是固定的,其他算法特征如下:
SHA-1:字母(a-f)和数字(0-9)混合,固定位数 40 位;
SHA-224/SHA3-224:字母(a-f)和数字(0-9)混合,固定位数 56 位;
SHA-256/SHA3-256:字母(a-f)和数字(0-9)混合,固定位数 64 位;
SHA-384/SHA3-384:字母(a-f)和数字(0-9)混合,固定位数 96 位;
SHA-512/SHA3-512:字母(a-f)和数字(0-9)混合,固定位数 128 位。
示例:
编码类型 | 示例 |
明文 | 123456 |
SHA-1 | 7c4a8d09ca3762af61e59520943dc26494f8941b |
SHA-256 | 8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92 |
SHA3-256 | c888c9ce9e098d5864d3ded6ebcc140a12142263bace3a23a36f9905f12bd64a |
HMAC 这种算法就是在 MD5、SHA 两种加密的基础上引入了秘钥,其密文也和 MD5、SHA 类似,密文的长度和使用的 MD5、SHA 算法对应密文的长度是一样的。特征如下:
HMAC-MD5:字母(a-f)和数字(0-9)混合,位数一般为 32 位;
HMAC-SHA-1:字母(a-f)和数字(0-9)混合,固定位数 40 位;
HMAC-SHA-224 / HMAC-SHA3-224:字母(a-f)和数字(0-9)混合,固定位数 56 位;
HMAC-SHA-256 / HMAC-SHA3-256:字母(a-f)和数字(0-9)混合,固定位数 64 位;
HMAC-SHA-384 / HMAC-SHA3-384:字母(a-f)和数字(0-9)混合,固定位数 96 位;
HMAC-SHA-512 / HMAC-SHA3-512:字母(a-f)和数字(0-9)混合,固定位数 128 位。
HMAC 和 SHA、MD5 的密文都很像,当无法确定是否为 HMAC 时,可以通过其名称搜索到加密方法,如果传入了密钥 key,说明就是 HMAC,当然你也可以直接当做是 SHA 或 MD5 来解,解密失败时就得考虑是否有密钥,是否为 HMAC 了,在 JS 中,通常一个 HMAC 加密方法是这样写的:
function HmacSHA1Encrypt(word, key) {
return CryptoJS.HmacSHA1(word, key).toString();
}
示例(密钥 123456abcde):
编码类型 | 示例 |
明文 | 123456 |
HMAC-MD5 | 432bb95bb00005ddce4a1c757488ed95 |
HMAC-SHA-1 | 37a04076b7736c44460d330ee0d00014428b175e |
HMAC-SHA-256 | 50cb1345366df11140fb91b43caaf69627e3f5529705ddf6b0d0cae67986e585 |
HMAC-SHA3-256 | b808ed9f66436e89fba527a01d1d6044318fea8599d9f39bfb6bec4843964bf3 |
对称加密是指数据加密与解密使用相同的密钥。
常用的算法实现:
- DES:Data Encryption Standard,秘钥长度为56位,2003年左右被破解--秘钥可以暴力破解。DES算法为密码体制中的对称密码体制,又被称为美国数据加密标准。
DES是一个分组加密算法,典型的DES以64位为分组对数据加密,加密和解密用的是同一个算法。
DES算法的入口参数有三个:Key、Data、Mode。其中Key为8个字节共64位,是DES算法的工作密钥;Data为8个字节64位,是要被加密或被解密的数据;Mode为DES的工作方式,有两种:加密或解密。
密钥长64位,密钥事实上是56位参与DES运算(第8、16、24、32、40、48、56、64位是校验位,使得每个密钥都有奇数个1),对64位二进制数据块进行加密,分组后的明文组和56位的密钥按位替代或交换的方法形成密文组。每次加密对64位的输入数据进行16轮编码,经过一系列替换和移位后转换成完全不同的64位输出数据。- 3DES:DES的改进版本。3DES(或称为Triple DES)是三重数据加密算法(TDEA,Triple Data Encryption Algorithm)块密码的通称。它相当于是对每个数据块应用三次DES加密算法。
由于计算机运算能力的增强,原版DES密码的密钥长度变得容易被暴力破解。3DES即是设计用来提供一种相对简单的方法,即通过增加DES的密钥长度来避免类似的攻击,而不是设计一种全新的块密码算法。
3DES(即Triple DES)是DES向AES过渡的加密算法(1999年,NIST将3-DES指定为过渡的加密标准),加密算法,其具体实现如下:设Ek()和Dk()代表DES算法的加密和解密过程,K代表DES算法使用的密钥,M代表明文,C代表密文,这样:
3DES加密过程为:C=Ek3(Dk2(Ek1(M)))
3DES解密过程为:M=Dk1(EK2(Dk3(C)))- AES:Advanced Encryption Standard,支持的秘钥长度包括 128bits,192bits,258bits,384bits,512bits。
**高级加密标准**(英语:**Advanced Encryption Standard**,缩写:**AES**),在密码学中又称**Rijndael加密法**,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院(NIST)于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准。2006年,高级加密标准已然成为对称密钥加密中最流行的算法之一。
AES为分组密码,分组密码也就是把明文分成一组一组的,每组长度相等,每次加密一组数据,直到加密完整个明文。在AES标准规范中,分组长度只能是128位,也就是说,每个分组为16个字节(每个字节8位)。密钥的长度可以使用128位、192位或256位。密钥的长度不同,推荐加密轮数也不同。
**一般常用的是128位**
需要说明的是,秘钥长度越长,数据加密与解密的时间就越久。
特点:
- - 加密与解密使用的密钥相同。
- - 但是由于算法一般都是公开的,因此机密性几乎完全依赖于密钥。
- - 通常使用的是相对较小的密钥,一般小于256bit。因为密钥越大,加密越强,但加密与解密的过程越慢。
AES、DES、3DES、RC4、Rabbit 等加密算法的密文通常没有固定的长度,他们通常使用 crypto-js 来实现,比如 AES 加解密示例如下:
CryptoJS = require("crypto-js")
var key = CryptoJS.enc.Utf8.parse("0123456789abcdef");
var iv = CryptoJS.enc.Utf8.parse("0123456789abcdef");function AESEncrypt(word) {
var srcs = CryptoJS.enc.Utf8.parse(word);
var encrypted = CryptoJS.AES.encrypt(srcs, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return encrypted.toString();
}function AESDecrypt(word) {
var srcs = word;
var decrypt = CryptoJS.AES.decrypt(srcs, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return decrypt.toString(CryptoJS.enc.Utf8);
}console.log(AESEncrypt("K哥爬虫"))
console.log(AESDecrypt("nSk3wCd92s08sQ9N+VHNvA=="))
在 crypto-js 中,也有一些特定的关键字,我们可以通过搜索这些关键字来快速定位到 crypto-js:
CryptoJS
、crypto-js
、iv
、mode
、padding
、createEncryptor
、createDecryptor
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=
、0xffffffff
、0xffff
定位到 CryptoJS 后,观察加密方法,比如 AES 就是 CryptoJS.AES.encrypt
,DES 就是 CryptoJS.DES.encrypt
,3DES 就是 CryptoJS.TripleDES.encrypt
,注意他的 iv、mode、padding,拿下来就可以本地复现了。
示例:对称加密代码如下:
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.Random import get_random_bytes# 要加密的内容
data = b"123456"
# 随机生成16字节(即128位)的加密密钥
key = get_random_bytes(16)
# 实例化加密套件,使用CBC模式
cipher = AES.new(key, AES.MODE_CBC)
# 对内容进行加密,pad函数用于分组和填充
encrypted_data = cipher.encrypt(pad(data, AES.block_size))# 将加密内容写入文件
file_out = open("encrypted.bin", "wb")
# 在文件中依次写入key、iv和密文encrypted_data
[file_out.write(x) for x in (key, cipher.iv, encrypted_data)]
对应解密代码如下:
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad# 从前边文件中读取出加密的内容
file_in = open("encrypted.bin", "rb")
# 依次读取key、iv和密文encrypted_data,16等是各变量长度,最后的-1则表示读取到文件末尾
key, iv, encrypted_data = [file_in.read(x) for x in (16, AES.block_size, -1)]# 实例化加密套件
cipher = AES.new(key, AES.MODE_CBC, iv)
# 解密,如无意外data值为最先加密的b"123456"
data = unpad(cipher.decrypt(encrypted_data), AES.block_size)
指的是加密和解密使用不同的秘钥。
一把作为公开的公钥,另一把作为私钥。这对密钥中的公钥进行加密,私钥用于解密。反之亦然(被私钥加密的数据也可以被公钥解密) 。
在实际使用中私钥一般保存在发布者手中,是私有的不对外公开的,只将公钥对外公布,就能实现只有私钥的持有者才能将数据解密的方法。 这种加密方式安全系数很高,因为它不用将解密的密钥进行传递,从而没有密钥在传递过程中被截获的风险,而破解密文几乎又是不可能的。
但是算法的效率低,所以常用于很重要数据的加密,常和对称配合使用,使用非对称加密的密钥去加密对称加密的密钥。
事实上,**公钥加密算法很少用于数据加密,它通常只是用来做身份认证**,因为它的密钥太长,加密速度太慢--公钥加密算法的速度甚至比对称加密算法的速度慢上3个数量级(1000倍)。主要作用:通常用于保证身份验证。
常用的公钥加密算法有:
- RSA:可以实现数字签名 和 数据加密。RSA公钥加密算法是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。RSA就是他们三人姓氏开头字母拼在一起组成的。 RSA是目前最有影响力的公钥加密算法,它能够抵抗到目前为止已知的绝大多数密码攻击,RSA算法基于一个十分简单的数论事实:将两个大质数相乘十分容易,但是想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。
- DSA:只能实现数字签名,不能实现数据加密
特点:
- - 加密与解密使用的不同的密钥。
- - 实际上它所使用的密钥是一对儿,一个叫公钥,一个叫私钥。这对密钥不是独立的,公钥是从私钥中提炼出来,因此私钥是很长的,968位、1024位、2048位、4096位的都有。
- - 通常公钥是公开的,所有人都可以得到;私钥是不能公开的,只有自己才有。
- - 用公钥机密的内容只能用与之对应的私钥才能解密,反之亦然。
RSA 加密算法是一种非对称加密算法,通过公钥加密结果,必须私钥解密。同样私钥加密结果,公钥可以解密,应用非常广泛,在网站中通常使用 JSEncrypt 库来实现,其最大的特征就是有一个设置公钥的过程,我们可以通过以下方法来快速初步判断是否为 RSA 算法:
搜索关键词 new JSEncrypt()
,JSEncrypt
等,一般会使用 JSEncrypt 库,会有 new 一个实例对象的操作;
搜索关键词 setPublicKey
、setKey
、setPrivateKey
、getPublicKey
等,一般实现的代码里都含有设置密钥的过程。
RSA 的私钥、公钥、明文、密文长度也有一定对应关系,也可以从这方面初步判断:
私钥长度 (Base64) |
公钥长度 (Base64) |
明文长度 | 密文长度 |
428 | 128 | 1~53 | 88 |
812 | 216 | 1~117 | 172 |
1588 | 392 | 1~245 | 344 |
非对称加密算法实现代码(以RSA为例)
生成密钥对代码如下:
from Crypto.PublicKey import RSA# 生成密钥对
key = RSA.generate(2048)# 提取私钥并存入文件
private_key = key.export_key()
file_out = open("private_key.pem", "wb")
file_out.write(private_key)# 提取公钥存入文件
public_key = key.publickey().export_key()
file_out = open("public_key.pem", "wb")
file_out.write(public_key)
加密代码如下:
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP# 要加密的内容
data = b"123456"
# 从文件中读取公钥
public_key = RSA.import_key(open("public_key.pem").read())
# 实例化加密套件
cipher = PKCS1_OAEP.new(public_key)
# 加密
encrypted_data = cipher.encrypt(data)# 将加密后的内容写入到文件
file_out = open("encrypted_data.bin", "wb")
file_out.write(encrypted_data)
解密代码如下:
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP# 从私钥文件中读取私钥
private_key = RSA.import_key(open("private_key.pem", "rb").read())
# 实例化加密套件
cipher = PKCS1_OAEP.new(private_key)
# 从文件中读取加密内容
encrypted_data = open("encrypted_data.bin", "rb").read()
# 解密,如无意外data值为最先加密的b"123456"
data = cipher.decrypt(encrypted_data)
非对称加密算法签名实现(以RSA为例)
我一直以为私钥加密公钥解密和公钥加密私钥解密没什么两样,但首先一是和一个朋友说用私钥加密发送回来时她疑或说私钥可以加密吗,然后回公司又和领导说私钥加密公钥解密的时候他直接说私钥不能加密只能做签名。
首先说私钥加密公钥解密在数学原理上是可行的,而且所谓的数字签名其本质就是我用你的公钥可以解开这加密的内容说明这些内容就是你的,即数字签名和签名认证本质就是私钥加密公钥解密。
但另一方面,因为公钥是公开的所以私钥加密并不能起加密通信的作用,所以一般没有直接的私钥加密公钥解密的操作----但我不太明白为什么PyCryptodome这些库在程序试图使用私钥加密时直接报错拒绝执行(TypeError: This is not a private key),虽然没什么用,私钥加密也没有什么危害吧?
我们直接使用5.2中的公私钥,另外因为签名要传给签名校验的内容比较多,所以就两部分不分开写了,代码如下:
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA# 以下是签名部分
# 要签名的内容
data = b'123456'
# 获取要签名的内容的HASH值。摘要算法是什么不重要,只要验证时使用一样的摘要算法即可
digest = SHA256.new(data)
# 读取私钥
private_key = RSA.import_key(open('private_key.pem').read())
# 对HASH值使用私钥进行签名。所谓签名,本质就是使用私钥对HASH值进行加密
signature = pkcs1_15.new(private_key).sign(digest)# 以下是签名校验部分
# 签名部分要传给签名校验部分三个信息:签名内容原文、摘要算法、HASH值签名结果
# 获取被签名的内容的HASH值。使用与签名部分一样的摘要算法计算
digest = SHA256.new(data)
# 读取公钥
public_key = RSA.import_key(open('public_key.pem').read())try:
# 进行签名校验。本质上就是使用公钥解密signature,看解密出来的值是否与digest相等
# 相等则校验通过,说明确实data确实原先的内容;不等则校验不通过,data或signature被篡改
# 可能有人会想,如果我先修改data然后再用自己的私钥算出signature,是不是可以完成欺骗?
# 答案是不能,因为此时使用原先的公钥去解signature,其结果不会等于digest
pkcs1_15.new(public_key).verify(digest, signature)
print(f"The signature is valid.")
except (ValueError, TypeError):
print("The signature is not valid.")
流加密算法说明。别称:流加密算法,又称序列加密算法,英文名Stream cipher。
原理:流加密算法加密和解密也使用同一个密钥,所以从咬文嚼字来说他也属于对称加密算法。流加密算法与前边单向加密算法、对称加密算法、非对称加密算法的区别是前三者都是分组加密算法即一个分组使用同一个密钥,而流加密算法每一位都使用不同的密钥。
用途:流加密主要基于异或(xor)操作,运算相对简单,但安全性较低,没有很多的使用场景,最典型的是WEP上的使用但也正由于其安全性问题导致WEP的淘汰。
流加密算法实现代码(以RC4为例)
from Crypto.Cipher import ARC4
from Crypto.Hash import SHA
from Crypto.Random import get_random_bytes# 要加密的内容
data = b"123456"
# 流加密密码长度是可变的,RC4为40到2048位
# 一般上使用一个字符串作为初始密钥,然后再用sha1等生成真正的密钥
# 我们这是直接点,随机生成16字节(即128位)作为密钥
key = get_random_bytes(16)
# 实例化加密套件
cipher = ARC4.new(key)
# 加密内容
encrypted_data = cipher.encrypt(data)# 注意在即便加解密像这里一样在同一文件里,解密时一定要重新实例化不然解密不正确
cipher = ARC4.new(key)
# 解密,如无意外data为前边加密的b"123456"
data = cipher.decrypt(encrypted_data)
[1]
snowland-smx-python: https://gitee.com/snowlandltd/snowland-smx-python[2]
gmssl: https://github.com/duanhongyi/gmssl[3]
gmssl-python: https://github.com/gongxian-ding/gmssl-python[4]
sm-crypto: https://www.npmjs.com/package/sm-crypto[5]
国家密码管理局: https://www.sca.gov.cn/[6]
密码标准委员会: http://www.gmbz.org.cn/[7]
开源国密算法工具箱: http://gmssl.org/[8]
国密算法源代码下载: http://www.scctc.org.cn/templates/Download/index.aspx?nodeid=71[9]
GM/T 密码行业标准查询: http://www.gmbz.org.cn/main/bzlb.html
SM 代表商密,即商业密码,是我国发布的一系列国产加密算法,SM 系列包括:SM1、SM2、SM3 、SM4、SM7、SM9,其中 SM1 和 SM7 的算法不公开,SM 系列算法在我国一些 gov 网站上有应用,有关国产加密算法 K 哥前期文章有介绍:《爬虫逆向基础,认识 SM1-SM9、ZUC 国密算法》,本文不再赘述。
在 SM 的 JavaScript 代码中一般会存在以下关键字,可以通过搜索关键字定位:
SM2
、SM3
、SM4
FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF
FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC
28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
getPublicKeyFromPrivateKey
、doEncrypt
、doDecrypt
、doSignature
国家密码管理局就发布了一系列国产加密算法,这其中就包括 SM1、SM2、SM3 、SM4、SM7、SM9、ZUC(祖冲之加密算法)等,SM 代表商密,即商业密码,是指用于商业的、不涉及国家秘密的密码技术。SM1 和 SM7 的算法不公开,其余算法都已成为 ISO/IEC 国际标准
在这些国产加密算法中,SM2、SM3、SM4 三种加密算法是比较常见的,在爬取部分网站时,也可能会遇到这些算法,所以作为爬虫工程师是有必要了解一下这些算法的,如下图所示某网站就使用了 SM2 和 SM4 加密算法:
算法概述
算法名称 | 算法类别 | 应用领域 | 特点 |
SM1 | 对称(分组)加密算法 | 芯片 | 分组长度、密钥长度均为 128 比特 |
SM2 | 非对称(基于椭圆曲线 ECC)加密算法 | 数据加密 | ECC 椭圆曲线密码机制 256 位,相比 RSA 处理速度快,消耗更少 |
SM3 | 散列(hash)函数算法 | 完整性校验 | 安全性及效率与 SHA-256 相当,压缩函数更复杂 |
SM4 | 对称(分组)加密算法 | 数据加密和局域网产品 | 分组长度、密钥长度均为 128 比特,计算轮数多 |
SM7 | 对称(分组)加密算法 | 非接触式 IC 卡 | 分组长度、密钥长度均为 128 比特 |
SM9 | 标识加密算法(IBE) | 端对端离线安全通讯 | 加密强度等同于 3072 位密钥的 RSA 加密算法 |
ZUC | 对称(序列)加密算法 | 移动通信 4G 网络 | 流密码 |
算法详解
分组加密算法
SM1 为分组加密算法,对称加密,分组长度和密钥长度都为 128 位,故对消息进行加解密时,若消息长度过长,需要进行分组,要消息长度不足,则要进行填充。算法安全保密强度及相关软硬件实现性能与 AES 相当,该算法不公开,仅以 IP 核的形式存在于芯片中,调用该算法时,需要通过加密芯片的接口进行调用,采用该算法已经研制了系列芯片、智能 IC 卡、智能密码钥匙、加密卡、加密机等安全产品,广泛应用于电子政务、电子商务及国民经济的各个应用领域(包括国家政务通、警务通等重要领域),一般了解的人比较少,爬虫工程师也不会遇到这种加密算法。
SM2 椭圆曲线公钥加密算法
SM2 为椭圆曲线(ECC)公钥加密算法,非对称加密,SM2 算法和 RSA 算法都是公钥加密算法,SM2 算法是一种更先进安全的算法,在我们国家商用密码体系中被用来替换 RSA 算法,在不少官方网站会见到此类加密算法。我国学者对椭圆曲线密码的研究从 20 世纪 80 年代开始,目前已取得不少成果,SM2 椭圆曲线公钥密码算法比 RSA 算法有以下优势:
SM2 | RSA | |
安全性 | 256 位 SM2 强度已超过 RSA-2048 | 一般 |
算法结构 | 基本椭圆曲线(ECC) | 基于特殊的可逆模幂运算 |
计算复杂度 | 完全指数级 | 亚指数级 |
存储空间(密钥长度) | 192-256 bit | 2048-4096 bit |
秘钥生成速度 | 较 RSA 算法快百倍以上 | 慢 |
解密加密速度 | 较快 | 一般 |
SM3 杂凑算法
SM3 为密码杂凑算法,采用密码散列(hash)函数标准,用于替代 MD5/SHA-1/SHA-2 等国际算法,是在 SHA-256 基础上改进实现的一种算法,消息分组长度为 512 位,摘要值长度为 256 位,其中使用了异或、模、模加、移位、与、或、非运算,由填充、迭代过程、消息扩展和压缩函数所构成。在商用密码体系中,SM3 主要用于数字签名及验证、消息认证码生成及验证、随机数生成等。据国家密码管理局表示,其安全性及效率要高于 MD5 算法和 SHA-1 算法,与 SHA-256 相当。
SM4 分组加密算法
SM4 为无线局域网标准的分组加密算法,对称加密,用于替代 DES/AES 等国际算法,SM4 算法与 AES 算法具有相同的密钥长度和分组长度,均为 128 位,故对消息进行加解密时,若消息长度过长,需要进行分组,要消息长度不足,则要进行填充。加密算法与密钥扩展算法都采用 32 轮非线性迭代结构,解密算法与加密算法的结构相同,只是轮密钥的使用顺序相反,解密轮密钥是加密轮密钥的逆序。
SM4 | DES | AES | |
计算轮数 | 32 | 16(3DES 为 16*3) | 10/12/14 |
密码部件 | S 盒、非线性变换、线性变换、合成变换 | 标准算术和逻辑运算、先替换后置换,不含线性变换 | S 盒、行移位变换、列混合变换、圈密钥加变换(AddRoundKey) |
SM7 分组加密算法
SM7 为分组加密算法,对称加密,该算法不公开,应用包括身份识别类应用(非接触式 IC 卡、门禁卡、工作证、参赛证等),票务类应用(大型赛事门票、展会门票等),支付与通卡类应用(积分消费卡、校园一卡通、企业一卡通等)。爬虫工程师基本上不会遇到此类算法。
SM9 标识加密算法
SM9 为标识加密算法(Identity-Based Cryptography),非对称加密,标识加密将用户的标识(如微信号、邮件地址、手机号码、QQ 号等)作为公钥,省略了交换数字证书和公钥过程,使得安全系统变得易于部署和管理,适用于互联网应用的各种新兴应用的安全保障,如基于云技术的密码服务、电子邮件安全、智能终端保护、物联网安全、云存储安全等等。这些安全应用可采用手机号码或邮件地址作为公钥,实现数据加密、身份认证、通话加密、通道加密等。在商用密码体系中,SM9 主要用于用户的身份认证,据新华网公开报道,SM9 的加密强度等同于 3072 位密钥的 RSA 加密算法。
ZUC 祖冲之算法
ZUC 为流密码算法,对称加密,该机密性算法可适用于 3GPP LTE 通信中的加密和解密,该算法包括祖冲之算法(ZUC)、机密性算法(128-EEA3)和完整性算法(128-EIA3)三个部分。已经被国际组织 3GPP 推荐为 4G 无线通信的第三套国际加密和完整性标准的候选算法。
编程语言实现
Python 语言实现
在 Python 里面并没有比较官方的库来实现国密算法,这里仅列出了其中两个较为完善的第三方库,需要注意的是,SM1 和 SM7 算法不公开,目前大多库仅实现了 SM2、SM3、SM4 三种密算法。
其中 gmssl-python 是 gmssl 的改进版,gmssl-python 新增支持了 SM9 算法,不过截止本文编写时,gmssl-python 并未发布 pypi,也未 PR 到 gmssl,使用 pip install gmssl
安装的 gmssl 不支持 SM9 算法。若要使用 SM9 算法,可下载 gmssl-python 源码手动安装。
以 gmssl 的 SM2 算法为例,实现如下(其他算法和详细用法可参考其官方文档):
SM2 加密(encrypt)和解密(decrypt):
from gmssl import sm2
# 16 进制的公钥和私钥
private_key = '00B9AB0B828FF68872F21A837FC303668428DEA11DCD1B24429D0C99E24EED83D5'
public_key = 'B9C9A6E04E9C91F7BA880429273747D7EF5DDEB0BB2FF6317EB00BEF331A83081A6994B8993F3F5D6EADDDB81872266C87C018FB4162F5AF347B483E24620207'
sm2_crypt = sm2.CryptSM2(public_key=public_key, private_key=private_key)
# 待加密数据和加密后数据为 bytes 类型
data = b"this is the data to be encrypted"
enc_data = sm2_crypt.encrypt(data)
dec_data = sm2_crypt.decrypt(enc_data)
print('enc_data: ', enc_data.hex())
print('dec_data: ', dec_data)
# enc_data: 3cb96dd2e0b6c24df8e22a5da3951d061a6ee6ce99f46a446426feca83e501073288b1553ca8d91fad79054e26696a27c982492466dafb5ed06a573fb09947f2aed8dfae243b095ab88115c584bb6f0814efe2f338a00de42b244c99698e81c7913c1d82b7609557677a36681dd10b646229350ad0261b51ca5ed6030d660947
# dec_data: b'this is the data to be encrypted'
SM2 签名(sign)和校验(verify):
from gmssl import sm2, func
# 16 进制的公钥和私钥
private_key = '00B9AB0B828FF68872F21A837FC303668428DEA11DCD1B24429D0C99E24EED83D5'
public_key = 'B9C9A6E04E9C91F7BA880429273747D7EF5DDEB0BB2FF6317EB00BEF331A83081A6994B8993F3F5D6EADDDB81872266C87C018FB4162F5AF347B483E24620207'
sm2_crypt = sm2.CryptSM2(public_key=public_key, private_key=private_key)
# 待签名数据为 bytes 类型
data = b"this is the data to be signed"
random_hex_str = func.random_hex(sm2_crypt.para_len)
# 16 进制
sign = sm2_crypt.sign(data, random_hex_str)
verify = sm2_crypt.verify(sign, data)
print('sign: ', sign)
print('verify: ', verify)
# sign: 45cfe5306b1a87cf5d0034ef6712babdd1d98547e75bcf89a17f3bcb617150a3f111ab05597601bab8c41e2b980754b74ebe9a169a59db37d549569910ae273a
# verify: True
JavaScript 语言实现
在 JavaScript 中已有比较成熟的实现库,这里推荐 sm-crypto[4],目前支持 SM2、SM3 和 SM4,需要注意的是,SM2 非对称加密的结果由 C1、C2、C3 三部分组成,其中 C1 是生成随机数的计算出的椭圆曲线点,C2 是密文数据,C3 是 SM3 的摘要值,最开始的国密标准的结果是按 C1C2C3 顺序的,新标准的是按 C1C3C2 顺序存放的,sm-crypto 支持设置 cipherMode,也就是 C1C2C3 的排列顺序。
以 SM2 算法为例,实现如下(其他算法和详细用法可参考其官方文档):
SM2 加密(encrypt)和解密(decrypt):
const sm2 = require('sm-crypto').sm2
// 1 - C1C3C2,0 - C1C2C3,默认为1
const cipherMode = 1
// 获取密钥对
let keypair = sm2.generateKeyPairHex()
let publicKey = keypair.publicKey // 公钥
let privateKey = keypair.privateKey // 私钥
let msgString = "this is the data to be encrypted"
let encryptData = sm2.doEncrypt(msgString, publicKey, cipherMode) // 加密结果
let decryptData = sm2.doDecrypt(encryptData, privateKey, cipherMode) // 解密结果
console.log("encryptData: ", encryptData)
console.log("decryptData: ", decryptData)
// encryptData: ddf261103fae06d0efe20ea0fe0d82bcc170e8efd8eeae24e9559b3835993f0ed2acb8ba6782fc21941ee74ca453d77664a5cb7dbb91517e6a3b0c27db7ce587ae7af54f8df48d7fa822b7062e2af66c112aa57de94d12ba28e5ba96bf4439d299b41da4a5282d054696adc64156d248049d1eb1d0af28d76b542fe8a95d427e
// decryptData: this is the data to be encrypted
SM2 签名(sign)和校验(verify):
const sm2 = require('sm-crypto').sm2
// 获取密钥对
let keypair = sm2.generateKeyPairHex()
let publicKey = keypair.publicKey // 公钥
let privateKey = keypair.privateKey // 私钥
// 纯签名 + 生成椭圆曲线点
let msgString = "this is the data to be signed"
let sigValueHex = sm2.doSignature(msgString, privateKey) // 签名
let verifyResult = sm2.doVerifySignature(msgString, sigValueHex, publicKey) // 验签结果
console.log("sigValueHex: ", sigValueHex)
console.log("verifyResult: ", verifyResult)
// sigValueHex: 924cbb9f2b5adb554ef77129ff1e3a00b2da42017ad3ec2f806d824a77646987ba8c8c4fb94576c38bc11ae69cc98ebbb40b5d47715171ec7dcea913dfc6ccc1
// verifyResult: true
Obfuscator 就是混淆的意思,简称 OB 混淆,实战可参考 K 哥以前的文章:【JS 逆向百例】W 店 UA,OB 反混淆,抓包替换 CORS 跨域错误分析,OB 混淆具有以下特征:
一般由一个大数组或者含有大数组的函数、一个自执行函数、解密函数和加密后的函数四部分组成;
函数名和变量名通常以 _0x
或者 0x
开头,后接 1~6 位数字或字母组合;
自执行函数,进行移位操作,有明显的 push、shift 关键字;
一段正常的代码如下:
function hi() {
console.log("Hello World!");
}
hi();
经过 OB 混淆后的结果:
function _0x3f26() {
var _0x2dad75 = ['5881925kTCKCP', 'Hello\x20World!', '600mDvfGa', '699564jYNxbu', '1083271cEvuvT', 'log', '18sKjcFY', '214857eMgFSU', '77856FUKcuE', '736425OzpdFI', '737172JqcGMg'];
_0x3f26 = function () {
return _0x2dad75;
};
return _0x3f26();
}(function (_0x307c88, _0x4f8223) {
var _0x32807d = _0x1fe9, _0x330c58 = _0x307c88();
while (!![]) {
try {
var _0x5d6354 = parseInt(_0x32807d(0x6f)) / 0x1 + parseInt(_0x32807d(0x6e)) / 0x2 + parseInt(_0x32807d(0x70)) / 0x3 + -parseInt(_0x32807d(0x69)) / 0x4 + parseInt(_0x32807d(0x71)) / 0x5 + parseInt(_0x32807d(0x6c)) / 0x6 * (parseInt(_0x32807d(0x6a)) / 0x7) + -parseInt(_0x32807d(0x73)) / 0x8 * (parseInt(_0x32807d(0x6d)) / 0x9);
if (_0x5d6354 === _0x4f8223) break; else _0x330c58['push'](_0x330c58['shift']());
} catch (_0x3f18e4) {
_0x330c58['push'](_0x330c58['shift']());
}
}
}(_0x3f26, 0xaa023));function _0x1fe9(_0xa907e7, _0x410a46) {
var _0x3f261f = _0x3f26();
return _0x1fe9 = function (_0x1fe950, _0x5a08da) {
_0x1fe950 = _0x1fe950 - 0x69;
var _0x82a06 = _0x3f261f[_0x1fe950];
return _0x82a06;
}, _0x1fe9(_0xa907e7, _0x410a46);
}function hi() {
var _0x12a222 = _0x1fe9;
console[_0x12a222(0x6b)](_0x12a222(0x72));
}hi();
JJEncode、AAEncode、JSFuck 都是同一个作者,实战可参考 K 哥以前的文章:【JS 逆向百例】网洛者反爬练习平台第二题:JJEncode 加密,JJEncode 具有以下特征:
大量 $
、_
符号、大量重复的自定义变量;
仅由 18 个符号组成:[]()!+,\"$.:;_{}~=
正常的一段 JS 代码:
alert("Hello, JavaScript" )
经过 JJEncode 混淆(自定义变量名为 $)之后的代码:
$=~[];$={___:++$,$$$$:(![]+"")[$],__$:++$,$_$_:(![]+"")[$],_$_:++$,$_$$:({}+"")[$],$$_$:($[$]+"")[$],_$$:++$,$$$_:(!""+"")[$],$__:++$,$_$:++$,$$__:({}+"")[$],$$_:++$,$$$:++$,$___:++$,$__$:++$};$.$_=($.$_=$+"")[$.$_$]+($._$=$.$_[$.__$])+($.$$=($.$+"")[$.__$])+((!$)+"")[$._$$]+($.__=$.$_[$.$$_])+($.$=(!""+"")[$.__$])+($._=(!""+"")[$._$_])+$.$_[$.$_$]+$.__+$._$+$.$;$.$$=$.$+(!""+"")[$._$$]+$.__+$._+$.$+$.$$;$.$=($.___)[$.$_][$.$_];$.$($.$($.$$+"\""+$.$_$_+(![]+"")[$._$_]+$.$$$_+"\\"+$.__$+$.$$_+$._$_+$.__+"(\\\"\\"+$.__$+$.__$+$.___+$.$$$_+(![]+"")[$._$_]+(![]+"")[$._$_]+$._$+",\\"+$.$__+$.___+"\\"+$.__$+$.__$+$._$_+$.$_$_+"\\"+$.__$+$.$$_+$.$$_+$.$_$_+"\\"+$.__$+$._$_+$._$$+$.$$__+"\\"+$.__$+$.$$_+$._$_+"\\"+$.__$+$.$_$+$.__$+"\\"+$.__$+$.$$_+$.___+$.__+"\\\"\\"+$.$__+$.___+")"+"\"")())();
JJEncode、AAEncode、JSFuck 都是同一个作者,实战可参考 K 哥以前的文章:【JS 逆向百例】网洛者反爬练习平台第三题:AAEncode 加密,AAEncode 具有以下特征:
仅由日式表情符号组成
正常的一段 JS 代码:
alert("Hello, JavaScript" )
经过 AAEncode 混淆之后的代码:
゚ω゚ノ= /`m´)ノ ~┻━┻ //*´∇`*/ ['_']; o=(゚ー゚) =_=3; c=(゚Θ゚) =(゚ー゚)-(゚ー゚); (゚Д゚) =(゚Θ゚)= (o^_^o)/ (o^_^o);(゚Д゚)={゚Θ゚: '_' ,゚ω゚ノ : ((゚ω゚ノ==3) +'_') [゚Θ゚] ,゚ー゚ノ :(゚ω゚ノ+ '_')[o^_^o -(゚Θ゚)] ,゚Д゚ノ:((゚ー゚==3) +'_')[゚ー゚] }; (゚Д゚) [゚Θ゚] =((゚ω゚ノ==3) +'_') [c^_^o];(゚Д゚) ['c'] = ((゚Д゚)+'_') [ (゚ー゚)+(゚ー゚)-(゚Θ゚) ];(゚Д゚) ['o'] = ((゚Д゚)+'_') [゚Θ゚];(゚o゚)=(゚Д゚) ['c']+(゚Д゚) ['o']+(゚ω゚ノ +'_')[゚Θ゚]+ ((゚ω゚ノ==3) +'_') [゚ー゚] + ((゚Д゚) +'_') [(゚ー゚)+(゚ー゚)]+ ((゚ー゚==3) +'_') [゚Θ゚]+((゚ー゚==3) +'_') [(゚ー゚) - (゚Θ゚)]+(゚Д゚) ['c']+((゚Д゚)+'_') [(゚ー゚)+(゚ー゚)]+ (゚Д゚) ['o']+((゚ー゚==3) +'_') [゚Θ゚];(゚Д゚) ['_'] =(o^_^o) [゚o゚] [゚o゚];(゚ε゚)=((゚ー゚==3) +'_') [゚Θ゚]+ (゚Д゚) .゚Д゚ノ+((゚Д゚)+'_') [(゚ー゚) + (゚ー゚)]+((゚ー゚==3) +'_') [o^_^o -゚Θ゚]+((゚ー゚==3) +'_') [゚Θ゚]+ (゚ω゚ノ +'_') [゚Θ゚]; (゚ー゚)+=(゚Θ゚); (゚Д゚)[゚ε゚]='\\'; (゚Д゚).゚Θ゚ノ=(゚Д゚+ ゚ー゚)[o^_^o -(゚Θ゚)];(o゚ー゚o)=(゚ω゚ノ +'_')[c^_^o];(゚Д゚) [゚o゚]='\"';(゚Д゚) ['_'] ( (゚Д゚) ['_'] (゚ε゚+(゚Д゚)[゚o゚]+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ ((゚ー゚) + (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚ー゚)+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚Θ゚)+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ ((゚ー゚) + (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ ((゚ー゚) + (o^_^o))+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚ー゚)+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚Θ゚)+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((o^_^o) +(o^_^o))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) - (゚Θ゚))+ (o^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (o^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚ー゚)+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (゚Θ゚)+ (゚Д゚)[゚o゚]) (゚Θ゚)) ('_');
JJEncode、AAEncode、JSFuck 都是同一个作者,实战可参考K哥以前的文章:【JS 逆向百例】网洛者反爬练习平台第四题:JSFuck 加密,JSFuck 具有以下特征:
仅由 6 个符号组成:[]()!+
正常的一段 JS 代码:
alert(1)
经过 JSFuck 混淆之后的代码类似于:
[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((!![]+[])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+!+[]]+(+[![]]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+(+(!+[]+!+[]+!+[]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[!+[]+!+[]])+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]])()((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[+!+[]+[!+[]+!+[]+!+[]]]+[+!+[]]+([+[]]+![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[!+[]+!+[]+[+[]]])
Jother 混淆和 JSFuck 有点儿类似,唯一的区别就是密文比 JSFuck 多了 {}
,其解密方式和 JSFuck 是一样的,Jother 混淆现在不太常见了,也很难找到在线混淆之类的工具了,原作者有个在线页面也关闭了,不过仍然可以了解一下,Jother 混淆具有以下特征:
仅由 8 个符号组成:[]()!+{}
正常的一段代码:
function anonymous() {
return location
}
经过 Jother 混淆之后的代码类似于:
[][(![]+[])[!![]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!![]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+({}[[]]+[])[+!![]]+(![]+[])[!![]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+({}[[]]+[])[+[]]+({}+[])[!![]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+(!![]+[])[!![]+!![]+!![]]+(!![]+[])[+[]]+({}[[]]+[])[+[]]+(!![]+[])[+!![]]+({}[[]]+[])[+!![]]+({}+[])[!![]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!![]+!![]]+({}+[])[+!![]]+({}+[])[!![]+!![]+!![]+!![]+!![]]+(![]+[])[+!![]]+(!![]+[])[+[]]+({}[[]]+[])[!![]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+({}[[]]+[])[+!![]])()
Brainfuck 实际上是一种极小化的计算机语言,又称为 BF 语言,该语言以其极简主义着称,仅包含八个简单的命令、一个数据指针和一个指令指针,这种语言在爬虫领域也可以是一种反爬手段,可以视为一种混淆方式,虽然不常见,这里给一个在线体验的网址:https://copy.sh/brainfuck/text.html ,感兴趣的同志可以深入研究一下,Brainfuck 具有以下特征:
仅由 <>+-.[]
组成,大量的 +-
符号。
正常的一段代码:
alert("Hello, Brainfuck")
经过 Brainfuck 混淆之后的代码类似于:
--[----->+<]>-----.+++++++++++.-------.+++++++++++++.++.+[--->+<]>+.------.++[->++<]>.-[->+++++<]>++.+++++++..+++.[->+++++<]>+.------------.+[->++<]>.---[----->+<]>-.+++[->+++<]>++.++++++++.+++++.--------.-[--->+<]>--.+[->+++<]>+.++++++++.+[++>---<]>.+++++++.
Ook! 和 Brainfuck 的原理都是类似的,只不过符号有差异,同样的,这种语言在爬虫领域也可以是一种反爬手段,可以视为一种混淆方式,虽然不常见,在线体验的网址:https://www.splitbrain.org/services/ook ,Ook! 具有以下特征:
完整 Ook!:仅由 3 种符号组成 Ook.
、Ook?
、Ook!
Short Ook!:仅由 3 种符号组成 .!?
正常的一段代码:
alert("Hello, Ook!")
经过 Ook! 混淆之后的代码类似于:
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook. Ook? Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook. Ook? Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook! Ook. Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook. Ook? Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook? Ook. Ook? Ook! Ook. Ook? Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook. Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook. Ook? Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook. Ook? Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook! Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook? Ook. Ook? Ook! Ook. Ook? Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook! Ook! Ook! Ook! Ook! Ook! Ook? Ook. Ook? Ook! Ook. Ook? Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook. Ook? Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook. Ook? Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook? Ook. Ook? Ook! Ook. Ook? Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook. Ook. Ook. Ook! Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook? Ook.
Trivial brainfuck substitution 不是一种单一的编程语言,而是一大类编程语言,成员超过 20 个,前面提到的 Brainfuck、Ook! 都是其中的一员,在爬虫领域中,说实话这种稀奇古怪的混淆其实并不常见,但是在一些 CTF 中有可能会出现,作为爬虫工程师也可以了解了解,具体可以参考:https://esolangs.org/wiki/Trivial_brainfuck_substitution
恺撒密码(Caesar cipher)又称为恺撒加密、恺撒变换、变换加密,它是一种替换加密的技术,明文中的所有字母都在字母表上向后(或向前)按照一个固定数目进行偏移后被替换成密文。例如,当偏移量是 3 的时候,所有的字母 A 将被替换成 D,B 变成 E,以此类推。这个加密方法是以罗马共和时期恺撒的名字命名的,当年恺撒曾用此方法与其将军们进行联系。
根据偏移量的不同,还存在若干特定的恺撒密码名称:偏移量为10:Avocat(A→K);偏移量为13:ROT13;偏移量为-5:Cassis (K 6);偏移量为-6:Cassette (K 7)
示例(偏移量 3):
•明文字母表:ABCDEFGHIJKLMNOPQRSTUVWXYZ•密文字母表:DEFGHIJKLMNOPQRSTUVWXYZABC
栅栏密码就是把要加密的明文分成 N 个一组,然后把每组的第 1 个字连起来,形成一段无规律的话。栅栏密码本身有一个潜规则,就是组成栅栏的字母一般不会太多,一般不超过 30 个。
示例:明文为 THE LONGEST DAY MUST HAVE AN END。加密时,把将要传递的信息中的字母交替排成上下两行:
T E O G S D Y U T A E N N
H L N E T A M S H V A E D
将下面一行字母排在上面一行的后边组合成密文:
TEOGSDYUTAENN HLNETAMSHVAED
栅栏密码还有一种变种,称为栅栏密码 W 型,它会先把明文类似 W 形状进行排列,然后再按栏目顺序 1-N,取每一栏的所有字符值,组成加密后密文,比如字符串 123456789,采用栏目数为 3 时,明文将采用如下排列:
1—5—9
-2-4-6-8-
–3—7–
取每一栏所有字符串,组成加密后密文:159246837
猪圈密码也称为朱高密码、共济会暗号、共济会密码或共济会员密码,是一种以格子为基础的简单替代式密码。只能对字母加解密并且符号无法复制,粘贴后会直接显示明文,即使使用符号,也不会影响密码分析,亦可用在其它替代式的方法。曾经是美国内战时盟军使用的密码,目前仅在密码教学、各种竞赛中使用。
摩斯密码(Morse code),又称为摩尔斯电码、摩斯电码,是一种时通时断的信号代码,这种信号代码通过不同的排列顺序来表达不同的英文字母、数字和标点符号等。
26个字母的摩斯密码表
字符 | 摩斯码 | 字符 | 摩斯码 | 字符 | 摩斯码 |
A | .━ |
B | ━ ... |
C | ━ .━ . |
D | ━ .. |
E | . |
F | ..━ . |
G | ━ ━ . |
H | .... |
I | .. |
J | .━ ━ ━ |
K | ━ .━ |
L | .━ .. |
M | ━ ━ |
N | ━ . |
O | ━ ━ ━ |
P | .━ ━ . |
Q | ━ ━ .━ |
R | .━ . |
S | ... |
T | ━ |
U | ..━ |
V | ...━ |
W | .━ ━ |
X | ━ ..━ |
Y | ━ .━ ━ |
Z | ━ ━ .. |
10个数字的摩斯密码表
字符 | 摩斯码 | 字符 | 摩斯码 | 字符 | 摩斯码 |
0 | ━ ━ ━ ━ ━ |
1 | .━ ━ ━ ━ |
2 | ..━ ━ ━ |
3 | ...━ ━ |
4 | ....━ |
5 | ..... |
6 | ━ .... |
7 | ━ ━ ... |
8 | ━ ━ ━ .. |
9 | ━ ━ ━ ━ . |
标点符号的摩斯密码表
字符 | 摩斯码 | 字符 | 摩斯码 | 字符 | 摩斯码 |
. | .━ .━ .━ |
: | ━ ━ ━ ... |
, | ━ ━ ..━ ━ |
; | ━ .━ .━ . |
? | ..━ ━ .. |
= | ━ ...━ |
' | .━ ━ ━ ━ . |
/ | ━ ..━ . |
! | ━ .━ .━ ━ |
━ | ━ ....━ |
_ | ..━ ━ .━ |
" | .━ ..━ . |
( | ━ .━ ━ . |
) | ━ .━ ━ .━ |
$ | ...━ ..━ |
& | . ... |
@ | .━ ━ .━ . |
培根密码,又名倍康尼密码(Bacon's cipher)是由法兰西斯·培根发明的一种隐写术,它是一种本质上用二进制数设计的,没有用通常的0和1来表示,而是采用a和b,看到一串的a和b,并且五个一组,那么就是培根加密了。
第一种方式:
字符 | 培根密码 | 字符 | 培根密码 | 字符 | 培根密码 | 字符 | 培根密码 |
A | aaaaa | H | aabbb | O | abbba | V | babab |
B | aaaab | I | abaaa | P | abbbb | W | babba |
C | aaaba | J | abaab | Q | baaaa | X | babbb |
D | aaabb | K | ababa | R | baaab | Y | bbaaa |
E | aabaa | L | ababb | S | baaba | Z | bbaab |
F | aabab | M | abbaa | T | baabb | ||
G | aabba | N | abbab | U | babaa |
第二种方式:
字符 | 培根密码 | 字符 | 培根密码 | 字符 | 培根密码 | 字符 | 培根密码 |
a | AAAAA | h | AABBB | p | ABBBA | x | BABAB |
b | AAAAB | i-j | ABAAA | q | ABBBB | y | BABBA |
c | AAABA | k | ABAAB | r | BAAAA | z | BABBB |
d | AAABB | l | ABABA | s | BAAAB | ||
e | AABAA | m | ABABB | t | BAABA | ||
f | AABAB | n | ABBAA | u-v | BAABB | ||
g | AABBA | o | ABBAB | w | BABAA |
示例:
•明文:kuaidaili•密文:ABABABABAAAAAAAABAAAAAABBAAAAAABAAAABABBABAAA
维吉尼亚密码是在凯撒密码基础上产生的一种加密方法,它将凯撒密码的全部25种位移排序为一张表,与原字母序列共同组成26行及26列的字母表。另外,维吉尼亚密码必须有一个密钥,这个密钥由字母组成,最少一个,最多可与明文字母数量相等。维吉尼亚密码表如下:
示例:
首先,密钥长度需要与明文长度相同,如果少于明文长度,则重复拼接直到相同。示例的明文长度为8个字母(非字母均被忽略),密钥会被程序补全为 okokokok
,然后根据维吉尼亚密码表进行加密:明文第一个字母是 I
,密钥第一个字母是 o
,在表格中找到 I
列与 o
行相交点,字母 W
就是密文第一个字母,同理,v
列与 k
行交点字母是 F
,e
列与 o
行交点字母是 S
,以此类推。注意:维吉尼亚密码只对字母进行加密,不区分大小写,若文本中出现非字母字符会原样保留,如果输入多行文本,每行是单独加密的。
字符串转换后,是一些佛语,在线体验:https://keyfc.net/bbs/tools/tudoucode.aspx
示例:
当铺密码在 CTF 比赛题目中出现过。该加密算法是根据当前汉字有多少笔画出头,对应的明文就是数字几。
示例:
总结了在爬虫中常见的各种加密算法、编码算法的原理、在 JavaScript 中和 Python 中的基本实现方法,遇到 JS 加密的时候可以快速还原加密过程,有的网站在加密的过程中可能还经过了其他处理,但是大致的方法是一样的。
常见加密算法:
对称加密(加密解密密钥相同):DES、3DES、AES、RC4、Rabbit
非对称加密(区分公钥和私钥):RSA、DSA、ECC
消息摘要算法/签名算法:MD5、SHA、HMAC、PBKDF2
常见编码算法:Base64
Crypto-JS 支持 MD5、SHA、RIPEMD-160、HMAC、PBKDF2、AES、DES、3DES(Triple DES)、Rabbit、RC4 等,不支持 RSA、ECC,是应用比较广的加密模块,使用命令 npm install crypto-js 安装。
参考资料:
Node-RSA 对 RSA 算法提供了支持,使用命令 npm install node-rsa 安装。
参考资料:Node-RSA Github:https://github.com/rzcoder/node-rsa
参考资料:JSEncrypt 对 RSA 算法提供了更加全面的支持,
使用命令 npm install jsencrypt 安装。
**注:两位十六进制常常用来显示一个二进制字节。**
利用`binascii`模块可以将十六进制显示的字节转换成我们在加解密中更常用的显示方式:
```python
In [1]: import binasciiIn [2]: '测试'.encode()
Out[2]: b'\xe6\xb5\x8b\xe8\xaf\x95'In [3]: binascii.b2a_hex('测试'.encode())
Out[3]: b'e6b58be8af95'In [4]: binascii.a2b_hex(b'e6b58be8af95')
Out[4]: b'\xe6\xb5\x8b\xe8\xaf\x95'In [5]: binascii.a2b_hex(b'e6b58be8af95').decode()
Out[5]: '测试'
```
**计算机使用的存储单位**
位(bit) 计算机使用的最小单位, 用来存放二进制数
字节(Byte) 计算机最常用的基本单位,一个字节有八位 (1Byte = 8 bit)
- KB( K字节) 1K = 1024 Byte
- MB(兆字节 ) 1M = 1024K
- GB(吉字节 ) 1G = 1204M
- TB(太字节) 1T = 1024G
在 Python 中有很多算法是通过第三方库 Cryptodome 或者 Crypto 来实现的,Cryptodome 几乎是 Crypto 的替代品,Crypto 已经停止更新好多年了,有很多未知错误,所以不建议安装 Crypto !
Cryptodome 支持几乎所有主流加密算法,包括 MD5、SHA、BLAKE2b、BLAKE2s、HMAC、PBKDF2、AES、DES、3DES(Triple DES)、ECC、RSA、RC4 等。
Cryptodome 使用命令 pip install pycryptodome 进行安装,Crypto 使用命令 pip install pycrypto 进行安装。
参考资料:
PyCryptodome 是 python 一个强大的加密算法库,可以实现常见的单向加密、对称加密、非对称加密和流加密算法
Python 的标准库 hashlib 提供了常见的摘要算法,如 MD5,SHA、BLAKE2b、BLAKE2s 等。
参考资料:
Python 的标准库 hmac 对 HMAC 算法提供了支持。
参考资料:
Python 的第三方库 pyDes 对 DES 算法提供了支持。使用命令 pip install pydes
进行安装。
参考资料:pyDes 库:https://github.com/twhiteman/pyDes
Python 的第三方库 rsa 对 RSA 算法提供了支持。使用命令 pip install rsa
进行安装。
参考资料:rsa 库:https://stuvel.eu/python-rsa-doc/
在一些对称和非对称加密算法中,经常会用到以下三个参数:初始向量 iv、加密模式 mode、填充方式 padding,先介绍一下这三个参数的含义和作用:
在密码学中,初始向量(initialization vector,缩写为 iv),又称初始变数(starting variable,缩写为 sv),与密钥结合使用,作为加密数据的手段,它是一个固定长度的值,iv 的长度取决于加密方法,通常与使用的加密密钥或密码块的长度相当,一般在使用过程中会要求它是随机数或拟随机数,使用随机数产生的初始向量才能达到语义安全,让攻击者难以对原文一致且使用同一把密钥生成的密文进行破解。
参考资料:维基百科:https://en.wikipedia.org/wiki/Initialization_vector
目前流行的加密和数字认证算法,都是采用块加密方式,就是将需要加密的明文分成固定大小的数据块,然后对其执行密码算法,得到密文。数据块的大小通常采用跟密钥一样的长度。加密模式在加密算法的基础上发展出来,同时也可以独立于加密算法而存在,加密模式定义了怎样通过重复利用加密算法将大于一个数据块大小的明文转化为密文,描述了加密每一数据块的过程。目前利用较多的加密模式有以下几种:
ECB:Electronic Code Book(电子码本模式),是一种基础的加密方式,密文被分割成分组长度相等的块(不足补齐),然后单独一个个加密,一个个输出组成密文。
CBC:Cipher Block Chaining(密码块链接模式),是一种循环模式,前一个分组的密文和当前分组的明文异或操作后再加密,这样做的目的是增强破解难度。
PCBC:Propagating Cipher Block Chaining(填充密码块链接模式),也称为明文密码块链接模式(Plaintext Cipher Block Chaining),是一种可以使密文中的微小更改在解密时导致明文大部分错误的模式,并在加密的时候也具有同样的特性。
CFB:Cipher Feedback(密码反馈模式),可以将块密码变为自同步的流密码,类似于 CBC,CFB 的解密过程几乎就是颠倒的 CBC 的加密过程。
OFB:Output Feedback(输出反馈模式),可以将块密码变成同步的流密码,它产生密钥流的块,然后将其与明文块进行异或,得到密文。与其它流密码一样,密文中一个位的翻转会使明文中同样位置的位也产生翻转。
CTR:Counter mode(计数器模式),也被称为 ICM 模式(Integer Counter Mode,整数计数模式)和 SIC 模式(Segmented Integer Counter),在 CTR 模式中,有一个自增的算子,这个算子用密钥加密之后的输出和明文异或的结果得到密文,相当于一次一密。这种加密方式简单快速,安全可靠,而且可以并行加密,但是在计算器不能维持很长的情况下,密钥只能使用一次。
参考资料:维基百科:https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
块密码只能对确定长度的数据块进行处理,而消息的长度通常是可变的。因此部分模式最后一块数据在加密前需要进行填充。有数种填充方法,其中最简单的一种是在明文的最后填充空字符以使其长度为块长度的整数倍。常见填充方式有以下几种:
PKCS7:在填充时首先获取需要填充的字节长度 = 块长度 - (数据长度 % 块长度), 在填充字节序列中所有字节填充为需要填充的字节长度值。
PKCS5:PKCS5 作为 PKCS7 的子集算法,概念上没有什么区别,只是在 blockSize 上固定为 8 bytes,即块大小固定为 8 字节。
ZeroPadding:在填充时首先获取需要填充的字节长度 = 块长度 - (数据长度 % 块长度), 在填充字节序列中所有字节填充为 0 。
ISO10126:在填充时首先获取需要填充的字节长度 = 块长度 - (数据长度 % 块长度),在填充字节序列中最后一个字节填充为需要填充的字节长度值,填充字节中其余字节均填充随机数值。
ANSIX923:在填充时首先获取需要填充的字节长度 = 块长度 - (数据长度 % 块长度),在填充字节序列中最后一个字节填充为需要填充的字节长度值,填充字节中其余字节均填充数字零。
参考资料:
维基百科:https://en.wikipedia.org/wiki/Padding_(cryptography)
PKCS7/PKCS5 填充算法:https://segmentfault.com/a/1190000019793040
简介:Base64 是一种用 64 个字符来表示任意二进制数据的方法。
参考资料:
Base64 百度百科:https://baike.baidu.com/item/base64/8545775
Base64 维基百科:https://en.wikipedia.org/wiki/Base64
JavaScript 实现
// 引用 crypto-js 加密模块
var CryptoJS = require('crypto-js')
function base64Encode() {
var srcs = CryptoJS.enc.Utf8.parse(text);
var encodeData = CryptoJS.enc.Base64.stringify(srcs);
return encodeData
}
function base64Decode() {
var srcs = CryptoJS.enc.Base64.parse(encodeData);
var decodeData = srcs.toString(CryptoJS.enc.Utf8);
return decodeData
}
var text = "I love Python!"
var encodeData = base64Encode()
var decodeData = base64Decode()
console.log("Base64 编码: ", encodeData)
console.log("Base64 解码: ", decodeData)
// Base64 编码: SSBsb3ZlIFB5dGhvbiE=
// Base64 解码: I love Python!
Python 实现
import base64
def base64_encode(text):
encode_data = base64.b64encode(text.encode())
return encode_data
def base64_decode(encode_data):
decode_data = base64.b64decode(encode_data)
return decode_data
if __name__ == '__main__':
text = 'I love Python!'
encode_data = base64_encode(text)
decode_data = base64_decode(encode_data)
print('Base64 编码:', encode_data)
print('Base64 解码:', decode_data)
# Base64 编码:b'SSBsb3ZlIFB5dGhvbiE='
# Base64 解码:b'I love Python!'
简介:全称 MD5 消息摘要算法(英文名称:MD5 Message-Digest Algorithm),又称哈希算法、散列算法,由美国密码学家罗纳德·李维斯特(Ronald Linn Rivest)设计,于 1992 年作为 RFC 1321 被公布,用以取代 MD4 算法。摘要算法是单向加密的,也就是说明文通过摘要算法加密之后,是不能解密的。摘要算法的第二个特点密文是固定长度的,它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。之所以叫摘要算法,它的算法就是提取明文重要的特征。所以,两个不同的明文,使用了摘要算法之后,有可能他们的密文是一样的,不过这个概率非常的低。
参考资料:
RFC 1321:https://datatracker.ietf.org/doc/rfc1321/
MD5 维基百科:https://en.wikipedia.org/wiki/MD5
JavaScript 实现
// 引用 crypto-js 加密模块
var CryptoJS = require('crypto-js')
function MD5Test() {
var text = "I love python!"
return CryptoJS.MD5(text).toString()
}
console.log(MD5Test()) // 21169ee3acd4a24e1fcb4322cfd9a2b8
Python 实现
import hashlib
def md5_test1():
md5 = hashlib.new('md5', 'I love python!'.encode('utf-8'))
print(md5.hexdigest())
def md5_test2():
md5 = hashlib.md5()
md5.update('I love '.encode('utf-8'))
md5.update('python!'.encode('utf-8'))
print(md5.hexdigest())
if __name__ == '__main__':
md5_test1() # 21169ee3acd4a24e1fcb4322cfd9a2b8
md5_test2() # 21169ee3acd4a24e1fcb4322cfd9a2b8
简介:英文名称:Password-Based Key Derivation Function 2,PBKDF2 是 RSA 实验室的公钥加密标准(PKCS)系列的一部分,2017 年发布的 RFC 8018 (PKCS #5 v2.1)推荐使用 PBKDF2 进行密码散列。PBKDF2 将伪随机函数(例如 HMAC),把明文和一个盐值(salt)作为输入参数,然后进行重复运算,并最终产生密钥,如果重复的次数足够大,破解的成本就会变得很高。
参考资料:
RFC 8018:https://datatracker.ietf.org/doc/rfc8018/
PBKDF2 维基百科:https://en.wikipedia.org/wiki/PBKDF2
JavaScript 实现
// 引用 crypto-js 加密模块
var CryptoJS = require('crypto-js')
function pbkdf2Encrypt() {
var text = "I love Python!"
var salt = "43215678"
// key 长度 128,10 次重复运算
var encryptedData = CryptoJS.PBKDF2(text, salt, {keySize: 128/32,iterations: 10});
return encryptedData.toString()
}
console.log(pbkdf2Encrypt()) // 7fee6e8350cfe96314c76aaa6e853a50
Python 实现
import binascii
from Cryptodome.Hash import SHA1
from Cryptodome.Protocol.KDF import PBKDF2
text = 'I love Python!'
salt = b'43215678'
result = PBKDF2(text, salt, count=10, hmac_hash_module=SHA1)
result = binascii.hexlify(result)
print(result)
# b'7fee6e8350cfe96314c76aaa6e853a50'
简介:全称安全哈希算法(英文名称:Secure Hash Algorithm),由美国国家安全局(NSA)所设计,主要适用于数字签名标准(Digital Signature Standard DSS)里面定义的数字签名算法(Digital Signature Algorithm DSA),SHA 通常指 SHA 家族的五个算法,分别是 SHA-1、SHA-224、SHA-256、SHA-384、SHA-512,后四者有时并称为 SHA-2,SHA 是比 MD5 更安全一点的摘要算法,MD5 的密文是 32 位,而 SHA-1 是 40 位,版本越强,密文越长,代价是速度越慢。
参考资料:
RFC 3174:https://datatracker.ietf.org/doc/rfc3174/
SHA 维基百科:https://en.wikipedia.org/wiki/Secure_Hash_Algorithms
JavaScript 实现
// 引用 crypto-js 加密模块
var CryptoJS = require('crypto-js')
function SHA1Encrypt() {
var text = "I love python!"
return CryptoJS.SHA1(text).toString();
}
console.log(SHA1Encrypt()) // 23c02b203bd2e2ca19da911f1d270a06d86719fb
Python 实现
import hashlib
def sha1_test1():
sha1 = hashlib.new('sha1', 'I love python!'.encode('utf-8'))
print(sha1.hexdigest())
def sha1_test2():
sha1 = hashlib.sha1()
sha1.update('I love python!'.encode('utf-8'))
print(sha1.hexdigest())
if __name__ == '__main__':
sha1_test1() # 23c02b203bd2e2ca19da911f1d270a06d86719fb
sha1_test2() # 23c02b203bd2e2ca19da911f1d270a06d86719fb
简介:全称散列消息认证码、密钥相关的哈希运算消息认证码(英文名称:Hash-based Message Authentication Code 或者 Keyed-hash Message Authentication Code),于 1996 年提出,1997 年作为 RFC 2104 被公布,HMAC 加密算法是一种安全的基于加密 Hash 函数和共享密钥的消息认证协议,它要求通信双方共享密钥 key、约定算法、对报文进行 Hash 运算,形成固定长度的认证码。通信双方通过认证码的校验来确定报文的合法性。
参考资料:
RFC 2104:https://datatracker.ietf.org/doc/rfc2104/
HMAC 维基百科:https://en.wikipedia.org/wiki/HMAC
JavaScript 实现
// 引用 crypto-js 加密模块
var CryptoJS = require('crypto-js')
function HMACEncrypt() {
var text = "I love python!"
var key = "secret"
return CryptoJS.HmacMD5(text, key).toString();
// return CryptoJS.HmacSHA1(text, key).toString();
// return CryptoJS.HmacSHA256(text, key).toString();
}
console.log(HMACEncrypt())
Python 实现
import hmac
def hmac_test1():
message = b'I love python!'
key = b'secret'
md5 = hmac.new(key, message, digestmod='MD5')
print(md5.hexdigest())
def hmac_test2():
key = 'secret'.encode('utf8')
sha1 = hmac.new(key, digestmod='sha1')
sha1.update('I love '.encode('utf8'))
sha1.update('Python!'.encode('utf8'))
print(sha1.hexdigest())
if __name__ == '__main__':
hmac_test1() # 9c503a1f852edcc3526ea56976c38edf
hmac_test2() # 2d8449a4292d4bbeed99ce9ea570880d6e19b61a
简介:全称数据加密标准(英文名称:Data Encryption Standard),加密与解密使用同一密钥,属于对称加密算法,1977 年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),DES 是一个分组加密算法,使用 56 位的密钥(一般认为密钥是 64 位,但是密钥的每个第 8 位设置为奇偶校验位,所以实际上有效位只有 56 位),由于 56 位密钥长度相对较短,所以 DES 是不安全的,现在基本上已被更高级的加密标准 AES 取代。
mode 支持:CBC,CFB,CTR,CTRGladman,ECB,OFB 等。
padding 支持:ZeroPadding,NoPadding,AnsiX923,Iso10126,Iso97971,Pkcs7 等。
参考资料:
RFC 4772:https://datatracker.ietf.org/doc/rfc4772/
DES 维基百科:https://en.wikipedia.org/wiki/Data_Encryption_Standard
JavaScript 实现
// 引用 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 love Python!" // 待加密对象
var desKey = "6f726c64f2c2057" // 密钥
var desIv = "0123456789ABCDEF" // 初始向量
var encryptedData = desEncrypt()
var decryptedData = desDecrypt()
console.log("加密字符串: ", encryptedData)
console.log("解密字符串: ", decryptedData)
// 加密字符串: +ndbEkWNw2QAfIYQtwC14w==
// 解密字符串: I love Python!
Python 实现
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 = '12345678' # 密钥
text = 'I love Python!' # 加密对象
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)
# 加密字符串:b'302d3abf2421169239f829b38a9545f1'
# 解密字符串:b'I love Python!'
简介:全称三重数据加密算法(英文名称:Triple Data Encryption Standard、 Triple Data Encryption Algorithm、TDES、TDEA),是对称加密算法中的一种。70 年代初由 IBM 研发,后 1977 年被美国国家标准局采纳为数据加密标准,它相当于是对每个数据块应用三次 DES 加密算法。由于计算机运算能力的增强,原版 DES 密码的密钥长度变得容易被暴力破解;3DES 即是设计用来提供一种相对简单的方法,即通过增加 DES 的密钥长度来避免破解,所以严格来说 3DES 不是设计一种全新的块密码算法。
mode 支持:CBC,CFB,CTR,CTRGladman,ECB,OFB 等。
padding 支持:ZeroPadding,NoPadding,AnsiX923,Iso10126,Iso97971,Pkcs7 等。
参考资料:
RFC 1851:https://datatracker.ietf.org/doc/rfc1851/
3DES 维基百科:https://en.wikipedia.org/wiki/Triple_DES
JavaScript 实现
// 引用 crypto-js 加密模块
var CryptoJS = require('crypto-js')
function tripleDesEncrypt() {
var key = CryptoJS.enc.Utf8.parse(desKey),
iv = CryptoJS.enc.Utf8.parse(desIv),
srcs = CryptoJS.enc.Utf8.parse(text),
// ECB 加密方式,Iso10126 填充方式
encrypted = CryptoJS.TripleDES.encrypt(srcs, key, {
iv: iv,
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Iso10126
});
return encrypted.toString();
}
function tripleDesDecrypt() {
var key = CryptoJS.enc.Utf8.parse(desKey),
iv = CryptoJS.enc.Utf8.parse(desIv),
srcs = encryptedData,
// ECB 加密方式,Iso10126 填充方式
decrypted = CryptoJS.TripleDES.decrypt(srcs, key, {
iv: iv,
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Iso10126
});
return decrypted.toString(CryptoJS.enc.Utf8);
}
var text = "I love Python!" // 待加密对象
var desKey = "6f726c64f2c2057c" // 密钥
var desIv = "0123456789ABCDEF" // 偏移量
var encryptedData = tripleDesEncrypt()
var decryptedData = tripleDesDecrypt()
console.log("加密字符串: ", encryptedData)
console.log("解密字符串: ", decryptedData)
// 加密字符串: 3J0NX7x6GbewjjhoW2HKqg==
// 解密字符串: I love Python!
Python 实现
from Cryptodome.Cipher import DES3
from Cryptodome import Random
# 需要补位,str不是16的倍数那就补足为16的倍数
def add_to_16(value):
while len(value) % 16 != 0:
value += '\0'
return str.encode(value)
def des_encrypt(key, text, iv):
# 加密模式 OFB
cipher_encrypt = DES3.new(add_to_16(key), DES3.MODE_OFB, iv)
encrypted_text = cipher_encrypt.encrypt(text.encode("utf-8"))
return encrypted_text
def des_decrypt(key, text, iv):
# 加密模式 OFB
cipher_decrypt = DES3.new(add_to_16(key), DES3.MODE_OFB, iv)
decrypted_text = cipher_decrypt.decrypt(text)
return decrypted_text
if __name__ == '__main__':
key = '12345678' # 密钥,16 位
text = 'I love Python!' # 加密对象
iv = Random.new().read(DES3.block_size) # DES3.block_size == 8
secret_str = des_encrypt(key, text, iv)
print('加密字符串:', secret_str)
clear_str = des_decrypt(key, secret_str, iv)
print('解密字符串:', clear_str)
# 加密字符串:b'\xa5\x8a\xd4R\x99\x16j\xba?vg\xf2\xb6\xa9'
# 解密字符串:b'I love Python!'
简介:全称高级加密标准(英文名称:Advanced Encryption Standard),在密码学中又称 Rijndael 加密法,由美国国家标准与技术研究院 (NIST)于 2001 年发布,并在 2002 年成为有效的标准,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的 DES,已经被多方分析且广为全世界所使用,它本身只有一个密钥,即用来实现加密,也用于解密。
mode 支持:CBC,CFB,CTR,CTRGladman,ECB,OFB 等。
padding 支持:ZeroPadding,NoPadding,AnsiX923,Iso10126,Iso97971,Pkcs7 等。
参考资料:
RFC 3268:https://datatracker.ietf.org/doc/rfc3268/
AES 维基百科:https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
JavaScript 实现
// 引用 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 = "I love Python!" // 待加密对象
var aesKey = "6f726c64f2c2057c" // 密钥,16 倍数
var aesIv = "0123456789ABCDEF" // 偏移量,16 倍数
var encryptedData = tripleAesEncrypt()
var decryptedData = tripleAesDecrypt()
console.log("加密字符串: ", encryptedData)
console.log("解密字符串: ", decryptedData)
// 加密字符串: dZL7TLJR786VGvuUvqYGoQ==
// 解密字符串: I love Python!
Python 实现
import base64
from Cryptodome.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 = '12345678' # 密钥
text = 'I love Python!' # 加密对象
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)
# 加密字符串:lAVKvkQh+GtdNpoKf4/mHA==
# 解密字符串:I love Python!
简介:英文名称:Rivest Cipher 4,也称为 ARC4 或 ARCFOUR,是一种流加密算法,密钥长度可变。它加解密使用相同的密钥,因此也属于对称加密算法。RC4 是有线等效加密(WEP)中采用的加密算法,也曾经是 TLS 可采用的算法之一,该算法的速度可以达到 DES 加密的 10 倍左右,且具有很高级别的非线性,虽然它在软件方面的简单性和速度非常出色,但在 RC4 中发现了多个漏洞,它特别容易受到攻击,RC4 作为一种老旧的验证和加密算法易于受到黑客攻击,现在逐渐不推荐使用了。
参考资料:
RFC 7465:https://datatracker.ietf.org/doc/rfc7465/
RC4 维基百科:https://en.wikipedia.org/wiki/RC4
JavaScript 实现
// 引用 crypto-js 加密模块
var CryptoJS = require('crypto-js')
function RC4Encrypt() {
return CryptoJS.RC4.encrypt(text, key).toString();
}
function RC4Decrypt(){
return CryptoJS.RC4.decrypt(encryptedData, key).toString(CryptoJS.enc.Utf8);
}
var text = "I love Python!"
var key = "6f726c64f2c2057c"
var encryptedData = RC4Encrypt()
var decryptedData = RC4Decrypt()
console.log("加密字符串: ", encryptedData)
console.log("解密字符串: ", decryptedData)
// 加密字符串: U2FsdGVkX18hMm9WWdoEQGPolnXzlg9ryArdGNwv
// 解密字符串: I love Python!
Python 实现
import base64
from Cryptodome.Cipher import ARC4
def rc4_encrypt(key, t):
enc = ARC4.new(key.encode('utf8'))
res = enc.encrypt(t.encode('utf-8'))
res = base64.b64encode(res)
return res
def rc4_decrypt(key, t):
data = base64.b64decode(t)
enc = ARC4.new(key.encode('utf8'))
res = enc.decrypt(data)
return res
if __name__ == "__main__":
secret_key = '12345678' # 密钥
text = 'I love Python!' # 加密对象
encrypted_str = rc4_encrypt(secret_key, text)
print('加密字符串:', encrypted_str)
decrypted_str = rc4_decrypt(secret_key, encrypted_str)
print('解密字符串:', decrypted_str)
# 加密字符串:b'8tNVu3/U/veJR2KgyBw='
# 解密字符串:b'I love Python!'
简介:Rabbit 加密算法是一个高性能的流密码加密方式,2003 年首次被提出,它从 128 位密钥和 64 位初始向量(iv)创建一个密钥流。
参考资料:
RFC 4503:https://datatracker.ietf.org/doc/rfc4503/
Rabbit 维基百科:https://en.wikipedia.org/wiki/Rabbit_(cipher)
JavaScript 实现
// 引用 crypto-js 加密模块
var CryptoJS = require('crypto-js')
function rabbitEncrypt() {
return CryptoJS.Rabbit.encrypt(text, key).toString();
}
function rabbitDecrypt() {
return CryptoJS.Rabbit.decrypt(encryptedData, key).toString(CryptoJS.enc.Utf8);
}
var text = "I love Python!"
var key = "6f726c64f2c2057"
var encryptedData = rabbitEncrypt()
var decryptedData = rabbitDecrypt()
console.log("加密字符串: ", encryptedData)
console.log("解密字符串: ", decryptedData)
// 加密字符串: U2FsdGVkX1+ZVCHRXlhmG5Xw87YPWMNIBlbukuh8
// 解密字符串: I love Python!
Python 实现
目前没有找到有第三方库可以直接实现 Rabbit 算法,在 Python 中实现可以参考:https://asecuritysite.com/encryption/rabbit2
简介:英文名称:Rivest-Shamir-Adleman,是 1977 年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的,RSA 就是他们三人姓氏开头字母拼在一起组成的,RSA 加密算法是一种非对称加密算法。在公开密钥加密和电子商业中RSA被广泛使用。它被普遍认为是目前比较优秀的公钥方案之一。RSA是第一个能同时用于加密和数字签名的算法,它能够抵抗到目前为止已知的所有密码攻击。
参考资料:
RSA 维基百科:https://en.wikipedia.org/wiki/RSA_(cryptosystem)
JavaScript 实现
// 引用 node-rsa 加密模块
var NodeRSA = require('node-rsa');
function rsaEncrypt() {
pubKey = new NodeRSA(publicKey,'pkcs8-public');
var encryptedData = pubKey.encrypt(text, 'base64');
return encryptedData
}
function rsaDecrypt() {
priKey = new NodeRSA(privatekey,'pkcs8-private');
var decryptedData = priKey.decrypt(encryptedData, 'utf8');
return decryptedData
}
var key = new NodeRSA({b: 512}); //生成512位秘钥
var publicKey = key.exportKey('pkcs8-public'); //导出公钥
var privatekey = key.exportKey('pkcs8-private'); //导出私钥
var text = "I love Python!"
var encryptedData = rsaEncrypt()
var decryptedData = rsaDecrypt()
console.log("公钥:\n", publicKey)
console.log("私钥:\n", privatekey)
console.log("加密字符串: ", encryptedData)
console.log("解密字符串: ", decryptedData)
/*
公钥:
-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAOV1BwTJSVce/QjJAro5fXG9WzOpal09
Qtv1yuXKE81vZSNTHxW6dICwPT/kjCfC3bA5Qs6wnYBANuwD6wlAS0UCAwEAAQ==
-----END PUBLIC KEY-----
私钥:
-----BEGIN PRIVATE KEY-----
MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEA5XUHBMlJVx79CMkC
ujl9cb1bM6lqXT1C2/XK5coTzW9lI1MfFbp0gLA9P+SMJ8LdsDlCzrCdgEA27APr
CUBLRQIDAQABAkAiXwJbJC+5PioXG80tyhjRZdT4iyMkrl2Kh2oKO9f1iLaBXLya
D0HW82wFh+cUy8GcMl9jse8DE8wd1TdORmHhAiEA/rwmWjXHVgDqcH/fqk8Ufku0
fXvs56h5QDoh1so5vokCIQDmmL3JDW6Y7RuK2qwFbHBZtYPRFRVdn5X1oqU2FOSX
3QIhAOVTjVN5RtNuT6Cn/jvcpZ5tmTe+8TA8w6vGqeAsfn/BAiBvKKIUEQ2HWoU0
YkUaODPQiteIKomqIAvB5S2O7HNlYQIgWMuLUxGZbbcAmIX+YmRXuET97S7OWv+z
WHVfb/rbXtI=
-----END PRIVATE KEY-----
加密字符串: hHXTF1K3w55Wd6OSjVYtqxceJ5VhlySNUahel9pwKD92Ef7wIT7DYPuJRKiqz5tuHtUqujbmbZBSL0qDE/EA+A==
解密字符串: I love Python!
*/
Python 实现
模块:rsa:
import rsa
def rsa_encrypt(pu_key, t):
# 公钥加密
rsa = rsa.encrypt(t.encode("utf-8"), pu_key)
return rsa
def rsa_decrypt(pr_key, t):
# 私钥解密
rsa = rsa.decrypt(t, pr_key).decode("utf-8")
return rsa
if __name__ == "__main__":
public_key, private_key = rsa.newkeys(512) # 生成公钥、私钥
print('公钥:', public_key)
print('私钥:', private_key)
text = 'I love Python!' # 加密对象
encrypted_str = rsa_encrypt(public_key, text)
print('加密字符串:', encrypted_str)
decrypted_str = rsa_decrypt(private_key, encrypted_str)
print('解密字符串:', decrypted_str)
'''
公钥:PublicKey(7636479066127060956100056267701318377455704072072698049978592945665550579944731953431504993757594103617537700972424661030900303472123028864161050235168613, 65537)
私钥:PrivateKey(7636479066127060956100056267701318377455704072072698049978592945665550579944731953431504993757594103617537700972424661030900303472123028864161050235168613, 65537, 3850457767980968449796700480128630632818465005441846698224554128042451115530564586537997896922067523638756079019054611200173122138274839877369624069360253, 4713180694194659323798858305046043997526301456820208338158979730140812744181638767, 1620238976946735819854194349514460863335347861649166352709029254680140139)
加密字符串:b"\x1aaeps\xa0c}\xb6\xcf\xa3\xb0\xbb\xedA\x7f}\x03\xdc\xd5\x1c\x9b\xdb\xda\xf9q\x80[=\xf5\x91\r\xd0'f\xce\x1f\x01\xef\xa5\xdb3\x96\t0qIxF\xbd\x11\xd6\xb25\xc5\xe1pM\xb4M\xc2\xd4\x03\xa6"
解密字符串:I love Python!
'''
模块 Cryptodome:
import base64
from Cryptodome.PublicKey import RSA
from Cryptodome.Cipher import PKCS1_v1_5
data = "cKK8B2rWwfwWeXhz"
public_key = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAM1xhOWaThSMpfxFsjV5YaWOFHt+6RvS+zH2Pa47VVr8PkZYnRaaKKy2MYBuEh7mZfM/R1dUXTgu0gp6VTNeNQkCAwEAAQ=="
rsa_key = RSA.import_key(base64.b64decode(public_key)) # 导入读取到的公钥
cipher = PKCS1_v1_5.new(rsa_key) # 生成对象
cipher_text = base64.b64encode(cipher.encrypt(data.encode(encoding="utf-8")))
print(cipher_text)