网易云音乐API分析 weapi(早期版本) - zousandian/NeteaseCloudMusicApi GitHub Wiki
0. 状态
该第二版 API 为早期版本,返回结构和第一版类似,交互地址有个特征 weapi
目前网页端采用的是新的第二版 API 详见 网易云音乐API分析(v2)新
1. 总览
所有交互需要指定一个参数:
key | value |
---|---|
referer | http://music.163.com |
下面列出的所有参数均为明文参数,实际提交需要进行加密,详见最下方第 7 条目
2. 搜索
POST http://music.163.com/weapi/search/pc
新方案
POST http://music.163.com/weapi/cloudsearch/get/web?csrf_token=
返回的结构不一样
明文参数
s: 关键字
limit: 返回数据条数限制
type: 搜索类型
- 1 单曲
- 10 专辑
- 100 歌手
- 1000 歌单
- 1002 用户
- 1009 电台
- NULL 推荐(实测失效)
offset: 偏移数量,用于分页
csrf_token: 非关键操作,值可为空
3. 专辑
POST http://music.163.com/weapi/album/{album_id}
参数
album_id(GET): 专辑 ID
csrf_token: 非关键操作,值可为空
4. 歌单
POST http://music.163.com/weapi/playlist/detail
参数
id: 歌单 ID
csrf_token: 非关键操作,值可为空
5. 单曲
POST http://music.163.com/weapi/song/detail
参数
ids: 歌曲 ID,数组形式,可为多个
csrf_token: 非关键操作,值可为空
6. URL 分析
http://m10.music.126.net/[expTime]/[key1]/ymusic/[key2]/[key2]/[key3]/[MD5].mp3
参数
expTime: 过期时间戳,标识本 URL 只能在过期时间前请求,否则返回 403
key123: 我还没搞明白,求大神们指教
MD5: 该歌曲的 MD5 校验码,小写
7. 加密请求
实际请求的参数应当加密为如下形式:
params: Db3xGrqMzXwQqWAIe7AlqILYssUuypZlUS+6Dr8g//OEK8SPpjsZht2j+/C+UZ/rAh1bsXoFDEZxQUwdVB5oc1nSRRT3gpmRgzmwhR8yBCboIk+Uf8PvV2azs2WQ10zewCEps4IAIW1Dbes4jxGcEh7pmUXurQQcrjf9VjzOp64XwKuzunQbb0JG8CwybMIyHjWAC6osTnopHVkSikLsMggTLCVADoK3s+a75VWaYY0srdCoS3FJq8tvvnVGim6k3TpsFkiII/r5DCm8EyvfLrZcsiB5Kpx96ZjxZXYw4yU=
encSecKey: 8ac3219a491bfde0c81532fb4d6c8cd919cc2613631f11a82a72a1fd02e535eb5fbce3c51e7bb8ad2ba06590c391ddec326aa6d6535f36bcb2dd074cf9601f1e874fbfe96787f341d824ea8f03781713355e745472949cb44d8b4c6ed1c76944e2d684add7e746f8ab6119caa7270dd3373320345d946dfb2647c66ab6ef51e5
其中 params 为 AES 对称加密后的参数,encSecKey 为 RSA 加密后的 AES 密钥。 所有公钥等参数详见代码,这里不列出。
1. 生成 AES 密钥
AES 密钥需要随机,否则服务器会 dump 掉相同密钥的请求(数量多的话)。
密钥的形式为 16 位随机字母或数字,[0-9a-zA-Z]
function createSecretKey($length=16){
$str='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$r='';
for($i=0;$i<$length;$i++){
$r.=$str[rand(0,strlen($str)-1)];
}
return $r;
}
2. AES 加密
在上述 API 中,首先将 POST 参数值拼写为如下形式,以搜索 hello
为例:
{"s":"hello","type":"1","limit":"1000","offset":"0","csrf_token":""}
然后对上面的明文进行两次 AES 加密,第一次密钥为 nonce,第二次为第一步生成的密钥。
$data['params']=$this->aes_encode($string,$this->_nonce);
$data['params']=$this->aes_encode($data['params'],$this->_secretKey);
AES 加密的具体算法为:AES-128-CBC
,输出格式为 base64
AES 加密时需要指定 iv:0102030405060708
本步骤加密后的结果即为 params
3. RSA 加密
最后对第一步生成的 secretKey 进行 RSA 加密,不过这个加密非常诡异,不是标准的形式。下面引用 darknessomi
的结论
RSA 加密采用非常规填充方式,既不是
PKCS1
也不是PKCS1_OAEP
,网易的做法是直接向前补0
这样加密出来的密文有个特点:加密过程没有随机因素,明文多次加密后得到的密文是相同的
然而,我们常用的RSA
加密模块均不支持此种加密,所以需要手写一段简单的RSA
加密
加密过程convertUtf8toHex(reversedText)^e%N
输入过程中需要对加密字符串进行hex
格式转码
PHP 中需要使用大数运算类,如果嫌不够清真的话可以自己实现,需要使用快速幂算法加速
function rsa_encode($text){
$rtext=strrev(utf8_encode($text));
$keytext=$this->bchexdec($this->strToHex($rtext));
$a=new Math_BigInteger($keytext);
$b=new Math_BigInteger($this->bchexdec($this->_pubKey));
$c=new Math_BigInteger($this->bchexdec($this->_modulus));
$key=$a->modPow($b, $c)->toHex();
return str_pad($key,256,'0',STR_PAD_LEFT);
}
function bchexdec($hex){
$dec=0;
$len=strlen($hex);
for($i=0;$i<$len;$i++) {
$dec=bcadd($dec,bcmul(strval(hexdec($hex[$i])),bcpow('16',strval($len-$i-1))));
}
return $dec;
}
function strToHex($str){
$hex='';
for($i=0;$i<strlen($str);$i++){
$hex.=dechex(ord($str[$i]));
}
return $hex;
}
本步骤加密后的结果即为 encSecKey