Я создаю небольшой клиент Python для службы FastAPI, которая отправляет одноразовые зашифрованные сообщения. Ключи должны оставаться на клиенте (никогда не отправляться на сервер и не храниться им).
Env/Libraries
Python 3.11
[PyNaCl] для X25519/box (nacl.public)
Необязательно криптография для KDF + AEAD
Цель
Сохранять долгоживущую пару ключей PyNaCl между запусками. Я хочу, чтобы закрытый ключ хранился на диске и расшифровывался только после того, как пользователь введет парольную фразу (или, альтернативно, используйте связку ключей ОС, если это считается лучшей практикой).
Что я пробовал
Запись необработанного 32-байтового закрытого ключа в файл → легко, но явно небезопасно в состоянии покоя.
Использование криптографии для получения ключа из файла парольную фразу (Scrypt) и зашифруйте закрытый ключ с помощью AES-GCM перед записью на диск. Это работает, но я не уверен насчет рекомендуемых параметров/шаблонов.
Использование пакета ключей для хранения закрытого ключа в хранилище учетных данных ОС (Windows DPAPI / macOS Keychain / Secret Service). Это позволяет избежать использования парольных фраз, но я не уверен в переносимости и резервном копировании/восстановлении.
Минимальное воспроизведение (эскиз)
Python 3.11
pip install pynacl шифрование
from nacl.public import PrivateKey
from cryptography.hazmat.primitives.kdf.scrypt import Scrypt
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
import os, json, base64
# 1) Generate
sk = PrivateKey.generate()
pk = sk.public_key
sk_bytes = bytes(sk) # 32 bytes
pk_bytes = bytes(pk) # 32 bytes (saved in clear is fine)
# 2) Derive wrap key from passphrase
def derive_key(passphrase: str, salt: bytes) -> bytes:
kdf = Scrypt(salt=salt, length=32, n=2**15, r=8, p=1,
backend=default_backend())
return kdf.derive(passphrase.encode("utf-8"))
3) Зашифруйте секретный ключ с помощью AES-GCM и сохраните (salt, nonce, ct)
def store_key(sk_bytes: bytes, passphrase: str, path="keystore.json"):
salt = os.urandom(16)
nonce = os.urandom(12)
aad_key = Decode_key(passphrase, salt)
ct = AESGCM(aeaad_key).encrypt(nonce, sk_bytes, Associated_data=None)
data = {
"salt": base64.b64encode(salt).decode(),
"nonce": base64.b64encode(nonce).decode(),
"ct": base64.b64encode(ct).decode(),
"pub": base64.b64encode(pk_bytes).decode(),
}
с open(path, "w") как f:
json.dump(data, f)
4) Загрузить + расшифровать
def load_key(passphrase: str, path="keystore.json") -> байты:
data = json.load(open(path))
salt = base64.b64decode(data["salt"])
nonce = base64.b64decode(data["nonce"])
ct = base64.b64decode(data["ct"])
aad_key = Decrypt(nonce, ct, Associated_data=None)
return sk_bytes
Использование:
store_key(sk_bytes, "my passphrase")
recovered = load_key("my passphrase")
assert восстановлен == sk_bytes
Вопросы
Является ли «ключ, полученный из Scrypt + упаковка AES-GCM, а затем сохранение JSON в файле» разумным шаблоном для сохранения ключа на стороне клиента с помощью PyNaCl? Какие-либо рекомендуемые параметры Scrypt для настольных компьютеров в 2025 году?
Рекомендуете ли вы вместо этого использовать связку ключей ОС через связку ключей (и если да, то как безопасно и портативно хранить 32 необработанных байта)?
Есть ли подводные камни с ключами X25519 в PyNaCl при такой сериализации/десериализации?
Ограничения
Я не могу хранить секреты на сервер. Можно запросить у пользователя парольную фразу при запуске; никаких инфраструктур пользовательского интерфейса не требуется.
Ожидается:
руководство или пример кода, демонстрирующий рекомендуемый подход (файл с парольной фразой или связка ключей ОС) и выбор параметров (стоимость шифрования, обработка nonce AES-GCM и т. д.).
Я создаю небольшой клиент Python для службы FastAPI, которая отправляет одноразовые зашифрованные сообщения. Ключи должны оставаться на клиенте (никогда не отправляться на сервер и не храниться им). Env/Libraries [list] [*]Python 3.11 [*][PyNaCl] для X25519/box (nacl.public) [*]Необязательно криптография для KDF + AEAD Цель [/list] Сохранять долгоживущую пару ключей PyNaCl между запусками. Я хочу, чтобы закрытый ключ хранился на диске и расшифровывался только после того, как пользователь введет парольную фразу (или, альтернативно, используйте связку ключей ОС, если это считается лучшей практикой). Что я пробовал [list] [*]Запись необработанного 32-байтового закрытого ключа в файл → легко, но явно небезопасно в состоянии покоя.
[*]Использование криптографии для получения ключа из файла парольную фразу (Scrypt) и зашифруйте закрытый ключ с помощью AES-GCM перед записью на диск. Это работает, но я не уверен насчет рекомендуемых параметров/шаблонов.
[*]Использование пакета ключей для хранения закрытого ключа в хранилище учетных данных ОС (Windows DPAPI / macOS Keychain / Secret Service). Это позволяет избежать использования парольных фраз, но я не уверен в переносимости и резервном копировании/восстановлении. Минимальное воспроизведение (эскиз) Python 3.11 pip install pynacl шифрование [code]from nacl.public import PrivateKey from cryptography.hazmat.primitives.kdf.scrypt import Scrypt from cryptography.hazmat.primitives.ciphers.aead import AESGCM from cryptography.hazmat.primitives import serialization from cryptography.hazmat.backends import default_backend import os, json, base64
# 1) Generate sk = PrivateKey.generate() pk = sk.public_key sk_bytes = bytes(sk) # 32 bytes pk_bytes = bytes(pk) # 32 bytes (saved in clear is fine)
[/list] Вопросы [list] [*]Является ли «ключ, полученный из Scrypt + упаковка AES-GCM, а затем сохранение JSON в файле» разумным шаблоном для сохранения ключа на стороне клиента с помощью PyNaCl? Какие-либо рекомендуемые параметры Scrypt для настольных компьютеров в 2025 году? [*]Рекомендуете ли вы вместо этого использовать связку ключей ОС через связку ключей (и если да, то как безопасно и портативно хранить 32 необработанных байта)? [*]Есть ли подводные камни с ключами X25519 в PyNaCl при такой сериализации/десериализации? Ограничения Я не могу хранить секреты на сервер. Можно запросить у пользователя парольную фразу при запуске; никаких инфраструктур пользовательского интерфейса не требуется. Ожидается: руководство или пример кода, демонстрирующий рекомендуемый подход (файл с парольной фразой или связка ключей ОС) и выбор параметров (стоимость шифрования, обработка nonce AES-GCM и т. д.). [/list]