BodyBalanceEvaluation/backend/tools/license_generator.py

216 lines
7.4 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
授权文件生成工具
用于生成和签名授权文件
"""
import json
import os
import sys
import argparse
from datetime import datetime, timedelta
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa, padding
import base64
class LicenseGenerator:
"""授权文件生成器"""
def __init__(self, private_key_path=None):
"""
初始化授权生成器
Args:
private_key_path: 私钥文件路径
"""
self.private_key = None
if private_key_path and os.path.exists(private_key_path):
self.load_private_key(private_key_path)
def generate_key_pair(self, private_key_path, public_key_path):
"""
生成RSA密钥对
Args:
private_key_path: 私钥保存路径
public_key_path: 公钥保存路径
"""
# 生成私钥
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048
)
# 保存私钥
with open(private_key_path, 'wb') as f:
f.write(private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
))
# 保存公钥
public_key = private_key.public_key()
with open(public_key_path, 'wb') as f:
f.write(public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
))
self.private_key = private_key
print(f"密钥对已生成:")
print(f" 私钥: {private_key_path}")
print(f" 公钥: {public_key_path}")
def load_private_key(self, private_key_path):
"""
加载私钥
Args:
private_key_path: 私钥文件路径
"""
with open(private_key_path, 'rb') as f:
self.private_key = serialization.load_pem_private_key(
f.read(),
password=None
)
def generate_license(self, license_info, output_path):
"""
生成授权文件
Args:
license_info: 授权信息字典
output_path: 输出文件路径
"""
if not self.private_key:
raise ValueError("未加载私钥,无法生成签名")
# 创建授权数据(与 LicenseManager 要求的顶层结构一致)
license_data = {
"product": "BodyBalanceEvaluation",
"version": license_info.get("version", "1.0"),
"license_id": license_info.get("license_id"),
"license_type": license_info.get("license_type"),
"company_name": license_info.get("company_name", ""),
"contact_info": license_info.get("contact_info", ""),
"issued_at": license_info.get("issued_at"),
"expires_at": license_info.get("expires_at"),
"machine_id": license_info.get("machine_id", "*"),
"features": license_info.get("features", {}),
}
# 生成签名(对除 signature 外的全部字段进行排序后签名)
sorted_json = json.dumps(license_data, sort_keys=True, separators=(',', ':'))
signature = self.private_key.sign(
sorted_json.encode('utf-8'),
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
# 将签名编码为base64并写入
license_data["signature"] = base64.b64encode(signature).decode('utf-8')
# 保存授权文件
with open(output_path, 'w', encoding='utf-8') as f:
json.dump(license_data, f, indent=2, ensure_ascii=False)
print(f"授权文件已生成: {output_path}")
return license_data
def main():
"""主函数"""
parser = argparse.ArgumentParser(description='授权文件生成工具')
parser.add_argument('--generate-keys', action='store_true', help='生成密钥对')
parser.add_argument('--private-key', help='私钥文件路径')
parser.add_argument('--public-key', help='公钥文件路径')
parser.add_argument('--license-id', help='授权ID')
parser.add_argument('--license-type', choices=['trial', 'standard', 'professional'],
default='standard', help='授权类型')
parser.add_argument('--company-name', help='公司名称')
parser.add_argument('--contact-info', help='联系信息')
parser.add_argument('--expires-days', type=int, default=365, help='有效期天数')
parser.add_argument('--machine-id', help='机器ID留空表示不限制')
parser.add_argument('--output', help='输出授权文件路径')
args = parser.parse_args()
generator = LicenseGenerator()
# 生成密钥对
if args.generate_keys:
if not args.private_key or not args.public_key:
print("错误: 生成密钥对需要指定 --private-key 和 --public-key 参数")
return
generator.generate_key_pair(args.private_key, args.public_key)
return
# 生成授权文件
if not args.license_id or not args.company_name or not args.output:
print("错误: 生成授权文件需要指定 --license-id, --company-name 和 --output 参数")
return
if not args.private_key or not os.path.exists(args.private_key):
print("错误: 需要指定有效的私钥文件路径")
return
# 加载私钥
generator.load_private_key(args.private_key)
# 创建授权信息
from datetime import timezone
now = datetime.now(timezone.utc)
expires_at = now + timedelta(days=args.expires_days)
# 根据授权类型设置功能
features = {
"recording": True,
"export": True
}
if args.license_type == 'trial':
features.update({
"trial_limit_minutes": 30,
"max_sessions": 10
})
elif args.license_type == 'standard':
features.update({
"max_sessions": 1000,
"max_users": 10
})
elif args.license_type == 'professional':
features.update({
"max_sessions": -1, # 无限制
"max_users": -1, # 无限制
"advanced_analytics": True
})
license_info = {
"license_id": args.license_id,
"license_type": args.license_type,
"company_name": args.company_name,
"contact_info": args.contact_info or "",
"issued_at": now.isoformat(),
"expires_at": expires_at.isoformat(),
"machine_id": args.machine_id or "*",
"features": features,
"version": "1.0"
}
# 生成授权文件
generator.generate_license(license_info, args.output)
print("\n授权信息:")
print(f" 授权ID: {license_info['license_id']}")
print(f" 授权类型: {license_info['license_type']}")
print(f" 公司名称: {license_info['company_name']}")
print(f" 有效期: {license_info['expires_at']}")
print(f" 功能: {', '.join(license_info['features'].keys())}")
if __name__ == '__main__':
main()