package handlers import ( "crypto/ecdsa" "crypto/sha256" "encoding/base64" "encoding/json" "fmt" "log" "math/big" "net/http" "time" "licensing-cotton/internal/database" "licensing-cotton/internal/security" ) // License 结构 type License struct { DeviceID string `json:"device_id"` Expiration string `json:"expiration"` SignDate string `json:"sign_date"` Signature string `json:"signature"` } type LicenseWithoutSign struct { DeviceID string `json:"device_id"` Expiration string `json:"expiration"` SignDate string `json:"sign_date"` } // 签发License (仅管理员) func HandleSignLicense(w http.ResponseWriter, r *http.Request) { if !isAdminRequest(w, r) { return } var req struct { DeviceID string `json:"device_id"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "解析请求失败", http.StatusBadRequest) return } // 查设备 var expiration time.Time err := database.DB.QueryRow(`SELECT expiration FROM devices WHERE device_id=?`, req.DeviceID). Scan(&expiration) if err != nil { http.Error(w, "设备不存在", http.StatusNotFound) return } // 构造 license lic := License{ DeviceID: req.DeviceID, Expiration: expiration.Format(time.RFC3339), } // 序列化并哈希 dataBytes, _ := json.Marshal(lic) hash := sha256.Sum256(dataBytes) // 签名 priv, _, _ := security.GetKeys() rSig, sSig, err := ecdsa.Sign(nil, priv, hash[:]) if err != nil { http.Error(w, "ECDSA签名失败", http.StatusInternalServerError) return } sigBytes := append(rSig.Bytes(), sSig.Bytes()...) lic.Signature = base64.StdEncoding.EncodeToString(sigBytes) w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(lic) } // 验证License (可供服务端或第三方调用) func HandleVerifyLicense(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "只允许POST", http.StatusMethodNotAllowed) return } var lic License if err := json.NewDecoder(r.Body).Decode(&lic); err != nil { http.Error(w, "解析License失败", http.StatusBadRequest) return } sigStr := lic.Signature lic.Signature = "" dataBytes, _ := json.Marshal(lic) hash := sha256.Sum256(dataBytes) sigData, err := base64.StdEncoding.DecodeString(sigStr) if err != nil { http.Error(w, "Signature base64解码失败", http.StatusBadRequest) return } half := len(sigData) / 2 rSig := new(big.Int).SetBytes(sigData[:half]) sSig := new(big.Int).SetBytes(sigData[half:]) _, pub, _ := security.GetKeys() if !ecdsa.Verify(pub, hash[:], rSig, sSig) { http.Error(w, "签名验证失败", http.StatusUnauthorized) return } // 检查是否过期 exp, err := time.Parse(time.RFC3339, lic.Expiration) if err != nil { http.Error(w, "时间格式错误", http.StatusBadRequest) return } if time.Now().After(exp) { http.Error(w, "License已过期", http.StatusUnauthorized) return } // 可进一步检查数据库中的device_id与expiration是否匹配 w.Header().Set("Content-Type", "application/json") w.Write([]byte(`{"message": "License有效"}`)) } func SignLicense(deviceID string, expirationTime time.Time) (License, error) { // 1. 准备待签名数据 licWithoutSign := LicenseWithoutSign{ DeviceID: deviceID, Expiration: expirationTime.Format(time.RFC3339), SignDate: time.Now().Format(time.RFC3339), } // 2. 序列化为 JSON licBytes, err := json.Marshal(licWithoutSign) if err != nil { return License{}, fmt.Errorf("序列化License失败: %w", err) } // 3. 调用你的 Ed25519Sign 函数签名 signature, signErr := security.Ed25519Sign(licBytes) // <-- 这里是你已有的签名函数 if signErr != nil { return License{}, fmt.Errorf("签名失败: %w", signErr) } // 4. 封装最终 License lic := License{ DeviceID: deviceID, Expiration: licWithoutSign.Expiration, SignDate: licWithoutSign.SignDate, Signature: base64.StdEncoding.EncodeToString(signature), } log.Println("Signed License for " + deviceID + ":" + expirationTime.Format(time.RFC3339)) return lic, nil }