文件预览

crypto.py

查看 jf-open-pro-device-image-flip 技能包中的文件内容。

文件内容

scripts/crypto.py

#!/usr/bin/env python3
"""
杰峰 OpenAPI 加密工具

包含:
- 签名算法
- 时间戳生成
"""

import hashlib
import time


def get_counter() -> str:
    """
    获取计数器(7 位,不足补 0)
    
    Returns:
        7 位计数字符串
    """
    counter = 1
    if counter < 10:
        return '000000' + str(counter)
    elif counter < 100:
        return '00000' + str(counter)
    elif counter < 1000:
        return '0000' + str(counter)
    elif counter < 10000:
        return '000' + str(counter)
    elif counter < 100000:
        return '00' + str(counter)
    elif counter < 1000000:
        return '0' + str(counter)
    else:
        return str(counter)


def get_time_millis() -> str:
    """
    获取组合时间戳(counter 7 位 + timeMillis 13 位)
    
    Returns:
        20 位时间戳字符串
    """
    counter = get_counter()
    time_millis = str(int(time.time() * 1000))
    return counter + time_millis


def str_to_bytes(s: str) -> list:
    """
    字符串转字节数组(处理 UTF-8 多字节字符)
    
    Args:
        s: 输入字符串
        
    Returns:
        字节数组
    """
    bytes_arr = []
    for char in s:
        code = ord(char)
        if code >= 0x010000 and code <= 0x10FFFF:
            bytes_arr.append(((code >> 18) & 0x07) | 0xF0)
            bytes_arr.append(((code >> 12) & 0x3F) | 0x80)
            bytes_arr.append(((code >> 6) & 0x3F) | 0x80)
            bytes_arr.append((code & 0x3F) | 0x80)
        elif code >= 0x000800 and code <= 0x00FFFF:
            bytes_arr.append(((code >> 12) & 0x0F) | 0xE0)
            bytes_arr.append(((code >> 6) & 0x3F) | 0x80)
            bytes_arr.append((code & 0x3F) | 0x80)
        elif code >= 0x000080 and code <= 0x0007FF:
            bytes_arr.append(((code >> 6) & 0x1F) | 0xC0)
            bytes_arr.append((code & 0x3F) | 0x80)
        else:
            bytes_arr.append(code & 0xFF)
    return bytes_arr


def change(encrypt_str: str, move_card: int) -> list:
    """
    简单移位算法(严格按照 Java 版本实现)
    
    Args:
        encrypt_str: 输入字符串
        move_card: 移动卡标识
        
    Returns:
        移位后的字节数组
    """
    try:
        encrypt_byte = list(encrypt_str.encode('iso-8859-1'))
    except UnicodeEncodeError:
        encrypt_byte = str_to_bytes(encrypt_str)
    
    encrypt_length = len(encrypt_byte)
    
    for i in range(encrypt_length):
        if (i % move_card) > ((encrypt_length - i) % move_card):
            temp = encrypt_byte[i]
        else:
            temp = encrypt_byte[encrypt_length - (i + 1)]
        
        encrypt_byte[i] = encrypt_byte[encrypt_length - (i + 1)]
        encrypt_byte[encrypt_length - (i + 1)] = temp
    
    return encrypt_byte


def merge_byte(encrypt_byte: list, change_byte: list) -> list:
    """
    合并字节数组
    
    Args:
        encrypt_byte: 原字节数组
        change_byte: 移位后的字节数组
        
    Returns:
        合并后的字节数组
    """
    encrypt_length = len(encrypt_byte)
    encrypt_length2 = encrypt_length * 2
    temp = [0] * encrypt_length2
    
    for i in range(encrypt_length):
        temp[i] = encrypt_byte[i]
        temp[encrypt_length2 - 1 - i] = change_byte[i]
    
    return temp


def generate_signature(uuid: str, app_key: str, app_secret: str, 
                       time_millis: str, move_card: int) -> str:
    """
    生成杰峰 OpenAPI 签名
    
    算法步骤:
    1. 将 uuid, appKey, appSecret, timeMillis 依次拼接
    2. 获取拼接后字符串的二进制数组
    3. 二进制数组进行简单移位算法
    4. 原二进制数组与移位后的二进制数组合并
    5. 将合并的二进制数组 md5 加密一次生成密钥
    
    Args:
        uuid: 客户唯一标识
        app_key: 应用 key
        app_secret: 应用密钥
        time_millis: 时间戳
        move_card: 移动取模基数
        
    Returns:
        32 位十六进制签名字符串
    """
    encrypt_str = uuid + app_key + app_secret + time_millis
    
    try:
        encrypt_byte = list(encrypt_str.encode('iso-8859-1'))
    except UnicodeEncodeError:
        encrypt_byte = str_to_bytes(encrypt_str)
    
    change_byte = change(encrypt_str, move_card)
    merge = merge_byte(encrypt_byte, change_byte)
    signature = hashlib.md5(bytes(merge)).hexdigest()
    
    return signature


# 测试
if __name__ == "__main__":
    uuid = "uuidxxxx"
    app_key = "appkeyxxxx"
    app_secret = "90f8bc17be2a425db6068c749deexxxx"
    time_millis = "00000011645153792342"
    move_card = 2
    
    signature = generate_signature(uuid, app_key, app_secret, time_millis, move_card)
    
    print(f"Signature: {signature}")
    print(f"注意:签名算法需与杰峰服务器验证逻辑一致")