使用 PyRsa 库解决新版正方教务的 RSA 加密问题并模拟登陆的 Python 实现

前言

先前有一个契机,需要模拟登陆学校所使用的正方教务来获取课程表,其所使用的 RSA 加密方法没有现成的 Python 库可使用,目前其他的 CSDN 博客所采用的方法均无法成功登陆,故只能自己研究正方教务所采用的 crypto/rsa 的 JavaScript 实现并转义为 Python 语言实现。现开源为 PyRsa 库发布到 PyPI。

PyRsa 使用

安装

pip install pyrsa

基本介绍

本模块的基本思路为 1:1 等价转义原生的 JavaScript 库,正方教务上的加密库与安装后 PyRsa 库文件截图对比:
在这里插入图片描述

其中有几个移植 JavaScript 到 Python 过程中值得注意的问题:
  1. 无符号整数位移
    由于 Python 独特的数据类型特性,在数据超过其数据类型长度后则会自动转化为 long,因此不存在JavaScript的 “>>>” 或者 “<<<” 的无符号整数操作,因此需要利用右移操作符 “>>” 来实现无符号整数右移:
def unsigned_right_shift(n, i):
    """
    无符号整数右移
    :param n:
    :param i:
    :return:
    """
    def int_overflow(val):
        maxint = 2147483647
        if not -maxint - 1 <= val <= maxint:
            val = (val + (maxint + 1)) % (2 * (maxint + 1)) - maxint - 1
        return val

    # 数字小于 0,则转为 32 位无符号 uint
    if n < 0:
        n = ctypes.c_uint32(n).value
    # 正常位移位数是为正数,但是为了兼容 js 之类的,负数就右移变成左移
    if i < 0:
        return -int_overflow(n << abs(i))
    # print(n)
    return int_overflow(n >> i)
  1. 原生 js 库 jsbn 用来实现任意精度的整数算法,其 API 类似于java.math.BigIntegerJava 中的 类。在此库中,BigInteger 为一个 Array() 类,但考虑到 Python 的数据类型结构,PyRsa 库采用名为 int_dict 的字典来存储,其键值对与 Array() 的数据一一对应。
    在这里插入图片描述
    在下图中演示的是在 modulus 和 exponent 相同时 Python 实现的 BigInteger 类与 jsbn 库实现的 BigInteger 类的对应情况。其余参数对应 BigInteger 类的 proto 隐式原型。
    在这里插入图片描述
  2. 利用 rng.js 库随机生成 seed 来填充字符串,使得每一次对相同原密码的加密后的字符串不可能重复产生。

基本使用

使用 RsaKey 模块

由于本模块是面向正方教务的加密,因此 modulus 长度应为 172;如果单纯是加密着玩的话,那么 modulus 与 pre_psw 两个参数的长度则有所限制,大概就是

modulus = int(psw / 3) + 15 + psw

嘛,谁会这么无聊干这事儿呢

from PyRsa.pyrsa import RsaKey
from PyRsa.pyb64 import Base64
modulus = "AJftLhHzsQPu1LwCgOR41hRKn4tbaD/ehyZKiBWDYCpaualtMyJIT0SzBl07O2NwjxI8uwr82SMvEW9iiSEoBylHOWNnEzyOYwXb29xMo+D4LTVqMX7NkAliIqH+wOSA1g0DVxmcQWCtGVI4vDUnGIN8tYPlxc9NIXN5zO0HwqKn"
exponent = 'AQAB'
rsakey.set_public(Base64().b64tohex(modulus), Base64().b64tohex(exponent))
psw = '1234567890'
en_psw = Base64().hex2b64(rsakey.encrypt(pre_psw))

模拟登陆正方教务

拿到经过 PyRsa 加密后的字符串后,就可以开始进行正方教务的模拟登陆了。新版正方教务基本样式如下:
在这里插入图片描述

完整实现思路
1. 黑盒模型

黑盒模型
最终获取到的 JSESSIONID 相当于整个教务的唯一识别代码,可用其获取得到任意想获取到的数据。换句话说,JSESSIONID 包含了用户的模拟登陆信息,并有一定的生命周期(没具体测过,不超过一天)。

2. 主要函数实现:
  • 获取初始 cookies:

    在这里插入图片描述
    通过抓包,在 login_slogin.html 页面的 Response Headers 找到 Set-Cookie 的两个参数 BIPipServerjwxtnew_BS80 和 JSESSIONID。
    代码实现如下:

    def get_raw_cookies_csrf(self):
        headers = {
         
            'Host': f'{self.domain}',
            'Connection': 'keep-alive',
            'Upgrade-Insecure-Requests': '1',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) '
                          'Chrome/76.0.3809.87 Safari/537.36',
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;'
                      'q=0.8,application/signed-exchange;v=b3',
            'Accept-Encoding': 'gzip, deflate',
            'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8'
        }
        url = f'http://{self.domain}/jwglxt/xtgl/login_slogin.html'
        res = self.session.get(url, headers=headers)
        doc = pq(res.text)
        self.csrf = doc('#csrftoken').attr('value')
        self.raw_cookie = requests.utils.dict_from_cookiejar(res.cookies)
    
  • 获取 modulus 和 exponent 两个参数生成 RSA 公钥

    在这里插入图片描述
    利用上一步获取到的初始 cookies 的两个参数,在 xhr 异步网页 login_getPublicKey.html 用 GET 方法可以获取到对应 json 的两个参数 exponent 和 modulus。

        def get_json(self):
            self.getpublickey_t = int(time.time() * 1000)
            url = f'http://{self.domain}/jwglxt/xtgl/login_getPublicKey.html?time={self.getpublickey_t}'
            headers = {
         
                'Accept': 'application/json, text/javascript, */*; q=0.01',
                'Accept-Encoding': 'gzip, deflate',
                'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
                'Connection'
  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值