Last active
December 27, 2020 07:26
-
-
Save TransparentLC/2bfde382067e42fafce1d8f3d255b940 to your computer and use it in GitHub Desktop.
ICAS模拟登录,可以用于对 https://jwxk.jnu.edu.cn/ 等各种使用 ICAS 进行统一登录的系统的自动化操作。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import getpass | |
import pydes | |
import re | |
import requests | |
username = input('Username: ') | |
password = getpass.getpass('Password: ') | |
session = requests.Session() | |
response = session.get('https://icas.jnu.edu.cn/cas/login') | |
icasLoginForm = { | |
'rsa': None, | |
'ul': len(username), | |
'pl': len(password), | |
'lt': re.search('<input type="hidden" id="lt" name="lt" value="([\s\S]*?)" \/>', response.text)[1], | |
'execution': re.search('<input type="hidden" name="execution" value="([\s\S]*?)" \/>', response.text)[1], | |
'_eventId': 'submit', | |
} | |
des = pydes.des() | |
icasLoginForm['rsa'] = f'{username}{password}{icasLoginForm["lt"]}'.encode('utf-16 be').decode('iso-8859-1') | |
if len(icasLoginForm['rsa']) % 8: | |
icasLoginForm['rsa'] += (8 - len(icasLoginForm['rsa']) % 8) * chr(0) | |
icasLoginForm['rsa'] = des.encrypt(b'\x00\x31\x00\x00\x00\x00\x00\x00', icasLoginForm['rsa']) | |
icasLoginForm['rsa'] = des.encrypt(b'\x00\x32\x00\x00\x00\x00\x00\x00', icasLoginForm['rsa']) | |
icasLoginForm['rsa'] = des.encrypt(b'\x00\x33\x00\x00\x00\x00\x00\x00', icasLoginForm['rsa']) | |
icasLoginForm['rsa'] = icasLoginForm['rsa'].encode('iso-8859-1').hex().upper() | |
print('ICAS login form:') | |
for k, v in icasLoginForm.items(): | |
if k == 'rsa': | |
v = v[:4] + '********' + v[-4:] | |
print(f' {k} = {v}') | |
response = session.post(response.url, icasLoginForm) | |
try: | |
dict(x.split('=') for x in requests.utils.urlparse(response.url).query.split('&'))['ticket'] | |
except: | |
print('Failed to login!') | |
exit(-1) | |
# Example: | |
# Get student profile from i.jnu.edu.cn | |
response = session.post( | |
'https://i.jnu.edu.cn/dcp/profile/profile.action', | |
json={ | |
'map': { | |
'method': 'getInfo', | |
'params': None, | |
}, | |
'javaClass': 'java.util.HashMap', | |
}, | |
headers={ | |
'clientType': 'json', | |
'render': 'json', | |
} | |
) | |
print('Profile:') | |
for k, v in response.json()['list'][0]['map'].items(): | |
print(f' {k} = {v}') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 基于 https://github.com/RobinDavid/pydes 进行了修改,原因见下 | |
# | |
# ICAS使用了网上广为流传的一段代码进行3DES加密 | |
# 参见 https://icas.jnu.edu.cn/cas/comm/js/des.js | |
# 但是这段代码在生成子密钥时(函数generateKeys) | |
# 使用的选择置换1(Permuted choice 1)的表搞错了 | |
# 所以直接使用各种标准库加密不能得到相同的结果 | |
# | |
# 找到了这个纯Python的实现,然后把PC1改成了那段代码的错误的表 | |
# 这样就可以模拟登录了 | |
# | |
# 其他的信息是,ICAS的加密是 | |
# 使用密钥'1'、'2'、'3' | |
# 对(username+password+lt) | |
# 进行3DES EEE ECB Zero-padding的加密 | |
# lt是登录页面上<input id="lt">的value,相当于一个CSRF Token | |
# 以上涉及到的字符串都需要通过UTF-16 BE转换成字节 | |
# | |
# 这个库输入的密钥是字节数组,但是明文或密文是字符串啊…… | |
# 总之用ISO-8859-1来回倒腾吧 | |
# Initial permut matrix for the datas | |
PI = [58, 50, 42, 34, 26, 18, 10, 2, | |
60, 52, 44, 36, 28, 20, 12, 4, | |
62, 54, 46, 38, 30, 22, 14, 6, | |
64, 56, 48, 40, 32, 24, 16, 8, | |
57, 49, 41, 33, 25, 17, 9, 1, | |
59, 51, 43, 35, 27, 19, 11, 3, | |
61, 53, 45, 37, 29, 21, 13, 5, | |
63, 55, 47, 39, 31, 23, 15, 7] | |
# Initial permut made on the key | |
# CP_1 = [57, 49, 41, 33, 25, 17, 9, | |
# 1, 58, 50, 42, 34, 26, 18, | |
# 10, 2, 59, 51, 43, 35, 27, | |
# 19, 11, 3, 60, 52, 44, 36, | |
# 63, 55, 47, 39, 31, 23, 15, | |
# 7, 62, 54, 46, 38, 30, 22, | |
# 14, 6, 61, 53, 45, 37, 29, | |
# 21, 13, 5, 28, 20, 12, 4] | |
CP_1 = [ | |
57, 49, 41, 33, 25, 17, 9, | |
1, 58, 50, 42, 34, 26, 18, | |
10, 2, 59, 51, 43, 35, 27, | |
19, 11, 3, 60, 52, 44, 36, | |
28, 20, 12, 4, 61, 53, 45, | |
37, 29, 21, 13, 5, 62, 54, | |
46, 38, 30, 22, 14, 6, 63, | |
55, 47, 39, 31, 23, 15, 7, | |
] | |
# Permut applied on shifted key to get Ki+1 | |
CP_2 = [14, 17, 11, 24, 1, 5, 3, 28, | |
15, 6, 21, 10, 23, 19, 12, 4, | |
26, 8, 16, 7, 27, 20, 13, 2, | |
41, 52, 31, 37, 47, 55, 30, 40, | |
51, 45, 33, 48, 44, 49, 39, 56, | |
34, 53, 46, 42, 50, 36, 29, 32] | |
# Expand matrix to get a 48bits matrix of datas to apply the xor with Ki | |
E = [32, 1, 2, 3, 4, 5, | |
4, 5, 6, 7, 8, 9, | |
8, 9, 10, 11, 12, 13, | |
12, 13, 14, 15, 16, 17, | |
16, 17, 18, 19, 20, 21, | |
20, 21, 22, 23, 24, 25, | |
24, 25, 26, 27, 28, 29, | |
28, 29, 30, 31, 32, 1] | |
# SBOX | |
S_BOX = [ | |
[[14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7], | |
[0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8], | |
[4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0], | |
[15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13], | |
], | |
[[15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10], | |
[3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5], | |
[0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15], | |
[13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9], | |
], | |
[[10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8], | |
[13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1], | |
[13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7], | |
[1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12], | |
], | |
[[7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15], | |
[13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9], | |
[10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4], | |
[3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14], | |
], | |
[[2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9], | |
[14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6], | |
[4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14], | |
[11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3], | |
], | |
[[12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11], | |
[10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8], | |
[9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6], | |
[4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13], | |
], | |
[[4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1], | |
[13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6], | |
[1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2], | |
[6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12], | |
], | |
[[13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7], | |
[1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2], | |
[7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8], | |
[2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11], | |
] | |
] | |
# Permut made after each SBox substitution for each round | |
P = [16, 7, 20, 21, 29, 12, 28, 17, | |
1, 15, 23, 26, 5, 18, 31, 10, | |
2, 8, 24, 14, 32, 27, 3, 9, | |
19, 13, 30, 6, 22, 11, 4, 25] | |
# Final permut for datas after the 16 rounds | |
PI_1 = [40, 8, 48, 16, 56, 24, 64, 32, | |
39, 7, 47, 15, 55, 23, 63, 31, | |
38, 6, 46, 14, 54, 22, 62, 30, | |
37, 5, 45, 13, 53, 21, 61, 29, | |
36, 4, 44, 12, 52, 20, 60, 28, | |
35, 3, 43, 11, 51, 19, 59, 27, | |
34, 2, 42, 10, 50, 18, 58, 26, | |
33, 1, 41, 9, 49, 17, 57, 25] | |
# Matrix that determine the shift for each round of keys | |
SHIFT = [1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1] | |
def string_to_bit_array(text):#Convert a string into a list of bits | |
array = list() | |
for char in text: | |
binval = binvalue(char, 8)#Get the char value on one byte | |
array.extend([int(x) for x in list(binval)]) #Add the bits to the final list | |
return array | |
def bit_array_to_string(array): #Recreate the string from the bit array | |
res = ''.join([chr(int(y,2)) for y in [''.join([str(x) for x in _bytes]) for _bytes in nsplit(array,8)]]) | |
return res | |
def binvalue(val, bitsize): #Return the binary value as a string of the given size | |
binval = bin(val)[2:] if isinstance(val, int) else bin(ord(val))[2:] | |
if len(binval) > bitsize: | |
raise "binary value larger than the expected size" | |
while len(binval) < bitsize: | |
binval = "0"+binval #Add as many 0 as needed to get the wanted size | |
return binval | |
def nsplit(s, n):#Split a list into sublists of size "n" | |
return [s[k:k+n] for k in range(0, len(s), n)] | |
ENCRYPT=1 | |
DECRYPT=0 | |
class des(): | |
def __init__(self): | |
self.password = None | |
self.text = None | |
self.keys = list() | |
def run(self, key, text, action=ENCRYPT, padding=False): | |
if len(key) < 8: | |
raise "Key Should be 8 bytes long" | |
elif len(key) > 8: | |
key = key[:8] # If key size is above 8bytes, cut to be 8bytes long | |
self.password = key | |
self.text = text | |
if padding and action==ENCRYPT: | |
self.addPadding() | |
elif len(self.text) % 8 != 0: # If not padding specified data size must be multiple of 8 bytes | |
raise "Data size should be multiple of 8" | |
self.generatekeys() #Generate all the keys | |
text_blocks = nsplit(self.text, 8) # Split the text in blocks of 8 bytes so 64 bits | |
result = list() | |
for block in text_blocks:#Loop over all the blocks of data | |
block = string_to_bit_array(block) # Convert the block in bit array | |
block = self.permut(block,PI) # Apply the initial permutation | |
g, d = nsplit(block, 32) # g(LEFT), d(RIGHT) | |
tmp = None | |
for i in range(16): # Do the 16 rounds | |
d_e = self.expand(d, E) # Expand d to match Ki size (48bits) | |
if action == ENCRYPT: | |
tmp = self.xor(self.keys[i], d_e) # If encrypt use Ki | |
else: | |
tmp = self.xor(self.keys[15-i], d_e) # If decrypt start by the last key | |
tmp = self.substitute(tmp) # Method that will apply the SBOXes | |
tmp = self.permut(tmp, P) | |
tmp = self.xor(g, tmp) | |
g = d | |
d = tmp | |
result += self.permut(d+g, PI_1) # Do the last permut and append the result to result | |
final_res = bit_array_to_string(result) | |
if padding and action==DECRYPT: | |
return self.removePadding(final_res) # Remove the padding if decrypt and padding is true | |
else: | |
return final_res #Return the final string of data ciphered/deciphered | |
def substitute(self, d_e): # Substitute bytes using SBOX | |
subblocks = nsplit(d_e, 6) # Split bit array into sublist of 6 bits | |
result = list() | |
for i in range(len(subblocks)): #For all the sublists | |
block = subblocks[i] | |
row = int(str(block[0])+str(block[5]),2) # Get the row with the first and last bit | |
column = int(''.join([str(x) for x in block[1:][:-1]]),2) #Column is the 2,3,4,5th bits | |
val = S_BOX[i][row][column] # Take the value in the SBOX appropriated for the round (i) | |
bin = binvalue(val, 4) # Convert the value to binary | |
result += [int(x) for x in bin] # And append it to the resulting list | |
return result | |
def permut(self, block, table): # Permut the given block using the given table (so generic method) | |
return [block[x-1] for x in table] | |
def expand(self, block, table): # Do the exact same thing than permut but for more clarity has been renamed | |
return [block[x-1] for x in table] | |
def xor(self, t1, t2): # Apply a xor and return the resulting list | |
return [x^y for x,y in zip(t1,t2)] | |
def generatekeys(self): # Algorithm that generates all the keys | |
self.keys = [] | |
key = string_to_bit_array(self.password) | |
key = self.permut(key, CP_1) # Apply the initial permut on the key | |
g, d = nsplit(key, 28) # Split it in to (g->LEFT),(d->RIGHT) | |
for i in range(16): # Apply the 16 rounds | |
g, d = self.shift(g, d, SHIFT[i]) #Apply the shift associated with the round (not always 1) | |
tmp = g + d # Merge them | |
self.keys.append(self.permut(tmp, CP_2)) # Apply the permut to get the Ki | |
def shift(self, g, d, n): # Shift a list of the given value | |
return g[n:] + g[:n], d[n:] + d[:n] | |
def addPadding(self): # Add padding to the datas using PKCS5 spec. | |
pad_len = 8 - (len(self.text) % 8) | |
self.text += pad_len * chr(pad_len) | |
def removePadding(self, data): # Remove the padding of the plain text (it assume there is padding) | |
pad_len = ord(data[-1]) | |
return data[:-pad_len] | |
def encrypt(self, key, text, padding=False): | |
return self.run(key, text, ENCRYPT, padding) | |
def decrypt(self, key, text, padding=False): | |
return self.run(key, text, DECRYPT, padding) | |
if __name__ == '__main__': | |
key = "secret_k" | |
text= "Hello wo" | |
d = des() | |
r = d.encrypt(key,text) | |
r2 = d.decrypt(key,r) | |
print("Ciphered: %r" % r) | |
print("Deciphered: ", r2) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment