license_checker.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. License 验证模块
  5. """
  6. import sys
  7. import json
  8. import base64
  9. import os
  10. from datetime import datetime
  11. from pathlib import Path
  12. try:
  13. from cryptography.hazmat.primitives import hashes, serialization
  14. from cryptography.hazmat.primitives.asymmetric import padding
  15. from cryptography.hazmat.backends import default_backend
  16. from cryptography.exceptions import InvalidSignature
  17. except ImportError:
  18. print("错误: 缺少 cryptography 库")
  19. sys.exit(1)
  20. PRODUCT_NAME = "laser_cal"
  21. def _get_base_dir():
  22. """获取程序基础目录"""
  23. if getattr(sys, 'frozen', False) or '__compiled__' in dir():
  24. # 如果是被打包的程序,寻找当前执行路径的目录
  25. return Path(sys.executable).parent
  26. # 开发环境下,如果是从共享库加载,__file__ 指向当前文件
  27. return Path(__file__).parent
  28. def _find_file(filename):
  29. """在可能的位置查找文件"""
  30. # 1. 尝试当前执行文件目录
  31. base = _get_base_dir()
  32. if (base / filename).exists():
  33. return base / filename
  34. # 2. 尝试当前工作目录
  35. cwd = Path.cwd()
  36. if (cwd / filename).exists():
  37. return cwd / filename
  38. # 3. 尝试开发环境下的 dist/license 目录
  39. dev_path = Path(__file__).parent / "dist" / "license" / filename
  40. if dev_path.exists():
  41. return dev_path
  42. return None
  43. def verify_license(verbose=False):
  44. """验证 License 文件"""
  45. license_path = _find_file("license.lic")
  46. pubkey_path = _find_file("public_key.pem")
  47. if not license_path:
  48. if verbose:
  49. print(f"[授权错误] License 文件 (license.lic) 不存在")
  50. return False
  51. if not pubkey_path:
  52. if verbose:
  53. print(f"[授权错误] 公钥文件 (public_key.pem) 不存在")
  54. return False
  55. try:
  56. license_content = json.loads(license_path.read_text(encoding="utf-8"))
  57. license_data = license_content["license_data"]
  58. signature_b64 = license_content["signature"]
  59. except (json.JSONDecodeError, KeyError):
  60. if verbose:
  61. print("[授权错误] License 文件格式无效")
  62. return False
  63. try:
  64. public_key = serialization.load_pem_public_key(
  65. pubkey_path.read_bytes(), backend=default_backend()
  66. )
  67. except Exception:
  68. if verbose:
  69. print("[授权错误] 公钥加载失败")
  70. return False
  71. # 验证 RSA 签名
  72. try:
  73. license_json = json.dumps(license_data, sort_keys=True, ensure_ascii=False)
  74. signature = base64.b64decode(signature_b64)
  75. public_key.verify(
  76. signature, license_json.encode("utf-8"), padding.PKCS1v15(), hashes.SHA256()
  77. )
  78. except InvalidSignature:
  79. if verbose:
  80. print("[授权错误] License 签名验证失败,文件可能被篡改")
  81. return False
  82. except Exception:
  83. if verbose:
  84. print("[授权错误] 签名验证过程出错")
  85. return False
  86. # 检查过期日期
  87. expire_str = license_data.get("expire_date", "")
  88. if expire_str.lower() != "never":
  89. try:
  90. expire_date = datetime.strptime(expire_str, "%Y-%m-%d")
  91. if datetime.now() > expire_date:
  92. if verbose:
  93. print(f"[授权错误] License 已过期: {expire_str}")
  94. return False
  95. except ValueError:
  96. if verbose:
  97. print("[授权错误] 过期日期格式无效")
  98. return False
  99. # 检查产品名
  100. if license_data.get("product", "") != PRODUCT_NAME:
  101. if verbose:
  102. print("[授权错误] License 产品不匹配")
  103. return False
  104. if verbose:
  105. print(f"[授权验证] 通过 | {license_data.get('licensee')} | 过期: {expire_str}")
  106. return True
  107. def get_license_info():
  108. """获取 License 信息"""
  109. license_path = _find_file("license.lic")
  110. if not license_path:
  111. return {}
  112. try:
  113. content = json.loads(license_path.read_text(encoding="utf-8"))
  114. return content.get("license_data", {})
  115. except Exception:
  116. return {}
  117. if __name__ == "__main__":
  118. verify_license(verbose=True)