Skip to content
17 changes: 17 additions & 0 deletions src/main/java/org/apache/xml/security/algorithms/JCEMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import org.apache.xml.security.encryption.XMLCipher;
import org.apache.xml.security.signature.XMLSignature;
import org.apache.xml.security.utils.EncryptionConstants;
import org.apache.xml.security.utils.JavaUtils;
import org.w3c.dom.Element;

Expand Down Expand Up @@ -318,6 +319,22 @@ public static void registerDefaultAlgorithms() {
XMLCipher.RSA_OAEP_11,
new Algorithm("RSA", "RSA/ECB/OAEPPadding", "KeyTransport")
);
algorithmsMap.put(
EncryptionConstants.ALGO_ID_KEYTRANSPORT_MLKEM_512,
new Algorithm("ML-KEM-512", "ML-KEM-512", "KeyTransport")
);
algorithmsMap.put(
EncryptionConstants.ALGO_ID_KEYTRANSPORT_MLKEM_768,
new Algorithm("ML-KEM-768", "ML-KEM-768", "KeyTransport")
);
algorithmsMap.put(
EncryptionConstants.ALGO_ID_KEYTRANSPORT_MLKEM_1024,
new Algorithm("ML-KEM-1024", "ML-KEM-1024", "KeyTransport")
);
algorithmsMap.put(
EncryptionConstants.ALGO_ID_KEYTRANSPORT_GENERIC_HYBRID,
new Algorithm("", "", "KeyTransport")
);
algorithmsMap.put(
XMLCipher.DIFFIE_HELLMAN,
new Algorithm("", "", "KeyAgreement")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,71 @@ public interface EncryptionMethod {
*/
String getMGFAlgorithm();

/**
* Returns the Key Encapsulation Method algorithm URI used for KEM-based key transport
* (W3C "XML Security: Generic Hybrid Cipher", https://www.w3.org/TR/xmlsec-generic-hybrid/),
* i.e. the {@code Algorithm} attribute of the {@code ghc:KeyEncapsulationMethod} element nested
* inside {@code ghc:GenericHybridCipherMethod}.
*
* @return the key encapsulation algorithm, or {@code null} if this is not a Generic Hybrid
* Cipher {@code EncryptionMethod}.
*/
String getKeyEncapsulationAlgorithm();

/**
* Sets the Key Encapsulation Method algorithm URI. See {@link #getKeyEncapsulationAlgorithm()}.
*
* @param algorithm the key encapsulation algorithm.
*/
void setKeyEncapsulationAlgorithm(String algorithm);

/**
* Returns the {@code xenc11:KeyDerivationMethod} nested inside {@code ghc:KeyEncapsulationMethod},
* used to derive the data-encapsulation (AES key-wrap) key from the KEM shared secret.
*
* @return the key derivation method, or {@code null} if not set.
*/
KeyDerivationMethod getKeyEncapsulationKeyDerivationMethod();

/**
* Sets the key derivation method. See {@link #getKeyEncapsulationKeyDerivationMethod()}.
*
* @param keyDerivationMethod the key derivation method.
*/
void setKeyEncapsulationKeyDerivationMethod(KeyDerivationMethod keyDerivationMethod);

/**
* Returns the {@code ghc:KeyLen} value nested inside {@code ghc:KeyEncapsulationMethod}: the
* length, in bytes, of the derived data-encapsulation key.
*
* @return the key length in bytes, or a non-positive value if not set.
*/
int getKeyEncapsulationKeyLength();

/**
* Sets the derived key length in bytes. See {@link #getKeyEncapsulationKeyLength()}.
*
* @param keyLength the key length in bytes.
*/
void setKeyEncapsulationKeyLength(int keyLength);

/**
* Returns the Data Encapsulation Method algorithm URI, i.e. the {@code Algorithm} attribute of
* the {@code ghc:DataEncapsulationMethod} element nested inside {@code ghc:GenericHybridCipherMethod}
* (typically an AES-KeyWrap algorithm URI).
*
* @return the data encapsulation algorithm, or {@code null} if this is not a Generic Hybrid
* Cipher {@code EncryptionMethod}.
*/
String getDataEncapsulationAlgorithm();

/**
* Sets the Data Encapsulation Method algorithm URI. See {@link #getDataEncapsulationAlgorithm()}.
*
* @param algorithm the data encapsulation algorithm.
*/
void setDataEncapsulationAlgorithm(String algorithm);

/**
* Returns an iterator over all the additional elements contained in the
* <code>EncryptionMethod</code>.
Expand Down
314 changes: 304 additions & 10 deletions src/main/java/org/apache/xml/security/encryption/XMLCipher.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@
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.keys.content.derivedKey.KeyDerivationMethodImpl;
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.utils.Constants;
import org.apache.xml.security.utils.EncryptionConstants;
import org.apache.xml.security.utils.KeyUtils;
import org.w3c.dom.Document;

import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
Expand Down Expand Up @@ -277,13 +280,77 @@ public static KeyDerivationParameters constructKeyDerivationParameter(KeyDerivat
}
HKDFParamsImpl hKDFParams = (HKDFParamsImpl) kdfParams;
return HKDFParams.createBuilder(keyBitLength, hKDFParams.getPRFAlgorithm())
.salt(hKDFParams.getSalt() != null ? Base64.getDecoder().decode(hKDFParams.getSalt()) : null)
.info(hKDFParams.getInfo() != null ? Base64.getDecoder().decode(hKDFParams.getInfo()) : null)
.salt(decodeBase64Parameter(hKDFParams.getSalt(), Constants._TAG_SALT))
.info(decodeBase64Parameter(hKDFParams.getInfo(), EncryptionConstants._TAG_INFO))
.build();
}
throw new XMLEncryptionException("unknownAlgorithm", keyDerivationAlgorithm);
}

/**
* Base64-decodes an optional key derivation parameter read from the message. Malformed
* base64 is reported as an {@link XMLEncryptionException} rather than escaping as the
* {@link IllegalArgumentException} thrown by {@link Base64.Decoder#decode(String)}.
*/
private static byte[] decodeBase64Parameter(String value, String parameterName) throws XMLEncryptionException {
if (value == null) {
return null;
}
try {
return Base64.getDecoder().decode(value);
} catch (IllegalArgumentException e) {
throw new XMLEncryptionException(e, "KeyDerivation.InvalidParameter", new Object[]{parameterName});
}
}

/**
* Construct a {@code KeyDerivationMethod} DOM element from the given {@link KeyDerivationParameters}.
* The inverse of {@link #constructKeyDerivationParameter(KeyDerivationMethod, int)}. Supports the same
* two key derivation functions as the ECDH-ES/X25519/X448 key-agreement path: ConcatKDF and HKDF.
*
* @param doc the {@link Document} in which the {@code KeyDerivationMethod} element will be created
* @param keyDerivationParameter the key derivation parameters (e.g. {@link HKDFParams} or {@link ConcatKDFParams})
* @return the constructed {@code KeyDerivationMethod}
* @throws XMLEncryptionException if the key derivation algorithm is not supported
*/
public static KeyDerivationMethod constructKeyDerivationMethod(Document doc, KeyDerivationParameters keyDerivationParameter)
throws XMLEncryptionException {
KeyDerivationMethodImpl keyDerivationMethod = new KeyDerivationMethodImpl(doc);
keyDerivationMethod.setAlgorithm(keyDerivationParameter.getAlgorithm());

KDFParams kdfParams;
if (keyDerivationParameter instanceof ConcatKDFParams) {
ConcatKDFParams kdfParameters = (ConcatKDFParams) keyDerivationParameter;
ConcatKDFParamsImpl concatKDFParams = new ConcatKDFParamsImpl(doc);
concatKDFParams.setDigestMethod(kdfParameters.getDigestAlgorithm());
concatKDFParams.setAlgorithmId(kdfParameters.getAlgorithmID());
concatKDFParams.setPartyUInfo(kdfParameters.getPartyUInfo());
concatKDFParams.setPartyVInfo(kdfParameters.getPartyVInfo());
concatKDFParams.setSuppPubInfo(kdfParameters.getSuppPubInfo());
concatKDFParams.setSuppPrivInfo(kdfParameters.getSuppPrivInfo());
kdfParams = concatKDFParams;
} else if (keyDerivationParameter instanceof HKDFParams) {
HKDFParams kdfParameters = (HKDFParams) keyDerivationParameter;
HKDFParamsImpl hkdfParams = new HKDFParamsImpl(doc);
hkdfParams.setPRFAlgorithm(kdfParameters.getHmacHashAlgorithm());
Base64.Encoder base64Encoder = Base64.getEncoder();
if (kdfParameters.getSalt() != null) {
hkdfParams.setSalt(base64Encoder.encodeToString(kdfParameters.getSalt()));
}
if (kdfParameters.getInfo() != null) {
hkdfParams.setInfo(base64Encoder.encodeToString(kdfParameters.getInfo()));
}
hkdfParams.setKeyLength(kdfParameters.getKeyBitLength() / 8);
kdfParams = hkdfParams;
} else {
throw new XMLEncryptionException("KeyDerivation.UnsupportedAlgorithm",
keyDerivationParameter.getAlgorithm(), keyDerivationParameter.getClass().getName());
}

keyDerivationMethod.setKDFParams(kdfParams);
return keyDerivationMethod;
}

/**
* Method hexStringToByteArray converts hex string to byte array.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.xml.security.encryption.params;

import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.AlgorithmParameterSpec;

/**
* This class is used to pass parameters to the Key Encapsulation Mechanism (KEM) based key
* transport, as specified in the W3C "XML Security: Generic Hybrid Cipher" note
* (https://www.w3.org/TR/xmlsec-generic-hybrid/). Unlike Diffie-Hellman key agreement
* ({@link KeyAgreementParameters}), a KEM has no ephemeral originator key pair: the
* encapsulating party only needs the recipient's public key, and the decapsulating party
* only needs the recipient's private key.
*/
public class KeyEncapsulationParameters implements AlgorithmParameterSpec {

private final String keyEncapsulationAlgorithm;
private final KeyDerivationParameters keyDerivationParameter;

private PublicKey recipientPublicKey;
private PrivateKey recipientPrivateKey;

public KeyEncapsulationParameters(String keyEncapsulationAlgorithm, KeyDerivationParameters keyDerivationParameter) {
this.keyEncapsulationAlgorithm = keyEncapsulationAlgorithm;
this.keyDerivationParameter = keyDerivationParameter;
}

public String getKeyEncapsulationAlgorithm() {
return keyEncapsulationAlgorithm;
}

public KeyDerivationParameters getKeyDerivationParameter() {
return keyDerivationParameter;
}

public PublicKey getRecipientPublicKey() {
return recipientPublicKey;
}

public void setRecipientPublicKey(PublicKey recipientPublicKey) {
this.recipientPublicKey = recipientPublicKey;
}

public PrivateKey getRecipientPrivateKey() {
return recipientPrivateKey;
}

public void setRecipientPrivateKey(PrivateKey recipientPrivateKey) {
this.recipientPrivateKey = recipientPrivateKey;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public class DEREncodedKeyValue extends Signature11ElementProxy implements KeyIn
private static final String[] supportedKeyTypes = { "RSA", "DSA", "EC",
"DiffieHellman", "DH", "XDH", "X25519", "X448",
"EdDSA", "Ed25519", "Ed448",
"ML-KEM-512", "ML-KEM-768", "ML-KEM-1024",
"RSASSA-PSS"};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,12 @@ public enum DIRECTION {
public static final String NS_DSIG = "http://www.w3.org/2000/09/xmldsig#";
public static final String NS_DSIG_MORE ="http://www.w3.org/2001/04/xmldsig-more#";
public static final String NS_DSIG_MORE_2007_05 = "http://www.w3.org/2007/05/xmldsig-more#";
public static final String NS_DSIG_MORE_2021_04 = "http://www.w3.org/2021/04/xmldsig-more#";
public static final String NS_DSIG11 = "http://www.w3.org/2009/xmldsig11#";
public static final String NS_WSSE11 = "http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd";
public static final String NS_XOP = "http://www.w3.org/2004/08/xop/include";
/** W3C "XML Security: Generic Hybrid Cipher" namespace (https://www.w3.org/TR/xmlsec-generic-hybrid/) */
public static final String NS_GHC = "http://www.w3.org/2010/xmlsec-ghc#";

public static final String PREFIX_XENC = "xenc";
public static final String PREFIX_XENC11 = "xenc11";
Expand All @@ -162,6 +165,22 @@ public enum DIRECTION {
public static final QName TAG_xenc_OAEPparams = new QName(NS_XMLENC, "OAEPparams", PREFIX_XENC);

public static final QName TAG_xenc11_MGF = new QName(NS_XMLENC11, "MGF", PREFIX_XENC11);
public static final QName TAG_xenc11_KeyDerivationMethod = new QName(NS_XMLENC11, "KeyDerivationMethod", PREFIX_XENC11);

public static final String PREFIX_GHC = "ghc";
public static final QName TAG_ghc_GenericHybridCipherMethod = new QName(NS_GHC, "GenericHybridCipherMethod", PREFIX_GHC);
public static final QName TAG_ghc_KeyEncapsulationMethod = new QName(NS_GHC, "KeyEncapsulationMethod", PREFIX_GHC);
public static final QName TAG_ghc_DataEncapsulationMethod = new QName(NS_GHC, "DataEncapsulationMethod", PREFIX_GHC);
public static final QName TAG_ghc_KeyLen = new QName(NS_GHC, "KeyLen", PREFIX_GHC);

public static final String PREFIX_HKDF = "hkdf";
public static final QName TAG_hkdf_HKDFParams = new QName(NS_DSIG_MORE_2021_04, "HKDFParams", PREFIX_HKDF);
public static final QName TAG_hkdf_PRF = new QName(NS_DSIG_MORE_2021_04, "PRF", PREFIX_HKDF);
public static final QName TAG_hkdf_Salt = new QName(NS_DSIG_MORE_2021_04, "Salt", PREFIX_HKDF);
public static final QName TAG_hkdf_Info = new QName(NS_DSIG_MORE_2021_04, "Info", PREFIX_HKDF);
public static final QName TAG_hkdf_KeyLength = new QName(NS_DSIG_MORE_2021_04, "KeyLength", PREFIX_HKDF);
/** HKDF key derivation algorithm URI (draft-eastlake-rfc9231bis-xmlsec-uris-09 section 3.8.1) */
public static final String NS_HKDF = NS_DSIG_MORE_2021_04 + "hkdf";

public static final String PREFIX_DSIG = "dsig";
public static final String PREFIX_DSIG_MORE_PSS = "pss";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ public class XMLSecurityProperties {
private String encryptionKeyTransportDigestAlgorithm;
private String encryptionKeyTransportMGFAlgorithm;
private byte[] encryptionKeyTransportOAEPParams;
// Generic Hybrid Cipher (W3C xmlsec-generic-hybrid) KEM-based key transport, e.g. ML-KEM (SANTUARIO-633).
// Used when encryptionKeyTransportAlgorithm is EncryptionConstants.ALGO_ID_KEYTRANSPORT_GENERIC_HYBRID.
private String encryptionKeyEncapsulationAlgorithm;
private String encryptionDataEncapsulationAlgorithm;
private String encryptionKeyEncapsulationHmacAlgorithm;
private final List<SecurePart> encryptionParts = new LinkedList<>();
private Key encryptionKey;
private Key encryptionTransportKey;
Expand Down Expand Up @@ -100,6 +105,9 @@ protected XMLSecurityProperties(XMLSecurityProperties xmlSecurityProperties) {
this.encryptionKeyTransportDigestAlgorithm = xmlSecurityProperties.encryptionKeyTransportDigestAlgorithm;
this.encryptionKeyTransportMGFAlgorithm = xmlSecurityProperties.encryptionKeyTransportMGFAlgorithm;
this.encryptionKeyTransportOAEPParams = xmlSecurityProperties.encryptionKeyTransportOAEPParams;
this.encryptionKeyEncapsulationAlgorithm = xmlSecurityProperties.encryptionKeyEncapsulationAlgorithm;
this.encryptionDataEncapsulationAlgorithm = xmlSecurityProperties.encryptionDataEncapsulationAlgorithm;
this.encryptionKeyEncapsulationHmacAlgorithm = xmlSecurityProperties.encryptionKeyEncapsulationHmacAlgorithm;
this.encryptionParts.addAll(xmlSecurityProperties.encryptionParts);
this.encryptionKey = xmlSecurityProperties.encryptionKey;
this.encryptionTransportKey = xmlSecurityProperties.encryptionTransportKey;
Expand Down Expand Up @@ -333,6 +341,43 @@ public void setEncryptionKeyTransportOAEPParams(byte[] encryptionKeyTransportOAE
this.encryptionKeyTransportOAEPParams = encryptionKeyTransportOAEPParams;
}

/**
* Returns the Key Encapsulation Method algorithm URI (e.g. an ML-KEM algorithm URI) used when
* {@link #getEncryptionKeyTransportAlgorithm()} is the Generic Hybrid Cipher algorithm
* (see {@code EncryptionConstants.ALGO_ID_KEYTRANSPORT_GENERIC_HYBRID}, SANTUARIO-633).
*/
public String getEncryptionKeyEncapsulationAlgorithm() {
return encryptionKeyEncapsulationAlgorithm;
}

public void setEncryptionKeyEncapsulationAlgorithm(String encryptionKeyEncapsulationAlgorithm) {
this.encryptionKeyEncapsulationAlgorithm = encryptionKeyEncapsulationAlgorithm;
}

/**
* Returns the Data Encapsulation Method algorithm URI (an AES-KeyWrap algorithm) used when
* {@link #getEncryptionKeyTransportAlgorithm()} is the Generic Hybrid Cipher algorithm.
*/
public String getEncryptionDataEncapsulationAlgorithm() {
return encryptionDataEncapsulationAlgorithm;
}

public void setEncryptionDataEncapsulationAlgorithm(String encryptionDataEncapsulationAlgorithm) {
this.encryptionDataEncapsulationAlgorithm = encryptionDataEncapsulationAlgorithm;
}

/**
* Returns the HMAC hash algorithm URI used as the HKDF PRF when deriving the data-encapsulation
* (AES-KeyWrap) key from the KEM shared secret. Defaults to HMAC-SHA256 if unset.
*/
public String getEncryptionKeyEncapsulationHmacAlgorithm() {
return encryptionKeyEncapsulationHmacAlgorithm;
}

public void setEncryptionKeyEncapsulationHmacAlgorithm(String encryptionKeyEncapsulationHmacAlgorithm) {
this.encryptionKeyEncapsulationHmacAlgorithm = encryptionKeyEncapsulationHmacAlgorithm;
}

public X509Certificate getEncryptionUseThisCertificate() {
return encryptionUseThisCertificate;
}
Expand Down
Loading
Loading