#!/usr/bin/env python
# -*- coding:utf-8 -*-
import base64
import binascii
import hashlib
import struct
import random
import time
from Crypto.Cipher import AES
SALTS = ["00000000000000000000000000000000",
"11111111111111111111111111111111",
"22222222222222222222222222222222",
"33333333333333333333333333333333",
"44444444444444444444444444444444",
"55555555555555555555555555555555",
"66666666666666666666666666666666",
"77777777777777777777777777777777",
"88888888888888888888888888888888",
"99999999999999999999999999999999"]
ENV = {
'dev': 1,
'test': 2,
'stg': 3,
'prod': 4,
}
# 注意业务字段event_id,为长度为32位的16进制字符串
def generate_ticket(env: str, account_id: int, event_id: str):
env_int = ENV[env]
create_time = int(time.time())
expire_time = create_time + 2 * 60 * 60 # ticket有效期2小时
first_char = ord(event_id[0])
salt = SALTS[abs(first_char % len(SALTS))]
event_type = 1 # 事件类型(业务字段)
# account_id + event_id + salt 用于生成AES key和iv(GCM使用nonce)
message = str(account_id) + event_id + salt
hash_str = hashlib.sha256(message.encode('utf-8')).digest()
length = int(len(hash_str) / 2)
# 前16字节作为key
aes_key = hash_str[:length]
# 后16字节作为iv(GCM为nonce)
aes_iv = hash_str[length:]
cipher = AES.new(aes_key, AES.MODE_GCM, aes_iv)
digest = str(account_id) + event_id + str(create_time) + str(expire_time) + str(env_int) + str(event_type) + salt
digest_sha = hashlib.sha256(digest.encode('utf-8')).digest()
# 待加密字符串由以下参数拼接而成,各字段占用的比特数依次为“i16siibb2s4s”
# binascii.unhexlify(event_id) 等价于 int(event_id, 16).to_bytes(length=16, byteorder='big')
sensitive_data = struct.pack(">i16siibb2s4s", account_id, binascii.unhexlify(event_id), create_time, expire_time, env_int, event_type, random.randbytes(2), digest_sha[-4:])
params, tag = cipher.encrypt_and_digest(sensitive_data)
base64_data = params + tag
return base64.b64encode(base64_data).decode('utf-8')
if __name__ == '__main__':
print('Token_' + generate_ticket('dev', 88888888, 'ffffffffffffffffffffffffffffffff'))
pycryptodome AES-GCM加密生成临时ticket python脚本
于 2023-03-16 01:17:36 首次发布