網(wǎng)易云音樂APP——接口加密算法

延續(xù)上一篇的文章趾断,今天我想聊聊網(wǎng)易云音樂的接口加密算法(先打開js的項目工程,謝謝https://github.com/Binaryify/NeteaseCloudMusicApi)

隨便搜了一下代碼增显,比如login脐帝,基本能鎖定兩個比較重要的文件,request.js和crypto.js

crytpo.js用來封裝加密算法炸站,也是請求部分的核心之一疚顷,我要做的其實很簡單,用Java實現(xiàn)里面js功能阀坏。
下面是js的登錄代碼封裝

// 手機登錄

const crypto = require('crypto')

module.exports = (query, request) => {
    query.cookie.os = 'pc'
    const data = {
        phone: query.phone,
        countrycode: query.countrycode,
        password: crypto.createHash('md5').update(query.password).digest('hex'),
        rememberLogin: 'true'
    }
    console.log(data);
    return request(
        'POST', `https://music.163.com/weapi/login/cellphone`, data,
        {crypto: 'weapi', ua: 'pc', cookie: query.cookie, proxy: query.proxy}
    )
}

要注意幾個細節(jié):
1忌堂、query.cookie.os = 'pc',在Java里面其實就是在請求的cookie里面加上"oc"枷遂,"pc"(用hashmap設(shè)置到"Cookie"里面李命,然后一起封裝到header里面)
2、對密碼做MD5加密黔州,這個沒啥難度阔籽,通用加密方式,如果你要偷懶也行

package music.netease.com.neteasecloudmusic.utils;

import java.security.MessageDigest;

public class MD5Utils {
    public final static String MD5(String s) {
        char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
                'a', 'b', 'c', 'd', 'e', 'f' };
        try {
            byte[] btInput = s.getBytes();
            // 獲得MD5摘要算法的 MessageDigest 對象
            MessageDigest mdInst = MessageDigest.getInstance("MD5");
            // 使用指定的字節(jié)更新摘要
            mdInst.update(btInput);
            // 獲得密文
            byte[] md = mdInst.digest();
            // 把密文轉(zhuǎn)換成十六進制的字符串形式
            int j = md.length;
            char str[] = new char[j * 2];
            int k = 0;
            for (int i = 0; i < j; i++) {
                byte byte0 = md[i];
                str[k++] = hexDigits[byte0 >>> 4 & 0xf];
                str[k++] = hexDigits[byte0 & 0xf];
            }
            return new String(str);
        } catch (Exception e) {
            return null;
        }
    }
}

MD5加密后的結(jié)果最好驗證一下,對比一下js和Java加密后的打印數(shù)據(jù)

3证薇、接著可以直接跳到request.js那個問題匆篓,里面封裝了最終的網(wǎng)絡(luò)請求,debug或者log能很快定位到參數(shù)處理的位置箩张,當然窗市,如果代碼感覺好可以直接看到


    if (options.crypto == 'weapi') {
      let csrfToken = (headers['Cookie'] || '').match(/_csrf=([^(;|$)]+)/)

      console.log(csrfToken);

      data.csrf_token = csrfToken ? csrfToken[1] : ''
      data = encrypt.weapi(data)
      console.log('weapi:'+queryString.stringify(data));
      url = url.replace(/\w*api/, 'weapi')

    } else if (options.crypto == 'linuxapi') {
      data = encrypt.linuxapi({
        method: method,
        url: url.replace(/\w*api/, 'api'),
        params: data
      })
      
      headers['User-Agent'] =
        'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36'
      url = 'https://music.163.com/api/linux/forward'
    }

登陸請求的option.crypto是weapi咨察,請求的參數(shù)都會以鍵值對的方式放到map中,然后轉(zhuǎn)成jsonObject調(diào)用Encrypt赴肚,看return就知道最終返回的還是個map

js:
function Encrypt(obj) {
  var text = JSON.stringify(obj);
  console.log(text);
  var secKey = createSecretKey(16)
  console.log(secKey);
  var encText = aesEncrypt(aesEncrypt(text, nonce), secKey);
  var encSecKey = rsaEncrypt(secKey, pubKey, modulus);

  return {
    params: encText,
    encSecKey: encSecKey
  }
}

對應的Java:
  public static Map<String, String> encrypt(String text) {
        String secKey = getRandomString(16);
//        String encText = aesEncrypt(aesEncrypt(text, nonce), secKey);
        String encText = aesEncrypt(aesEncrypt(text, nonce), secKey);
        String encSecKey = rsaEncrypt(secKey, pubKey, modulus);

        Map<String, String> map = new HashMap<String, String>();
        map.put(PARAMS, encText);
        map.put(ENCSECKEY, encSecKey);
        return map;
    }

這部分的代碼大致意思應該是誉券,把傳進來的參數(shù)轉(zhuǎn)成字符串賦值給text刊愚,Java我就用string,randomByte(16)就很簡單了商玫,獲取一個16位的隨機數(shù)(從26個字母大小寫+0~9這10個數(shù)字)牡借,賦值給secretKey,然后再這兩個參數(shù)做兩次aes加密炬藤,一次rsa加密碴里。
隨機數(shù)的處理我在Java里面單獨寫了一個方法,不知道為啥js可以這么簡潔羹膳,我一定要找時間學一下

  /*獲取由字母和數(shù)字組成的隨機數(shù)*/
    static private String getRandomString(int length){
        String str="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        Random random=new Random();
        StringBuffer sb=new StringBuffer();
        for(int i=0;i<length;i++){
            int number=random.nextInt(62);
            sb.append(str.charAt(number));
        }
        return sb.toString();
    }

str對應了js代碼里面的base62那個變量陵像,aes加密需要傳入text和seckey

js:
function aesEncrypt(text, secKey) {
  var _text = text;
  var lv = new Buffer('0102030405060708', "binary");
  var _secKey = new Buffer(secKey, "binary");
  var cipher = crypto.createCipheriv('AES-128-CBC', _secKey, lv);
  var encrypted = cipher.update(_text, 'utf8', 'base64');
  encrypted += cipher.final('base64');
  return encrypted;
}
java:
    private static String aesEncrypt(String text,String mode,String key,IvParameterSpec iv){
        try {
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);

            byte[] encrypted = cipher.doFinal(text.getBytes());

            return Base64Encoder.encode(encrypted);
        } catch (Exception e) {
            return "";
        }

    }

沒啥好說的寇壳,就按照原裝的寫法,百度图贸,弄出一個類似的表達式冕广,把參數(shù)對應上撒汉,有一個關(guān)鍵的字段nonce,應該是秘鑰睬辐,第一次aes加密用text(也就是參數(shù)轉(zhuǎn)化的json)和nonce,再將加密后的結(jié)果和那個16位的隨機數(shù)(隨機秘鑰)穿進去生成最終的encText侵俗。最終的encSecKey通過rsa加密實現(xiàn)

js:
function rsaEncrypt(text, pubKey, modulus) {
  var _text = text.split('').reverse().join('');
  var biText = bigInt(new Buffer(_text).toString('hex'), 16),
      biEx = bigInt(pubKey, 16),
      biMod = bigInt(modulus, 16),
      biRet = biText.modPow(biEx, biMod);
  return zfill(biRet.toString(16), 256);
}

java:
   private static String rsaEncrypt(String text, String pubKey, String modulus) {
        text = new StringBuilder(text).reverse().toString();
        BigInteger rs = new BigInteger(String.format("%x", new BigInteger(1, text.getBytes())), 16)
                .modPow(new BigInteger(pubKey, 16), new BigInteger(modulus, 16));
        String r = rs.toString(16);
        if (r.length() >= 256) {
            return r.substring(r.length() - 256, r.length());
        } else {
            while (r.length() < 256) {
                r = 0 + r;
            }
            return r;
        }
    }

text就是那個16位的隨機數(shù)隘谣,pubkey和modulus是抓包拿到的寻歧,js文件里面有。最后將兩個參數(shù)封裝到map里面返回出去码泛。
不要看到j(luò)s代碼的接口都是get猾封,其實那是因為大神在底層已經(jīng)封裝了一層,所有的接口都能用post的方式實現(xiàn)噪珊,還有就是很多接口的請求地址并非是大神提供的那些晌缘,你要好好接口文檔,或者學我直接調(diào)試看日志卿城。加密后的map最后轉(zhuǎn)化成formbody枚钓,然后用post的方式去請求,完工瑟押!至于大神提到的很多head cookies搀捷,我跑了一下Java控制臺debug了一下,基本都不用帶多望。對于js加密轉(zhuǎn)Java加密嫩舟,我就介紹到這里怀偷,有啥不理解的歡迎評論留言家厌,Android版本的網(wǎng)易云音樂包括登錄只實現(xiàn)了5個接口,獲取用戶歌單椎工、歌單的歌曲列表饭于、收藏的MV,視頻播放鏈接獲取维蒙。然后加上了ijkPlayer庫掰吕,實現(xiàn)基本的視頻播放功能,由于頁面太low颅痊,功能太少就暫時不開放了殖熟,爭取月底完成音樂播放功能,到時候?qū)⑺写a開放到GitHub上斑响,需要技術(shù)交流的話可以私下溝通菱属。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末钳榨,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子纽门,更是在濱河造成了極大的恐慌薛耻,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件膜毁,死亡現(xiàn)場離奇詭異昭卓,居然都是意外死亡,警方通過查閱死者的電腦和手機瘟滨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來能颁,“玉大人杂瘸,你說我怎么就攤上這事』锞眨” “怎么了败玉?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長镜硕。 經(jīng)常有香客問我运翼,道長,這世上最難降的妖魔是什么兴枯? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任血淌,我火速辦了婚禮,結(jié)果婚禮上财剖,老公的妹妹穿的比我還像新娘悠夯。我一直安慰自己,他們只是感情好躺坟,可當我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布沦补。 她就那樣靜靜地躺著,像睡著了一般咪橙。 火紅的嫁衣襯著肌膚如雪夕膀。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天美侦,我揣著相機與錄音产舞,去河邊找鬼。 笑死音榜,一個胖子當著我的面吹牛庞瘸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播赠叼,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼擦囊,長吁一口氣:“原來是場噩夢啊……” “哼违霞!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起瞬场,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤买鸽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后贯被,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體眼五,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年彤灶,在試婚紗的時候發(fā)現(xiàn)自己被綠了看幼。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡幌陕,死狀恐怖诵姜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情搏熄,我是刑警寧澤棚唆,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站心例,受9級特大地震影響宵凌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜止后,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一瞎惫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧坯门,春花似錦微饥、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至现恼,卻和暖如春肃续,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背叉袍。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工始锚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人喳逛。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓瞧捌,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子姐呐,可洞房花燭夜當晚...
    茶點故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容