CRYSTALS Kyber for Post-Quantum Hybrid Encryption with Java

Udara Pathum
3 min readDec 8, 2023

--

Kyber is a Post-Quantum Key-Encapsulation Mechanism (KEM) which can be used for share secrets between two parties. It is one of the finalists selected by NIST’s Post-Quantum Cryptography Standardization.

Key-Encapsulation Mechanism (KEM)

A Key-Encapsulation Mechanism (KEM) is used to send a symmetric key between two parties using asymmetric algorithms. Unlike Diffie-Hellman key exchange method where the shared secret is directly generated through mutual computations, a KEM employs asymmetric algorithms. In this method, the sender encapsulates the symmetric key within a cipher-text using the recipient’s public key. Upon receiving the cipher-text, the recipient then decapsulates and retrieves the symmetric key using their private key, ensuring a secure and authenticated exchange without directly sharing the symmetric key during transmission.

KEM vs Diffie-Hellman — Cloudfare

Hybrid encryption

Hybrid encryption blends symmetric and asymmetric encryption techniques for robust data security. In this approach, asymmetric encryption generates a unique key to wrap the message, which is then secured using symmetric encryption. The sender passes on this doubly-secured package, with the outer layer containing the symmetrically encrypted key. The recipient uses their private key to unlock this outer layer, gaining access to the key that opens the inner layer, revealing the intended message. This method optimizes both speed and security, ensuring data protection during transmission and storage.

Hybrid Encryption — SimpliLearn

Java implementation using Bouncycastle

I am going to create a hybrid encryption mechanism using Kyber and AES. Here I am using Bouncycastle 1.77 with Java 11 for the implementation .

  • Client creates the keypair using Kyber, and send the public key to the Server
private static KeyPair generateKeyPair() Exception {
//generate key pair
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("Kyber", "BCPQC");
keyPairGenerator.initialize(KyberParameterSpec.kyber512, new SecureRandom());
return keyPairGenerator.generateKeyPair();
}
  • Server use the public key to generate a shared secret, encapsulate it, and send the encapsulation to the Client.
private static SecretKeyWithEncapsulation generateSecretKeySender(PublicKey publicKey) throws Exception {
// You will request a key generator
KeyGenerator keyGenerator = KeyGenerator.getInstance("Kyber", "BCPQC");
// You will set up a KEM Generate Spec with the public key
KEMGenerateSpec kemGenerateSpec = new KEMGenerateSpec(publicKey, "Secret");
// Now you can initialize the key generator with the kem generate spec and generate out share secret
keyGenerator.init(kemGenerateSpec);
return (SecretKeyWithEncapsulation)keyGenerator.generateKey();
}
  • Client decapsulates the shared secret using private key.
private static SecretKeyWithEncapsulation generateSecretKeyReciever(PrivateKey privateKey, byte[] encapsulation) throws Exception {
// You will set up a KEM Extract Spec with the receiver private key - in this case you need the encapsulation
// generated by the receiver as well.
KEMExtractSpec kemExtractSpec = new KEMExtractSpec(privateKey, encapsulation, "Secret");
// You will request a key generator
KeyGenerator keyGenerator = KeyGenerator.getInstance(KEM_ALGORITHM, PROVIDER);
// Now you can initialize the key generator with the kem extract spec and retrieve the KEM secret.
keyGenerator.init(kemExtractSpec);
return (SecretKeyWithEncapsulation)keyGenerator.generateKey();
}
  • Since now both Client and server has the shared secret, they can use AES to encrypt and share data between them.
public static String encrypt(String plainText, byte[] key) throws Exception {
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes());
return Base64.getEncoder().encodeToString(encryptedBytes);
}

public static String decrypt(String encryptedText, byte[] key) throws Exception {
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] decodedBytes = Base64.getDecoder().decode(encryptedText);
byte[] decryptedBytes = cipher.doFinal(decodedBytes);
return new String(decryptedBytes);
}

Here is the final code

--

--