package api import ( "crypto/hmac" "crypto/md5" "crypto/rand" "crypto/sha256" "encoding/base64" "encoding/hex" "encoding/json" "fmt" "io" "net/http" "net/url" "strings" ) func generateRandomDeviceId(size int) string { tempStr := "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" deviceId := make([]byte, size) _, err := rand.Read(deviceId) if err != nil { panic(err) } for i := range deviceId { deviceId[i] = tempStr[deviceId[i]%byte(len(tempStr))] } return string(deviceId) } type Authorize struct { CUserId string `json:"cUserId"` Code int `json:"code"` DeviceId string `json:"deviceId"` Message string `json:"message"` SecurityToken string `json:"securityToken"` ServiceToken string `json:"serviceToken"` Sid string `json:"sid"` UserId int64 `json:"userId"` } func mapToStruct(data map[string]interface{}) (Authorize, error) { jsonStr, err := json.Marshal(data) if err != nil { return Authorize{}, err } var myStruct Authorize err = json.Unmarshal(jsonStr, &myStruct) if err != nil { return Authorize{}, err } return myStruct, nil } func Login(user, pwd string) (Authorize, error) { msgURL := fmt.Sprintf("https://account.xiaomi.com/pass/serviceLogin?sid=xiaomiio&_json=true") loginURL := "https://account.xiaomi.com/pass/serviceLoginAuth2" deviceID := generateRandomDeviceId(16) authorize := make(map[string]interface{}) userAgent := "APP/com.xiaomi.mihome APPV/6.0.103 iosPassportSDK/3.9.0 iOS/14.4 miHSTS" client := &http.Client{} req, _ := http.NewRequest("GET", msgURL, nil) req.Header.Set("User-Agent", userAgent) req.Header.Set("Accept", "*/*") req.Header.Set("Accept-Language", "zh-tw") req.Header.Set("Cookie", fmt.Sprintf("deviceId=%s; sdkVersion=3.4.1", deviceID)) resp, _ := client.Do(req) defer resp.Body.Close() bodyBytes, _ := io.ReadAll(resp.Body) var result map[string]interface{} json.Unmarshal(bodyBytes[11:], &result) body := url.Values{} body.Set("qs", result["qs"].(string)) body.Set("sid", result["sid"].(string)) body.Set("_sign", result["_sign"].(string)) body.Set("callback", result["callback"].(string)) body.Set("user", user) pwdHash := md5.Sum([]byte(pwd)) pwdHashStr := strings.ToUpper(hex.EncodeToString(pwdHash[:])) body.Set("hash", pwdHashStr) body.Set("_json", "true") loginReq, _ := http.NewRequest("POST", loginURL, strings.NewReader(body.Encode())) loginReq.Header.Set("Content-Type", "application/x-www-form-urlencoded") loginResp, _ := client.Do(loginReq) defer loginResp.Body.Close() loginBodyBytes, _ := io.ReadAll(loginResp.Body) json.Unmarshal(loginBodyBytes[11:], &result) if result["code"].(float64) != 0 { authorize["code"] = result["code"] authorize["message"] = result["desc"] //return authorize } redirectURL := result["location"].(string) redirectReq, _ := http.NewRequest("GET", redirectURL, nil) redirectResp, _ := client.Do(redirectReq) defer redirectResp.Body.Close() cookies := redirectResp.Header["Set-Cookie"] for _, cookie := range cookies { cookieParts := strings.Split(strings.Split(cookie, "; ")[0], "=") authorize[cookieParts[0]] = cookieParts[1] } authorize["code"] = 0 authorize["sid"] = "xiaomiio" authorize["userId"] = result["userId"] authorize["securityToken"] = result["ssecurity"] authorize["deviceId"] = deviceID authorize["message"] = "成功" return mapToStruct(authorize) } func generateSignedNonce(secret, nonce string) string { hash := sha256.New() decodeString, _ := base64.StdEncoding.DecodeString(secret) hash.Write(decodeString) V, _ := base64.StdEncoding.DecodeString(nonce) hash.Write(V) return base64.StdEncoding.EncodeToString(hash.Sum(nil)) } func generateSignature(uri, signedNonce, nonce, data string) string { sign := uri + "&" + signedNonce + "&" + nonce + "&data=" + data decodeString, _ := base64.StdEncoding.DecodeString(signedNonce) mac := hmac.New(sha256.New, decodeString) mac.Write([]byte(sign)) return base64.StdEncoding.EncodeToString(mac.Sum(nil)) } func mapToJSON(data map[string]interface{}) (string, error) { jsonBytes, err := json.Marshal(data) if err != nil { return "", err } return string(jsonBytes), nil } func PostData(uri string, data map[string]interface{}, Certificate Authorize) []byte { Certificate.UserId = 2251648609 Certificate.ServiceToken = "x2K8nowPRvFEaeEKt2aj35xTgEZLTspxURrRIbbETcLJ5WLNdzxFrI1DfI3M3+/tvxJyfQMdaYu+8EfedWgSfB81T0P8R5zofX6GUd/jE2KlslBLyzn3vabqhHIL93ahoPUog1Q8pKBJJTRqOrL2pypDy1ODWkQ07jUC6fWqf+k=" Certificate.Code = 0 Certificate.Sid = "xiaomiio" Certificate.SecurityToken = "btZAHcCqLvQZp0eot8IlOQ==" Certificate.DeviceId = "3P5v99CdV4vwkpsS" dataStr, err := mapToJSON(data) if err != nil { return nil } nonce := generateRandomDeviceId(16) nonce = "H9qOd9J9wRRMTXj0" signedNonce := generateSignedNonce(Certificate.SecurityToken, nonce) signature := generateSignature(uri, signedNonce, nonce, dataStr) body := url.Values{} body.Set("_nonce", nonce) body.Set("data", dataStr) body.Set("signature", signature) userAgent := "APP/com.xiaomi.mihome APPV/6.0.103 iosPassportSDK/3.9.0 iOS/14.4 miHSTS" client := &http.Client{} req, _ := http.NewRequest("POST", "https://api.io.mi.com/app"+uri, strings.NewReader(body.Encode())) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("User-Agent", userAgent) req.Header.Set("x-xiaomi-protocal-flag-cli", "PROTOCAL-HTTP2") req.Header.Set("Cookie", fmt.Sprintf("PassportDeviceId=%s;userId=%v;serviceToken=%s;", Certificate.DeviceId, Certificate.UserId, Certificate.ServiceToken)) resp, err := client.Do(req) if err != nil { fmt.Println("Error:", err) } defer resp.Body.Close() respBody, _ := io.ReadAll(resp.Body) return respBody }