# JiaJia - RSA-AES 混合加密框架
一个轻量级的加密解密框架,提供RSA非对称加密、AES对称加密、HMAC签名验证等功能。可以直接在其他项目中import使用。
## 功能特性
- 🔐 **RSA非对称加密**: 支持2048位和4096位密钥
- 🔑 **AES对称加密**: 支持GCM和CBC模式
- ✍️ **HMAC签名验证**: 确保数据完整性和真实性
- 🗂️ **会话管理**: 支持会话创建、存储和失效
- ⚡ **异步支持**: 全异步API设计
- 🛡️ **安全配置**: 灵活的安全参数配置
- 📦 **便捷函数**: 提供简单易用的便捷函数
- 🔄 **缓存支持**: 支持内存和Redis缓存后端
## 安装
```bash
pip install jiajia
```
## 快速开始
### 基本使用
```python
import asyncio
from jiajia import key_exchange, encrypt_data, decrypt_data, set_config, SecurityConfig
async def main():
# 1. 设置配置
config = SecurityConfig(
rsa_key_size=2048,
aes_mode="GCM",
session_expire_time=3600
)
set_config(config)
# 2. 密钥交换(建立安全会话)
exchange_result = await key_exchange("client_app", "1.0.0")
session_id = exchange_result["session_id"]
# 3. 加密数据
sensitive_data = {"user_id": "12345", "email": "user@example.com"}
encrypt_result = await encrypt_data(sensitive_data, session_id)
# 4. 解密数据
decrypt_payload = encrypt_result.copy()
decrypt_payload["session_id"] = session_id
decrypt_result = await decrypt_data(decrypt_payload)
print(f"解密结果: {decrypt_result['data']}")
asyncio.run(main())
```
### 高级使用
```python
import asyncio
from jiajia import EncryptionAPI, SecurityConfig
async def advanced_example():
# 创建自定义配置
config = SecurityConfig(
rsa_key_size=4096,
aes_mode="GCM",
session_expire_time=7200,
max_timestamp_diff=600
)
# 创建API实例
api = EncryptionAPI(config)
# 密钥交换
exchange_result = await api.key_exchange("advanced_client", "2.0.0")
session_id = exchange_result["session_id"]
# 加密数据
data = {"message": "Hello, World!", "timestamp": 1234567890}
encrypt_result = await api.encrypt_data(data, session_id)
# 解密数据
decrypt_payload = encrypt_result.copy()
decrypt_payload["session_id"] = session_id
decrypt_result = await api.decrypt_data(decrypt_payload)
# 健康检查
health_result = await api.health_check()
# 获取安全配置
config_result = await api.get_security_config()
asyncio.run(advanced_example())
```
## API 参考
### 核心类
#### `SecurityConfig`
安全配置类,用于设置加密参数。
```python
config = SecurityConfig(
rsa_key_size=2048, # RSA密钥大小
aes_mode="GCM", # AES模式 (GCM/CBC)
session_expire_time=3600, # 会话过期时间(秒)
max_timestamp_diff=300, # 时间戳容差(秒)
cache_backend="memory" # 缓存后端 (memory/redis)
)
```
#### `EncryptionAPI`
主要的加密API类。
```python
api = EncryptionAPI(config)
# 密钥交换
result = await api.key_exchange(client_id, client_version)
# 加密数据
result = await api.encrypt_data(data, session_id)
# 解密数据
result = await api.decrypt_data(payload)
# 会话管理
result = await api.get_session_info(session_id)
result = await api.invalidate_session(session_id)
# 系统状态
result = await api.health_check()
result = await api.get_security_config()
```
#### `EncryptionCore`
加密核心类,提供底层加密功能。
```python
core = EncryptionCore(config)
# RSA操作
private_key, public_key = core.generate_rsa_keypair()
encrypted = core.rsa_encrypt(public_key, data)
decrypted = core.rsa_decrypt(private_key, encrypted)
# AES操作
aes_key = core.generate_aes_key()
iv, ciphertext, tag = core.aes_encrypt(aes_key, data)
decrypted = core.aes_decrypt(aes_key, iv, ciphertext, tag)
# HMAC操作
hmac_key = core.generate_hmac_key()
signature = core.hmac_sign(hmac_key, data)
is_valid = core.hmac_verify(hmac_key, data, signature)
```
#### `SessionManager`
会话管理类,处理会话的创建、存储和验证。
```python
session_manager = SessionManager(config)
# 会话操作
session_info = await session_manager.create_session(client_id, version)
await session_manager.store_keys(session_id, private_key, hmac_key)
await session_manager.store_aes_key(session_id, aes_key)
session_info = await session_manager.get_session_info(session_id)
await session_manager.invalidate_session(session_id)
is_valid = await session_manager.is_session_valid(session_id)
```
### 便捷函数
框架提供了便捷函数,简化常见操作:
```python
from jiajia import (
key_exchange,
encrypt_data,
decrypt_data,
get_session_info,
invalidate_session,
health_check,
set_config,
get_api
)
# 设置全局配置
set_config(config)
# 获取全局API实例
api = get_api()
# 使用便捷函数
result = await key_exchange("client", "1.0.0")
result = await encrypt_data(data, session_id)
result = await decrypt_data(payload)
result = await get_session_info(session_id)
result = await invalidate_session(session_id)
result = await health_check()
```
## 配置选项
### 环境变量
可以通过环境变量配置框架:
```bash
export RSA_KEY_SIZE=4096
export AES_MODE=GCM
export SESSION_EXPIRE_TIME=7200
export MAX_TIMESTAMP_DIFF=600
export CACHE_BACKEND=memory
export LOG_LEVEL=INFO
export ENABLE_LOGGING=true
```
### 配置参数
| 参数 | 默认值 | 说明 |
|------|--------|------|
| `rsa_key_size` | 2048 | RSA密钥大小(位) |
| `aes_mode` | "GCM" | AES加密模式 |
| `aes_key_size` | 32 | AES密钥大小(字节) |
| `session_expire_time` | 3600 | 会话过期时间(秒) |
| `max_timestamp_diff` | 300 | 时间戳容差(秒) |
| `cache_backend` | "memory" | 缓存后端类型 |
| `redis_url` | None | Redis连接URL |
| `log_level` | "INFO" | 日志级别 |
| `enable_logging` | True | 是否启用日志 |
## 安全特性
- **混合加密**: 结合RSA和AES的优势
- **HMAC签名**: 确保数据完整性和真实性
- **时间戳验证**: 防止重放攻击
- **会话管理**: 安全的会话生命周期管理
- **密钥轮换**: 支持动态密钥生成和存储
## 性能特性
- **异步设计**: 全异步API,支持高并发
- **缓存支持**: 内存和Redis缓存后端
- **批量操作**: 支持批量数据处理
- **轻量级**: 最小化依赖,快速启动
## 错误处理
框架提供了完善的错误处理机制:
```python
try:
result = await encrypt_data(data, session_id)
if result["success"]:
# 处理成功结果
pass
else:
# 处理错误
print(f"错误: {result['error']}")
except Exception as e:
# 处理异常
print(f"异常: {e}")
```
## 测试
运行测试确保功能正常:
```bash
python -c "
import asyncio
from jiajia import key_exchange, encrypt_data, decrypt_data, SecurityConfig, set_config
async def test():
config = SecurityConfig()
set_config(config)
# 测试基本功能
exchange = await key_exchange('test', '1.0.0')
session_id = exchange['session_id']
data = {'test': 'data'}
encrypted = await encrypt_data(data, session_id)
payload = encrypted.copy()
payload['session_id'] = session_id
decrypted = await decrypt_data(payload)
print('测试通过!' if decrypted['data'] == data else '测试失败!')
asyncio.run(test())
"
```
## 许可证
MIT License
## 作者
夏云龙
## 版本
1.1.0
Raw data
{
"_id": null,
"home_page": "https://github.com/xiatian/jiajia",
"name": "jiajia",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": "\u590f\u4e91\u9f99 <1900098962@qq.com>",
"keywords": "encryption, cryptography, rsa, aes, hmac, security, hybrid",
"author": "\u590f\u4e91\u9f99",
"author_email": "\u590f\u4e91\u9f99 <1900098962@qq.com>",
"download_url": "https://files.pythonhosted.org/packages/ac/a4/732594620ec5274c8144b6b18257914639e3e8b94912e948deb3eb13f6b5/jiajia-1.1.0.tar.gz",
"platform": null,
"description": "# JiaJia - RSA-AES \u6df7\u5408\u52a0\u5bc6\u6846\u67b6\n\n\u4e00\u4e2a\u8f7b\u91cf\u7ea7\u7684\u52a0\u5bc6\u89e3\u5bc6\u6846\u67b6\uff0c\u63d0\u4f9bRSA\u975e\u5bf9\u79f0\u52a0\u5bc6\u3001AES\u5bf9\u79f0\u52a0\u5bc6\u3001HMAC\u7b7e\u540d\u9a8c\u8bc1\u7b49\u529f\u80fd\u3002\u53ef\u4ee5\u76f4\u63a5\u5728\u5176\u4ed6\u9879\u76ee\u4e2dimport\u4f7f\u7528\u3002\n\n## \u529f\u80fd\u7279\u6027\n\n- \ud83d\udd10 **RSA\u975e\u5bf9\u79f0\u52a0\u5bc6**: \u652f\u63012048\u4f4d\u548c4096\u4f4d\u5bc6\u94a5\n- \ud83d\udd11 **AES\u5bf9\u79f0\u52a0\u5bc6**: \u652f\u6301GCM\u548cCBC\u6a21\u5f0f\n- \u270d\ufe0f **HMAC\u7b7e\u540d\u9a8c\u8bc1**: \u786e\u4fdd\u6570\u636e\u5b8c\u6574\u6027\u548c\u771f\u5b9e\u6027\n- \ud83d\uddc2\ufe0f **\u4f1a\u8bdd\u7ba1\u7406**: \u652f\u6301\u4f1a\u8bdd\u521b\u5efa\u3001\u5b58\u50a8\u548c\u5931\u6548\n- \u26a1 **\u5f02\u6b65\u652f\u6301**: \u5168\u5f02\u6b65API\u8bbe\u8ba1\n- \ud83d\udee1\ufe0f **\u5b89\u5168\u914d\u7f6e**: \u7075\u6d3b\u7684\u5b89\u5168\u53c2\u6570\u914d\u7f6e\n- \ud83d\udce6 **\u4fbf\u6377\u51fd\u6570**: \u63d0\u4f9b\u7b80\u5355\u6613\u7528\u7684\u4fbf\u6377\u51fd\u6570\n- \ud83d\udd04 **\u7f13\u5b58\u652f\u6301**: \u652f\u6301\u5185\u5b58\u548cRedis\u7f13\u5b58\u540e\u7aef\n\n## \u5b89\u88c5\n\n```bash\npip install jiajia\n```\n\n## \u5feb\u901f\u5f00\u59cb\n\n### \u57fa\u672c\u4f7f\u7528\n\n```python\nimport asyncio\nfrom jiajia import key_exchange, encrypt_data, decrypt_data, set_config, SecurityConfig\n\nasync def main():\n # 1. \u8bbe\u7f6e\u914d\u7f6e\n config = SecurityConfig(\n rsa_key_size=2048,\n aes_mode=\"GCM\",\n session_expire_time=3600\n )\n set_config(config)\n \n # 2. \u5bc6\u94a5\u4ea4\u6362\uff08\u5efa\u7acb\u5b89\u5168\u4f1a\u8bdd\uff09\n exchange_result = await key_exchange(\"client_app\", \"1.0.0\")\n session_id = exchange_result[\"session_id\"]\n \n # 3. \u52a0\u5bc6\u6570\u636e\n sensitive_data = {\"user_id\": \"12345\", \"email\": \"user@example.com\"}\n encrypt_result = await encrypt_data(sensitive_data, session_id)\n \n # 4. \u89e3\u5bc6\u6570\u636e\n decrypt_payload = encrypt_result.copy()\n decrypt_payload[\"session_id\"] = session_id\n decrypt_result = await decrypt_data(decrypt_payload)\n \n print(f\"\u89e3\u5bc6\u7ed3\u679c: {decrypt_result['data']}\")\n\nasyncio.run(main())\n```\n\n### \u9ad8\u7ea7\u4f7f\u7528\n\n```python\nimport asyncio\nfrom jiajia import EncryptionAPI, SecurityConfig\n\nasync def advanced_example():\n # \u521b\u5efa\u81ea\u5b9a\u4e49\u914d\u7f6e\n config = SecurityConfig(\n rsa_key_size=4096,\n aes_mode=\"GCM\",\n session_expire_time=7200,\n max_timestamp_diff=600\n )\n \n # \u521b\u5efaAPI\u5b9e\u4f8b\n api = EncryptionAPI(config)\n \n # \u5bc6\u94a5\u4ea4\u6362\n exchange_result = await api.key_exchange(\"advanced_client\", \"2.0.0\")\n session_id = exchange_result[\"session_id\"]\n \n # \u52a0\u5bc6\u6570\u636e\n data = {\"message\": \"Hello, World!\", \"timestamp\": 1234567890}\n encrypt_result = await api.encrypt_data(data, session_id)\n \n # \u89e3\u5bc6\u6570\u636e\n decrypt_payload = encrypt_result.copy()\n decrypt_payload[\"session_id\"] = session_id\n decrypt_result = await api.decrypt_data(decrypt_payload)\n \n # \u5065\u5eb7\u68c0\u67e5\n health_result = await api.health_check()\n \n # \u83b7\u53d6\u5b89\u5168\u914d\u7f6e\n config_result = await api.get_security_config()\n\nasyncio.run(advanced_example())\n```\n\n## API \u53c2\u8003\n\n### \u6838\u5fc3\u7c7b\n\n#### `SecurityConfig`\n\n\u5b89\u5168\u914d\u7f6e\u7c7b\uff0c\u7528\u4e8e\u8bbe\u7f6e\u52a0\u5bc6\u53c2\u6570\u3002\n\n```python\nconfig = SecurityConfig(\n rsa_key_size=2048, # RSA\u5bc6\u94a5\u5927\u5c0f\n aes_mode=\"GCM\", # AES\u6a21\u5f0f (GCM/CBC)\n session_expire_time=3600, # \u4f1a\u8bdd\u8fc7\u671f\u65f6\u95f4\uff08\u79d2\uff09\n max_timestamp_diff=300, # \u65f6\u95f4\u6233\u5bb9\u5dee\uff08\u79d2\uff09\n cache_backend=\"memory\" # \u7f13\u5b58\u540e\u7aef (memory/redis)\n)\n```\n\n#### `EncryptionAPI`\n\n\u4e3b\u8981\u7684\u52a0\u5bc6API\u7c7b\u3002\n\n```python\napi = EncryptionAPI(config)\n\n# \u5bc6\u94a5\u4ea4\u6362\nresult = await api.key_exchange(client_id, client_version)\n\n# \u52a0\u5bc6\u6570\u636e\nresult = await api.encrypt_data(data, session_id)\n\n# \u89e3\u5bc6\u6570\u636e\nresult = await api.decrypt_data(payload)\n\n# \u4f1a\u8bdd\u7ba1\u7406\nresult = await api.get_session_info(session_id)\nresult = await api.invalidate_session(session_id)\n\n# \u7cfb\u7edf\u72b6\u6001\nresult = await api.health_check()\nresult = await api.get_security_config()\n```\n\n#### `EncryptionCore`\n\n\u52a0\u5bc6\u6838\u5fc3\u7c7b\uff0c\u63d0\u4f9b\u5e95\u5c42\u52a0\u5bc6\u529f\u80fd\u3002\n\n```python\ncore = EncryptionCore(config)\n\n# RSA\u64cd\u4f5c\nprivate_key, public_key = core.generate_rsa_keypair()\nencrypted = core.rsa_encrypt(public_key, data)\ndecrypted = core.rsa_decrypt(private_key, encrypted)\n\n# AES\u64cd\u4f5c\naes_key = core.generate_aes_key()\niv, ciphertext, tag = core.aes_encrypt(aes_key, data)\ndecrypted = core.aes_decrypt(aes_key, iv, ciphertext, tag)\n\n# HMAC\u64cd\u4f5c\nhmac_key = core.generate_hmac_key()\nsignature = core.hmac_sign(hmac_key, data)\nis_valid = core.hmac_verify(hmac_key, data, signature)\n```\n\n#### `SessionManager`\n\n\u4f1a\u8bdd\u7ba1\u7406\u7c7b\uff0c\u5904\u7406\u4f1a\u8bdd\u7684\u521b\u5efa\u3001\u5b58\u50a8\u548c\u9a8c\u8bc1\u3002\n\n```python\nsession_manager = SessionManager(config)\n\n# \u4f1a\u8bdd\u64cd\u4f5c\nsession_info = await session_manager.create_session(client_id, version)\nawait session_manager.store_keys(session_id, private_key, hmac_key)\nawait session_manager.store_aes_key(session_id, aes_key)\nsession_info = await session_manager.get_session_info(session_id)\nawait session_manager.invalidate_session(session_id)\nis_valid = await session_manager.is_session_valid(session_id)\n```\n\n### \u4fbf\u6377\u51fd\u6570\n\n\u6846\u67b6\u63d0\u4f9b\u4e86\u4fbf\u6377\u51fd\u6570\uff0c\u7b80\u5316\u5e38\u89c1\u64cd\u4f5c\uff1a\n\n```python\nfrom jiajia import (\n key_exchange,\n encrypt_data,\n decrypt_data,\n get_session_info,\n invalidate_session,\n health_check,\n set_config,\n get_api\n)\n\n# \u8bbe\u7f6e\u5168\u5c40\u914d\u7f6e\nset_config(config)\n\n# \u83b7\u53d6\u5168\u5c40API\u5b9e\u4f8b\napi = get_api()\n\n# \u4f7f\u7528\u4fbf\u6377\u51fd\u6570\nresult = await key_exchange(\"client\", \"1.0.0\")\nresult = await encrypt_data(data, session_id)\nresult = await decrypt_data(payload)\nresult = await get_session_info(session_id)\nresult = await invalidate_session(session_id)\nresult = await health_check()\n```\n\n## \u914d\u7f6e\u9009\u9879\n\n### \u73af\u5883\u53d8\u91cf\n\n\u53ef\u4ee5\u901a\u8fc7\u73af\u5883\u53d8\u91cf\u914d\u7f6e\u6846\u67b6\uff1a\n\n```bash\nexport RSA_KEY_SIZE=4096\nexport AES_MODE=GCM\nexport SESSION_EXPIRE_TIME=7200\nexport MAX_TIMESTAMP_DIFF=600\nexport CACHE_BACKEND=memory\nexport LOG_LEVEL=INFO\nexport ENABLE_LOGGING=true\n```\n\n### \u914d\u7f6e\u53c2\u6570\n\n| \u53c2\u6570 | \u9ed8\u8ba4\u503c | \u8bf4\u660e |\n|------|--------|------|\n| `rsa_key_size` | 2048 | RSA\u5bc6\u94a5\u5927\u5c0f\uff08\u4f4d\uff09 |\n| `aes_mode` | \"GCM\" | AES\u52a0\u5bc6\u6a21\u5f0f |\n| `aes_key_size` | 32 | AES\u5bc6\u94a5\u5927\u5c0f\uff08\u5b57\u8282\uff09 |\n| `session_expire_time` | 3600 | \u4f1a\u8bdd\u8fc7\u671f\u65f6\u95f4\uff08\u79d2\uff09 |\n| `max_timestamp_diff` | 300 | \u65f6\u95f4\u6233\u5bb9\u5dee\uff08\u79d2\uff09 |\n| `cache_backend` | \"memory\" | \u7f13\u5b58\u540e\u7aef\u7c7b\u578b |\n| `redis_url` | None | Redis\u8fde\u63a5URL |\n| `log_level` | \"INFO\" | \u65e5\u5fd7\u7ea7\u522b |\n| `enable_logging` | True | \u662f\u5426\u542f\u7528\u65e5\u5fd7 |\n\n## \u5b89\u5168\u7279\u6027\n\n- **\u6df7\u5408\u52a0\u5bc6**: \u7ed3\u5408RSA\u548cAES\u7684\u4f18\u52bf\n- **HMAC\u7b7e\u540d**: \u786e\u4fdd\u6570\u636e\u5b8c\u6574\u6027\u548c\u771f\u5b9e\u6027\n- **\u65f6\u95f4\u6233\u9a8c\u8bc1**: \u9632\u6b62\u91cd\u653e\u653b\u51fb\n- **\u4f1a\u8bdd\u7ba1\u7406**: \u5b89\u5168\u7684\u4f1a\u8bdd\u751f\u547d\u5468\u671f\u7ba1\u7406\n- **\u5bc6\u94a5\u8f6e\u6362**: \u652f\u6301\u52a8\u6001\u5bc6\u94a5\u751f\u6210\u548c\u5b58\u50a8\n\n## \u6027\u80fd\u7279\u6027\n\n- **\u5f02\u6b65\u8bbe\u8ba1**: \u5168\u5f02\u6b65API\uff0c\u652f\u6301\u9ad8\u5e76\u53d1\n- **\u7f13\u5b58\u652f\u6301**: \u5185\u5b58\u548cRedis\u7f13\u5b58\u540e\u7aef\n- **\u6279\u91cf\u64cd\u4f5c**: \u652f\u6301\u6279\u91cf\u6570\u636e\u5904\u7406\n- **\u8f7b\u91cf\u7ea7**: \u6700\u5c0f\u5316\u4f9d\u8d56\uff0c\u5feb\u901f\u542f\u52a8\n\n## \u9519\u8bef\u5904\u7406\n\n\u6846\u67b6\u63d0\u4f9b\u4e86\u5b8c\u5584\u7684\u9519\u8bef\u5904\u7406\u673a\u5236\uff1a\n\n```python\ntry:\n result = await encrypt_data(data, session_id)\n if result[\"success\"]:\n # \u5904\u7406\u6210\u529f\u7ed3\u679c\n pass\n else:\n # \u5904\u7406\u9519\u8bef\n print(f\"\u9519\u8bef: {result['error']}\")\nexcept Exception as e:\n # \u5904\u7406\u5f02\u5e38\n print(f\"\u5f02\u5e38: {e}\")\n```\n\n## \u6d4b\u8bd5\n\n\u8fd0\u884c\u6d4b\u8bd5\u786e\u4fdd\u529f\u80fd\u6b63\u5e38\uff1a\n\n```bash\npython -c \"\nimport asyncio\nfrom jiajia import key_exchange, encrypt_data, decrypt_data, SecurityConfig, set_config\n\nasync def test():\n config = SecurityConfig()\n set_config(config)\n \n # \u6d4b\u8bd5\u57fa\u672c\u529f\u80fd\n exchange = await key_exchange('test', '1.0.0')\n session_id = exchange['session_id']\n \n data = {'test': 'data'}\n encrypted = await encrypt_data(data, session_id)\n \n payload = encrypted.copy()\n payload['session_id'] = session_id\n decrypted = await decrypt_data(payload)\n \n print('\u6d4b\u8bd5\u901a\u8fc7!' if decrypted['data'] == data else '\u6d4b\u8bd5\u5931\u8d25!')\n\nasyncio.run(test())\n\"\n```\n\n## \u8bb8\u53ef\u8bc1\n\nMIT License\n\n## \u4f5c\u8005\n\n\u590f\u4e91\u9f99\n\n## \u7248\u672c\n\n1.1.0\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "\u8f7b\u91cf\u7ea7RSA-AES\u6df7\u5408\u52a0\u5bc6\u6846\u67b6",
"version": "1.1.0",
"project_urls": {
"Documentation": "https://github.com/xiatian/jiajia#readme",
"Homepage": "https://github.com/xiatian/jiajia",
"Issues": "https://github.com/xiatian/jiajia/issues",
"Repository": "https://github.com/xiatian/jiajia"
},
"split_keywords": [
"encryption",
" cryptography",
" rsa",
" aes",
" hmac",
" security",
" hybrid"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "ed570b8f90f2f4c29868e9caa20dbc50e5f0790dfaf2510ec2ffc69f2f91f844",
"md5": "15a2bc3fa5bf2c6cf2fe73bcec846f8c",
"sha256": "98625965694c92b916294911b27f2b4fc79664b3a85fb6337617a4e12830ca3e"
},
"downloads": -1,
"filename": "jiajia-1.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "15a2bc3fa5bf2c6cf2fe73bcec846f8c",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 14319,
"upload_time": "2025-08-01T06:19:06",
"upload_time_iso_8601": "2025-08-01T06:19:06.131593Z",
"url": "https://files.pythonhosted.org/packages/ed/57/0b8f90f2f4c29868e9caa20dbc50e5f0790dfaf2510ec2ffc69f2f91f844/jiajia-1.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "aca4732594620ec5274c8144b6b18257914639e3e8b94912e948deb3eb13f6b5",
"md5": "1097a0a41cc1c7fc897f6164cc9c9b41",
"sha256": "5cf700c9ea2f471589968a2be05b8218d47561668ac4c54791f31803132a77d4"
},
"downloads": -1,
"filename": "jiajia-1.1.0.tar.gz",
"has_sig": false,
"md5_digest": "1097a0a41cc1c7fc897f6164cc9c9b41",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 15599,
"upload_time": "2025-08-01T06:19:07",
"upload_time_iso_8601": "2025-08-01T06:19:07.323252Z",
"url": "https://files.pythonhosted.org/packages/ac/a4/732594620ec5274c8144b6b18257914639e3e8b94912e948deb3eb13f6b5/jiajia-1.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-01 06:19:07",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "xiatian",
"github_project": "jiajia",
"github_not_found": true,
"lcname": "jiajia"
}