 Java, Spring and Web Development tutorials  1. Introduction
Cryptographic key management has always been a central concern in secure application development using Java.
Java 25 introduced the Key Derivation Function (KDF) API after its first preview in JDK 24 under JEP 478. This new API introduces a clean, extensible model for deriving cryptographic keys from initial key material using well-established algorithms.
In this article, we’ll explore the motivation behind the Key Derivation Function (KDF) API, the architecture of the new API and its core components.
2. Motivation Behind KDFs
A Key Derivation Function takes some initial key material (IKM) and derives one or more cryptographically strong keys from it.
We can think of IKM as the raw cryptographic seed fed into the function. IKMs can be a shared secret negotiated over a network, a user-supplied password, or entropy produced by a key agreement protocol.
Let’s see where KDFs are useful:
- TLS and protocol handshakes: After a Diffie-Hellman or ECDH exchange, the raw shared secret shouldn’t be used directly. A KDF transforms it into session keys with the required length and entropy.
- Password-based encryption: Raw passwords are weak keys. Algorithms like PBKDF2 stretch and salt passwords to produce secure cryptographic material.
- Key diversification: A single master key can be safely expanded into multiple purpose-specific sub-keys without compromising the original.
Before Java 25, there was no unified API to express any of these use cases. Therefore, developers either relied on low-level MAC-based constructions, vendor-specific provider APIs, or embedded third-party libraries such as Bouncy Castle.
The new KDF API solves this by offering a standard, JCA-integrated interface that is both algorithm-agnostic and provider-extensible.
3. Architecture of the New KDF API
The KDF API lives in the javax.crypto package and follows the same factory-method pattern that Java’s existing cryptographic APIs use:
KDF kdf = KDF.getInstance("HKDF-SHA256");
The factory pattern is a creational design pattern. It provides a way to create objects, generally with the getInstance() method, without having to specify the class of the object that will be created.
In our case getInstance() method accepts an algorithm name and, optionally, a Provider. This keeps the API consistent with the broader JCA architecture and allows alternative implementations to be plugged in without changing application code.
Once obtained, a KDF instance can derive either a typed SecretKey or raw byte material as follows:
SecretKey key = kdf.deriveKey("AES", paramSpec);
byte[] rawKeyMaterial = kdf.deriveData(paramSpec);
4. Derivation Methods
Let’s discuss how the KDF class exposes the primary derivation methods:
4.1. deriveKey(String alg, AlgorithmParameterSpec params)
The first method deriveKey(), as the name suggests, derives a SecretKey for the specified target algorithm. Internally, the provider uses the params to determine the required key length and derivation inputs. The returned key is then ready to be used with the corresponding JCA algorithm:
SecretKey aesKey = kdf.deriveKey("AES", hkdfParams);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, aesKey, gcmSpec);
4.2. deriveData(AlgorithmParameterSpec params)
The deriveData() method, on the other hand, returns the derived bytes directly as a byte[]. It’s useful when the downstream consumer isn’t a JCA algorithm, for example, when feeding the output into a MAC or a custom protocol:
byte[] okm = kdf.deriveData(hkdfParams);
We should note that both methods throw the InvalidAlgorithmParameterException if the parameter specification is incompatible with the chosen algorithm, and NoSuchAlgorithmException propagates from the getInstance() method if the algorithm isn’t available from any loaded provider.
In this section, let’s understand what input parameters are required to apply and use the KDF API.
The KDF API uses AlgorithmParameterSpec implementations to carry derivation inputs.
For HKDF, the JDK provides a class that models the three distinct phases of the HKDF specification (RFC 5869): Extract, Expand, and the combined Extract-then-Expand. HKDFParameterSpec class is at the centre of this.
Each method comes with a different set of methods chained and expects a different set of parameters.
5.1. Extract-then-Expand
The first approach is an extract followed by an expand based approach:
HKDFParameterSpec params = HKDFParameterSpec
.ofExtract()
.addIKM(ikm)
material.addSalt(salt) // optional, randomizes extraction
.thenExpand(info, 32);
It exposes an extract() method that returns a builder. We then call thenExpand() on it and seal the spec. This signals that both phases should run together.
The info parameter is a context-binding byte array that ties the derived key to a specific purpose (e.g. encryption vs authentication).
In this approach, the HKDFParameterSpec only utilises extraction:
HKDFParameterSpec extractOnly = HKDFParameterSpec
.ofExtract()
.addIKM(ikm)
.addSalt(salt)
.extractOnly();
This produces a pseudorandom key (PRK) from the IKM and salt without expanding it. We can then store or pass the PRK to a subsequent Expand-only step.
5.3. Expand-only
Finally, let’s talk about the expand-only scenario:
HKDFParameterSpec expandOnly = HKDFParameterSpec
.expandOnly(prk, info, 64);
Here, prk is a previously derived SecretKey. This is useful in protocols that separate the extract and expand phases across different stages of a handshake.
6. Supported Algorithms in Java 25
Java 25 ships with built-in support for the HKDF family of algorithms through the default SunJCE provider.
The supported algorithm names are:
- HKDF-SHA256
- HKDF-SHA384
- HKDF-SHA512
All three follow the HMAC-based Extract-and-Expand construction defined in RFC 5869. The maximum output length for each variant is 255 times the hash output length, as mandated by the specification.
7. Comparing the Old and the New
Before the KDF API, deriving a key using HKDF required implementing the Extract and Expand steps manually using javax.crypto.Mac:
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(salt, "HmacSHA256"));
byte[] prk = mac.doFinal(ikm);
mac.init(new SecretKeySpec(prk, "HmacSHA256"));
byte[] t = new byte[0];
byte[] okm = new byte[32];
byte counter = 1;
mac.update(t);
mac.update(info);
mac.update(counter);
t = mac.doFinal();
System.arraycopy(t, 0, okm, 0, 32);
SecretKey aesKey = new SecretKeySpec(okm, "AES");
This is fragile, verbose, and easy to get wrong. Additionally, it also conflates cryptographic logic with application code, making audits harder.
The new API reduces the same operation to a self-documenting, three-line construct:
KDF hkdf = KDF.getInstance("HKDF-SHA256");
SecretKey aesKey = hkdf.deriveKey("AES",
HKDFParameterSpec.ofExtract()
.addIKM(ikm)
.addSalt(salt)
.thenExpand(info, 32));
The algorithm name is validated at instantiation time, not buried in a string passed to Mac.getInstance().
Finally, let’s talk a little bit about error handling. Parameter errors surface as typed exceptions rather than silent data corruption. This was a missing piece in the older approach.
Since KDF has a provider-backed implementation, a security team can exchange the underlying algorithmic implementation, for example, to a FIPS-compliant provider without touching application code. This makes the new KDF Spec design much more robust.
8. Conclusion
In this article, we explored the Key Derivation Function API introduced in Java 25. We looked at why KDFs are essential in modern cryptographic workflows, how the KDF class fits into the existing JCA architecture, and how HKDFParameterSpec models the Extract and Expand phases of the HKDF specification.
We also saw how the new API eliminates the manual, error-prone workarounds that developers previously had to write, replacing them with a clean, algorithm-agnostic, and provider-extensible interface.
The full source code for the examples in this article is available over on GitHub. The post Key Derivation Function API in Java 25 first appeared on Baeldung.
Content mobilized by FeedBlitz RSS Services, the premium FeedBurner alternative. |