Initial commit: SMS Receiver Web Service
Features: - Receive SMS from TranspondSms Android APP - HMAC-SHA256 signature verification (optional) - SQLite database storage - Web UI with login authentication - Multiple API tokens support - Timezone conversion (Asia/Shanghai) - Search, filter, and statistics - Auto refresh and session management Tech Stack: - Flask 3.0 - SQLite database - HTML5/CSS3 responsive design
This commit is contained in:
118
sign_verify.py
Normal file
118
sign_verify.py
Normal file
@@ -0,0 +1,118 @@
|
||||
"""
|
||||
签名验证工具
|
||||
参考 TranspondSms 的签名规则:
|
||||
timestamp + "\n" + secret -> HMAC-SHA256 -> Base64 -> URL Encode
|
||||
"""
|
||||
|
||||
import hmac
|
||||
import hashlib
|
||||
import base64
|
||||
import urllib.parse
|
||||
import time
|
||||
|
||||
|
||||
def generate_sign(secret: str, timestamp: int = None) -> str:
|
||||
"""
|
||||
生成签名
|
||||
|
||||
Args:
|
||||
secret: 密钥
|
||||
timestamp: 时间戳(毫秒),不传则使用当前时间
|
||||
|
||||
Returns:
|
||||
签名字符串
|
||||
"""
|
||||
if timestamp is None:
|
||||
timestamp = int(time.time() * 1000)
|
||||
|
||||
string_to_sign = f"{timestamp}\n{secret}"
|
||||
hmac_code = hmac.new(
|
||||
secret.encode('utf-8'),
|
||||
string_to_sign.encode('utf-8'),
|
||||
digestmod=hashlib.sha256
|
||||
).digest()
|
||||
sign = urllib.parse.quote(base64.b64encode(hmac_code).decode())
|
||||
|
||||
return sign
|
||||
|
||||
|
||||
def verify_sign(secret: str, sign: str, timestamp: int, max_age: int = 3600000) -> tuple[bool, str]:
|
||||
"""
|
||||
验证签名
|
||||
|
||||
Args:
|
||||
secret: 密钥
|
||||
sign: 待验证的签名
|
||||
timestamp: 时间戳(毫秒)
|
||||
max_age: 签名最大有效时间(毫秒),默认1小时
|
||||
|
||||
Returns:
|
||||
(是否有效, 错误信息)
|
||||
"""
|
||||
current_time = int(time.time() * 1000)
|
||||
|
||||
# 检查时间戳是否过期
|
||||
if abs(current_time - timestamp) > max_age:
|
||||
return False, f"签名过期,时间差: {abs(current_time - timestamp) / 1000:.1f}秒"
|
||||
|
||||
# 生成期望的签名
|
||||
expected_sign = generate_sign(secret, timestamp)
|
||||
|
||||
# 比较签名
|
||||
if sign != expected_sign:
|
||||
return False, "签名不匹配"
|
||||
|
||||
return True, "签名有效"
|
||||
|
||||
|
||||
def verify_from_app(from_number: str, content: str, timestamp: int,
|
||||
sign: str, secret: str, max_age: int = 3600000) -> tuple[bool, str]:
|
||||
"""
|
||||
验证 TranspondSms APP 发来的请求
|
||||
|
||||
Args:
|
||||
from_number: 发送方手机号
|
||||
content: 短信内容
|
||||
timestamp: 时间戳
|
||||
sign: 签名
|
||||
secret: 密钥
|
||||
max_age: 最大有效时间(毫秒)
|
||||
|
||||
Returns:
|
||||
(是否有效, 错误信息)
|
||||
"""
|
||||
# 检查必填字段
|
||||
if not from_number or not content:
|
||||
return False, "缺少必填字段"
|
||||
|
||||
# 如果没有签名,跳过验证(取决于配置)
|
||||
if not sign:
|
||||
return True, "无签名,跳过验证"
|
||||
|
||||
return verify_sign(secret, sign, timestamp, max_age)
|
||||
|
||||
|
||||
# 测试代码
|
||||
if __name__ == '__main__':
|
||||
# 测试签名生成和验证
|
||||
secret = "test_secret"
|
||||
timestamp = int(time.time() * 1000)
|
||||
|
||||
# 生成签名
|
||||
sign = generate_sign(secret, timestamp)
|
||||
print(f"Timestamp: {timestamp}")
|
||||
print(f"Sign: {sign}")
|
||||
|
||||
# 验证签名
|
||||
is_valid, message = verify_sign(secret, sign, timestamp)
|
||||
print(f"验证结果: {is_valid}, {message}")
|
||||
|
||||
# 测试过期签名
|
||||
old_timestamp = timestamp - 7200000 # 2小时前
|
||||
is_valid, message = verify_sign(secret, sign, old_timestamp)
|
||||
print(f"过期验证: {is_valid}, {message}")
|
||||
|
||||
# 测试错误签名
|
||||
wrong_sign = "wrong_signature"
|
||||
is_valid, message = verify_sign(secret, wrong_sign, timestamp)
|
||||
print(f"错误签名: {is_valid}, {message}")
|
||||
Reference in New Issue
Block a user