package fqapi import ( "bytes" "compress/gzip" "crypto/aes" "crypto/cipher" "encoding/base64" "encoding/hex" "encoding/json" "fanqie/internal/common" "fmt" "math/rand" "regexp" "sort" "strings" "time" ) // KeyInfo 密钥信息 type KeyInfo struct { Key string `json:"key"` Ver int `json:"ver"` } var ( keyInfo *KeyInfo keyLogger = common.NewLogger("register_key", "INFO", "") ) // rHex 将数字转换为反转的16进制字符串 func rHex(num int64) string { hexStr := fmt.Sprintf("%032x", num) result := "" for i := len(hexStr); i > 0; i -= 2 { result += hexStr[i-2 : i] } return result } // createSymmetricCrypto 创建对称加密器 func createSymmetricCrypto(algorithm string, key, iv []byte) (func([]byte) ([]byte, error), func([]byte) ([]byte, error), error) { if algorithm != "AES/CBC/PKCS5Padding" { return nil, nil, fmt.Errorf("不支持的加密算法") } block, err := aes.NewCipher(key) if err != nil { return nil, nil, err } encrypt := func(data []byte) ([]byte, error) { // PKCS7填充 padding := aes.BlockSize - len(data)%aes.BlockSize padText := make([]byte, padding) for i := range padText { padText[i] = byte(padding) } paddedData := append(data, padText...) ciphertext := make([]byte, len(paddedData)) mode := cipher.NewCBCEncrypter(block, iv) mode.CryptBlocks(ciphertext, paddedData) return ciphertext, nil } decrypt := func(data []byte) ([]byte, error) { mode := cipher.NewCBCDecrypter(block, iv) decrypted := make([]byte, len(data)) mode.CryptBlocks(decrypted, data) // 移除PKCS7填充 padding := int(decrypted[len(decrypted)-1]) if padding > len(decrypted) || padding > aes.BlockSize { return nil, fmt.Errorf("无效的填充") } return decrypted[:len(decrypted)-padding], nil } return encrypt, decrypt, nil } // En AES加密 func En(data, key, iv []byte) ([]byte, error) { encrypt, _, err := createSymmetricCrypto("AES/CBC/PKCS5Padding", key, iv) if err != nil { return nil, err } return encrypt(data) } // De AES解密 func De(data, key, iv []byte) ([]byte, error) { _, decrypt, err := createSymmetricCrypto("AES/CBC/PKCS5Padding", key, iv) if err != nil { return nil, err } return decrypt(data) } // randomStringKey 生成随机字符串(密钥用) func randomStringKey(length int) string { const charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" b := make([]byte, length) for i := range b { b[i] = charset[rand.Intn(len(charset))] } return string(b) } // GetKey 获取密钥 func GetKey(header string) (*KeyInfo, error) { if header == "" { keyLogger.Error("请先注册设备") return nil, fmt.Errorf("请先注册设备") } key, _ := base64.StdEncoding.DecodeString("rCXGfd2POMGzeiNIgo4iLg==") iv := []byte(randomStringKey(16)) // 提取device_id re := regexp.MustCompile(`device_id=(\d+)`) matches := re.FindStringSubmatch(header) if len(matches) < 2 { return nil, fmt.Errorf("无法从header中提取device_id") } var deviceIDInt int64 fmt.Sscanf(matches[1], "%d", &deviceIDInt) // 转换device_id hexStr := rHex(deviceIDInt) data, _ := hex.DecodeString(hexStr) keyLogger.Debug(fmt.Sprintf("rKey: %v", iv)) // 加密 encrypted, err := En(data, key, iv) if err != nil { return nil, err } // 组合iv和加密数据 newArr := append(iv, encrypted...) content := base64.StdEncoding.EncodeToString(newArr) keyLogger.Info("正在获取密钥...") // 构建参数 params := []string{ "aid=1967", "app_name=novelapp", "channel=0", "device_platform=android", "os_version=0", "version_code=5.8.9.32", header, } allParams := strings.Join(params, "&") paramList := strings.Split(allParams, "&") sort.Strings(paramList) sortedParams := strings.Join(paramList, "&") // 准备请求体 requestBody := map[string]string{ "content": content, } bodyJSON, _ := json.Marshal(requestBody) // gzip压缩 var gzipBuf bytes.Buffer gzipWriter := gzip.NewWriter(&gzipBuf) gzipWriter.Write(bodyJSON) gzipWriter.Close() reqURL := "https://reading.snssdk.com/reading/crypt/registerkey?" + sortedParams resp, err := GorgonRequest(reqURL, map[string]interface{}{ "method": "POST", "body": gzipBuf.Bytes(), "headers": map[string]string{ "User-Agent": "com.dragon.read", "Content-Encoding": "gzip", }, }) if err != nil { keyLogger.Error("获取密钥失败: " + err.Error()) return nil, err } if resp.Status == 200 { var respData map[string]interface{} json.Unmarshal(resp.Content, &respData) dataObj, ok := respData["data"].(map[string]interface{}) if !ok { return nil, fmt.Errorf("响应格式错误") } keyStr, ok := dataObj["key"].(string) if !ok { return nil, fmt.Errorf("密钥字段不存在") } // 解码返回的密钥 res, _ := base64.StdEncoding.DecodeString(keyStr) if len(res) < 32 { return nil, fmt.Errorf("密钥长度不足") } data := res[16:] ivResp := res[0:16] // 解密密钥 decryptedKey, err := De(data, key, ivResp) if err != nil { return nil, err } finalKey := base64.StdEncoding.EncodeToString(decryptedKey) keyVer, _ := dataObj["keyver"].(float64) keyInfo = &KeyInfo{ Key: finalKey, Ver: int(keyVer), } keyLogger.Info(fmt.Sprintf("密钥获取成功!")) return keyInfo, nil } return nil, fmt.Errorf("获取密钥失败") } // GetKeyInfo 获取当前密钥信息 func GetKeyInfo() *KeyInfo { return keyInfo } func init() { rand.Seed(time.Now().UnixNano()) }