Java

Java

Java

Topics on Semeru (Java) on IBM Z

 View Only

QSC on Java: CRYSTALS-Kyber tutorial (IBMJCECCA)

By Gregory Cernera posted Mon April 08, 2024 05:30 PM

  

Overview

CRYSTALS-Kyber is an IND-CCA2-secure key encapsulation mechanism (KEM), whose security is based on the hardness of solving the learning-with-errors (LWE) problem over module lattices. CRYSTALS-Kyber is a quantum-safe algorithm (QSA) and is a member of the CRYSTALS (Cryptographic Suite for Algebraic Lattices) suite of algorithms [2].

Support for CRYSTALS-Kyber is now supported for IBM Semeru Java versions 11 and 17 on the IBMJCECCA security provider [3]. IBM Semeru currently supports Kyber-1024 Round 2. Kyber-1024 aims at security roughly equivalent to AES-256 [2]. IBMJCECCA currently offers the following implementations:

  • CRYSTALS-Kyber 1024 Round 2

With CRYSTALS-Kyber, it is now possible to perform a quantum-safe hybrid key exchange scheme that combines the protection of traditional Elliptic Curve Cryptography (ECC) with the quantum-safe CRYSTALS-Kyber algorithm. This hybrid key exchange scheme provides two layers of protection and ensures that all key exchanges are protected from attacks by traditional and quantum computers [2].

This tutorial will demonstrate how to integrate CRYSTALS-KYber into a Java application. Before you continue reading, please ensure your environment is properly configured by reading QSC on Java: Configuring your IBM Z system to use Quantum-safe algorithms.

Step 1. Generate Alice's keys

1.1. Generate Kyber keys

The first step is to create a Kyber key pair for Alice using the KeyPairGenerator and KyberKeyParameterSpec classes.

The KyberKeyParameterSpec class accepts the following values based on the desired implementation outlined in the Overview section:

  • kyber1024r2
KeyPairGenerator kyberKPG =
    KeyPairGenerator.getInstance("CRYSTALS-Kyber", "IBMJCECCA");
KyberKeyParameterSpec kyberGenPS =
    new KyberKeyParameterSpec("kyber1024r2");
kyberKPG.initialize(kyberGenPS, null);
KeyPair kybPairAlice = kyberKPG.generateKeyPair();

1.2. Generate EC keys

Next, we must create an EC key pair for Alice using the IBMJCECCA provider.

ECGenParameterSpec ecGenPSAlice = new ECGenParameterSpec("secp256r1");
KeyPairGenerator ecKPGAlice = KeyPairGenerator.getInstance("EC", "IBMJCECCA");
ecKPGAlice.initialize(ecGenPSAlice, null);
KeyPair ecPairAlice = ecKPGAlice.generateKeyPair();

Step 2. Generate Bob's keys

2.1. Generate EC keys

Bob must also create his own EC key pair.

ECGenParameterSpec ecGenPSBob = new ECGenParameterSpec("secp256r1");
KeyPairGenerator ecKPGBob = KeyPairGenerator.getInstance("EC", "IBMJCECCA");
ecKPGBob.initialize(ecGenPSBob, null);
KeyPair ecPairBob = ecKPGBob.generateKeyPair();

2.2. Generate AES CIPHER key

Instead of creating his own Kyber keys, Bob must create his own AES CIPHER key. Only AES CIPHER keys are allowed for a CRYSTALS-Kyber key agreement.

CCAAlgorithmParameterSpec ccaAlgParmSpec =
    new CCAAlgorithmParameterSpec(256);
ccaAlgParmSpec.setKeyUsage(KeyUsage.OP_CIPHER);
KeyGenerator keyGen = KeyGenerator.getInstance("AES", "IBMJCECCA");
keyGen.init(ccaAlgParmSpec, null);
SecretKey aesKeyBob = keyGen.generateKey();

Step 3. Derive secret for Bob

Now that all of the necessary keys are created for Alice and Bob, we can begin the process of generating the key agreement. IBMJCECCA uses the classes KyberDerivationInput and KyberKeyAgreement classes to perform a CRYSTALS-Kyber key agreement.

KyberDerivationInput takes in an AES CIPHER key, CRYSTALS-Kyber public key, and a 16-byte initialization vector (IV) to perform a PKA Encrypt service via ICSF. This service generates a random 32B value which is then encrypted in two different ways:

  1. Using the CRYSTALS-Kyber public key (Kyber-encrypted value)
  2. Using the AES CIPHER key with the IV (AES-encrypted value)

These two different values will then be used with the KyberKeyAgreement class. The KyberKeyAgreement class takes in a series of keys and values and derives the final shared key agreement. The input for this class varies for Alice and Bob. See the steps below for a full rundown of the steps.

3.1. Create IV

First, we must derive a 16-byte initialization vector.

SecureRandom random = new SecureRandom();
byte[] iv = new byte[16];
random.nextBytes(iv);

3.2. Use KyberDerivationInput to get encrypted values

We now have all of the inputs necessary to create the KyberDerivationInput class. The encrypted values are calculated after the encrypt() method is invoked.

KyberPublicKey kybPubAlice = (KyberPublicKey) kybPairAlice.getPublic();
KyberDerivationInput kdi =
    new KyberDerivationInput(aesKeyBob, kybPubAlice, iv);
kdi.encrypt();
byte[] aesEncryptedValue = kdi.getEncryptedAesValue();
byte[] kyberEncryptedValue = kdi.getEncryptedKyberValue();

3.3. Create and initialize KyberKeyAgreement

Bob can now initialize a KyberKeyAgreement class with the information gathered from the previous steps.

ECPublicKey ecPubAlice = (ECPublicKey) ecPairAlice.getPublic();
ECPrivateHWKey ecPrivBob = (ECPrivateHWKey) ecPairBob.getPrivate();
KyberKeyAgreement bobKyberAgree = new KyberKeyAgreement();
bobKyberAgree.init(aesKeyBob, ecPrivBob, ecPubAlice, aesEncryptedValue, iv);

3.4. Generate shared secret

Finally, Bob just has to invoke the generateSecret() method to generate the final key to be shared between him and Alice.

The generateSecret() method accepts AES, DES, and DESede as the final shared secret algorithm.

AESKey bobSharedSecretKey = (AESKey) bobKyberAgree.generateSecret("AES");

Step 4. Derive secret for Alice

4.1. Create and initialize KyberKeyAgreement

Now that Bob has generated the shared secret, Alice must do the same. Alice does not need to perform a KyberDerivationInput because she will be using the Kyber-encrypted value generated when Bob performed that step. So, Alice can go right to initializing KyberKeyAgreement.

ECPublicKey ecPubBob = (ECPublicKey) ecPairBob.getPublic();
KyberPrivateKey kybPrivAlice = (KyberPrivateKey) kybPairAlice.getPrivate();
ECPrivateHWKey ecPrivAlice = (ECPrivateHWKey) ecPairAlice.getPrivate();
KyberKeyAgreement aliceKyberAgree = new KyberKeyAgreement();
aliceKyberAgree.init(kybPrivAlice, ecPrivAlice, ecPubBob, kyberEncryptedValue, null);

4.2. Generate shared secret

Finally, Alice can invoke the generateSecret() method to generate the final key to be shared between her and Bob.

AESKey aliceSharedSecretKey = (AESKey) aliceKyberAgree.generateSecret("AES");

Step 5. Verify secrets match

Let's add in a quick check to verify that the shared secrets match.

if (Arrays.equals(aliceSharedSecretKey.getEncoded(),
    bobSharedSecretKey.getEncoded())
) {
    System.out.println("The secrets match!");
} else {
    System.out.println("The secrets DO NOT match.");
}

Example

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.spec.ECGenParameterSpec;
import java.util.Arrays;

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;

import com.ibm.crypto.hdwrCCA.provider.AESKey;
import com.ibm.crypto.hdwrCCA.provider.CCAAlgorithmParameterSpec;
import com.ibm.crypto.hdwrCCA.provider.ECPrivateHWKey;
import com.ibm.crypto.hdwrCCA.provider.KyberDerivationInput;
import com.ibm.crypto.hdwrCCA.provider.KyberKeyAgreement;
import com.ibm.crypto.hdwrCCA.provider.KyberKeyParameterSpec;
import com.ibm.crypto.hdwrCCA.provider.KyberPrivateKey;
import com.ibm.crypto.hdwrCCA.provider.KyberPublicKey;
import com.ibm.crypto.hdwrCCA.provider.SymmetricKeyConstants.KeyUsage;
import com.ibm.crypto.hdwrCCA.provider.ECPublicKey;

public class KyberExample {

    public static void main(String[] args) throws Exception {
        // -----------------------------------------------------------------
        // STEP 1: Generate Alice's keys
        // -----------------------------------------------------------------

        // 1.1. Generate Kyber keys
        KeyPairGenerator kyberKPG =
            KeyPairGenerator.getInstance("CRYSTALS-Kyber", "IBMJCECCA");
        KyberKeyParameterSpec kyberGenPS =
            new KyberKeyParameterSpec("kyber1024r2");
        kyberKPG.initialize(kyberGenPS, null);
        KeyPair kybPairAlice = kyberKPG.generateKeyPair();

        // 1.2. Generate EC keys
        ECGenParameterSpec ecGenPSAlice = new ECGenParameterSpec("secp256r1");
        KeyPairGenerator ecKPGAlice = KeyPairGenerator.getInstance("EC", "IBMJCECCA");
        ecKPGAlice.initialize(ecGenPSAlice, null);
        KeyPair ecPairAlice = ecKPGAlice.generateKeyPair();

        // -----------------------------------------------------------------
        // STEP 2: Generate Bob's keys
        // -----------------------------------------------------------------

        // 2.1. Generate EC keys
        ECGenParameterSpec ecGenPSBob = new ECGenParameterSpec("secp256r1");
        KeyPairGenerator ecKPGBob = KeyPairGenerator.getInstance("EC", "IBMJCECCA");
        ecKPGBob.initialize(ecGenPSBob, null);
        KeyPair ecPairBob = ecKPGBob.generateKeyPair();

        // 2.2 Generate AES CIPHER key
        CCAAlgorithmParameterSpec ccaAlgParmSpec =
            new CCAAlgorithmParameterSpec(256);
        ccaAlgParmSpec.setKeyUsage(KeyUsage.OP_CIPHER);
        KeyGenerator keyGen = KeyGenerator.getInstance("AES", "IBMJCECCA");
        keyGen.init(ccaAlgParmSpec, null);
        SecretKey aesKeyBob = keyGen.generateKey();

        // -----------------------------------------------------------------
        // STEP 3: Derive secret for Bob
        // -----------------------------------------------------------------

        // 3.1. Create IV
        SecureRandom random = new SecureRandom();
        byte[] iv = new byte[16];
        random.nextBytes(iv);

        // 3.2. Use KyberDerivationInput to get encrypted values
        KyberPublicKey kybPubAlice = (KyberPublicKey) kybPairAlice.getPublic();
        KyberDerivationInput kdi =
            new KyberDerivationInput(aesKeyBob, kybPubAlice, iv);
        kdi.encrypt();
        byte[] aesEncryptedValue = kdi.getEncryptedAesValue();
        byte[] kyberEncryptedValue = kdi.getEncryptedKyberValue();

        // 3.3. Create and initialize KyberKeyAgreement
        ECPublicKey ecPubAlice = (ECPublicKey) ecPairAlice.getPublic();
        ECPrivateHWKey ecPrivBob = (ECPrivateHWKey) ecPairBob.getPrivate();
        KyberKeyAgreement bobKyberAgree = new KyberKeyAgreement();
        bobKyberAgree.init(aesKeyBob, ecPrivBob, ecPubAlice, aesEncryptedValue, iv);

        // 3.4. Generate shared secret
        AESKey bobSharedSecretKey = (AESKey) bobKyberAgree.generateSecret("AES");

        // -----------------------------------------------------------------
        // STEP 4: Derive secret for Alice
        // -----------------------------------------------------------------

        // 4.1. Create and initialize KyberKeyAgreement
        ECPublicKey ecPubBob = (ECPublicKey) ecPairBob.getPublic();
        KyberPrivateKey kybPrivAlice = (KyberPrivateKey) kybPairAlice.getPrivate();
        ECPrivateHWKey ecPrivAlice = (ECPrivateHWKey) ecPairAlice.getPrivate();
        KyberKeyAgreement aliceKyberAgree = new KyberKeyAgreement();
        aliceKyberAgree.init(kybPrivAlice, ecPrivAlice, ecPubBob, kyberEncryptedValue, null);

        // 4.2. Generate shared secret
        AESKey aliceSharedSecretKey = (AESKey) aliceKyberAgree.generateSecret("AES");

        // -----------------------------------------------------------------
        // STEP 5: Verify secrets match
        // -----------------------------------------------------------------

        if (Arrays.equals(aliceSharedSecretKey.getEncoded(),
            bobSharedSecretKey.getEncoded())
        ) {
            System.out.println("The secrets match!");
        } else {
            System.out.println("The secrets DO NOT match.");
        }
    }
}

Output

The previous Example should produce the following output:

The secrets match!

Conclusion

In this tutorial, we learned how to use the CRYSTALS-Kyber QSC algorithm with IBMJCECCA to create keys and generate a shared secret between two parties. Thanks for reading!

If you have additional questions, please email me at Gregory.Cernera@ibm.com.

References

  1. QSC on Java: Configuring your IBM Z system to use Quantum-safe algorithms
  2. Transitioning to Quantum-Safe Cryptography on IBM Z
  3. Semeru for zOS, V11.0.22.0 and V17.0.10.0 GA!
  4. CRYSTALS-Kyber Algorithm

How to obtain an IBM Semeru JDK

The IBM Java SAF APIs are included in the IBM Semeru Runtime Certified Edition for z/OS download. Please follow the links below to download the IBM Semeru JDK onto your own machines.

 How to obtain IBM Semeru Runtime Certified Edition for z/OS?
IBM Semeru Runtime Certified Edition for z/OS is available for zero license charge through Shopz SMP/E, or you can download the non-SMP/E here. The subscription and service number is 5655-I48.

Supporting Links:
IBM Semeru Runtime Certified Edition for z/OS product page
For additional information on installation, troubleshooting and support please visit IBM Documentation.

0 comments
35 views

Permalink