| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338 |
- """
- HMAC密钥管理服务
- 处理动态HMAC密钥的生成、验证、同步等功能
- """
- import secrets
- import hashlib
- import hmac as hmac_lib
- from datetime import datetime, timedelta
- from typing import Optional, Tuple, Dict, Any
- from sqlalchemy.orm import Session
- from sqlalchemy import and_, or_
- from app.models.hmac_key import HmacKey
- from app.models.user import User
- from app.models.software import Software
- from app.models.user_software_subscription import UserSoftwareSubscription
- from app.core.logger import logger
- from app.core.encrypt import encryptor
- from app.utils.timezone import beijing_now_naive
- class HmacKeyService:
- """HMAC密钥管理服务"""
-
- # HMAC密钥长度
- HMAC_KEY_LENGTH = 64
-
- # 密钥有效期(秒)
- KEY_EXPIRY_SECONDS = 3600 * 24 # 24小时
-
- # 密钥轮换阈值(秒)- 提前1小时开始轮换
- KEY_ROTATION_THRESHOLD = 3600
-
- # 密钥颁发者标识
- KEY_ISSUER = "buff-server"
-
- def __init__(self, db: Session):
- self.db = db
-
- def generate_hmac_key(self) -> str:
- """
- 生成安全的HMAC密钥
-
- Returns:
- Base64编码的HMAC密钥
- """
- # 生成随机字节
- key_bytes = secrets.token_bytes(self.HMAC_KEY_LENGTH)
- return key_bytes.hex()
-
- def create_hmac_key(
- self,
- user_id: int,
- username: str,
- machine_code: str,
- expires_in_seconds: int = None
- ) -> Tuple[bool, str, Dict[str, Any]]:
- """
- 为用户创建新的HMAC密钥
-
- Args:
- user_id: 用户ID
- username: 用户名
- machine_code: 机器码
- expires_in_seconds: 密钥有效期(秒)
-
- Returns:
- (success, message, data)
- """
- try:
- # 检查用户是否存在
- user = self.db.query(User).filter(User.id == user_id).first()
- if not user:
- return False, "用户不存在", {}
-
- # 检查用户状态
- if user.status == 0:
- return False, "账号已被禁用", {}
-
- # 检查机器码是否匹配
- if user.machine_code and user.machine_code != machine_code:
- return False, "机器码不匹配", {}
-
- # 获取当前活跃密钥
- current_key = self.db.query(HmacKey).filter(
- HmacKey.user_id == user_id,
- HmacKey.machine_code == machine_code,
- HmacKey.is_active == 1
- ).first()
-
- # 如果存在活跃密钥,检查是否需要轮换
- key_version = 1
- if current_key:
- # 检查是否即将过期
- time_until_expiry = (current_key.expires_at - beijing_now_naive()).total_seconds()
- if time_until_expiry > self.KEY_ROTATION_THRESHOLD:
- # 密钥仍然有效,直接返回
- return True, "密钥有效", {
- "hmac_key": current_key.hmac_key,
- "expires_at": current_key.expires_at.isoformat(),
- "key_version": current_key.key_version,
- "rotated": False
- }
-
- # 停用旧密钥
- current_key.is_active = 0
- key_version = current_key.key_version + 1
- logger.info(f"轮换用户 {username} 的HMAC密钥,版本: {key_version}")
-
- # 生成新密钥
- hmac_key = self.generate_hmac_key()
- expiry_seconds = expires_in_seconds or self.KEY_EXPIRY_SECONDS
- expires_at = beijing_now_naive() + timedelta(seconds=expiry_seconds)
-
- # 创建新密钥记录
- hmac_key_record = HmacKey(
- user_id=user_id,
- username=username,
- machine_code=machine_code,
- hmac_key=hmac_key,
- key_version=key_version,
- expires_at=expires_at,
- is_active=1
- )
-
- self.db.add(hmac_key_record)
- self.db.commit()
-
- logger.info(f"为用户 {username} 创建HMAC密钥成功,版本: {key_version}")
-
- return True, "密钥创建成功", {
- "hmac_key": hmac_key,
- "expires_at": expires_at.isoformat(),
- "key_version": key_version,
- "rotated": True
- }
-
- except Exception as e:
- self.db.rollback()
- logger.exception(f"创建HMAC密钥失败: {e}")
- return False, f"创建HMAC密钥失败: {str(e)}", {}
-
- def get_hmac_key(
- self,
- user_id: int,
- machine_code: str
- ) -> Tuple[bool, str, Dict[str, Any]]:
- """
- 获取用户的HMAC密钥
-
- Args:
- user_id: 用户ID
- machine_code: 机器码
-
- Returns:
- (success, message, data)
- """
- try:
- hmac_key_record = self.db.query(HmacKey).filter(
- HmacKey.user_id == user_id,
- HmacKey.machine_code == machine_code,
- HmacKey.is_active == 1
- ).first()
-
- if not hmac_key_record:
- return False, "未找到活跃的HMAC密钥", {}
-
- # 检查是否过期
- if hmac_key_record.is_expired():
- hmac_key_record.is_active = 0
- self.db.commit()
- return False, "HMAC密钥已过期", {}
-
- return True, "获取成功", {
- "hmac_key": hmac_key_record.hmac_key,
- "expires_at": hmac_key_record.expires_at.isoformat(),
- "key_version": hmac_key_record.key_version
- }
-
- except Exception as e:
- logger.exception(f"获取HMAC密钥失败: {e}")
- return False, f"获取HMAC密钥失败: {str(e)}", {}
-
- def verify_hmac_signature(
- self,
- data: str,
- signature: str,
- hmac_key: str
- ) -> Tuple[bool, str]:
- """
- 验证HMAC签名
-
- Args:
- data: 原始数据
- signature: 要验证的签名
- hmac_key: HMAC密钥
-
- Returns:
- (is_valid, message)
- """
- try:
- # 计算期望的签名
- expected_signature = hmac_lib.new(
- hmac_key.encode(),
- data.encode(),
- hashlib.sha256
- ).hexdigest()
-
- # 使用constant-time比较
- if secrets.compare_digest(signature, expected_signature):
- return True, "签名验证成功"
- else:
- return False, "签名验证失败"
-
- except Exception as e:
- logger.exception(f"HMAC签名验证异常: {e}")
- return False, f"签名验证异常: {str(e)}"
-
- def revoke_hmac_key(
- self,
- user_id: int,
- machine_code: str = None
- ) -> Tuple[bool, str]:
- """
- 撤销用户的HMAC密钥
-
- Args:
- user_id: 用户ID
- machine_code: 机器码(可选,不指定则撤销所有)
-
- Returns:
- (success, message)
- """
- try:
- query = self.db.query(HmacKey).filter(
- HmacKey.user_id == user_id,
- HmacKey.is_active == 1
- )
-
- if machine_code:
- query = query.filter(HmacKey.machine_code == machine_code)
-
- # 停用所有匹配的密钥
- count = query.update({"is_active": 0})
- self.db.commit()
-
- logger.info(f"撤销用户 {user_id} 的 {count} 个HMAC密钥")
-
- return True, f"已撤销 {count} 个密钥"
-
- except Exception as e:
- self.db.rollback()
- logger.exception(f"撤销HMAC密钥失败: {e}")
- return False, f"撤销HMAC密钥失败: {str(e)}"
-
- def cleanup_expired_keys(self) -> Tuple[bool, str, int]:
- """
- 清理过期的HMAC密钥
-
- Returns:
- (success, message, cleaned_count)
- """
- try:
- # 查找所有过期但仍标记为活跃的密钥
- expired_keys = self.db.query(HmacKey).filter(
- HmacKey.is_active == 1,
- HmacKey.expires_at < beijing_now_naive()
- ).all()
-
- cleaned_count = 0
- for key in expired_keys:
- key.is_active = 0
- cleaned_count += 1
-
- self.db.commit()
-
- logger.info(f"清理了 {cleaned_count} 个过期的HMAC密钥")
-
- return True, f"清理完成", cleaned_count
-
- except Exception as e:
- self.db.rollback()
- logger.exception(f"清理HMAC密钥失败: {e}")
- return False, f"清理失败: {str(e)}", 0
-
- def validate_user_for_hmac(
- self,
- username: str,
- machine_code: str
- ) -> Tuple[bool, str, int]:
- """
- 验证用户是否有资格获取HMAC密钥
-
- Args:
- username: 用户名
- machine_code: 机器码
-
- Returns:
- (is_valid, message, user_id)
- """
- try:
- user = self.db.query(User).filter(User.username == username).first()
-
- if not user:
- return False, "用户不存在", 0
-
- if user.status == 0:
- return False, "账号已被禁用", 0
-
- # 验证机器码
- if user.machine_code and user.machine_code != machine_code:
- return False, "机器码不匹配", 0
- # 验证用户卡密/订阅是否过期(与心跳、登录一致)
- software_code = "buff-xp-steam-sh"
- sw = self.db.query(Software).filter(
- Software.code == software_code,
- Software.is_deleted == 0
- ).first()
- expire_time = None
- if sw:
- sub = self.db.query(UserSoftwareSubscription).filter(
- UserSoftwareSubscription.user_id == user.id,
- UserSoftwareSubscription.software_id == sw.id,
- UserSoftwareSubscription.is_deleted == 0
- ).first()
- if sub and sub.expire_time is not None:
- expire_time = sub.expire_time
- if expire_time is None:
- expire_time = user.expire_time
- if expire_time and expire_time < beijing_now_naive():
- return False, "您的卡密已到期,请前往续费或联系客户支持", 0
-
- return True, "验证通过", user.id
-
- except Exception as e:
- logger.exception(f"验证用户失败: {e}")
- return False, f"验证失败: {str(e)}", 0
|