Detailed Key Management Generation & Retrieval in KeyManagementService
This document explains how ANKA Secure handles various key types—symmetric, traditional asymmetric (RSA/ECC), and post-quantum cryptography (PQC) algorithms—through the KeyManagementService
class. It builds on the keystore infrastructure (e.g., BouncyCastle .bks
file, AWS KMS placeholders, etc.) by showing exactly how keys are generated, stored, and retrieved.
1. Overview
The KeyManagementService
centralizes logic for:
- Generating symmetric keys (AES).
- Generating asymmetric keys (RSA, ECC, Dilithium, Falcon, Kyber, SPHINCS+).
- Storing and retrieving those keys from the keystore (via
KeyStoreProvider
). - Handling special nuances for post-quantum (PQ) algorithms like Kyber (KEM), Dilithium, Falcon, and SPHINCS+.
Why a Single Service?
Keeping key generation/storage in a dedicated service simplifies:
- Separation of Concerns: Other services (e.g. encryption/decryption logic) do not need to worry about how keys are physically stored or how certificate entries are created.
- Extensibility: You can add new algorithms (like Azure Vault providers or new PQC algorithms) by extending this class or the underlying
KeyStoreProvider
logic.
2. Symmetric Key Management (AES)
2.1 Generating a Symmetric Key
- Supported Algorithms: Currently, the code focuses on
"AES"
, with supported key sizes:128
,192
, or256
bits.- - Steps:
- Validate algorithm/key size.
- Use BouncyCastle's
KeyGenerator
to create the AES key. - Store the generated
SecretKey
in the keystore under the specified alias. (KeyStore.SecretKeyEntry
)
- Usage Tracking: A call to
UsageReporterUtil.reportUsageAlgorithm(...)
logs usage metrics for licensing or analytics.
Example
This creates an AES-256 key and places it in the keystore (app.log
, if configured).
2.2 Retrieving a Symmetric Key
Example
- Looks up the specified alias (alias
) in the keystore.
- Expects the entry to be a KeyStore.SecretKeyEntry
.
- Returns the SecretKey
object (e.g., AES key).
Important: If the alias does not exist or is not a
SecretKeyEntry
, aResourceNotFoundException
is thrown. Unexpected I/O or cryptographic errors result in aCryptoOperationException
.
3. Traditional Asymmetric Key Management (RSA & ECC)
3.1 Generating RSA/ECC Key Pairs
Example
1. Algorithm Registry: Looks up theAsymmetricAlgorithmSpec
from AsymmetricAlgorithmRegistry
to figure out details like:
- Is it RSA (e.g., RSA-2048
)?
- Is it ECC (e.g., EC-256
)?
- Is it a PQC signature or PQC KEM? (more below)
2. Key Pair Creation:
- RSA: KeyPairGenerator("RSA")
+ kpg.initialize(<key_size>, new SecureRandom())
.
- ECC: KeyPairGenerator("EC")
+ kpg.initialize(<curve_spec>, new SecureRandom())
.
3. Storing the Key:
- By default, the service calls storeSignatureKeyPair(...)
, which:
- Creates a self-signed certificate (X.509) for the public key (using BouncyCastle).
- Stores the private key + the certificate chain in a KeyStore.PrivateKeyEntry
.
Example
This results in:- A private key stored under
alias = "myRsaKey"
as aPrivateKeyEntry
. - A self-signed certificate associated with that private key (allowing standard Java usage).
3.2 Retrieving or Using RSA/ECC Keys
To retrieve these keys for signature or decryption operations:
- The service typically calls
keyStoreProvider.getEntry(alias, ...)
, ensuring you get aPrivateKey
for the alias. - For public key export, the code calls
exportPublicKey
, which pulls the key from the certificate or from an alias-suffix like_pub
if relevant.
4. Post-Quantum Cryptography (PQC)
The code supports or references multiple PQC algorithms:
- Dilithium (signature)
- Falcon (signature)
- SPHINCS+ (signature)
- Kyber (KEM)
Why Special Handling?\
Java's native KeyStore does not always recognize these as standard PrivateKey
entries. Some PQC algorithms produce key formats that do not map neatly to Java's built-in classes. Hence, the code uses a mix of:
PrivateKeyEntry
(if BouncyCastle can treat them like standard private keys + certificates), orSecretKeyEntry
to store raw key bytes (like Kyber or SPHINCS+).
Below are the key differences for each PQC algorithm.
4.1 Dilithium & Falcon (Signature-Only PQC)
- Creation:
- The user calls
generateAsymmetricKey("Dilithium3", "myDilKey")
(or similarly for Falcon). - The service detects it is a signature PQC (
spec.isSignatureOnly()
) and callsstoreSignatureKeyPair(...)
. - Self-Signed Certificate is generated, stored as a
KeyStore.PrivateKeyEntry
.
- The user calls
- Result:
- The private key is stored in a standard
PrivateKeyEntry
along with a self-signed certificate (like RSA/ECC). - The approach works if the BouncyCastle PQC provider supports creating an X.509 structure (some are fully recognized as "private keys" in the keystore).
- The private key is stored in a standard
Benefits:
- This means usage is consistent with RSA/ECC from Java's perspective (assuming the BouncyCastle PQC provider is recognized as a valid
PrivateKey
type).
4.2 SPHINCS+ (Signature PQC) Stored as SecretKeyEntry
Special Case:
Example
if ("SPHINCSPLUS".equalsIgnoreCase(algoForCert)) {
// Store private key + public key as SecretKeyEntries.
}
- SPHINCS+ might not be recognized as a standard
PrivateKey
(the Java keystore can reject it). - The code places them in the keystore as
SecretKeyEntry
objects:- Private key under
alias
. - Public key under
alias + "_pub"
.
- Private key under
- No self-signed certificate is generated for SPHINCS+ because Java X.509 cannot handle that algorithm by default.
Implication:
- SPHINCS+ keys appear similarly to a "symmetric" key in the keystore, though conceptually they are an asymmetric signature key pair.
- When exporting the public key, code must specifically check
_pub
.
4.3 Kyber (KEM) -- Stored in Two SecretKeyEntry
Objects
Kyber is a Key Encapsulation Mechanism (KEM) for encryption rather than a signature scheme, so it's handled differently:
- Storage:
- The private key is stored under
alias
(as aSecretKeyEntry
). - The public key is stored under
alias + "_pub"
(also as aSecretKeyEntry
). - Each entry is simply the raw byte array from
keyPair.getPrivate().getEncoded()
orkeyPair.getPublic().getEncoded()
, wrapped in aSecretKeySpec
.
- The private key is stored under
- No Certificate:
- Since KEM public keys typically do not integrate with X.509 for signatures, no self-signed certificate is generated for Kyber.
- Retrieval:
- For encryption or encapsulation, you'd get the public key from
alias + "_pub"
. - For decryption or decapsulation, you'd get the private key from
alias
.
- For encryption or encapsulation, you'd get the public key from
Note: The code sets
private alias = "kyberKey"; public alias = "kyberKey_pub"
with different raw key bytes.
5. How Keys Appear in the Keystore
Summarizing the storage differences:
Algorithm | Private Key Storage | Public Key Storage | Certificate? |
---|---|---|---|
AES (Symmetric) | SecretKeyEntry under alias (e.g., "myAesKey" ) |
N/A | N/A |
RSA, ECC | PrivateKeyEntry under alias (includes a self-signed cert) |
In the same entry (public key from the cert) | Yes (self-signed) |
Dilithium, Falcon | PrivateKeyEntry under alias (includes a self-signed cert, if recognized by BC) |
In the same entry (public key from the cert) | Yes (self-signed) |
SPHINCS+ | Private: SecretKeyEntry (alias) Public: SecretKeyEntry (alias + _pub ) |
Same file, but stored in a SecretKeySpec as public key bytes |
No certificate |
Kyber | Private: SecretKeyEntry (alias) Public: SecretKeyEntry (alias + _pub ) |
Separate alias: alias + "_pub" |
No certificate |
6. Removing & Listing Keys
-
removeKey(String alias)
- Deletes the primary alias from the keystore.
- For Kyber/SPHINCS+, it also deletes
alias + "_pub"
if present. -
listAllKeyEntries()
-
Enumerates all aliases from
keyStoreProvider.aliases()
. - For each alias, tries retrieving the key (
getKey
) and certificate (getCertificate
). - Builds a
KeyEntryDto
summarizing the entry type (PRIVATE_KEY
,PUBLIC_KEY
,SECRET_KEY
, etc.), algorithm, key size, etc.
This design ensures you can quickly see what's in your keystore, including PQC keys that are stored as SecretKeyEntries
.
7. Import/Export Functions
In addition to generation, the service supports importing and exporting keys:
-
Export:
exportPublicKey(alias, outputFile)
writes the public key bytes to a file.- Checks
alias + "_pub"
if it's a KEM (Kyber) or SPHINCS+ style. Otherwise, it looks up the X.509 certificate's public key. -
Import:
-
importPublicKey(alias, inputFile, algorithm)
reads raw bytes from disk and re-creates the key in the keystore. - If PQC KEM (
Kyber
), stores in_pub
. If signature-based, it might create a dummy self-signed certificate. -
Import PKCS#12:
-
importPrivateKeyPkcs12(alias, p12Password, pkcs12Bytes)
loads a.p12
or.pfx
file, extracts the private key + cert chain, and stores them as aPrivateKeyEntry
. This is typically used for RSA/ECC certificates from a Certificate Authority.
8. Combining with the Keystore Provider
All of these operations rely on:
KeyStoreProvider
: The interface that interacts with either a BouncyCastle.bks
file, AWS KMS, or other possible backends (Azure Vault in the future).BcKeyStoreProvider
(BKS) orAwsKmsKeyStoreProvider
(dummy).- The code in
KeyManagementService
is effectively a higher-level manager, ensuring each key (PQC or classical) is properly stored.