js逆向中常用的加密算法的python实现

发布于:2023-05-22 ⋅ 阅读:(286) ⋅ 点赞:(0)

js逆向中常用的加密算法的python实现

SHA256

import hashlib
str_input = '123456'.encode('utf-8')
str_out = hashlib.sha256(str_input).hexdigest()

base64

import base64
str_input = '123456'.encode('utf-8')
str_out = base64.b64encode(str_input).decode(encoding = 'utf-8')

RSA

可以参考文章:
https://blog.csdn.net/u013073067/article/details/86674474
RSA密钥长度、明文长度和密文长度

RSA的公钥、私钥的组成,以及加密过程、解密过程的公式可见于下表:
在这里插入图片描述
例1:使用Crypto模块(较常见)

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5

key = RSA.generate(1024)     #生成一个1024位的公钥和私钥对

publickey = key.publickey().export_key()   #拿到公钥
print('publickey:',publickey)
print('publickey_len:',len(publickey))

privatekey = key.export_key()  #拿到私钥
print('publickey:',privatekey)
print('publickey_len:',len(privatekey))

以上这段代码一般针对于项目的开发阶段,运行一次,拿到公钥和私钥后,开发人员可能就将公钥写死放在了代码里,然后把私钥放在服务器中。

以下是一个rsa的加密和解密流程,要注意,待加密的明文长度是要小于RSA.generate(1024)生成的公钥和私钥对的长度的(以字节计算),比如RSA.generate(1024)所加密的明文,要小于1024字节。

rsa_key = RSA.importKey(publickey)     #加载公钥,importKey方法要注意传入
# 的格式要为b'-----BEGIN PUBLIC KEY-----\nMIG...DAQAB\n-----END PUBLIC
# KEY-----'的形式,也可以传入的是key先进行base64.b64decode()解码后的这种形式,如b'0\x81\x...00\x01'
print('n:',rsa_key.n)   #打印公钥的n值
print('e:',rsa_key.e)  #打印公钥的e值

cipher = Cipher_pkcs1_v1_5.new(rsa_key)
text = 'jiami'
cipher_text = cipher.encrypt(text.encode())
print('密文:',cipher_text)
print('密文长度:',len(cipher_text))

rsa_key = RSA.importKey(privatekey)   #加载私钥
print('d:',rsa_key.d)   #打印私钥的d值
cipher = Cipher_pkcs1_v1_5.new(rsa_key)
plain_text = cipher.decrypt(cipher_text,None).decode(encoding='utf-8')
print('密文解密:',plain_text)

输出结果:
在这里插入图片描述
例2:使用rsa模块(较少见)
在进行js逆向的时候,遇到这样一段js代码:

var public_key="B0AAFA4C9D388208E9F55B14DF04C8603D0CD81B7B65BBD669FA893096C985E33682FE7DEEE6500E1C4C6722C9855B6DD2E130F3672BEBA446B72D8DFFF2DD1F4E23D6BD728E267A9DC2C544C6680712884926D67AF74B74E5AD8298034D8C16FE8E5A37706EF5E447E423E69CA7FD3E47BBF7A9B137EF9B0310E2560E13D3C1";
var public_length="10001";
function rsa_encrypt(str){
    var BLOCK_SIZE=public_key.length/2-11;
    var ret='';
    while(str.length>0){
        var i=BLOCK_SIZE;
        if(str.length<i)i=str.length;
        str_1=str.substr(0,i);
        str=str.substr(i,str.length-i);
        ret+=rsa_encrypt1(str_1)+' ';
    }
    return(ret);
}
function rsa_encrypt1(str){
    var rsa = new RSAKey();
    rsa.setPublic(public_key, public_length);
    var res = rsa.encrypt(str);
    res=hex2b64(res);
    return(res);
}

可以发现这里的public_key和以往见到的-----BEGIN PUBLIC KEY-----\nMIG…DAQAB\n-----END PUBLIC KEY----- 这样的形式不太一样,因为这里是直接使用的模值,也即公钥KU中的n;同理,public_length就是公钥KU中的e。我们可以使用rsa库来改写和处理这段代码:

import rsa
import base64

publickey_n_value = 'B0AAFA4C9D388208E9F55B14DF04C8603D0CD81B7B65BBD669FA893096C985E33682FE7DEEE6500E1C4C6722C9855B6DD2E130F3672BEBA446B72D8DFFF2DD1F4E23D6BD728E267A9DC2C544C6680712884926D67AF74B74E5AD8298034D8C16FE8E5A37706EF5E447E423E69CA7FD3E47BBF7A9B137EF9B0310E2560E13D3C1'
publickey_e_value = '10001'
def func_rsa_encrypt(str):
    BLOCK_SIZE = int(len(publickey_n_value) / 2 - 11)
    ret = ''
    while len(str) > 0:
        i = BLOCK_SIZE
        if len(str) < i:
            i = len(str)
        str_1 = str[0:i]
        str = str[i:len(str)*2-i]    #此处仿照js中的用法进行一个python化的改写
        ret += func_rsa_encrypt1(str_1) + ' '   
    return ret 

def func_rsa_encrypt1(str):
    e = int(publickey_e_value,16)  #会发现此处16进制转10进制的结果是65537
    n = int(publickey_n_value,16)   
    pub_key = rsa.PublicKey(e = e,n = n)
    result = base64.b64encode(rsa.encrypt(str.encode(),pub_key=pub_key)).decode()
    return result
    
test = func_rsa_encrypt1('123456')
print(test)

为什么需要填充(padding)
算法特点决定了对要加密的数据有长度要求,通常是16的整数倍,不是16的倍数,就需要填充到这么长,如果刚好是16倍数,也需要再填充。
密文长度等于密钥长度
明文最大长度=密钥长度-11(单位字节),超过最大长度需要分块加密或者先把明文用对称加密方式加密缩短长度后再加密。假设密钥为1024位(密钥至少1024位才安全,长度小于它被证明会被破解),即1024位/8位-11=128-11=117字节,所以加密的明文最大长度是117字节,解密密文最大长度为128字节。
为什么明文=密钥长度-11字节?
因为RSA加密有填充模式(padding),需要留11字节用来随机填充,随机填充会达到同一个明文、同一个公钥每次生成不同的密文。
尤其需要注意的一点:
android的rsa加密填充方式是RSA时,是NoPadind RSA/ECB/NoPadding,而标准jdk里填充是RSA时,是指PKCS1填充,RSA/ECB/PKCS1Padding。很多时候在模拟APP的RSA时会发现请求失败,有可能就是这个原因造成的。from Crypto.PublicKey import RSA这个RSA库相对封装得更完善,所以遇到NoPadding的时候可以选择使用import rsa库,这个库有一些偏向于底层的api可以调用,同时也需要对rsa加密的底层数学实现原理了解得更加清楚。
公钥指数如何确定?
公钥指数是随意选的,但目前行业上公钥指数普遍选的都是65537(0x10001,5bits),该值是除了1、3、5、17、257之外的最小素数,为什么不选的大一点?当然可以,只是考虑到既要满足相对安全、又想运算的快一点(加密时),PKCS#1的一个建议值而已。有意的把公钥指数选的小一点,但是对应私钥指数肯定很大,意图也很明确,大家都要用公钥加密,所以大家时间很宝贵,需要快一点,您一个人私钥解密,时间长一点就多担待,少数服从多数的典型应用。

AES

AES的实现原理,可以参考文章:
https://blog.csdn.net/gulang03/article/details/81175854

实现AES的4种模式:

  1. ECB模式(电子密码本模式:Electronic codebook):ECB是最简单的块密码加密模式,加密前根据加密块大小(如AES为128位)分成若干块,之后将每块使用相同的密钥单独加密,解密同理
  2. CBC模式(密码分组链接:Cipher-block chaining):CBC模式对于每个待加密的密码块在加密前会先与前一个密码块的密文异或然后再用加密器加密。第一个明文块与一个叫初始化向量的数据块异或
  3. CFB模式(密文反馈:Cipher feedback):与ECB和CBC模式只能够加密块数据不同,CFB能够将块密文(Block Cipher)转换为流密文(Stream Cipher)
  4. OFB模式(输出反馈:Output feedback):OFB是先用块加密器生成密钥流(Keystream),然后再将密钥流与明文流异或得到密文流,解密是先用块加密器生成密钥流,再将密钥流与密文流异或得到明文,由于异或操作的对称性所以加密和解密的流程是完全一样的

在对数据进行加密的时候,某些加密算法需要明文满足某些长度的要求,比如DES和AES等分组加密需要明文满足是分组的倍数,但是大多数情况下,明文恰好满足需求的概率是非常低的,不满足的情况下需要进行Padding。可以参考文章:【密码学】Padding模式
因此对于AES的Padding来说,PKCS5/PKCS7这两种方式是一样的。

CBC模式

from Crypto.Cipher import AES
import base64

# 如果text不足16位的倍数就用空格补足为16位
def add_to_16(text):
    if len(text.encode('utf-8')) % 16:
        add = 16 - (len(text.encode('utf-8')) % 16)
    else:
        add = 0
    text = text + ('\0' * add)
    return text.encode('utf-8')
 
 # 加密
def encrypt(text):
    key = 'u9J7A4LkUTQSdak='.encode('utf-8')
    mode = AES.MODE_CBC
    iv = b'6di50aH901duea7d'
    text = add_to_16(text)
    cryptos = AES.new(key, mode, iv)
    cipher_text = cryptos.encrypt(text)
    return base64.b64encode(cipher_text)
 
 # 解密后,去掉补足的空格用strip() 去掉
def decrypt(text):   
    key = 'u9J7A4LkUTQSdak='.encode('utf8')
    text = base64.b64decode(text)
    iv = b"6di50aH901duea7d"
    mode = AES.MODE_CBC
    cryptos = AES.new(key, mode, iv)    
    plain_text = cryptos.decrypt(text)
    return plain_text.decode('unicode_escape')
    
text = 'jiamizifuchuan'
print(encrypt('jiamizifuchuan'))

encrypt_text = 'POQdRbFy+3ZNIqhhwa9crg=='
print(decrypt(encrypt_text))

pkcs7填充

# -*- coding: UTF-8 -*-


from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
import base64


def aes_cipher(encrypt_key, plain):
    aes = AES.new(encrypt_key.encode(), AES.MODE_ECB)
    padding_text = pad(plain.encode(), AES.block_size, style='pkcs7')
    encrypted_text = aes.encrypt(padding_text)

    return base64.b64encode(encrypted_text).decode()


if __name__ == '__main__':
    # key为16的倍数
    key = "MTYyMTg2Njk0NTUz"
    # 加密字符串长同样需要16倍数
    plain = "abfd0a0740136f8b76d85828126468ce,ce705a94ff6384c50afc74fdf0033c17,76bc8268f69d1a390f39a75b06d901f0,0af7f5c52a765a9133b476bbc9657fbd,36309bbd71fa8c8322ca7e937800e483"
    res = aes_cipher(key, plain)
    print(res)

DES

简介:
Des对称加密,是一种比较传统的加密方式,其加密运算、解密运算使用的是同样的密钥,信息的发送者和信息的接收者在进行信息的传输与处理时,必须共同持有该密码(称为对称密码),是一种对称加密算法。

ECB模式

ECB模式没有iv值

from binascii import b2a_hex, a2b_hex
from Crypto.Cipher import DES
import base64

def add_to_16(text):
    if len(text.encode('utf-8')) % 16:
        add = 16 - (len(text.encode('utf-8')) % 16)
    else:
        add = 0
    text = text + ('\0' * add)
    return text.encode('utf-8')

def encrypt(text):
    key = 'f3faa7f7a8fax8sd'
    des_key = key[:8].encode('utf-8')   #des的key长度限制
    cryptos = DES.new(key = des_key, mode = DES.MODE_ECB)
    encrypt_text = cryptos.encrypt(add_to_16(text))
    print('base64形式输出: \n',base64.b64encode(encrypt_text).decode())
    print('\n16进制形式输出:\n',b2a_hex(encrypt_text).decode())   #返回二进制数据的十六进制表示,每个字节被转换成相应的2位十六进制表示形式

def decrypt(text):
    text = base64.b64decode(text)   #此处的例子是以64编码形式为例,当传入的密文为16进制时也可以用a2b_hex进行转换
    key = 'f3faa7f7a8fax8sd'
    des_key = key[:8].encode('utf-8')
    cryptos = DES.new(key = des_key, mode = DES.MODE_ECB)
    plain_text = cryptos.decrypt(text)
    print('解密结果:\n',plain_text.decode())

text = 'jiami'
encrypt(text)
print('-----------------------')
text = 'bMeLo6h/18O9EV7Np7xupw=='
decrypt(text)

输出结果:
在这里插入图片描述

3DES

import pyDes
import base64
import binascii


class My3DES:
    BASE64 = 1
    HEX = 2

    def __init__(self, key: bytes):
        self.key = key

    def encrypt(self, text: bytes, padmode: int, outmode: int):
        """
        :param text: bytes类型的明文
        :param padmode: 填充模式
        :param outmode: 输出形式
        :return:
        """
        crypto = pyDes.triple_des(key=self.key, padmode=padmode)
        encrypt_byte = crypto.encrypt(text)
        if outmode == 1:
            return base64.b64encode(encrypt_byte).decode()
        elif outmode == 2:
            return binascii.b2a_hex(encrypt_byte).decode()
        else:
            raise ValueError("输出模式有误")

    def decrypt(self, encrypt_byte: bytes, encmode: int):
        """
        :param encrypt_byte: bytes类型的密文
        :param encmod: 密文形式
        :return:
        """
        crypto = pyDes.triple_des(key=self.key)
        if encmode == 1:
            encrypt_byte = base64.b64decode(encrypt_byte)
        elif encmode == 2:
            encrypt_byte = binascii.b2a_hex(encrypt_byte)
        else:
            raise ValueError("密文形式有误")
        text = crypto.decrypt(encrypt_byte)
        return text.decode()


text = "需要加密的明文".encode()
key = 'sagasda23513asga'.encode()

my3des = My3DES(key)
encrypt_byte = my3des.encrypt(text, padmode=pyDes.PAD_PKCS5, outmode=1)
print("得到密文-->",encrypt_byte)

t = my3des.decrypt(encrypt_byte.encode(), encmode=1)
print("解密得到明文-->",t)

RC4

from Crypto.Cipher import ARC4 as rc4cipher

import base64

def rc4_algorithm(encrypt_or_decrypt, data, key1):

    if encrypt_or_decrypt == "encrypt":
        key = bytes(key1, encoding='utf-8')
        enc = rc4cipher.new(key)
        res = enc.encrypt(data.encode('utf-8'))
        res=base64.b64encode(res)
        
        res = str(res,'utf8')

        return res

    elif encrypt_or_decrypt == "decrypt":
        data = base64.b64decode(data)
        key = bytes(key1, encoding='utf-8')
        enc = rc4cipher.new(key)
        res = enc.decrypt(data)
        res = str(res,'utf8')

        return res

if __name__ == "__main__":
    data = '加密字符串'
    key = '12345678912345678912345678912345'
    print(rc4_algorithm('encrypt',data,key))

    key = '12345678912345678912345678912345'
    res ='mXR2UM1+2bXcfY96Nw/N'
    print(rc4_algorithm('decrypt', res, key))

网站公告

今日签到

点亮在社区的每一天
去签到