Keyrings Cryptfile
keyrings.cryptfile is a Python library that provides an encrypted file keyring backend for the standard `keyring` package. It enables secure storage of plaintext passwords in a portable encrypted file, particularly useful when typical desktop environment keyring implementations are unsuitable. The project encrypts data using Argon2 for key derivation and authenticated AES encryption (GCM by default). The latest version is 1.3.9, released on November 20, 2022. While there isn't a strict release cadence, updates appear to be infrequent.
Common errors
-
ImportError: cannot import name 'properties' from 'keyring.util'
cause This error likely indicates a compatibility issue between `keyrings-cryptfile` and a newer version of the `keyring` library, where `properties` might have been moved or removed from `keyring.util`.fixCheck the GitHub issues for `keyrings-cryptfile` for known compatibility problems. Consider pinning the `keyring` library to an older, compatible version (e.g., `pip install keyring==X.Y.Z`) or awaiting an update to `keyrings-cryptfile`. -
ValueError: MAC check failed
cause This error typically occurs during decryption, indicating that the master password (`keyring_key`) used is incorrect, the encrypted file has been corrupted, or it has been tampered with.fixVerify that the `keyring_key` (master password) provided is absolutely correct. If the password is correct and the error persists, the keyring file may be damaged, and recreating it might be the only solution. -
Application hangs or waits for input in a non-interactive environment (e.g., CI/CD pipeline)
cause The `keyrings.cryptfile` backend uses `getpass.getpass()` for its master password if `kr.keyring_key` is not set. In a non-interactive environment, this will cause the application to hang indefinitely while waiting for input.fixBefore any password operations, ensure that the `kr.keyring_key` attribute is set programmatically, typically by reading the master password from a secure environment variable (e.g., `os.environ.get('KEYRING_CRYPTFILE_MASTER_PASSWORD')`).
Warnings
- gotcha Hardcoding the `keyring_key` (the master password for the encrypted file) directly in code is a severe security vulnerability, as it defeats the purpose of encryption.
- gotcha Operations like `set_password()` and `get_password()` can involve an interactive prompt for the keyring's master password if `keyring_key` is not set programmatically. The Key Derivation Function (KDF) also introduces a noticeable delay (~1 second) for cryptographic processing.
- gotcha As a backend, `keyrings.cryptfile` relies on the `keyring` library for integration. If `keyring` is not installed or configured to use this backend, it might not be automatically selected.
Install
-
pip install keyrings-cryptfile
Imports
- CryptFileKeyring
from keyrings_cryptfile.cryptfile import CryptFileKeyring
from keyrings.cryptfile.cryptfile import CryptFileKeyring
Quickstart
import keyring
from keyrings.cryptfile.cryptfile import CryptFileKeyring
import os
# Create an instance of the CryptFileKeyring
kr = CryptFileKeyring()
# Optional: Set a keyring key programmatically (e.g., from an environment variable)
# If not set, the library will prompt interactively for the master password.
# Hardcoding the keyring_key is insecure and should be avoided in production.
keyring_master_password = os.environ.get('KEYRING_CRYPTFILE_MASTER_PASSWORD', '')
if keyring_master_password:
kr.keyring_key = keyring_master_password # type: ignore
# Set a password for a service and user
service_name = "my_application"
username = "api_user"
secret_password = "my_super_secret_password"
# This will prompt for the master password if not set programmatically
kr.set_password(service_name, username, secret_password)
print(f"Password for service '{service_name}' and user '{username}' set successfully.")
# Retrieve the password
# This will also prompt for the master password if not set programmatically
retrieved_password = kr.get_password(service_name, username)
if retrieved_password:
print(f"Retrieved password for '{service_name}' user '{username}': {retrieved_password}")
else:
print(f"No password found for '{service_name}' user '{username}'.")
# To make CryptFileKeyring the default for the standard `keyring` library:
# keyring.set_keyring(kr)
# # Now, calls to keyring.set_password() or keyring.get_password() will use CryptFileKeyring
# keyring.set_password("another_service", "another_user", "another_secret")
# print(keyring.get_password("another_service", "another_user"))