突然发现之前的一个接口登陆不上了,重新打开网页看看接口的变化。看到负载上那串乱七八糟的字符,原来是在前端将密码做了加密处理。

01

查看前一个接口请求,发现是获取key的,那应该就是先获取公钥,然后对明文密码做加密。返回的结果包含type和key两个字段,type是加密算法,key是公钥。

02

SM2作为关键词在前端js脚本上搜一下,看下是怎么处理的,这个e应该就是前一个接口获取到的key了,看样子加密方式type除了SM2外还有RSA。用python浅浅尝试一下吧。

03

对未知的事情,先放搜索下吧,

04

得到GMSSL这么个答案,翻文档看看咯。安装,初始化,加密,结束。拿到要用的信息就可以搞起来了

05

多说无益,上代码

# -*- coding: utf-8 -*-
"""
@author: pedro
@Function: 提供SM2公钥和需加密的字符串,返回SM2加密后的字符串
@file: sm2_encrypt.py
@time: 2023/6/4 16:07:56
"""
from gmssl import sm2


def sm2_encrypt(psd, key):
    """
    :param psd: 需要SM2加密的密码
    :param key: 公钥
    :return: 加密后的密码
    """
    sm2_crypt = sm2.CryptSM2(
        public_key=key,  mode=1)
    return '04' + sm2_crypt.encrypt(psd.encode()).hex()


if __name__ == '__main__':
    public_key = '03bdfbc634f2a6de5cab543f59830418c5ab312ccf13e9a18a947306f0e16d014f'
    passwd = 'a123456'

    print(sm2_encrypt(passwd, public_key))

公钥,要加密的字符,都有了,执行下看看结果。好家伙,一个报错直接怼我脸上,TypeError: __init__() missing 1 required positional argument: 'private_key'。再看下官方文档提供的示例代码

#16进制的公钥和私钥
private_key = '00B9AB0B828FF68872F21A837FC303668428DEA11DCD1B24429D0C99E24EED83D5'
public_key = 'B9C9A6E04E9C91F7BA880429273747D7EF5DDEB0BB2FF6317EB00BEF331A83081A6994B8993F3F5D6EADDDB81872266C87C018FB4162F5AF347B483E24620207'
sm2_crypt = sm2.CryptSM2(
    public_key=public_key, private_key=private_key)

初始化 CryptSM2 私钥居然是必填项,可我没有啊,这咋弄。而且私钥是解密用的吧,先给个空字符串糊弄一下。

def sm2_encrypt(psd, key):
    """
    :param psd: 需要SM2加密的密码
    :param key: 公钥
    :return: 加密后的密码
    """
    sm2_crypt = sm2.CryptSM2(
        public_key=key, private_key='',  mode=1)
    return '04' + sm2_crypt.encrypt(psd.encode()).hex()

再运行下,又是满屏错误,看样子是公钥有问题。

06

将示例中的公钥替换上看看,两个放一起就觉得不对劲了,咋我拿到的key比示例短这么大一截,不管了,先试试。

07

(⊙ˍ⊙)还真是我公钥有问题。。。

08

找一圈,发现是我的公钥是压缩后的,而gmssl不会还原压缩后的公钥,就直接抛异常出来了。

09

虽然找到了帖子写如何解压缩,但原贴是用的不是python,暂时先不琢磨了。看看有没有在线工具可以完成这个步骤吧。

10

将还原后的带入试试,不报错,也加密出来了,将加密后的拿登录接口上试试

11

完工,不过总不能每次都人工来查一下吧,暂时又不想去理解那繁琐的公钥解压原理,怎么又能省事又能达到自己的效果呢。

12

干脆各退一步,写解压的过程是暂时不想写了,以后再说吧。这个在线解压的工具蛮好用,利用一下咯,扒一下接口。先用着吧,直接上代码。

# -*- coding: utf-8 -*-
"""
@author: pedro
@Function: 使用网络接口还原被压缩的SM2公钥
@file: re_key.py
@time: 2023/6/4 17:06:56
"""

import requests
from bs4 import BeautifulSoup


def re_key(key: str) -> str:
    """
    :param key:  被压缩过的 SM2 公钥
    :return: 返回解压后的 public key
    """
    url = "https://const.net.cn/tool/sm2/sm2-pubkey-decompress/"
    payload = f'pubkey={key}'
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Accept': '*/*',
        'Host': 'const.net.cn',
        'Connection': 'keep-alive'
    }

    response = requests.request("POST", url, headers=headers, data=payload)
    con = response.content.decode('utf-8')
    soup = BeautifulSoup(con, "html.parser")

    reKey = soup.select_one('input#uncompress-pubkey').get('value').replace(" ", "")
    print(reKey)
    return reKey

加了调用的完整加密代码也放下面了

# -*- coding: utf-8 -*-
"""
@author: pedro
@Function: 提供SM2公钥和需加密的字符串,返回SM2加密后的字符串
@file: sm2_encrypt.py
@time: 2023/6/4 16:07:56
"""
from gmssl import sm2
from re_key import re_key


def sm2_encrypt(psd, key):
    """
    :param psd: 需要SM2加密的密码
    :param key: 公钥
    :return: 加密后的密码
    """
    
    # 还原压缩后的 key
    public_key = re_key(key)
    
    sm2_crypt = sm2.CryptSM2(
        public_key=public_key, private_key='', mode=1)
    return '04' + sm2_crypt.encrypt(psd.encode()).hex()


if __name__ == '__main__':
    public_key = '03bdfbc634f2a6de5cab543f59830418c5ab312ccf13e9a18a947306f0e16d014f'
    passwd = 'a123456'

    print(sm2_encrypt(passwd, public_key))