Serialización de Claves
El módulo aegisq.keys provee helpers de alto nivel para exportar/importar claves desde/hacia disco, variables de entorno y bytes en memoria, construidos sobre KeyPair.
El módulo es v1.3.0 y vive en aegisq/keys.py. Cada función acepta tanto un string de ruta como un pathlib.Path.
Referencia Rápida
Sección titulada «Referencia Rápida»| Función | Dirección | Formato | Cifrado |
|---|---|---|---|
save_public_key | KeyPair → archivo | PEM (default) o JSON | Ninguno |
load_public_key | archivo → bytes | Auto-detecta PEM/JSON | Ninguno |
save_secret_key | KeyPair → archivo | PEM cifrado | AES-256-GCM (HKDF-SHA3-256) |
load_secret_key | archivo → bytes | PEM cifrado | AES-256-GCM (HKDF-SHA3-256) |
public_key_to_pem | KeyPair → str | PEM | Ninguno |
public_key_to_json | KeyPair → str | JSON | Ninguno |
secret_key_to_pem | KeyPair → str | PEM cifrado | AES-256-GCM (HKDF-SHA3-256) |
Persistencia de Clave Pública
Sección titulada «Persistencia de Clave Pública»save_public_key
Sección titulada «save_public_key»def save_public_key(keypair: KeyPair, path: str | Path, *, fmt: str = "pem") -> NoneEscribe la clave pública en path en formato PEM (default) o JSON.
| Parámetro | Tipo | Default | Descripción |
|---|---|---|---|
keypair | KeyPair | — | Keypair fuente. |
path | str | Path | — | Archivo destino. Se recomienda extensión .pem para PEM o .json para JSON. |
fmt | str | "pem" | "pem" o "json". |
Lanza: ValueError si fmt no es "pem" ni "json"; OSError si no se puede escribir el archivo.
from aegisq import AegisCipherfrom aegisq.keys import save_public_key
cipher = AegisCipher()keypair = cipher.generate_keypair()
# PEM (recomendado para almacenamiento de largo plazo)save_public_key(keypair, "recipient.pem")
# JSON (auto-descriptivo, útil para interoperabilidad)save_public_key(keypair, "recipient.json", fmt="json")load_public_key
Sección titulada «load_public_key»def load_public_key(path: str | Path, *, level: SecurityLevel | None = None) -> bytesLee una clave pública desde disco y la retorna como bytes (lista para pasar a AegisCipher.encrypt()).
La detección de formato es automática — la función inspecciona la primera línea no vacía del archivo:
-----BEGIN ML-KEM PUBLIC KEY-----→ PEM (el llamador debe proveerlevel=){→ JSON (el nivel se lee del campo"level"y el argumentolevelse ignora)
| Parámetro | Tipo | Default | Descripción |
|---|---|---|---|
path | str | Path | — | Archivo fuente. |
level | SecurityLevel | None | None | Obligatorio para PEM. Ignorado para JSON. |
Retorna: bytes — la clave pública.
Lanza:
ValueError— formato de archivo no reconocible, o archivo PEM sinlevel=KeySerializationError— PEM/JSON malformadoInvalidParameterError— el tamaño decodificado no coincide con el nivelOSError— el archivo no se puede leer
from aegisq import SecurityLevelfrom aegisq.keys import load_public_key
# Archivo PEM — debés conocer el nivelpk = load_public_key("recipient.pem", level=SecurityLevel.ML_KEM_768)
# Archivo JSON — el nivel se lee del archivopk = load_public_key("recipient.json")Persistencia de Clave Secreta (Cifrada)
Sección titulada «Persistencia de Clave Secreta (Cifrada)»save_secret_key
Sección titulada «save_secret_key»def save_secret_key(keypair: KeyPair, path: str | Path, *, password: bytes) -> NoneCifra la clave secreta con AES-256-GCM (clave derivada vía HKDF-SHA3-256 desde tu password) y la escribe como PEM cifrado.
El archivo luce así:
-----BEGIN ENCRYPTED ML-KEM PRIVATE KEY-----<Base64 STANDARD del blob cifrado>-----END ENCRYPTED ML-KEM PRIVATE KEY-----El blob contiene magic || version || level_id || salt (16 B) || nonce (12 B) || ciphertext || tag (16 B). El bridge usa getrandom::fill para generar salt y nonce frescos y propaga los fallos del RNG; las exportaciones son aleatorias, no garantizadamente distintas.
Lanza: RngError si el CSPRNG del OS no está disponible; OSError si no se puede escribir el archivo.
from aegisq.keys import save_secret_key
import secretspassword = secrets.token_bytes(32) # Conservar de forma segura para recuperarlasave_secret_key(keypair, "private.key", password=password)load_secret_key
Sección titulada «load_secret_key»def load_secret_key(path: str | Path, *, password: bytes) -> bytesLee un archivo PEM cifrado, verifica el auth tag y retorna la clave secreta como bytes.
Lanza:
DecryptionError— contraseña incorrecta o archivo corruptoKeySerializationError— header PEM, Base64, o magic/version inválidosOSError— el archivo no se puede leer
from aegisq.keys import load_secret_key
sk = load_secret_key("private.key", password=password)plaintext = cipher.decrypt(encrypted_package, sk)Funciones de Conveniencia Solo-String
Sección titulada «Funciones de Conveniencia Solo-String»Estas tres funciones producen o consumen str en lugar de archivos, lo cual es útil cuando las claves viven en variables de entorno, secret managers o columnas de bases de datos.
public_key_to_pem
Sección titulada «public_key_to_pem»def public_key_to_pem(keypair: KeyPair) -> strEquivalente a keypair.public_key_pem(), expuesto aquí para comodidad de import.
public_key_to_json
Sección titulada «public_key_to_json»def public_key_to_json(keypair: KeyPair) -> strEquivalente a keypair.public_key_json(), expuesto aquí para comodidad de import.
secret_key_to_pem
Sección titulada «secret_key_to_pem»def secret_key_to_pem(keypair: KeyPair, *, password: bytes) -> strEquivalente a keypair.export_secret_key_pem(password), expuesto aquí para comodidad de import.
Ejemplo End-to-End
Sección titulada «Ejemplo End-to-End»from aegisq import AegisCipher, SecurityLevelfrom aegisq.keys import ( save_public_key, load_public_key, save_secret_key, load_secret_key,)
# Lado del receptor: generar y persistircipher = AegisCipher()keypair = cipher.generate_keypair()save_public_key(keypair, "alice.pub.pem", fmt="pem")import secretspassword = secrets.token_bytes(32) # Guardar por separado en un gestor de secretossave_secret_key(keypair, "alice.sec.pem", password=password)
# Más tarde (o en otro proceso): cargar de vueltacipher = AegisCipher(level=SecurityLevel.ML_KEM_768)pub = load_public_key("alice.pub.pem", level=SecurityLevel.ML_KEM_768)sec = load_secret_key("alice.sec.pem", password=password)
# Usar normalmentepackage = cipher.encrypt(b"mensaje secreto", pub)plaintext = cipher.decrypt(package, sec)assert plaintext == b"mensaje secreto"Referencia de Formatos
Sección titulada «Referencia de Formatos»PEM de Clave Pública
Sección titulada «PEM de Clave Pública»-----BEGIN ML-KEM PUBLIC KEY-----<Base64 STANDARD de los bytes de public_key>-----END ML-KEM PUBLIC KEY-----JSON de Clave Pública
Sección titulada «JSON de Clave Pública»{ "algorithm": "ML-KEM", "level": "ML_KEM_768", "public_key": "<Base64 URL-safe sin padding>"}PEM Cifrado de Clave Secreta
Sección titulada «PEM Cifrado de Clave Secreta»-----BEGIN ENCRYPTED ML-KEM PRIVATE KEY-----<Base64 STANDARD de: magic || version || level_id || salt || nonce || ciphertext || tag>-----END ENCRYPTED ML-KEM PRIVATE KEY-----El formato interno del blob es un detalle de implementación; trátelo como opaco. El descifrado público desde archivos usa aegisq.keys.load_secret_key(path, password=...); el usuario no debe importar el bridge nativo directamente.
Ver También
Sección titulada «Ver También»KeyPair— la clase subyacente con bytes crudos y métodos de serializaciónMlKem— API KEM de bajo nivel conload_public_key_b64- Excepciones —
KeySerializationError,DecryptionErrorlanzadas por estos helpers