Some checks failed
Build docker and publish / build (20.15.1) (push) Failing after 4m44s
186 lines
4.8 KiB
Go
186 lines
4.8 KiB
Go
//go:build ignore
|
||
|
||
package main
|
||
|
||
import (
|
||
"bytes"
|
||
"crypto/md5"
|
||
"crypto/sha256"
|
||
"encoding/base64"
|
||
"encoding/hex"
|
||
"encoding/json"
|
||
"flag"
|
||
"fmt"
|
||
"io"
|
||
"net/http"
|
||
"time"
|
||
|
||
"github.com/forgoer/openssl"
|
||
)
|
||
|
||
// ===== AES 加解密(与 pkg/aes/aes.go 一致)=====
|
||
|
||
func generateKey(key string) []byte {
|
||
hash := sha256.Sum256([]byte(key))
|
||
return hash[:32]
|
||
}
|
||
|
||
func generateIv(iv, key string) []byte {
|
||
h := md5.New()
|
||
h.Write([]byte(iv))
|
||
return generateKey(hex.EncodeToString(h.Sum(nil)) + key)
|
||
}
|
||
|
||
func aesEncrypt(plainText []byte, keyStr string) (string, string, error) {
|
||
nonce := fmt.Sprintf("%x", time.Now().UnixNano())
|
||
key := generateKey(keyStr)
|
||
iv := generateIv(nonce, keyStr)
|
||
dst, err := openssl.AesCBCEncrypt(plainText, key, iv, openssl.PKCS7_PADDING)
|
||
if err != nil {
|
||
return "", "", err
|
||
}
|
||
return base64.StdEncoding.EncodeToString(dst), nonce, nil
|
||
}
|
||
|
||
func aesDecrypt(cipherText, keyStr, ivStr string) (string, error) {
|
||
decode, err := base64.StdEncoding.DecodeString(cipherText)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
key := generateKey(keyStr)
|
||
iv := generateIv(ivStr, keyStr)
|
||
dst, err := openssl.AesCBCDecrypt(decode, key, iv, openssl.PKCS7_PADDING)
|
||
return string(dst), err
|
||
}
|
||
|
||
// ===== 主逻辑 =====
|
||
|
||
func main() {
|
||
deviceID := flag.String("id", "", "设备 ID (identifier)")
|
||
secret := flag.String("secret", "", "security_secret (device.security_secret)")
|
||
host := flag.String("host", "https://api.hifast.biz", "API 地址")
|
||
flag.Parse()
|
||
|
||
if *deviceID == "" || *secret == "" {
|
||
fmt.Println("用法: go run scripts/debug_device_login.go -id <设备ID> -secret <security_secret>")
|
||
return
|
||
}
|
||
|
||
// 1. 构造登录请求体
|
||
loginBody := map[string]interface{}{
|
||
"identifier": *deviceID,
|
||
"user_agent": "DebugScript/1.0",
|
||
}
|
||
loginJSON, _ := json.Marshal(loginBody)
|
||
|
||
// 2. AES 加密请求体
|
||
encData, nonce, err := aesEncrypt(loginJSON, *secret)
|
||
if err != nil {
|
||
fmt.Printf("❌ 加密失败: %v\n", err)
|
||
return
|
||
}
|
||
|
||
encBody := map[string]interface{}{
|
||
"data": encData,
|
||
"time": nonce,
|
||
}
|
||
encBodyJSON, _ := json.Marshal(encBody)
|
||
|
||
fmt.Printf("📤 登录请求体(加密): %s\n\n", encBodyJSON)
|
||
|
||
// 3. 发起设备登录请求
|
||
loginURL := *host + "/v1/auth/login/device"
|
||
req, _ := http.NewRequest("POST", loginURL, bytes.NewReader(encBodyJSON))
|
||
req.Header.Set("Content-Type", "application/json")
|
||
req.Header.Set("Login-Type", "device")
|
||
|
||
client := &http.Client{Timeout: 10 * time.Second}
|
||
resp, err := client.Do(req)
|
||
if err != nil {
|
||
fmt.Printf("❌ 登录请求失败: %v\n", err)
|
||
return
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
respBody, _ := io.ReadAll(resp.Body)
|
||
fmt.Printf("📥 登录响应(原始): %s\n\n", respBody)
|
||
|
||
// 4. 解密响应
|
||
var respMap map[string]interface{}
|
||
if err := json.Unmarshal(respBody, &respMap); err != nil {
|
||
fmt.Printf("❌ 解析响应 JSON 失败: %v\n", err)
|
||
return
|
||
}
|
||
|
||
var token string
|
||
if dataField, ok := respMap["data"]; ok {
|
||
switch d := dataField.(type) {
|
||
case map[string]interface{}:
|
||
// 加密响应
|
||
encResp, _ := d["data"].(string)
|
||
ivResp, _ := d["time"].(string)
|
||
if encResp != "" && ivResp != "" {
|
||
decrypted, err := aesDecrypt(encResp, *secret, ivResp)
|
||
if err != nil {
|
||
fmt.Printf("❌ 解密响应失败: %v\n", err)
|
||
return
|
||
}
|
||
fmt.Printf("📥 登录响应(解密): %s\n\n", decrypted)
|
||
var loginData map[string]interface{}
|
||
if err := json.Unmarshal([]byte(decrypted), &loginData); err == nil {
|
||
token, _ = loginData["token"].(string)
|
||
}
|
||
}
|
||
case string:
|
||
// 未加密直接是 token 字符串
|
||
token = d
|
||
}
|
||
}
|
||
|
||
if token == "" {
|
||
fmt.Println("❌ 未获取到 token,登录失败")
|
||
return
|
||
}
|
||
fmt.Printf("✅ Token: %s\n\n", token)
|
||
|
||
// 5. 查询订阅
|
||
subURL := *host + "/v1/public/user/subscribe"
|
||
subReq, _ := http.NewRequest("GET", subURL, nil)
|
||
subReq.Header.Set("Authorization", "Bearer "+token)
|
||
subReq.Header.Set("Login-Type", "device")
|
||
subReq.Header.Set("X-App-Id", "debug")
|
||
|
||
subResp, err := client.Do(subReq)
|
||
if err != nil {
|
||
fmt.Printf("❌ 查询订阅失败: %v\n", err)
|
||
return
|
||
}
|
||
defer subResp.Body.Close()
|
||
|
||
subBody, _ := io.ReadAll(subResp.Body)
|
||
fmt.Printf("📥 订阅响应(原始): %s\n\n", subBody)
|
||
|
||
// 6. 解密订阅响应
|
||
var subRespMap map[string]interface{}
|
||
if err := json.Unmarshal(subBody, &subRespMap); err == nil {
|
||
if dataField, ok := subRespMap["data"]; ok {
|
||
if d, ok := dataField.(map[string]interface{}); ok {
|
||
encResp, _ := d["data"].(string)
|
||
ivResp, _ := d["time"].(string)
|
||
if encResp != "" && ivResp != "" {
|
||
decrypted, err := aesDecrypt(encResp, *secret, ivResp)
|
||
if err != nil {
|
||
fmt.Printf("❌ 解密订阅响应失败: %v\n", err)
|
||
return
|
||
}
|
||
// 格式化输出
|
||
var pretty interface{}
|
||
json.Unmarshal([]byte(decrypted), &pretty)
|
||
out, _ := json.MarshalIndent(pretty, "", " ")
|
||
fmt.Printf("📋 订阅信息(解密):\n%s\n", out)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|