Skip to content

网易云音乐新登录API分析

Lizhooh edited this page Aug 19, 2017 · 4 revisions

2015年7月份,网易云音乐停用了使用已久的旧登录API

原有的 http://music.163.com/api/loginhttp://music.163.com/api/login/cellphone 均不能使用

新 API 被修改为 http://music.163.com/weapi/login/cellphonehttp://music.163.com/weapi/login ,并且将账户信息进行了多重加密进行传输。

加密算法

核心过程如下

    text = {
        'username': username,
        'password': password,
        'rememberLogin': 'true'
    }
    text = json.dumps(text)
    secKey = createSecretKey(16)
    encText = aesEncrypt(aesEncrypt(text, nonce), secKey)
    encSecKey = rsaEncrypt(secKey, pubKey, modulus)
    data = {
        'params': encText,
        'encSecKey': encSecKey
    }

其中modulus\nonce\pubKey均为已知,算法先通过createSecretKey生成一个16位的随机字符串作为密钥secKey,然后将明文text进行两次AES加密获得密文encText,因为secKey是在客户端上生成的,所以还需要对其进行RSA加密再传给服务端。

AES 加密部分

AES加密的具体算法为:AES-128-CBC,输出格式为base64
AES加密时需要指定iv:0102030405060708
AES加密时需要padding

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

这里使用了pycrypto,相比nodejs 的crypto模块来说,需要手动做padding 参考 stackoverflow

RSA 加密部分

RSA 加密采用非常规填充方式,既不是PKCS1也不是PKCS1_OAEP,网易的做法是直接向前补0
这样加密出来的密文有个特点:加密过程没有随机因素,明文多次加密后得到的密文是相同的
然而,我们常用的 RSA 加密模块均不支持此种加密,所以需要手写一段简单的 RSA 加密
加密过程 convertUtf8toHex(reversedText)^e%N
输入过程中需要对加密字符串进行 hex 格式转码

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)

附录

需要的modulus\nonce\pubKey

modulus = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
nonce = '0CoJUm6Qyw8W8jud'
pubKey = '010001'

Python 随机数生成

def createSecretKey(size):
    return (''.join(map(lambda xx: (hex(ord(xx))[2:]), os.urandom(size))))[0:16]

感谢 stkevintan 提供的 nodejs 版本代码,以及 加密算法