-
Notifications
You must be signed in to change notification settings - Fork 0
Description
参考文章:
- https://github.com/darknessomi/musicbox/wiki/%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90%E6%96%B0%E7%89%88WebAPI%E5%88%86%E6%9E%90%E3%80%82
- http://www.ruanyifeng.com/blog/2013/07/rsa_algorithm_part_two.html
下面这段代码来自上面这篇文章
modulus = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
nonce = '0CoJUm6Qyw8W8jud'
pubKey = '010001'
def createSecretKey(size):
return (''.join(map(lambda xx: (hex(ord(xx))[2:]), os.urandom(size))))[0:16]
def aesEncrypt(text, secKey):
pad = 16 - len(text) % 16
text = text + pad * chr(pad)
encryptor = AES.new(secKey, 2, '0102030405060708')
ciphertext = encryptor.encrypt(text)
ciphertext = base64.b64encode(ciphertext)
return ciphertext
def rsaEncrypt(text, pubKey, modulus):
text = text[::-1]
rs = int(text.encode('hex'), 16) ** int(pubKey, 16) % int(modulus, 16)
return format(rs, 'x').zfill(256)
def encrypted_request(text):
text = json.dumps(text)
secKey = createSecretKey(16)
encText = aesEncrypt(aesEncrypt(text, nonce), secKey)
encSecKey = rsaEncrypt(secKey, pubKey, modulus)
data = {
'params': encText,
'encSecKey': encSecKey
}
return data
这段代码最难懂的应该是 rs = int(text.encode('hex'), 16) ** int(pubKey, 16) % int(modulus, 16)
这句话。所以我就分析分析这句话,以及这些变量的作用。
上面的modulus
是个16进制的数,这个数由两个质数相乘得到,转换为2进制之后的长度为1024。它的长度代表了RSA加密算法的密钥的长度。目前技术,1024位长度的密钥基本不能被破解。
pubKey
应该是一个小于φ(modulus) 的一个随机整数。modulus
和pubKey
组合起来就是RSA加密的公钥。(所以,这个pubkey变量命名其实不是很妥)
rs = int(text.encode('hex'), 16) ** int(pubKey, 16) % int(modulus, 16)
,这段代码的根据是这个公式:m^e ≡ c (mod n)
。m 相当于 int(text.encode('hex'), 16)
, e 相当于 int(pubKey, 16), n 相当于int(modulus, 16),rs 相当于 c,也就是加密后的内容。所以,这句话的意思就是:使用公钥对text进行加密,得到rs。
encrypted_request
这个函数比较好理解。
先secKey = createSecretKey(16)
随机生成一个密钥。
代码中的 nonce = '0CoJUm6Qyw8W8jud'
的 nonce 变量也是一个密钥,是AES加密的密钥。
encText = aesEncrypt(aesEncrypt(text, nonce), secKey)
就是先使用nonce作为AES密钥对text加密一次,然后使用随机生成的密钥seckey对加密后的文字再加密一次得到 encText。
到这里为止,我再次把所知道的东西列出来:
- 我们知道了 RSA 加密算法的公钥。服务器有私钥,用来解密消息。
- 我们知道了网页端对 内容 进行了两次AES加密,才把内容发给后台,而第一次AES加密的密钥我们已经知道(服务器也知道)。但是第二次AES加密的密钥是随机生成的,程序知道,我们不知道,服务器也不知道。
所以程序对这个 随机生成的密钥 使用 RSA 加密,发给后端,后端就知道了这个随机密钥到底是多少。
基本完了。
问题来了:int(text.encode('hex'), 16) ** int(pubKey, 16) % int(modulus, 16)
这不就是 RSA 加密算法么,为啥要自己写,难道没有现成的函数么。答案是有的,所以 rsa_encrypt
这个函数可以改写为:
from Crypto.PublicKey import RSA
def rsa_encrypt(self, text):
e = '010001'
n = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615'\
'bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf'\
'695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46'\
'bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b'\
'8e289dc6935b3ece0462db0a22b8e7'
reverse_text = text[::-1]
pub_key = RSA.contruct([int(n, 16), int(e, 16)])
encrypt_text = pub_key.encrypt(reverse_text)[0]
return encrypt_text
不过,如果当初作者写成这个样子,我也就不去研究 RSA 算法的原理了。(一把泪 =.= )不过也好,学了点数学。
另外,一个有趣的地方:
运算 int(text.encode('hex'), 16) ** int(pubKey, 16)
需要几秒的时间,但是运行这整句代码,却可以秒出结果。(表示不懂啊)
使用 python3 改写之后
def _create_aes_key(self, size):
return (''.join([hex(b)[2:] for b in os.urandom(size)]))[0:16]
def _aes_encrypt(self, text, key):
pad = 16 - len(text) % 16
text = text + pad * chr(pad)
encryptor = AES.new(key, 2, '0102030405060708')
enc_text = encryptor.encrypt(text)
enc_text_encode = base64.b64encode(enc_text)
return enc_text_encode
def _rsa_encrypt(self, text):
e = '010001'
n = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615'\
'bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf'\
'695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46'\
'bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b'\
'8e289dc6935b3ece0462db0a22b8e7'
reverse_text = text[::-1]
pub_key = RSA.construct([int(n, 16), int(e, 16)])
encrypt_text = pub_key.encrypt(int(binascii.hexlify(reverse_text), 16),
None)[0]
return format(encrypt_text, 'x').zfill(256)
def encrypt_request(self, data):
text = json.dumps(data)
first_aes_key = '0CoJUm6Qyw8W8jud'
second_aes_key = self._create_aes_key(16)
enc_text = self._aes_encrypt(
self._aes_encrypt(text, first_aes_key).decode('ascii'),
second_aes_key).decode('ascii')
enc_aes_key = self._rsa_encrypt(second_aes_key.encode('ascii'))
payload = {
'params': enc_text,
'encSecKey': enc_aes_key,
}
return payload
Activity
[-]网易云音乐网页端 API 的加密算法详细分析[/-][+]网易云音乐网页端 API 的加密算法代码的分析[/+]jixunmoe commentedon Sep 21, 2016
运算整句快应该是 python 做了优化吧。
https://en.wikipedia.org/wiki/Modular_exponentiation