Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation of the HKDF derivation function #271

Merged
merged 16 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -363,20 +363,22 @@
<schemaInclude>bindings/schemas/exc-c14n.xsd</schemaInclude>
<schemaInclude>bindings/schemas/xmldsig-core-schema.xsd</schemaInclude>
<schemaInclude>bindings/schemas/xmldsig11-schema.xsd</schemaInclude>
<schemaInclude>bindings/schemas/dsig-more_2001_04.xsd</schemaInclude>
<schemaInclude>bindings/schemas/dsig-more_2007_05.xsd</schemaInclude>
<schemaInclude>bindings/schemas/dsig-more_2021_04.xsd</schemaInclude>
<schemaInclude>bindings/schemas/xenc-schema.xsd</schemaInclude>
<schemaInclude>bindings/schemas/xenc-schema-11.xsd</schemaInclude>
<schemaInclude>bindings/schemas/rsa-pss.xsd</schemaInclude>
</schemaIncludes>
<bindingDirectory>${basedir}/src/main/resources/bindings/</bindingDirectory>
<bindingIncludes>
<bindingInclude>c14n.xjb</bindingInclude>
<bindingInclude>dsig.xjb</bindingInclude>
<bindingInclude>dsig11.xjb</bindingInclude>
<bindingInclude>dsig-more.xjb</bindingInclude>
<bindingInclude>xenc.xjb</bindingInclude>
<bindingInclude>xenc11.xjb</bindingInclude>
<bindingInclude>security-config.xjb</bindingInclude>
<bindingInclude>xop.xjb</bindingInclude>
<bindingInclude>rsa-pss.xjb</bindingInclude>
</bindingIncludes>
<catalog>${basedir}/src/main/resources/bindings/bindings.cat</catalog>
<forceRegenerate>false</forceRegenerate>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ public interface AgreementMethod {
*/
void setKANonce(byte[] kanonce);


/**
* Returns KeyDerivationMethod information used in the <code>AgreementMethod</code>.
* @return The KeyDerivationMethod information regarding the <code>AgreementMethod</code>.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,15 @@

package org.apache.xml.security.encryption;

import org.apache.xml.security.encryption.keys.content.derivedKey.KDFParams;
import org.apache.xml.security.exceptions.XMLSecurityException;

/**
* The key derivation is to generate new cryptographic key material from existing key material such as the shared
* secret and any other (private or public) information. The purpose of the key derivation is an extension of a given
* but limited set of original key materials and to limit the use (exposure) of such key material.
* The key derivation is to generate new cryptographic key material from existing
* key material such as the shared secret and any other (private or public)
* information. The purpose of the key derivation is an extension of a given
* but limited set of original key materials and to limit the use (exposure)
* of such key material.
jrihtarsic marked this conversation as resolved.
Show resolved Hide resolved
*
* The Schema for KeyDerivationMethod is as follows:
* <pre>
Expand All @@ -38,9 +43,21 @@
public interface KeyDerivationMethod {

/**
* Returns the algorithm URI of this <code>KeyDerivationMethod</code>.
* Returns the algorithm URI of this <code>KeyDerivationMethod</code>
jrihtarsic marked this conversation as resolved.
Show resolved Hide resolved
*
* @return the algorithm URI of this <code>KeyDerivationMethod</code>
*/
String getAlgorithm();

/**
* Returns the KDF parameters used by the key derivation algorithm.
* Currently supported types are:
* {@link org.apache.xml.security.encryption.params.ConcatKDFParams} and
* {@link org.apache.xml.security.encryption.params.HKDFParams}
jrihtarsic marked this conversation as resolved.
Show resolved Hide resolved
*
* @return the KDFParams used by the key derivation algorithm
* @throws XMLSecurityException if the KDFParams cannot be created
*/
KDFParams getKDFParams() throws XMLSecurityException;

}
181 changes: 129 additions & 52 deletions src/main/java/org/apache/xml/security/encryption/XMLCipherUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,46 +18,46 @@
*/
package org.apache.xml.security.encryption;

import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.security.AccessController;
import java.security.PrivateKey;
import java.security.PrivilegedAction;
import java.security.PublicKey;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.MGF1ParameterSpec;

import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;

import org.apache.xml.security.algorithms.JCEMapper;
import org.apache.xml.security.encryption.keys.content.derivedKey.ConcatKDFParamsImpl;
import org.apache.xml.security.encryption.keys.content.derivedKey.HKDFParamsImpl;
import org.apache.xml.security.encryption.keys.content.derivedKey.KDFParams;
import org.apache.xml.security.encryption.params.ConcatKDFParams;
import org.apache.xml.security.encryption.params.HKDFParams;
import org.apache.xml.security.encryption.params.KeyAgreementParameters;
import org.apache.xml.security.encryption.params.KeyDerivationParameters;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.encryption.keys.content.derivedKey.ConcatKDFParamsImpl;
import org.apache.xml.security.encryption.keys.content.derivedKey.KeyDerivationMethodImpl;
import org.apache.xml.security.signature.XMLSignature;
import org.apache.xml.security.utils.EncryptionConstants;
import org.apache.xml.security.utils.KeyUtils;

import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.security.*;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.MGF1ParameterSpec;
import java.util.Base64;

public final class XMLCipherUtil {

private static final Logger LOG = System.getLogger(XMLCipherUtil.class.getName());

private static final boolean gcmUseIvParameterSpec =
AccessController.doPrivileged((PrivilegedAction<Boolean>)
() -> Boolean.getBoolean("org.apache.xml.security.cipher.gcm.useIvParameterSpec"));
AccessController.doPrivileged((PrivilegedAction<Boolean>)
() -> Boolean.getBoolean("org.apache.xml.security.cipher.gcm.useIvParameterSpec"));

/**
* Build an <code>AlgorithmParameterSpec</code> instance used to initialize a <code>Cipher</code> instance
* for block cipher encryption and decryption.
*
* @param algorithm the XML encryption algorithm URI
* @param iv the initialization vector
* @param iv the initialization vector
* @return the newly constructed AlgorithmParameterSpec instance, appropriate for the
* specified algorithm
* specified algorithm
*/
public static AlgorithmParameterSpec constructBlockCipherParameters(String algorithm, byte[] iv) {
if (EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128_GCM.equals(algorithm)
Expand Down Expand Up @@ -140,7 +140,7 @@ public static OAEPParameterSpec constructOAEPParameters(
public static MGF1ParameterSpec constructMGF1Parameter(String mgh1AlgorithmURI) {
LOG.log(Level.DEBUG, "Creating MGF1ParameterSpec for [{0}]", mgh1AlgorithmURI);
if (mgh1AlgorithmURI == null || mgh1AlgorithmURI.isEmpty()) {
LOG.log(Level.WARNING,"MGF1 algorithm URI is null or empty. Using SHA-1 as default.");
LOG.log(Level.WARNING, "MGF1 algorithm URI is null or empty. Using SHA-1 as default.");
jrihtarsic marked this conversation as resolved.
Show resolved Hide resolved
return new MGF1ParameterSpec("SHA-1");
}

Expand Down Expand Up @@ -187,6 +187,33 @@ public static String getMgf1URIForParameter(MGF1ParameterSpec parameterSpec) {
}
}

/**
* Get the JCE hmac algorithm name for the given digest uri
*
* @param hmacAlgorithmURI the digest algorithm
* @return the JCE hmac name algorithm
* @throws IllegalArgumentException if the digest algorithm is not supported/unknown
*/
public static String getJCEMacHashForUri(String hmacAlgorithmURI) throws NoSuchAlgorithmException {

LOG.log(Level.DEBUG, "Get JCE HMac name for algorithm URI [{0}]", hmacAlgorithmURI);
switch (hmacAlgorithmURI) {
case XMLSignature.ALGO_ID_MAC_HMAC_SHA1:
return "HmacSHA1";
case XMLSignature.ALGO_ID_MAC_HMAC_SHA224:
return "HmacSHA224";
case XMLSignature.ALGO_ID_MAC_HMAC_SHA256:
return "HmacSHA256";
case XMLSignature.ALGO_ID_MAC_HMAC_SHA384:
return "HmacSHA384";
case XMLSignature.ALGO_ID_MAC_HMAC_SHA512:
return "HmacSHA512";
case XMLSignature.ALGO_ID_MAC_HMAC_RIPEMD160:
return "HMACRIPEMD160";
default:
throw new NoSuchAlgorithmException("Unknown/not supported hash algorithm: [" + hmacAlgorithmURI + "] for MacHash algorithm");
}
}

/**
* Construct an KeyAgreementParameterSpec object from the given parameters
Expand Down Expand Up @@ -229,7 +256,7 @@ public static KeyAgreementParameters constructAgreementParameters(String agreeme
KeyAgreementParameters ecdhKeyAgreementParameters = new KeyAgreementParameters(
actorType,
agreementAlgorithmURI, keyDerivationParameter);
if (actorType == KeyAgreementParameters.ActorType.RECIPIENT ) {
if (actorType == KeyAgreementParameters.ActorType.RECIPIENT) {
ecdhKeyAgreementParameters.setRecipientPrivateKey(keyAgreementPrivateKey);
ecdhKeyAgreementParameters.setOriginatorPublicKey(keyAgreementPublicKey);
} else {
Expand All @@ -243,48 +270,55 @@ public static KeyAgreementParameters constructAgreementParameters(String agreeme
/**
* Construct a KeyDerivationParameter object from the given keyDerivationMethod and keyBitLength
*
* @param keyDerivationMethod element to parse
* @param keyBitLength expected derived key length
* @return KeyDerivationParameter object
* @throws XMLSecurityException if the keyDerivationMethod is not supported
* @param keyDerivationMethod element with the key derivation method data
* @param keyBitLength expected derived key length
* @return KeyDerivationParameters data
* @throws XMLSecurityException if the keyDerivationMethod is not supported or invalid parameters are provided
*/
public static KeyDerivationParameters constructKeyDerivationParameter(KeyDerivationMethod keyDerivationMethod, int keyBitLength) throws XMLSecurityException {
String keyDerivationAlgorithm = keyDerivationMethod.getAlgorithm();
if (!EncryptionConstants.ALGO_ID_KEYDERIVATION_CONCATKDF.equals(keyDerivationAlgorithm)) {
throw new XMLEncryptionException("unknownAlgorithm", keyDerivationAlgorithm);
}
ConcatKDFParamsImpl concatKDFParams = ((KeyDerivationMethodImpl) keyDerivationMethod).getConcatKDFParams();
KDFParams kdfParams = keyDerivationMethod.getKDFParams();
if (EncryptionConstants.ALGO_ID_KEYDERIVATION_CONCATKDF.equals(keyDerivationAlgorithm)) {
if (! (kdfParams instanceof ConcatKDFParamsImpl)) {
throw new XMLEncryptionException("KeyDerivation.InvalidParametersType", keyDerivationAlgorithm, ConcatKDFParamsImpl.class.getName());
}

return constructConcatKeyDerivationParameter(keyBitLength, concatKDFParams.getDigestMethod(), concatKDFParams.getAlgorithmId(),
concatKDFParams.getPartyUInfo(), concatKDFParams.getPartyVInfo(),
concatKDFParams.getSuppPubInfo(),concatKDFParams.getSuppPrivInfo());
ConcatKDFParamsImpl concatKDFParams = (ConcatKDFParamsImpl) kdfParams;

}
return constructConcatKeyDerivationParameter(keyBitLength, concatKDFParams.getDigestMethod(), concatKDFParams.getAlgorithmId(),
concatKDFParams.getPartyUInfo(), concatKDFParams.getPartyVInfo(),
concatKDFParams.getSuppPubInfo(), concatKDFParams.getSuppPrivInfo());

} else if (EncryptionConstants.ALGO_ID_KEYDERIVATION_HKDF.equals(keyDerivationAlgorithm)) {
if (! (kdfParams instanceof HKDFParamsImpl)) {

/**
* Construct a ConcatKeyDerivationParameter object from the key length and digest method.
*
* @param keyBitLength expected derived key length
* @param digestMethod digest method
* @return ConcatKeyDerivationParameter object
*/
public static ConcatKDFParams constructConcatKeyDerivationParameter(int keyBitLength,
String digestMethod){
return constructConcatKeyDerivationParameter(keyBitLength, digestMethod, null, null, null, null, null);
throw new XMLEncryptionException("KeyDerivation.InvalidParametersType", keyDerivationAlgorithm, HKDFParamsImpl.class.getName());
}
HKDFParamsImpl hKDFParams = (HKDFParamsImpl) kdfParams;
return constructHKDFKeyDerivationParameter(keyBitLength,
hKDFParams.getPRFAlgorithm(),
hKDFParams.getSalt() != null ? Base64.getDecoder().decode(hKDFParams.getSalt()) : null,
hKDFParams.getInfo() != null ? Base64.getDecoder().decode(hKDFParams.getInfo()) : null);
}
throw new XMLEncryptionException("unknownAlgorithm", keyDerivationAlgorithm);
}

/**
* Construct a ConcatKeyDerivationParameter object from the given parameters
* Construct a ConcatKeyDerivationParameter object from the given parameters as specified in the XML Encryption 1.1
* and NIST SP 800-56Ar2 specifications. (In a key establishment transaction, the participants,
* parties U and V, are considered to be the first and second parties)
*
* @param keyBitLength expected derived key length
* @param digestMethod digest method
* @param algorithmId algorithm id
* @param partyUInfo partyUInfo
* @param partyVInfo partyVInfo
* @param suppPubInfo suppPubInfo
* @param suppPrivInfo suppPrivInfo
* @return ConcatKeyDerivationParameter object
* @param digestMethod digest method element identifies the digest algorithm used by the KDF
* @param algorithmId algorithm id indicates how the derived keying material will be parsed and for which
* algorithm(s) the derived secret keying material will be used.
* @param partyUInfo partyUInfo containing public information about party
* @param partyVInfo partyVInfo containing public information about party
* @param suppPubInfo suppPubInfo An optional subfield, which could be null that contains additional, mutually
* known public information
* @param suppPrivInfo suppPrivInfo An optional subfield, which could be null, that contains additional, mutually
* known private information
* @return ConcatKeyDerivationParameter parameters used as input to the Concatenation Key Derivation Function
jrihtarsic marked this conversation as resolved.
Show resolved Hide resolved
*/
public static ConcatKDFParams constructConcatKeyDerivationParameter(int keyBitLength,
String digestMethod,
Expand All @@ -302,4 +336,47 @@ public static ConcatKDFParams constructConcatKeyDerivationParameter(int keyBitLe
kdp.setSuppPrivInfo(suppPrivInfo);
return kdp;
}

jrihtarsic marked this conversation as resolved.
Show resolved Hide resolved
/**
* Construct a HKDFParams object from the given parameters as specified in the rfc5869 specification.
*
* @param keyBitLength The length of the derived key in bits (for example, 128 or 256)
* @param hmacHashAlgorithm The HMAC hash algorithm to use for the key derivation. If null, the default algorithm
* hmac-sha256 is used.
* @param salt The salt value (a non-secret random value) used in the key derivation. If parameter is null,
* the string of hmac-length zeros is used when deriving the key.
* @param info The context and application specific information (can be null)
* @return HKDFParams parameters used as input to the HMAC-based Extract-and-Expand Key Derivation Function.
*/
public static HKDFParams constructHKDFKeyDerivationParameter(int keyBitLength,
String hmacHashAlgorithm,
byte[] salt,
byte[] info) {
HKDFParams kdp = new HKDFParams(keyBitLength, hmacHashAlgorithm);
kdp.setSalt(salt);
kdp.setInfo(info);
return kdp;
}

/**
* Method hexStringToByteArray converts hex string to byte array.
*
* @param hexString the hex string to convert
* @return the byte array of the input param, empty array if the hex string is empty, or null if input param is null
*/
public static byte[] hexStringToByteArray(String hexString) {
if (hexString == null){
return null;
}
if (hexString.isEmpty()) {
return new byte[0];
}
int len = hexString.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
+ Character.digit(hexString.charAt(i+1), 16));
}
return data;
}
}
Loading