网易云音乐登陆签到

;;;; 网易云音乐 签到功能
;;;; 使用: (cl-163-music:daily-sign "username" "password")


(ql:quickload '("drakma"
        "yason"
        "flexi-streams"
        "cl-base64"
        "ironclad"))

(defpackage #:cl-163-music
  (:use :cl))

(in-package :cl-163-music)

(defun square (x)
  (* x x))

(defun fast-expt (base exponent)
  "base ^ power"
  (labels ((expt-iter (b e a)
         (cond ((= e 0) a)
           ((evenp e) (expt-iter (square b) (/ e 2) a))
           (t (expt-iter b (- e 1) (* b a))))))
    (expt-iter base exponent 1)))

(defun expt-mod (base exponent modulus)
  "As (mod (expt base exponent) modulus), but more efficient.
^表示幂运算  %表示取余 
1. (x * y) % m = (x * (y % m)) % m = ((x % m) * (y % m)) % m
2. (x ^ y) % m = ((x % m) ^ y) % m

e.g.
(evenp n) => t
x^n % m ==> (x^(n / 2))^2 % m ==> (x^(n / 2) % m)^2 % m
(evenp n) => nil
x^n % m ==> (x * x^(n - 1)) % m ==> (x * (x^(n - 1) % m)) % m
"
  (if (= exponent 0)
      1
      (if (evenp exponent)
      (mod (square (expt-mod base (/ exponent 2) modulus)) modulus)
      (mod (* base (expt-mod base (- exponent 1) modulus)) modulus))))


(defun string+ (&rest s)
  (apply #'concatenate 'string s))

(defun md5 (str)
  (ironclad:byte-array-to-hex-string
   (ironclad:digest-sequence
    :md5    
    ;(ironclad:ascii-string-to-byte-array str)
    (flexi-streams:string-to-octets str :external-format :utf8))))

(defun create-secret-key (&optional (length 16))
  (cond ((or (< length 0) (> length 32)) (error "Illegal length. Need [0 - 32]"))
    (t (let ((string "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"))
       (labels ((random-string ()
              (string (char string (random (length string)))))
            (create (depth)
              (if (= 0 depth)
              (random-string)
              (string+ (random-string) (create (- depth 1))))))
         (create (- length 1)))))))

(defun aes-padding (text)
  (let* ((pad-num (- 16 (mod (length text) 16)))
    (str (string (code-char pad-num))))
    (labels ((fn (depth)
           (if (= 0 depth)
           str
           (string+ str (fn (- depth 1))))))
      (string+ text (fn (- pad-num 1))))))

(defun add-padding (enc-text modulus)
  (let ((ml (length modulus)))
    (labels ((calc-num (num i)
           (if (and (> num 0) (char= #\0 (aref modulus i)))
           (calc-num (- num 1) (+ i 1))
           (- num (length enc-text))))
         (res-str (i)
           (if (= i 0)
           enc-text
           (string+ "0" (res-str (- i 1))))))
      (res-str (calc-num ml 0)))))

(defun convert-utf8-to-hex (str)
  (let* ((arr (flexi-streams:string-to-octets str :external-format :utf8))
     (len (length arr))
     (radix 16))
    (labels ((convert (arr index str-hex)
           (if (= index (- len 1))
           (concatenate 'string
                str-hex
                (write-to-string (aref arr index) :base radix))
           (convert arr
                (+ 1 index)
                (concatenate 'string
                     str-hex
                     (write-to-string (aref arr index)
                              :base radix))))))
      (convert arr 0 nil))))

(defun aes-cbc-encrypt (plaintext key &optional (iv "0102030405060708"))
  "
AES具体算法: AES-128-CBC, 输出格式: base64
默认初始化向量 0102030405060708
明文需要padding
"
  (let* ((cipher (ironclad:make-cipher 'ironclad:aes
                       :key (flexi-streams:string-to-octets key)
                       :mode 'ironclad:cbc
                       :initialization-vector (flexi-streams:string-to-octets iv)))
     (ptp (aes-padding plaintext))
     (ptp-byte-arr (flexi-streams:string-to-octets ptp :external-format :utf8))
     (cipher-byte-arr (make-array (length ptp-byte-arr)
                      :initial-element 0
                      :element-type '(unsigned-byte 8))))
    (ironclad:encrypt cipher ptp-byte-arr cipher-byte-arr)
    (cl-base64:usb8-array-to-base64-string cipher-byte-arr)))

(defun rsa-encrypt (text pubkey modulus)
  "
RSA加密采用非常规填充方式(非PKCS1 / PKCS1_OAEP)
此处是向前补0
这样加密出来的密文有个特点:加密过程没有随机因素,明文多次加密后得到的密文是相同的
然而,我们常用的 RSA 加密模块均不支持此种加密,所以需要手写一段简单的 RSA 加密
加密过程 convertUtf8toHex(reversedText) ^ e % N
输入过程中需要对加密字符串进行 hex 格式转码
"
  (let ((n-text (parse-integer (convert-utf8-to-hex (reverse text)) :radix 16))
    (n-pubkey (parse-integer pubkey :radix 16))
    (n-modulus (parse-integer modulus :radix 16)))
    (add-padding (write-to-string (expt-mod n-text n-pubkey n-modulus) :base 16) modulus)))

(defun encrypt-info (&rest lst)
  (let ((table (make-hash-table :size (length lst)))
    (sec-key (create-secret-key))
    (nonce "0CoJUm6Qyw8W8jud")
    (pubkey "010001")
    (modulus "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"))
    (dotimes (i (length lst))
      (let ((e (nth i lst)))
    (setf (gethash (car e) table) (cdr e))))
    (let* ((text (with-output-to-string (stream) (yason:encode table stream)))
       (enc-text (aes-cbc-encrypt (aes-cbc-encrypt text nonce) sec-key))
       (enc-sec-key (rsa-encrypt sec-key pubkey modulus)))
      (list (cons "params" enc-text)
        (cons "encSecKey" (string-downcase enc-sec-key))))))

(defun encrypt-user-account (username password &optional (remember "true"))
  (encrypt-info `("username" . ,username)
        `("password" . ,(md5 password))
        `("rememberLogin" . ,remember)))


;;;;网易云登录

;;;;登录整体思路
;    text = {
;        username: xx,
;        password: md5(xx),
;        rememberLogin: true
;    }
;    
;    sec-key = random-secret-key (16)
;    
;    enc-text = aes(aes(text, nonce), sec-key)
;    enc-sec-key = rsa(sec-key, pub-key, modulus)
;
;    http request
;    post parameters:
;        params = enc-text
;        encSecKey = enc-sec-key

(defun mail-login (username password)
  "
项目最终目的就是想实现自动签到
目前只需要cookie就够了
捕捉: 返回信息(目前暂时将json转为plist)/http状态码/和cookie集合
"
  (let ((cookie-jar (make-instance 'drakma:cookie-jar)))
    (multiple-value-bind (stream http-code)
    (drakma:http-request "http://music.163.com/weapi/login/"
                 :method :post
                 :user-agent "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:46.0) Gecko/20100101 Firefox/46.0"
                 :accept "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
                 :content-type "application/x-www-form-urlencoded"
                 ;对于请求头 如果已有关键字选项 就不能放入additional-headers
                 :additional-headers '(("Host" . "music.163.com")
                           ("Referer" . "http://music.163.com")
                           ("Accept-Language" . "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3")
                           ("Accep:at-Encoding" . "gzip, deflate"))
                 :parameters (encrypt-user-account username password)
                 :external-format-in :utf8
                 :external-format-out :utf8
                 :want-stream t
                 :cookie-jar cookie-jar)
      ;(setf (flexi-streams:flexi-stream-external-format stream) :utf8)
      ;(values (yason:parse stream :object-as :plist) http-code cookie-jar)
      (values stream http-code cookie-jar))))


;;;; 每日签到
(defun daily-sign-with-cookie (cookie-jar &optional (type 0))
  (multiple-value-bind (stream http-code)
      (drakma:http-request "http://music.163.com/weapi/point/dailyTask"
               :method :post
               :user-agent " Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:46.0) Gecko/20100101 Firefox/46.0"
               :accept "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
               :content-type "application/x-www-form-urlencoded"
               :additional-headers '(("Host" . "music.163.com")
                         ("Referer" . "http://music.163.com/discover")
                         ("Accept-Language" . "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3")
                         ("Accep:at-Encoding" . "gzip, deflate"))
               :parameters (encrypt-info `("type" . ,type))
               :external-format-in :utf8
               :external-format-out :utf8
               :want-stream t
               :cookie-jar cookie-jar)
    ;(setf (flexi-streams:flexi-stream-external-format stream) :utf8)
    ;(values (yason:parse stream :object-as :plist) http-code)
    (values stream http-code)))

(defun daily-sign (username password)
  (multiple-value-bind (stream http-code cookie-jar)
      (mail-login username password)
    (if (= http-code 200)
    (let ((m (daily-sign-with-cookie cookie-jar 0))
          (w (daily-sign-with-cookie cookie-jar 1)))
      (setf (flexi-streams:flexi-stream-external-format m) :utf8)
      (setf (flexi-streams:flexi-stream-external-format w) :utf8)
      (values (format nil "mobile daily sign: ~a" (yason:parse m :object-as :plist))
          (format nil "web daily sign: ~a" (yason:parse w :object-as :plist)))))))
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容