sdpy-kit 0.4.2__tar.gz → 0.4.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/PKG-INFO +1 -1
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/__init__.py +1 -1
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/license_kit.py +154 -147
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/.gitignore +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/README.md +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/ai/__init__.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/ai/base_service.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/ai/chat_service.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/ai/embedding_service.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/ai/exceptions.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/ai/failover.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/ai/image_service.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/ai/model_builder.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/ai/model_client.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/ai/pool.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/ai/profiles.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/ai/rerank_service.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/ai/runner.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/ai/types.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/anydoc_kit.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/common_kit.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/config/__init__.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/config/kit_config.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/config/log_config.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/crawl/__init__.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/crawl/article_models.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/crawl/web_article_kit.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/crawl/wx_article_kit.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/db/__init__.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/db/base_db_kit.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/db/crud.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/db/engine.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/db/exceptions.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/db/models/__init__.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/db/models/base.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/db/mysql_kit.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/db/pg_kit.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/db/query.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/db/session.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/db/sqlite_kit.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/db/transaction.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/docx_kit.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/file_kit.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/laya_kit.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/marker_kit.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/md2doc_kit.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/mineru_kit.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/kit/redis_kit.py +0 -0
- {sdpy_kit-0.4.2 → sdpy_kit-0.4.3}/pyproject.toml +0 -0
|
@@ -23,9 +23,9 @@ Author: Clark Chang
|
|
|
23
23
|
面向单机分发场景的轻量授权体系(防君子定位):应用以打包 exe 形式离线分发,
|
|
24
24
|
无远端授权服务。工作流:
|
|
25
25
|
|
|
26
|
-
1. 客户机 `export_fingerprint_file()` 导出 `.fp` 指纹文件;
|
|
27
|
-
2. 签发方 `parse_fingerprint_file()` 解析,填入 `LicensePayload`
|
|
28
|
-
`sign()` 生成 `.lic` 授权文件回签;
|
|
26
|
+
1. 客户机 `LicenseKit.export_fingerprint_file()` 导出 `.fp` 指纹文件;
|
|
27
|
+
2. 签发方 `LicenseKit.parse_fingerprint_file()` 解析,填入 `LicensePayload`
|
|
28
|
+
后 `LicenseKit.sign()` 生成 `.lic` 授权文件回签;
|
|
29
29
|
3. 客户机 `LicenseVerifier.verify()` 验签 + 绑定校验(product / 机器码 / 有效期)。
|
|
30
30
|
|
|
31
31
|
安全边界(务必知悉):
|
|
@@ -41,19 +41,20 @@ Author: Clark Chang
|
|
|
41
41
|
规范化 JSON 序列化后签名,杜绝字段顺序 / 空白差异导致验签漂移。
|
|
42
42
|
|
|
43
43
|
用法:
|
|
44
|
-
from kit.license_kit import
|
|
45
|
-
LicensePayload, LicenseVerifier, export_fingerprint_file,
|
|
46
|
-
generate_keypair, parse_fingerprint_file, sign,
|
|
47
|
-
)
|
|
44
|
+
from kit.license_kit import LicenseKit, LicensePayload, LicenseVerifier
|
|
48
45
|
|
|
49
|
-
private_pem, public_pem = generate_keypair()
|
|
46
|
+
private_pem, public_pem = LicenseKit.generate_keypair()
|
|
50
47
|
payload = LicensePayload(product="myapp", machine_code="0123456789abcdef",
|
|
51
48
|
issued_at=datetime.now(timezone.utc),
|
|
52
49
|
expires_at=None)
|
|
53
|
-
lic_text = sign(payload, private_pem)
|
|
50
|
+
lic_text = LicenseKit.sign(payload, private_pem)
|
|
54
51
|
|
|
55
52
|
verifier = LicenseVerifier(public_pem, Path("license.lic"))
|
|
56
53
|
info = verifier.verify(product="myapp", machine_code="0123456789abcdef")
|
|
54
|
+
|
|
55
|
+
形态说明:0.4.2 为模块级函数,0.4.3 起收进静态工具类 `LicenseKit`
|
|
56
|
+
(与 FileKit / LayaKit 等其它 kit 形态一致);`LicenseVerifier` 因持有
|
|
57
|
+
公钥缓存与验签上下文实例状态,保持独立类。
|
|
57
58
|
"""
|
|
58
59
|
|
|
59
60
|
import base64
|
|
@@ -125,7 +126,7 @@ class LicenseExpiredError(LicenseError):
|
|
|
125
126
|
|
|
126
127
|
|
|
127
128
|
# ---------------------------------------------------------------------------
|
|
128
|
-
#
|
|
129
|
+
# 机器指纹私有实现(Windows 专属;winreg/ctypes 延迟导入保证模块任意平台可 import)
|
|
129
130
|
# ---------------------------------------------------------------------------
|
|
130
131
|
|
|
131
132
|
|
|
@@ -175,118 +176,14 @@ def _collect_components() -> tuple[str, str]:
|
|
|
175
176
|
return _read_machine_guid(), _read_volume_serial()
|
|
176
177
|
|
|
177
178
|
|
|
178
|
-
def
|
|
179
|
-
"""
|
|
180
|
-
|
|
181
|
-
Returns:
|
|
182
|
-
str: 16 位十六进制机器码
|
|
183
|
-
|
|
184
|
-
Raises:
|
|
185
|
-
LicenseUnsupportedPlatformError: 非 Windows 平台
|
|
186
|
-
LicenseFormatError: 指纹组件读取失败
|
|
187
|
-
"""
|
|
188
|
-
machine_guid, volume_serial = _collect_components()
|
|
179
|
+
def _machine_code_of(machine_guid: str, volume_serial: str) -> str:
|
|
180
|
+
"""由原始组件计算机器码短码(签发与导出共用,避免两处公式漂移)"""
|
|
189
181
|
digest = hashlib.sha256(f"{machine_guid}|{volume_serial}".encode("utf-8"))
|
|
190
182
|
return digest.hexdigest()[:MACHINE_CODE_LENGTH]
|
|
191
183
|
|
|
192
184
|
|
|
193
|
-
def export_fingerprint_file(path: Path) -> Path:
|
|
194
|
-
"""导出指纹文件(.fp):机器码 + 原始组件 + 导出时间
|
|
195
|
-
|
|
196
|
-
原始组件仅用于报障诊断(作者侧可看出哪个组件不匹配),验签只比短码。
|
|
197
|
-
|
|
198
|
-
Args:
|
|
199
|
-
path: 指纹文件输出路径
|
|
200
|
-
|
|
201
|
-
Returns:
|
|
202
|
-
Path: 实际写入的文件路径
|
|
203
|
-
|
|
204
|
-
Raises:
|
|
205
|
-
LicenseUnsupportedPlatformError: 非 Windows 平台
|
|
206
|
-
LicenseFormatError: 指纹组件读取失败
|
|
207
|
-
"""
|
|
208
|
-
machine_guid, volume_serial = _collect_components()
|
|
209
|
-
machine_code = hashlib.sha256(
|
|
210
|
-
f"{machine_guid}|{volume_serial}".encode("utf-8")
|
|
211
|
-
).hexdigest()[:MACHINE_CODE_LENGTH]
|
|
212
|
-
document = {
|
|
213
|
-
"kind": _FINGERPRINT_KIND,
|
|
214
|
-
"format_version": FINGERPRINT_FORMAT_VERSION,
|
|
215
|
-
"machine_code": machine_code,
|
|
216
|
-
"components": {
|
|
217
|
-
"machine_guid": machine_guid,
|
|
218
|
-
"volume_serial": volume_serial,
|
|
219
|
-
},
|
|
220
|
-
"exported_at": datetime.now(timezone.utc).isoformat(),
|
|
221
|
-
}
|
|
222
|
-
target = Path(path)
|
|
223
|
-
target.parent.mkdir(parents=True, exist_ok=True)
|
|
224
|
-
target.write_text(
|
|
225
|
-
json.dumps(document, ensure_ascii=False, indent=2), encoding="utf-8"
|
|
226
|
-
)
|
|
227
|
-
logger.info("指纹文件已导出:%s(机器码 %s)", target, machine_code)
|
|
228
|
-
return target
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
def parse_fingerprint_file(path: Path) -> str:
|
|
232
|
-
"""解析指纹文件,返回机器码(签发侧使用;跨机器无法重算原文,只校验结构)
|
|
233
|
-
|
|
234
|
-
Args:
|
|
235
|
-
path: 指纹文件路径
|
|
236
|
-
|
|
237
|
-
Returns:
|
|
238
|
-
str: 16 位十六进制机器码
|
|
239
|
-
|
|
240
|
-
Raises:
|
|
241
|
-
LicenseFormatError: 文件缺失 / JSON 损坏 / 标识或版本不符 / 机器码格式非法
|
|
242
|
-
"""
|
|
243
|
-
source = Path(path)
|
|
244
|
-
if not source.is_file():
|
|
245
|
-
raise LicenseFormatError(f"指纹文件不存在:{source}")
|
|
246
|
-
try:
|
|
247
|
-
document = json.loads(source.read_text(encoding="utf-8"))
|
|
248
|
-
except (OSError, json.JSONDecodeError) as exc:
|
|
249
|
-
raise LicenseFormatError(f"指纹文件读取或解析失败:{exc}") from exc
|
|
250
|
-
if not isinstance(document, dict):
|
|
251
|
-
raise LicenseFormatError("指纹文件结构不合法(顶层应为 JSON 对象)")
|
|
252
|
-
if document.get("kind") != _FINGERPRINT_KIND:
|
|
253
|
-
raise LicenseFormatError("指纹文件标识不符(kind)")
|
|
254
|
-
version = document.get("format_version")
|
|
255
|
-
if not isinstance(version, int) or version > FINGERPRINT_FORMAT_VERSION:
|
|
256
|
-
raise LicenseFormatError(f"指纹文件格式版本不识别:{version!r}")
|
|
257
|
-
machine_code = document.get("machine_code")
|
|
258
|
-
if not isinstance(machine_code, str) or not _HEX16_RE.fullmatch(machine_code):
|
|
259
|
-
raise LicenseFormatError("指纹文件缺少合法机器码(16 hex)")
|
|
260
|
-
return machine_code
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
# ---------------------------------------------------------------------------
|
|
264
|
-
# 密钥
|
|
265
|
-
# ---------------------------------------------------------------------------
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
def generate_keypair() -> tuple[str, str]:
|
|
269
|
-
"""生成 Ed25519 密钥对(一次性初始化用)
|
|
270
|
-
|
|
271
|
-
Returns:
|
|
272
|
-
tuple[str, str]: (private_pem, public_pem),均为 PEM 文本;
|
|
273
|
-
私钥务必本机妥善保管(丢失 = 已发授权全部作废)
|
|
274
|
-
"""
|
|
275
|
-
private_key = Ed25519PrivateKey.generate()
|
|
276
|
-
private_pem = private_key.private_bytes(
|
|
277
|
-
encoding=serialization.Encoding.PEM,
|
|
278
|
-
format=serialization.PrivateFormat.PKCS8,
|
|
279
|
-
encryption_algorithm=serialization.NoEncryption(),
|
|
280
|
-
).decode("utf-8")
|
|
281
|
-
public_pem = private_key.public_key().public_bytes(
|
|
282
|
-
encoding=serialization.Encoding.PEM,
|
|
283
|
-
format=serialization.PublicFormat.SubjectPublicKeyInfo,
|
|
284
|
-
).decode("utf-8")
|
|
285
|
-
return private_pem, public_pem
|
|
286
|
-
|
|
287
|
-
|
|
288
185
|
# ---------------------------------------------------------------------------
|
|
289
|
-
#
|
|
186
|
+
# 载荷(签发与验签共用)
|
|
290
187
|
# ---------------------------------------------------------------------------
|
|
291
188
|
|
|
292
189
|
|
|
@@ -340,39 +237,149 @@ def _canonical_bytes(payload: dict) -> bytes:
|
|
|
340
237
|
).encode("utf-8")
|
|
341
238
|
|
|
342
239
|
|
|
343
|
-
|
|
344
|
-
|
|
240
|
+
# ---------------------------------------------------------------------------
|
|
241
|
+
# 静态工具类(与其它 kit 形态一致;格式唯一事实源——签发与验签共用本模块实现)
|
|
242
|
+
# ---------------------------------------------------------------------------
|
|
345
243
|
|
|
346
|
-
Args:
|
|
347
|
-
payload: 授权载荷
|
|
348
|
-
private_key_pem: Ed25519 私钥 PEM(PKCS8)
|
|
349
244
|
|
|
350
|
-
|
|
351
|
-
|
|
245
|
+
class LicenseKit:
|
|
246
|
+
"""license 授权静态工具类:指纹、密钥、签发"""
|
|
352
247
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
248
|
+
@staticmethod
|
|
249
|
+
def get_machine_code() -> str:
|
|
250
|
+
"""计算本机机器码:组合指纹 SHA256 短码(16 hex 小写)
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
str: 16 位十六进制机器码
|
|
254
|
+
|
|
255
|
+
Raises:
|
|
256
|
+
LicenseUnsupportedPlatformError: 非 Windows 平台
|
|
257
|
+
LicenseFormatError: 指纹组件读取失败
|
|
258
|
+
"""
|
|
259
|
+
machine_guid, volume_serial = _collect_components()
|
|
260
|
+
return _machine_code_of(machine_guid, volume_serial)
|
|
261
|
+
|
|
262
|
+
@staticmethod
|
|
263
|
+
def export_fingerprint_file(path: Path) -> Path:
|
|
264
|
+
"""导出指纹文件(.fp):机器码 + 原始组件 + 导出时间
|
|
265
|
+
|
|
266
|
+
原始组件仅用于报障诊断(作者侧可看出哪个组件不匹配),验签只比短码。
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
path: 指纹文件输出路径
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
Path: 实际写入的文件路径
|
|
273
|
+
|
|
274
|
+
Raises:
|
|
275
|
+
LicenseUnsupportedPlatformError: 非 Windows 平台
|
|
276
|
+
LicenseFormatError: 指纹组件读取失败
|
|
277
|
+
"""
|
|
278
|
+
machine_guid, volume_serial = _collect_components()
|
|
279
|
+
document = {
|
|
280
|
+
"kind": _FINGERPRINT_KIND,
|
|
281
|
+
"format_version": FINGERPRINT_FORMAT_VERSION,
|
|
282
|
+
"machine_code": _machine_code_of(machine_guid, volume_serial),
|
|
283
|
+
"components": {
|
|
284
|
+
"machine_guid": machine_guid,
|
|
285
|
+
"volume_serial": volume_serial,
|
|
286
|
+
},
|
|
287
|
+
"exported_at": datetime.now(timezone.utc).isoformat(),
|
|
288
|
+
}
|
|
289
|
+
target = Path(path)
|
|
290
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
291
|
+
target.write_text(
|
|
292
|
+
json.dumps(document, ensure_ascii=False, indent=2), encoding="utf-8"
|
|
359
293
|
)
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
294
|
+
logger.info("指纹文件已导出:%s(机器码 %s)", target, document["machine_code"])
|
|
295
|
+
return target
|
|
296
|
+
|
|
297
|
+
@staticmethod
|
|
298
|
+
def parse_fingerprint_file(path: Path) -> str:
|
|
299
|
+
"""解析指纹文件,返回机器码(签发侧使用;跨机器无法重算原文,只校验结构)
|
|
300
|
+
|
|
301
|
+
Args:
|
|
302
|
+
path: 指纹文件路径
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
str: 16 位十六进制机器码
|
|
306
|
+
|
|
307
|
+
Raises:
|
|
308
|
+
LicenseFormatError: 文件缺失 / JSON 损坏 / 标识或版本不符 / 机器码格式非法
|
|
309
|
+
"""
|
|
310
|
+
source = Path(path)
|
|
311
|
+
if not source.is_file():
|
|
312
|
+
raise LicenseFormatError(f"指纹文件不存在:{source}")
|
|
313
|
+
try:
|
|
314
|
+
document = json.loads(source.read_text(encoding="utf-8"))
|
|
315
|
+
except (OSError, json.JSONDecodeError) as exc:
|
|
316
|
+
raise LicenseFormatError(f"指纹文件读取或解析失败:{exc}") from exc
|
|
317
|
+
if not isinstance(document, dict):
|
|
318
|
+
raise LicenseFormatError("指纹文件结构不合法(顶层应为 JSON 对象)")
|
|
319
|
+
if document.get("kind") != _FINGERPRINT_KIND:
|
|
320
|
+
raise LicenseFormatError("指纹文件标识不符(kind)")
|
|
321
|
+
version = document.get("format_version")
|
|
322
|
+
if not isinstance(version, int) or version > FINGERPRINT_FORMAT_VERSION:
|
|
323
|
+
raise LicenseFormatError(f"指纹文件格式版本不识别:{version!r}")
|
|
324
|
+
machine_code = document.get("machine_code")
|
|
325
|
+
if not isinstance(machine_code, str) or not _HEX16_RE.fullmatch(machine_code):
|
|
326
|
+
raise LicenseFormatError("指纹文件缺少合法机器码(16 hex)")
|
|
327
|
+
return machine_code
|
|
328
|
+
|
|
329
|
+
@staticmethod
|
|
330
|
+
def generate_keypair() -> tuple[str, str]:
|
|
331
|
+
"""生成 Ed25519 密钥对(一次性初始化用)
|
|
332
|
+
|
|
333
|
+
Returns:
|
|
334
|
+
tuple[str, str]: (private_pem, public_pem),均为 PEM 文本;
|
|
335
|
+
私钥务必本机妥善保管(丢失 = 已发授权全部作废)
|
|
336
|
+
"""
|
|
337
|
+
private_key = Ed25519PrivateKey.generate()
|
|
338
|
+
private_pem = private_key.private_bytes(
|
|
339
|
+
encoding=serialization.Encoding.PEM,
|
|
340
|
+
format=serialization.PrivateFormat.PKCS8,
|
|
341
|
+
encryption_algorithm=serialization.NoEncryption(),
|
|
342
|
+
).decode("utf-8")
|
|
343
|
+
public_pem = private_key.public_key().public_bytes(
|
|
344
|
+
encoding=serialization.Encoding.PEM,
|
|
345
|
+
format=serialization.PublicFormat.SubjectPublicKeyInfo,
|
|
346
|
+
).decode("utf-8")
|
|
347
|
+
return private_pem, public_pem
|
|
348
|
+
|
|
349
|
+
@staticmethod
|
|
350
|
+
def sign(payload: LicensePayload, private_key_pem: str) -> str:
|
|
351
|
+
"""对载荷签名,返回 license 文件内容(JSON 文本,即 .lic)
|
|
352
|
+
|
|
353
|
+
Args:
|
|
354
|
+
payload: 授权载荷
|
|
355
|
+
private_key_pem: Ed25519 私钥 PEM(PKCS8)
|
|
356
|
+
|
|
357
|
+
Returns:
|
|
358
|
+
str: license 文件内容 `{"payload": {...}, "signature": "<b64>"}`
|
|
359
|
+
|
|
360
|
+
Raises:
|
|
361
|
+
LicenseFormatError: 私钥解析失败或类型不是 Ed25519
|
|
362
|
+
"""
|
|
363
|
+
try:
|
|
364
|
+
private_key = serialization.load_pem_private_key(
|
|
365
|
+
private_key_pem.encode("utf-8"), password=None
|
|
366
|
+
)
|
|
367
|
+
except (ValueError, TypeError) as exc:
|
|
368
|
+
raise LicenseFormatError(f"私钥 PEM 解析失败:{exc}") from exc
|
|
369
|
+
if not isinstance(private_key, Ed25519PrivateKey):
|
|
370
|
+
raise LicenseFormatError("私钥类型不是 Ed25519")
|
|
371
|
+
payload_dict = payload.model_dump(mode="json")
|
|
372
|
+
signature = private_key.sign(_canonical_bytes(payload_dict))
|
|
373
|
+
document = {
|
|
374
|
+
"payload": payload_dict,
|
|
375
|
+
"signature": base64.b64encode(signature).decode("ascii"),
|
|
376
|
+
}
|
|
377
|
+
logger.info(
|
|
378
|
+
"license 已签发:product=%s machine_code=%s",
|
|
379
|
+
payload.product,
|
|
380
|
+
payload.machine_code,
|
|
381
|
+
)
|
|
382
|
+
return json.dumps(document, ensure_ascii=False, indent=2)
|
|
376
383
|
|
|
377
384
|
|
|
378
385
|
# ---------------------------------------------------------------------------
|
|
@@ -445,7 +452,7 @@ class LicenseVerifier:
|
|
|
445
452
|
|
|
446
453
|
Args:
|
|
447
454
|
product: 期望的应用标识
|
|
448
|
-
machine_code: 期望的机器码(本机 `get_machine_code()` 结果)
|
|
455
|
+
machine_code: 期望的机器码(本机 `LicenseKit.get_machine_code()` 结果)
|
|
449
456
|
now: 判定"当前时间";None 取 aware UTC 当前时刻;naive 按 UTC 处理
|
|
450
457
|
|
|
451
458
|
Returns:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|