Overview
ML-DSA is a lattice-based digital signature scheme whose security is based on the hardness of finding short vectors in lattices.
Support for ML-DSA is now supported on IBM Semeru Java versions 17/21/25 on the IBMJCECCA security provider. IBMJCECCA currently offers the following implementations:
-
Pure ML-DSA (4,4)
-
Pure ML-DSA (6,5)
-
Pure ML-DSA (8,7)
-
Pre-Hash ML-DSA (4,4) with SHA-512
-
Pre-Hash ML-DSA (6,5) with SHA-512
-
Pre-Hash ML-DSA (8,7) with SHA-512
-
CRYSTALS-Dilithium (6,5) Round 2 (superseded by ML-DSA)
-
CRYSTALS-Dilithium (8,7) Round 2 (superseded by ML-DSA)
-
CRYSTALS-Dilithium (6,5) Round 3 (superseded by ML-DSA)
-
CRYSTALS-Dilithium (8,7) Round 3 (superseded by ML-DSA)
The strength of an ML-DSA key is represented by the size of its matrix of polynomials. For example, ML-DSA (6,5) has a matrix size of 6x5. The bigger the matrix size, the stronger the key. ML-DSA keys can only be used for Digital Signature Generation and Verification.
This tutorial will demonstrate how to integrate ML-DSA into a Java application. Before you continue reading, please ensure your environment is properly configured by reading PQC on Java: Configuring your IBM Z system to use Quantum-safe algorithms.
Step 1: Create ML-DSA key pair
The first step is to create a ML-DSA key pair using the KeyPairGenerator and MLDSAKeyParameterSpec classes.
The MLDSAKeyParameterSpec class accepts the following values based on the desired implementation outlined in the Overview section:
- ML-DSA-44
- ML-DSA-65
- ML-DSA-87
- HashML-DSA-44
- HashML-DSA-65
- HashML-DSA-87
KeyPairGenerator kpg =
KeyPairGenerator.getInstance("ML-DSA-65", "IBMJCECCA");
MLDSAKeyParameterSpec kps =
new MLDSAKeyParameterSpec("ML-DSA-65");
kpg.initialize(kps, null);
KeyPair keyPair = kpg.generateKeyPair();
Step 2: Get public and private keys
Now that we have the key pair, we can separate the keys into their own variables.
MLDSAPrivateKey privKey = (MLDSAPrivateKey) keyPair.getPrivate();
MLDSAPublicKey pubKey = (MLDSAPublicKey) keyPair.getPublic();
Step 3: Create Signature object
If you're already experienced with the JCE framework, the remainder of the steps may look familiar with other Signature algorithms. Use the Signature class and specify ML-DSA and IBMJCECCA.
Signature sign = Signature.getInstance("ML-DSA-65", "IBMJCECCA");
Step 4: Initialize Signature for signing
Use the ML-DSA private key to initialize the Signature object.
sign.initSign(privKey);
Step 5: Add bytes to Signature
Pass the data to be signend to the update() method of the Signature object.
byte[] bytes = "IBM - Let's create".getBytes();
sign.update(bytes);
Step 6: Perform signature
Finally, use the sign() method to get the signature bytes of the updated data.
byte[] sigBytes = sign.sign();
Step 7: Initialize the Signature for verification
Now, we can initialize the Signature object to verify the signature bytes in the next step.
sign.initVerify(pubKey);
sign.update(bytes);
Step 8: Verify
Verify the signature.
boolean verified = sign.verify(sigBytes);
if (verified) {
System.out.println("Signature verified");
} else {
System.out.println("Signature could NOT be verified");
}
Example
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.spec.NamedParameterSpec;
import com.ibm.crypto.hdwrCCA.provider.*;
public class MLDSAExample {
public static void main(String[] args) throws Exception {
// STEP 1: Create key pair
KeyPairGenerator kpg = KeyPairGenerator.getInstance("ML-DSA-65", "IBMJCECCA");
NamedParameterSpec mlkemNPS = new NamedParameterSpec("ML-DSA-65");
kpg.initialize(mlkemNPS, null);
// STEP 2: Get public and private keys
KeyPair keyPair = kpg.generateKeyPair();
PrivateKey privKey = keyPair.getPrivate();
PublicKey pubKey = keyPair.getPublic();
// STEP 3: Create Signature object
Signature sign = Signature.getInstance("ML-DSA-65", "IBMJCECCA");
// STEP 4: Initialize Signature for signing
sign.initSign(privKey);
// STEP 5: Add bytes to Signature
byte[] bytes = "IBM - Let's create".getBytes();
sign.update(bytes);
// STEP 6: Perform signature
byte[] sigBytes = sign.sign();
// STEP 7: Initialize the Signature for verification
sign.initVerify(pubKey);
sign.update(bytes);
// STEP 8: Verify
boolean verified = sign.verify(sigBytes);
if (verified) {
System.out.println("Signature verified");
} else {
System.out.println("Signature could NOT be verified");
}
}
}
Output
The previous Example should produce the following output:
Signature verified
Conclusion
In this tutorial, we learned how to use the ML-DSA PQC algorithm with IBMJCECCA to create keys and use a Signature to sign & verify bytes. Thanks for reading!
If you have additional questions, please email me at Emily.Popovic1@ibm.com
References
- Configuring your IBM Z system to use Post-Quantum Cryptography
- Everything to know about PQC keys
- PQC support with IBMJCECCA
- ML-DSA, CRYSTALS-Dilithium Digital Signature Algorithm
- z17 IBM Semeru Runtimes: PQC Cryptography Enhancements