This is an automated email from the ASF dual-hosted git repository. coheigea pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/ws-wss4j.git
The following commit(s) were added to refs/heads/master by this push: new 7ab6a7e50 Support for the ECDH KeyAgreement method algorithm (#264) 7ab6a7e50 is described below commit 7ab6a7e50ffa8d2a2de372f0d48e37a336584874 Author: jrihtarsic <jrihtars...@gmail.com> AuthorDate: Thu Jan 25 14:06:30 2024 +0100 Support for the ECDH KeyAgreement method algorithm (#264) * Support for the ECDH KeyAgreement method algorithm * Fix typos, regenerate example keystores using keytool/ * Clean double spaces, NPE validation for KeyAgreement * XDH and EdEC key length validation for AlgorithmSuiteValidator * Add check for KeyAgreement --------- Co-authored-by: RIHTARSIC Joze <joze.rihtar...@ext.ec.europa.eu> --- parent/pom.xml | 10 +- policy/pom.xml | 3 +- .../model/AbstractSymmetricAsymmetricBinding.java | 2 +- pom.xml | 3 +- ws-security-common/pom.xml | 5 + .../wss4j/common/ConfigurationConstants.java | 15 + .../apache/wss4j/common/EncryptionActionToken.java | 7 + .../org/apache/wss4j/common/WSS4JConstants.java | 12 + .../apache/wss4j/common/crypto/AlgorithmSuite.java | 15 +- .../common/crypto/AlgorithmSuiteValidator.java | 65 ++++- .../org/apache/wss4j/common/crypto/Merlin.java | 4 +- .../wss4j/common/crypto/WSProviderConfig.java | 2 +- .../org/apache/wss4j/common/saml/OpenSAMLUtil.java | 4 +- .../wss4j/common/saml/SamlAssertionWrapper.java | 2 +- .../apache/wss4j/common/util/InetAddressUtils.java | 4 +- .../java/org/apache/wss4j/common/util/NSStack.java | 14 +- .../org/apache/wss4j/common/util/XMLUtils.java | 4 +- .../resources/messages/wss4j_errors.properties | 3 + .../common/crypto/AlgorithmSuiteValidatorTest.java | 81 ++++++ .../org/apache/wss4j/common/util/JDKTestUtils.java | 148 ++++++++++ .../org/apache/wss4j/common/util/SOAPUtil.java | 2 +- .../src/test/resources/keys/README.txt | 59 ++++ .../src/test/resources/keys/wss-ecdh.p12 | Bin 0 -> 8502 bytes .../src/test/resources/keys/wss-eddsa.p12 | Bin 1687 -> 1927 bytes .../src/test/resources/wss-ecdh.properties | 4 + ws-security-dom/pom.xml | 8 +- .../apache/wss4j/dom/action/EncryptionAction.java | 7 +- .../apache/wss4j/dom/action/SignatureAction.java | 2 +- .../org/apache/wss4j/dom/handler/RequestData.java | 2 +- .../org/apache/wss4j/dom/handler/WSHandler.java | 15 +- .../wss4j/dom/message/WSSecEncryptedKey.java | 145 +++++++--- .../apache/wss4j/dom/message/WSSecSignature.java | 4 +- .../dom/message/token/SignatureConfirmation.java | 2 +- .../wss4j/dom/processor/CertificateResult.java | 106 +++++++ .../wss4j/dom/processor/EncryptedKeyProcessor.java | 308 ++++++++++++++------- .../org/apache/wss4j/dom/saml/DOMSAMLUtil.java | 2 +- .../wss4j/dom/common/KeystoreCallbackHandler.java | 7 +- .../apache/wss4j/dom/message/EncryptionTest.java | 76 ++++- .../wss4j/dom/message/SignatureCertTest.java | 66 +---- .../AlgorithmSuiteAssertionState.java | 10 +- .../apache/wss4j/stax/test/EncDecryptionTest.java | 4 +- .../apache/wss4j/stax/test/HeaderOrderingTest.java | 10 +- .../wss4j/stax/test/SecurityContextTokenTest.java | 8 +- .../apache/wss4j/stax/test/saml/SAMLTokenTest.java | 8 +- 44 files changed, 1000 insertions(+), 258 deletions(-) diff --git a/parent/pom.xml b/parent/pom.xml index a1149c244..8f80a9fa8 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -17,7 +17,8 @@ specific language governing permissions and limitations under the License. --> -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> @@ -48,7 +49,7 @@ <wsdl4j.version>1.6.3</wsdl4j.version> <woodstox.version>6.6.0</woodstox.version> <xz.version>1.9</xz.version> - <xmlsec.version>4.0.1</xmlsec.version> + <xmlsec.version>4.0.2-SNAPSHOT</xmlsec.version> <xmlunit.version>2.9.1</xmlunit.version> <!-- OSGi related properties --> <wss4j.osgi.import /> @@ -107,6 +108,11 @@ <artifactId>junit-jupiter-engine</artifactId> <version>${junit.version}</version> </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-params</artifactId> + <version>${junit.version}</version> + </dependency> <dependency> <groupId>wsdl4j</groupId> <artifactId>wsdl4j</artifactId> diff --git a/policy/pom.xml b/policy/pom.xml index 2c30fb4f9..82c444956 100644 --- a/policy/pom.xml +++ b/policy/pom.xml @@ -17,7 +17,8 @@ specific language governing permissions and limitations under the License. --> -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> diff --git a/policy/src/main/java/org/apache/wss4j/policy/model/AbstractSymmetricAsymmetricBinding.java b/policy/src/main/java/org/apache/wss4j/policy/model/AbstractSymmetricAsymmetricBinding.java index 292c32f2b..30e815333 100644 --- a/policy/src/main/java/org/apache/wss4j/policy/model/AbstractSymmetricAsymmetricBinding.java +++ b/policy/src/main/java/org/apache/wss4j/policy/model/AbstractSymmetricAsymmetricBinding.java @@ -91,7 +91,7 @@ public abstract class AbstractSymmetricAsymmetricBinding extends AbstractBinding } protected void parseNestedSymmetricAsymmetricBindingBasePolicy( - Policy nestedPolicy, AbstractSymmetricAsymmetricBinding asymmetricBindingBase + Policy nestedPolicy, AbstractSymmetricAsymmetricBinding asymmetricBindingBase ) { Iterator<List<Assertion>> alternatives = nestedPolicy.getAlternatives(); //we just process the first alternative diff --git a/pom.xml b/pom.xml index e8ad6f16c..12cdaa07b 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,8 @@ specific language governing permissions and limitations under the License. --> -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.apache.wss4j</groupId> diff --git a/ws-security-common/pom.xml b/ws-security-common/pom.xml index 5be260691..0c5eaf433 100644 --- a/ws-security-common/pom.xml +++ b/ws-security-common/pom.xml @@ -233,6 +233,11 @@ <artifactId>junit-jupiter-engine</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-params</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-reload4j</artifactId> diff --git a/ws-security-common/src/main/java/org/apache/wss4j/common/ConfigurationConstants.java b/ws-security-common/src/main/java/org/apache/wss4j/common/ConfigurationConstants.java index 31a21dc98..131ea60c8 100644 --- a/ws-security-common/src/main/java/org/apache/wss4j/common/ConfigurationConstants.java +++ b/ws-security-common/src/main/java/org/apache/wss4j/common/ConfigurationConstants.java @@ -737,6 +737,21 @@ public class ConfigurationConstants { */ public static final String ENC_KEY_TRANSPORT = "encryptionKeyTransportAlgorithm"; + /** + * Defines the Agreement method algorithm to derive encryption key. + * The default algorithm is: + * "http://www.w3.org/2009/xmlenc11#ECDH-ES" + * + * <p/> + * The application may set this parameter using the following method: + * <pre> + * call.setProperty(ConfigurationConstants.ENC_KEY_AGREEMENT_METHOD, + * WSConstants.AGREEMENT_METHOD_ECDH_ES); + * </pre> + * + */ + public static final String ENC_KEY_AGREEMENT_METHOD = "encryptionKeyAgreementMethod"; + /** * Parameter to define which parts of the request shall be encrypted. * <p/> diff --git a/ws-security-common/src/main/java/org/apache/wss4j/common/EncryptionActionToken.java b/ws-security-common/src/main/java/org/apache/wss4j/common/EncryptionActionToken.java index ab74296c3..472e09fbf 100644 --- a/ws-security-common/src/main/java/org/apache/wss4j/common/EncryptionActionToken.java +++ b/ws-security-common/src/main/java/org/apache/wss4j/common/EncryptionActionToken.java @@ -27,6 +27,7 @@ public class EncryptionActionToken extends SignatureEncryptionActionToken { private boolean encSymmetricEncryptionKey = true; private String mgfAlgorithm; private String symmetricAlgorithm; + private String keyAgreementMethodAlgorithm; private String keyTransportAlgorithm; private boolean getSymmetricKeyFromCallbackHandler; @@ -54,6 +55,12 @@ public class EncryptionActionToken extends SignatureEncryptionActionToken { public void setKeyTransportAlgorithm(String keyTransportAlgorithm) { this.keyTransportAlgorithm = keyTransportAlgorithm; } + public String getKeyAgreementMethodAlgorithm() { + return keyAgreementMethodAlgorithm; + } + public void setKeyAgreementMethodAlgorithm(String keyAgreementMethodAlgorithm) { + this.keyAgreementMethodAlgorithm = keyAgreementMethodAlgorithm; + } public boolean isGetSymmetricKeyFromCallbackHandler() { return getSymmetricKeyFromCallbackHandler; } diff --git a/ws-security-common/src/main/java/org/apache/wss4j/common/WSS4JConstants.java b/ws-security-common/src/main/java/org/apache/wss4j/common/WSS4JConstants.java index af54a2090..9e5c80291 100644 --- a/ws-security-common/src/main/java/org/apache/wss4j/common/WSS4JConstants.java +++ b/ws-security-common/src/main/java/org/apache/wss4j/common/WSS4JConstants.java @@ -108,6 +108,18 @@ public class WSS4JConstants { "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"; public static final String KEYTRANSPORT_RSAOAEP_XENC11 = "http://www.w3.org/2009/xmlenc11#rsa-oaep"; + public static final String KEYWRAP_AES128 = + "http://www.w3.org/2001/04/xmlenc#kw-aes128"; + public static final String KEYWRAP_AES192 = + "http://www.w3.org/2001/04/xmlenc#kw-aes192"; + public static final String KEYWRAP_AES256 = + "http://www.w3.org/2001/04/xmlenc#kw-aes256"; + public static final String KEYWRAP_TRIPLEDES = + "http://www.w3.org/2001/04/xmlenc#kw-tripledes"; + public static final String KDF_CONCAT = + "http://www.w3.org/2009/xmlenc11#ConcatKDF"; + public static final String AGREEMENT_METHOD_ECDH_ES = + "http://www.w3.org/2009/xmlenc11#ECDH-ES"; public static final String TRIPLE_DES = "http://www.w3.org/2001/04/xmlenc#tripledes-cbc"; public static final String AES_128 = diff --git a/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/AlgorithmSuite.java b/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/AlgorithmSuite.java index b854eed54..1a4823c5d 100644 --- a/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/AlgorithmSuite.java +++ b/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/AlgorithmSuite.java @@ -37,7 +37,7 @@ public class AlgorithmSuite { private Set<String> encryptionMethods = Collections.emptySet(); private Set<String> keyWrapAlgorithms = Collections.emptySet(); - + private Set<String> keyAgreementAlgorithms = Collections.emptySet(); private Set<String> derivedKeyAlgorithms = Collections.emptySet(); private int maximumSymmetricKeyLength = 256; @@ -116,6 +116,17 @@ public class AlgorithmSuite { return keyWrapAlgorithms; } + public void addKeyAgreementMethodAlgorithm(String keyAgreementAlgorithm) { + if (keyAgreementAlgorithms.isEmpty()) { + keyAgreementAlgorithms = new HashSet<>(); + } + keyAgreementAlgorithms.add(keyAgreementAlgorithm); + } + + public Set<String> getKeyAgreementMethodAlgorithms() { + return keyAgreementAlgorithms; + } + public void addDerivedKeyAlgorithm(String derivedKeyAlgorithm) { if (derivedKeyAlgorithms.isEmpty()) { derivedKeyAlgorithms = new HashSet<>(); @@ -191,4 +202,4 @@ public class AlgorithmSuite { this.minimumEllipticCurveKeyLength = minimumEllipticCurveKeyLength; } -} \ No newline at end of file +} diff --git a/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/AlgorithmSuiteValidator.java b/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/AlgorithmSuiteValidator.java index 714101fa8..d193819fc 100644 --- a/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/AlgorithmSuiteValidator.java +++ b/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/AlgorithmSuiteValidator.java @@ -21,9 +21,7 @@ package org.apache.wss4j.common.crypto; import java.security.PublicKey; import java.security.cert.X509Certificate; -import java.security.interfaces.DSAPublicKey; -import java.security.interfaces.ECPublicKey; -import java.security.interfaces.RSAPublicKey; +import java.security.interfaces.*; import java.util.Set; import javax.xml.crypto.dsig.Reference; @@ -31,6 +29,9 @@ import javax.xml.crypto.dsig.Transform; import javax.xml.crypto.dsig.XMLSignature; import org.apache.wss4j.common.ext.WSSecurityException; +import org.apache.xml.security.exceptions.DERDecodingException; +import org.apache.xml.security.utils.DERDecoderUtils; +import org.apache.xml.security.utils.KeyUtils; /** * Validate signature/encryption/etc. algorithms against an AlgorithmSuite policy. @@ -138,6 +139,19 @@ public class AlgorithmSuiteValidator { } } + public void checkKeyAgreementMethodAlgorithm( + String keyAgreementMethodAlgorithm + ) throws WSSecurityException { + Set<String> keyAgreementMethodAlgorithms = algorithmSuite.getKeyAgreementMethodAlgorithms(); + if (!keyAgreementMethodAlgorithms.isEmpty() + && !keyAgreementMethodAlgorithms.contains(keyAgreementMethodAlgorithm)) { + LOG.warn( + "The Key agreement method does not match the requirement" + ); + throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY); + } + } + public void checkSymmetricEncryptionAlgorithm( String symmetricAlgorithm ) throws WSSecurityException { @@ -216,11 +230,50 @@ public class AlgorithmSuiteValidator { throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY); } } else { - LOG.warn( - "An unknown public key was provided" - ); + // Try with last supported key types EdEC and XDH + int keySize = getEdECndXDHKeyLength(publicKey); + if (keySize < algorithmSuite.getMinimumEllipticCurveKeyLength() + || keySize > algorithmSuite.getMaximumEllipticCurveKeyLength()) { + LOG.warn( + "The asymmetric key length does not match the requirement" + ); + throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY); + } + } + } + + /** + * A generic method to determinate key length for keys x25519, x448, ed25519 and ed448 keys. Method does not rely on + * any specific implementation of the key, but uses OID to determine the key type. + * + * @param publicKey the public key to check the key length + * @return the key length in bits + * @throws WSSecurityException if the key is not EdEC or XDH or if length can not be determined + */ + private int getEdECndXDHKeyLength(PublicKey publicKey) throws WSSecurityException { + String keyAlgorithmOId; + try { + keyAlgorithmOId = DERDecoderUtils.getAlgorithmIdFromPublicKey(publicKey); + } catch (DERDecodingException e) { + LOG.warn("Can not parse the public key to determine key size!", e); + throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY); + } + KeyUtils.KeyType keyType = KeyUtils.KeyType.getByOid(keyAlgorithmOId); + if (keyType == null) { + LOG.warn("An unknown public key was provided"); throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY); } + + return switch (keyType) { + case ED25519, X25519 -> 256; + case ED448, X448 -> 456; + default -> { + LOG.warn( + "An unknown public key was provided" + ); + throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY); + } + }; } /** diff --git a/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/Merlin.java b/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/Merlin.java index c783e781b..2fa2f204b 100644 --- a/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/Merlin.java +++ b/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/Merlin.java @@ -1454,7 +1454,7 @@ public class Merlin extends CryptoBase { Certificate[] certs = store.getCertificateChain(alias); if (certs == null || certs.length == 0) { - // no cert chain, so lets check if getCertificate gives us a result. + // no cert chain, so lets check if getCertificate gives us a result. Certificate retrievedCert = store.getCertificate(alias); if (retrievedCert != null) { certs = new Certificate[]{retrievedCert}; @@ -1479,7 +1479,7 @@ public class Merlin extends CryptoBase { Certificate[] certs = store.getCertificateChain(alias); if (certs == null || certs.length == 0) { - // no cert chain, so lets check if getCertificate gives us a result. + // no cert chain, so lets check if getCertificate gives us a result. Certificate retrievedCert = store.getCertificate(alias); if (retrievedCert != null) { certs = new Certificate[]{retrievedCert}; diff --git a/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/WSProviderConfig.java b/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/WSProviderConfig.java index 9444cc2ca..7d66541e6 100644 --- a/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/WSProviderConfig.java +++ b/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/WSProviderConfig.java @@ -143,7 +143,7 @@ public final class WSProviderConfig { * Set the value of the internal addJceProviders flag. This flag * turns on (or off) automatic registration of known JCE providers * that provide necessary cryptographic algorithms for use with WSS4J. - * By default, this flag is true. You may wish (or need) to initialize + * By default, this flag is true. You may wish (or need) to initialize * the JCE manually, e.g., in some JVMs. */ public static void setAddJceProviders(boolean value) { diff --git a/ws-security-common/src/main/java/org/apache/wss4j/common/saml/OpenSAMLUtil.java b/ws-security-common/src/main/java/org/apache/wss4j/common/saml/OpenSAMLUtil.java index 0305686b6..4fca170bd 100644 --- a/ws-security-common/src/main/java/org/apache/wss4j/common/saml/OpenSAMLUtil.java +++ b/ws-security-common/src/main/java/org/apache/wss4j/common/saml/OpenSAMLUtil.java @@ -166,7 +166,7 @@ public final class OpenSAMLUtil { * Convert a SAML Assertion from a XMLObject to a DOM Element * * @param xmlObject of type XMLObject - * @param doc of type Document + * @param doc of type Document * @return Element * @throws WSSecurityException */ @@ -181,7 +181,7 @@ public final class OpenSAMLUtil { * Convert a SAML Assertion from a XMLObject to a DOM Element * * @param xmlObject of type XMLObject - * @param doc of type Document + * @param doc of type Document * @param signObject whether to sign the XMLObject during marshalling * @return Element * @throws WSSecurityException diff --git a/ws-security-common/src/main/java/org/apache/wss4j/common/saml/SamlAssertionWrapper.java b/ws-security-common/src/main/java/org/apache/wss4j/common/saml/SamlAssertionWrapper.java index e158570bb..29e9b3277 100644 --- a/ws-security-common/src/main/java/org/apache/wss4j/common/saml/SamlAssertionWrapper.java +++ b/ws-security-common/src/main/java/org/apache/wss4j/common/saml/SamlAssertionWrapper.java @@ -144,7 +144,7 @@ public class SamlAssertionWrapper { /** * Constructor SamlAssertionWrapper creates a new SamlAssertionWrapper instance. - * This is the primary constructor. All other constructor calls should + * This is the primary constructor. All other constructor calls should * be routed to this method to ensure that the wrapper is initialized * correctly. * diff --git a/ws-security-common/src/main/java/org/apache/wss4j/common/util/InetAddressUtils.java b/ws-security-common/src/main/java/org/apache/wss4j/common/util/InetAddressUtils.java index 8b7fb288f..1246f11b6 100644 --- a/ws-security-common/src/main/java/org/apache/wss4j/common/util/InetAddressUtils.java +++ b/ws-security-common/src/main/java/org/apache/wss4j/common/util/InetAddressUtils.java @@ -18,7 +18,7 @@ */ /* * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more + * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * @@ -56,7 +56,7 @@ public final class InetAddressUtils { + "(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,5})?)$"); // 0-6 hex fields /* - * The above pattern is not totally rigorous as it allows for more than 7 hex fields in total + * The above pattern is not totally rigorous as it allows for more than 7 hex fields in total */ private static final char COLON_CHAR = ':'; diff --git a/ws-security-common/src/main/java/org/apache/wss4j/common/util/NSStack.java b/ws-security-common/src/main/java/org/apache/wss4j/common/util/NSStack.java index 6e2eb051c..f53b9e26a 100644 --- a/ws-security-common/src/main/java/org/apache/wss4j/common/util/NSStack.java +++ b/ws-security-common/src/main/java/org/apache/wss4j/common/util/NSStack.java @@ -21,14 +21,14 @@ package org.apache.wss4j.common.util; /** * The abstraction this class provides is a push down stack of variable - * length frames of prefix to namespace mappings. Used for keeping track + * length frames of prefix to namespace mappings. Used for keeping track * of what namespaces are active at any given point as an XML document is * traversed or produced. * <p/> * From a performance point of view, this data will both be modified frequently * (at a minimum, there will be one push and pop per XML element processed), * and scanned frequently (many of the "good" mappings will be at the bottom - * of the stack). The one saving grace is that the expected maximum + * of the stack). The one saving grace is that the expected maximum * cardinalities of the number of frames and the number of total mappings * is only in the dozens, representing the nesting depth of an XML document * and the number of active namespaces at any point in the processing. @@ -113,8 +113,8 @@ public class NSStack { /** * Reset the embedded iterator in this class to the top of the current - * (i.e., last) frame. Note that this is not threadsafe, nor does it - * provide multiple iterators, so don't use this recursively. Nor + * (i.e., last) frame. Note that this is not threadsafe, nor does it + * provide multiple iterators, so don't use this recursively. Nor * should you modify the stack while iterating over it. */ public Mapping topOfFrame() { @@ -139,7 +139,7 @@ public class NSStack { /** * Add a mapping for a namespaceURI to the specified prefix to the top - * frame in the stack. If the prefix is already mapped in that frame, + * frame in the stack. If the prefix is already mapped in that frame, * remap it to the (possibly different) namespaceURI. */ public void add(String namespaceURI, String prefix) { @@ -166,10 +166,10 @@ public class NSStack { } /** - * Return an active prefix for the given namespaceURI. NOTE : This + * Return an active prefix for the given namespaceURI. NOTE : This * may return null even if the namespaceURI was actually mapped further * up the stack IF the prefix which was used has been repeated further - * down the stack. I.e.: + * down the stack. I.e.: * <p/> * <pre:outer xmlns:pre="namespace"> * <pre:inner xmlns:pre="otherNamespace"> diff --git a/ws-security-common/src/main/java/org/apache/wss4j/common/util/XMLUtils.java b/ws-security-common/src/main/java/org/apache/wss4j/common/util/XMLUtils.java index 5497e9007..136b18410 100644 --- a/ws-security-common/src/main/java/org/apache/wss4j/common/util/XMLUtils.java +++ b/ws-security-common/src/main/java/org/apache/wss4j/common/util/XMLUtils.java @@ -64,8 +64,8 @@ public final class XMLUtils { * Gets a direct child with specified localname and namespace. <p/> * * @param parentNode the node where to start the search - * @param localName local name of the child to get - * @param namespace the namespace of the child to get + * @param localName local name of the child to get + * @param namespace the namespace of the child to get * @return the node or <code>null</code> if not such node found */ public static Element getDirectChildElement(Node parentNode, String localName, String namespace) { diff --git a/ws-security-common/src/main/resources/messages/wss4j_errors.properties b/ws-security-common/src/main/resources/messages/wss4j_errors.properties index 8bd5ae051..9eebaa483 100644 --- a/ws-security-common/src/main/resources/messages/wss4j_errors.properties +++ b/ws-security-common/src/main/resources/messages/wss4j_errors.properties @@ -53,6 +53,9 @@ noDecCryptoFile = No crypto property file supplied for decryption noEncAlgo = xenc:EncryptedKey does not contain xenc:EncryptionMethod/@Algorithm noEncElement = Element to encrypt/sign not found: {0} noEncKey = EncryptedData does not contain xenc:EncryptedKey +noAgreementMethod = ds:KeyInfo does not contain xenc:AgreementMethod +noRecipientKeyInfo = AgreementMethod does not contain xenc:RecipientKeyInfo +noRecipientSecTokRef = AgreementMethod does not contain xenc:RecipientKeyInfo/wsse:SecurityTokenReference noEncryptionUser = Encryption user is not set noKeyinfo = EncryptedKey/EncryptedData does not contain ds:KeyInfo noKeyInSAMLToken = Provided SAML token does not contain a suitable key diff --git a/ws-security-common/src/test/java/org/apache/wss4j/common/crypto/AlgorithmSuiteValidatorTest.java b/ws-security-common/src/test/java/org/apache/wss4j/common/crypto/AlgorithmSuiteValidatorTest.java new file mode 100644 index 000000000..c7eac46d3 --- /dev/null +++ b/ws-security-common/src/test/java/org/apache/wss4j/common/crypto/AlgorithmSuiteValidatorTest.java @@ -0,0 +1,81 @@ +/** + * 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.wss4j.common.crypto; + +import org.apache.wss4j.common.ext.WSSecurityException; +import org.apache.wss4j.common.util.JDKTestUtils; +import org.junit.jupiter.api.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; + + +import static org.junit.jupiter.api.Assertions.*; + +class AlgorithmSuiteValidatorTest { + private static final org.slf4j.Logger LOG = + org.slf4j.LoggerFactory.getLogger(AlgorithmSuiteValidatorTest.class); + + @BeforeAll + public static void setUp() throws Exception { + WSProviderConfig.init(); + } + + @AfterEach + public void cleanTest() { + JDKTestUtils.unregisterAuxiliaryProvider(); + } + + @ParameterizedTest + @CsvSource({"X25519, 160, 512, false", + "X448, 160, 512, false", + "ED25519, 160, 512, false", + "ED448, 160, 512, false", + "ED25519, 300, 512, true", + "X25519, 300, 512, true", + "X448, 160, 300, true", + "ED448, 160, 300, true", + }) + void checkAsymmetricKeyLength(String keyAlgorithm, int iMinECKelLength, int iMaxECKelLength, boolean fail) throws NoSuchAlgorithmException { + if (!JDKTestUtils.isAlgorithmSupportedByJDK(keyAlgorithm)) { + LOG.info("Add AuxiliaryProvider to execute test with algorithm [{}]", keyAlgorithm); + JDKTestUtils.registerAuxiliaryProvider(); + } + AlgorithmSuite algorithmSuite = new AlgorithmSuite(); + algorithmSuite.setMinimumEllipticCurveKeyLength(iMinECKelLength); + algorithmSuite.setMaximumEllipticCurveKeyLength(iMaxECKelLength); + + AlgorithmSuiteValidator validator = new AlgorithmSuiteValidator(algorithmSuite); + KeyPairGenerator keygen = KeyPairGenerator.getInstance(keyAlgorithm); + KeyPair keyPair = keygen.generateKeyPair(); + if (fail) { + WSSecurityException result = Assertions.assertThrows(WSSecurityException.class, + () -> validator.checkAsymmetricKeyLength(keyPair.getPublic())); + assertEquals(WSSecurityException.ErrorCode.INVALID_SECURITY, result.getErrorCode()); + } + else { + Assertions.assertDoesNotThrow( + () -> validator.checkAsymmetricKeyLength(keyPair.getPublic())); + } + } +} diff --git a/ws-security-common/src/test/java/org/apache/wss4j/common/util/JDKTestUtils.java b/ws-security-common/src/test/java/org/apache/wss4j/common/util/JDKTestUtils.java new file mode 100644 index 000000000..e705be627 --- /dev/null +++ b/ws-security-common/src/test/java/org/apache/wss4j/common/util/JDKTestUtils.java @@ -0,0 +1,148 @@ +/** + * 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.wss4j.common.util; + +import java.lang.reflect.Constructor; +import java.security.Provider; +import java.security.Security; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + + +/** + * The class provides testing utility methods to test wss4j functionality with various JDK version. Where possible + * we use JDK provided algorithm implementations. However, some algorithms are not supported in lower JDK versions. For example + * XDH keys were supported from JDK 11, EdDSA keys from JDK 16, etc. To ensure tests are executed for various JDK versions, + * we need to know which algorithms are supported from particular JDK version. + * + * If the JDK security providers do not support algorithm, the class provides auxiliary security provider (BouncyCastle) to the test + * wss4j functionalities ... + * + */ +public class JDKTestUtils { + + private static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(JDKTestUtils.class); + // Purpose of auxiliary security provider is to enable testing of algorithms not supported by default JDK security providers. + private static final String TEST_PROVIDER_CLASSNAME_PROPERTY = "test.auxiliary.jce.provider.classname"; + private static final String TEST_PROVIDER_CLASSNAME_DEFAULT = "org.bouncycastle.jce.provider.BouncyCastleProvider"; + + + private static Provider auxiliaryProvider; + private static boolean auxiliaryProviderInitialized = false; + private static Set<String> supportedAuxiliaryProviderAlgorithms = null; + + private static final Map<String, Integer> javaAlgSupportFrom = Stream.of( + new AbstractMap.SimpleImmutableEntry<>("eddsa", 16), + new AbstractMap.SimpleImmutableEntry<>("ed25519", 16), + new AbstractMap.SimpleImmutableEntry<>("ed448", 16), + new AbstractMap.SimpleImmutableEntry<>("xdh", 11), + new AbstractMap.SimpleImmutableEntry<>("x25519", 11), + new AbstractMap.SimpleImmutableEntry<>("x448", 11)) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + + private static final Set<String> SUPPORTED_ALGORITHMS = Stream.of(Security.getProviders()) + .flatMap(provider -> provider.getServices().stream()) + .map(Provider.Service::getAlgorithm) + .map(String::toLowerCase) + .collect(Collectors.toSet()); + + + public static int getJDKVersion() { + try { + return Integer.getInteger("java.specification.version", 0); + } catch (NumberFormatException ex) { + LOG.warn("Can not determine JDK version! Error message [{}]", ex.getMessage()); + } + return 0; + } + + public static synchronized Provider getAuxiliaryProvider() { + if (auxiliaryProviderInitialized) { + return auxiliaryProvider; + } + try { + String providerClassName = System.getProperty(TEST_PROVIDER_CLASSNAME_PROPERTY, TEST_PROVIDER_CLASSNAME_DEFAULT); + LOG.info("Initialize the auxiliary security provider: [{}]", providerClassName); + Class<?> c = Class.forName(providerClassName); + Constructor<?> cons = c.getConstructor(); + auxiliaryProvider = (Provider)cons.newInstance(); + supportedAuxiliaryProviderAlgorithms = auxiliaryProvider.getServices().stream() + .map(Provider.Service::getAlgorithm) + .map(String::toLowerCase) + .collect(Collectors.toSet()); + } catch (Exception e) { + LOG.warn("Failed to initialize the auxiliary security provider: [{}]", e.getMessage()); + } + auxiliaryProviderInitialized = true; + return auxiliaryProvider; + } + + public static void registerAuxiliaryProvider() { + // init provider if needed + Provider provider = getAuxiliaryProvider(); + if (provider == null) { + LOG.warn("Auxiliary security provider is not initialized. Cannot register it."); + return; + } + Security.addProvider(provider); + } + + public static void unregisterAuxiliaryProvider() { + if (auxiliaryProvider == null) { + LOG.debug("Auxiliary security provider is not initialized. Cannot unregister it."); + return; + } + LOG.debug("Unregister auxiliary security provider [{}]", auxiliaryProvider.getName()); + Security.removeProvider(auxiliaryProvider.getName()); + } + + public static boolean isAuxiliaryProviderRegistered() { + return auxiliaryProvider!=null && Security.getProvider(auxiliaryProvider.getName())!=null ; + } + + + public static boolean isAlgorithmSupported(String algorithm, boolean useAuxiliaryProvider) { + String alg = algorithm.toLowerCase(); + int iJDKVersion = getJDKVersion(); + if (javaAlgSupportFrom.containsKey(alg) + && javaAlgSupportFrom.get(alg) <= iJDKVersion + || SUPPORTED_ALGORITHMS.contains(alg)) { + LOG.debug("Algorithm [{}] is supported by JDK version [{}]", alg, iJDKVersion); + return true; + } + Provider provider = getAuxiliaryProvider(); + if (useAuxiliaryProvider + && provider!=null + && supportedAuxiliaryProviderAlgorithms.contains(alg)){ + LOG.debug("Algorithm [{}] is supported by auxiliary Provider [{}].", + alg, provider.getName()); + return true; + } + // double check in all supported algorithms ... + LOG.debug("Algorithm [{}] is NOT supported!", alg); + return false; + } + + public static boolean isAlgorithmSupportedByJDK(String algorithm) { + return isAlgorithmSupported(algorithm, false); + } +} + diff --git a/ws-security-common/src/test/java/org/apache/wss4j/common/util/SOAPUtil.java b/ws-security-common/src/test/java/org/apache/wss4j/common/util/SOAPUtil.java index 6e12ec4c9..b63cd2c91 100644 --- a/ws-security-common/src/test/java/org/apache/wss4j/common/util/SOAPUtil.java +++ b/ws-security-common/src/test/java/org/apache/wss4j/common/util/SOAPUtil.java @@ -39,7 +39,7 @@ public class SOAPUtil { + "</SOAP-ENV:Body>" + "</SOAP-ENV:Envelope>"; - private static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(SOAPUtil.class); + private static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(SOAPUtil.class); private static DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); private static MessageFactory saajFactory = null; diff --git a/ws-security-common/src/test/resources/keys/README.txt b/ws-security-common/src/test/resources/keys/README.txt index 0fd5a214e..3b80c264a 100644 --- a/ws-security-common/src/test/resources/keys/README.txt +++ b/ws-security-common/src/test/resources/keys/README.txt @@ -117,3 +117,62 @@ openssl ca -gencrl -keyfile wss40CAKey.pem -cert wss40CA.pem -out wss40CACRL.pem ===== +wss-eddsa: + +NOTE: Use the keytool from JDK 16 and above, where support for Ed25519, Ed448 keys and eddsa signature were supported. + +keytool -genkeypair -keystore wss-eddsa.p12 -alias ed25519 -keyalg ED25519 -sigalg ED25519 \ + -storepass security -keypass security \ + -dname "CN=ed25519,OU=eDeliveryAS4-2.0,OU=wss4j,O=apache,C=EU" \ + -validity 3650 + +keytool -genkeypair -keystore wss-eddsa.p12 -alias ed448 -keyalg ED448 -sigalg ED448 \ + -storepass security -keypass security \ + -dname "CN=ed448,OU=eDeliveryAS4-2.0,OU=wss4j,O=apache,C=EU" \ + -validity 3650 + +===== + +wss-ecdh: + +NOTE: Use the keytool from JDK 17 and above, where support was added for specifying a signer of the certificate using +the keytool -genkeypair. See: https://www.oracle.com/java/technologies/javase/17-relnote-issues.html + +keytool -genkeypair -keystore wss-ecdh.p12 -alias issuer-ca -keyalg ED25519 -sigalg ED25519 \ + -storepass security -keypass security \ + -ext bc:c,ca:true,pathlen:2 \ + -dname "CN=issuer-ca,OU=eDeliveryAS4-2.0,OU=wss4j,O=apache,C=EU" \ + -validity 3651 + +keytool -genkeypair -keystore wss-ecdh.p12 -alias x25519 -keyalg X25519 \ + -sigalg ED25519 -signer issuer-ca -signerkeypass security \ + -storepass security -keypass security \ + -dname "CN=x25519, OU=eDeliveryAS4-2.0,OU=wss4j,O=apache,C=EU" \ + -validity 3650 + +keytool -genkeypair -keystore wss-ecdh.p12 -alias x448 -keyalg X448 \ + -sigalg ED25519 -signer issuer-ca -signerkeypass security \ + -storepass security -keypass security \ + -dname "CN=x448, OU=eDeliveryAS4-2.0,OU=wss4j,O=apache,C=EU" \ + -validity 3650 + +keytool -genkeypair -keystore wss-ecdh.p12 -alias secp256r1 -keyalg EC -groupname secp256r1 \ + -sigalg ED25519 -signer issuer-ca -signerkeypass security \ + -storepass security -keypass security \ + -dname "CN=secp256r1, OU=eDeliveryAS4-2.0,OU=wss4j,O=apache,C=EU" \ + -validity 3650 + +keytool -genkeypair -keystore wss-ecdh.p12 -alias secp384r1 -keyalg EC -groupname secp384r1 \ + -sigalg ED25519 -signer issuer-ca -signerkeypass security \ + -storepass security -keypass security \ + -dname "CN=secp384r1, OU=eDeliveryAS4-2.0,OU=wss4j,O=apache,C=EU" \ + -validity 3650 + +keytool -genkeypair -keystore wss-ecdh.p12 -alias secp521r1 -keyalg EC -groupname secp521r1 \ + -sigalg ED25519 -signer issuer-ca -signerkeypass security \ + -storepass security -keypass security \ + -dname "CN=secp521r1, OU=eDeliveryAS4-2.0,OU=wss4j,O=apache,C=EU" \ + -validity 3650 + +===== + diff --git a/ws-security-common/src/test/resources/keys/wss-ecdh.p12 b/ws-security-common/src/test/resources/keys/wss-ecdh.p12 new file mode 100644 index 000000000..c9c271e5e Binary files /dev/null and b/ws-security-common/src/test/resources/keys/wss-ecdh.p12 differ diff --git a/ws-security-common/src/test/resources/keys/wss-eddsa.p12 b/ws-security-common/src/test/resources/keys/wss-eddsa.p12 index 3e02ef16e..624421816 100644 Binary files a/ws-security-common/src/test/resources/keys/wss-eddsa.p12 and b/ws-security-common/src/test/resources/keys/wss-eddsa.p12 differ diff --git a/ws-security-common/src/test/resources/wss-ecdh.properties b/ws-security-common/src/test/resources/wss-ecdh.properties new file mode 100644 index 000000000..d1a488f7a --- /dev/null +++ b/ws-security-common/src/test/resources/wss-ecdh.properties @@ -0,0 +1,4 @@ +org.apache.wss4j.crypto.provider=org.apache.wss4j.common.crypto.Merlin +org.apache.wss4j.crypto.merlin.keystore.type=PKCS12 +org.apache.wss4j.crypto.merlin.keystore.password=security +org.apache.wss4j.crypto.merlin.keystore.file=keys/wss-ecdh.p12 diff --git a/ws-security-dom/pom.xml b/ws-security-dom/pom.xml index a7840a400..bbc492824 100644 --- a/ws-security-dom/pom.xml +++ b/ws-security-dom/pom.xml @@ -16,7 +16,8 @@ specific language governing permissions and limitations under the License. --> -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> @@ -112,6 +113,11 @@ <artifactId>junit-jupiter-engine</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-params</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-reload4j</artifactId> diff --git a/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/EncryptionAction.java b/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/EncryptionAction.java index 2e61dd080..08de7af59 100644 --- a/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/EncryptionAction.java +++ b/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/EncryptionAction.java @@ -64,6 +64,9 @@ public class EncryptionAction implements Action { if (encryptionToken.getKeyTransportAlgorithm() != null) { wsEncrypt.setKeyEncAlgo(encryptionToken.getKeyTransportAlgorithm()); } + if (encryptionToken.getKeyAgreementMethodAlgorithm() != null) { + wsEncrypt.setKeyAgreementMethod(encryptionToken.getKeyAgreementMethodAlgorithm()); + } if (encryptionToken.getDigestAlgorithm() != null) { wsEncrypt.setDigestAlgorithm(encryptionToken.getDigestAlgorithm()); } @@ -77,7 +80,7 @@ public class EncryptionAction implements Action { wsEncrypt.setUserInfo(encryptionToken.getUser()); wsEncrypt.setUseThisCert(encryptionToken.getCertificate()); Crypto crypto = encryptionToken.getCrypto(); - boolean enableRevocation = Boolean.valueOf(handler.getStringOption(WSHandlerConstants.ENABLE_REVOCATION)); + boolean enableRevocation = Boolean.parseBoolean(handler.getStringOption(WSHandlerConstants.ENABLE_REVOCATION)); if (enableRevocation && crypto != null) { CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS); cryptoType.setAlias(encryptionToken.getUser()); @@ -112,7 +115,7 @@ public class EncryptionAction implements Action { wsEncrypt.setCustomEKKeyInfoElement(pwcb.getKeyInfoReference()); } - SecretKey symmetricKey = null; + SecretKey symmetricKey; if (ephemeralKey != null) { symmetricKey = KeyUtils.prepareSecretKey(wsEncrypt.getSymmetricEncAlgorithm(), ephemeralKey); } else { diff --git a/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/SignatureAction.java b/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/SignatureAction.java index 7c1b6abec..6184c1f2c 100644 --- a/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/SignatureAction.java +++ b/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/SignatureAction.java @@ -137,7 +137,7 @@ public class SignatureAction implements Action { } else if (WSConstants.WSSE_NS.equals(part.getNamespace()) && WSConstants.BINARY_TOKEN_LN.equals(part.getName())) { signBST = true; - } else if ("KeyInfo".equals(part.getName()) && WSConstants.SIG_NS.equals(part.getNamespace()) + } else if ("KeyInfo".equals(part.getName()) && WSConstants.SIG_NS.equals(part.getNamespace()) && part.getElement() == null) { // Special code to sign the KeyInfo part.setId(wsSign.getKeyInfoUri()); diff --git a/ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/RequestData.java b/ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/RequestData.java index 9f9528fe4..84bcb1717 100644 --- a/ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/RequestData.java +++ b/ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/RequestData.java @@ -395,7 +395,7 @@ public class RequestData { return validatorMap.get(qName); } } - if (wssConfig != null) { + if (wssConfig != null) { return wssConfig.getValidator(qName); } return null; diff --git a/ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/WSHandler.java b/ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/WSHandler.java index ecfaf7ddb..7869346c7 100644 --- a/ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/WSHandler.java +++ b/ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/WSHandler.java @@ -647,19 +647,24 @@ public abstract class WSHandler { algorithmSuite.addSignatureMethod(signatureAlgorithm); } String signatureDigestAlgorithm = getString(WSHandlerConstants.SIG_DIGEST_ALGO, mc); - if (signatureDigestAlgorithm != null && signatureDigestAlgorithm.length() != 0) { + if (signatureDigestAlgorithm != null && !signatureDigestAlgorithm.isEmpty()) { algorithmSuite.addDigestAlgorithm(signatureDigestAlgorithm); } String encrAlgorithm = getString(WSHandlerConstants.ENC_SYM_ALGO, mc); - if (encrAlgorithm != null && encrAlgorithm.length() != 0) { + if (encrAlgorithm != null && !encrAlgorithm.isEmpty()) { algorithmSuite.addEncryptionMethod(encrAlgorithm); } String transportAlgorithm = getString(WSHandlerConstants.ENC_KEY_TRANSPORT, mc); - if (transportAlgorithm != null && transportAlgorithm.length() != 0) { + if (transportAlgorithm != null && !transportAlgorithm.isEmpty()) { algorithmSuite.addKeyWrapAlgorithm(transportAlgorithm); } + String keyAgreementMethodAlgorithm = getString(WSHandlerConstants.ENC_KEY_AGREEMENT_METHOD, mc); + if (keyAgreementMethodAlgorithm != null && !keyAgreementMethodAlgorithm.isEmpty()) { + algorithmSuite.addKeyAgreementMethodAlgorithm(transportAlgorithm); + } + reqData.setAlgorithmSuite(algorithmSuite); } @@ -709,6 +714,10 @@ public abstract class WSHandler { getString(WSHandlerConstants.ENC_KEY_TRANSPORT, mc); actionToken.setKeyTransportAlgorithm(encKeyTransport); + String encKeyAgreementMethod = + getString(WSHandlerConstants.ENC_KEY_AGREEMENT_METHOD, mc); + actionToken.setKeyAgreementMethodAlgorithm(encKeyAgreementMethod); + String derivedKeyReference = getString(WSHandlerConstants.DERIVED_TOKEN_REFERENCE, mc); actionToken.setDerivedKeyTokenReference(derivedKeyReference); diff --git a/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecEncryptedKey.java b/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecEncryptedKey.java index 150437064..adda858c2 100644 --- a/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecEncryptedKey.java +++ b/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecEncryptedKey.java @@ -19,20 +19,12 @@ package org.apache.wss4j.dom.message; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.Key; -import java.security.NoSuchProviderException; -import java.security.Provider; -import java.security.PublicKey; +import java.security.*; import java.security.cert.X509Certificate; -import java.security.spec.MGF1ParameterSpec; - import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.SecretKey; import javax.crypto.spec.OAEPParameterSpec; -import javax.crypto.spec.PSource; import javax.xml.crypto.MarshalException; import javax.xml.crypto.dom.DOMStructure; import javax.xml.crypto.dsig.XMLSignatureFactory; @@ -53,7 +45,12 @@ import org.apache.wss4j.common.util.AttachmentUtils; import org.apache.wss4j.common.util.KeyUtils; import org.apache.wss4j.dom.WSConstants; import org.apache.wss4j.dom.util.WSSecurityUtil; -import org.apache.xml.security.algorithms.JCEMapper; +import org.apache.xml.security.encryption.XMLCipherUtil; +import org.apache.xml.security.encryption.XMLEncryptionException; +import org.apache.xml.security.encryption.keys.content.AgreementMethodImpl; +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.stax.impl.util.IDGenerator; import org.apache.xml.security.utils.Constants; import org.apache.xml.security.utils.XMLUtils; @@ -80,6 +77,13 @@ public class WSSecEncryptedKey extends WSSecBase { */ private String keyEncAlgo = WSConstants.KEYTRANSPORT_RSAOAEP; + /** + * Key agreement method algorithm used to encrypt the transport key. + * Example for ECDH-ES: http://www.w3.org/2009/xmlenc11#ECDH-ES + * + */ + private String keyAgreementMethod; + /** * Digest Algorithm to be used with RSA-OAEP. The default is SHA-1 (which is not * written out unless it is explicitly configured). @@ -209,8 +213,18 @@ public class WSSecEncryptedKey extends WSSecBase { remoteCert = certs[0]; } - createEncryptedKeyElement(remoteCert, crypto); - byte[] encryptedEphemeralKey = encryptSymmetricKey(remoteCert.getPublicKey(), symmetricKey); + Key kek; + KeyAgreementParameters dhSpec = null; + if (WSConstants.AGREEMENT_METHOD_ECDH_ES.equals(keyAgreementMethod)) { + // generate ephemeral keys the key must match receivers keys + dhSpec = buildKeyAgreementParameter(remoteCert.getPublicKey()); + kek = generateEncryptionKey(dhSpec); + } else { + kek = remoteCert.getPublicKey(); + } + + createEncryptedKeyElement(remoteCert, crypto, dhSpec); + byte[] encryptedEphemeralKey = encryptSymmetricKey(kek, symmetricKey); addCipherValueElement(encryptedEphemeralKey); } } @@ -240,9 +254,10 @@ public class WSSecEncryptedKey extends WSSecBase { * 3) Create and set up the SecurityTokenReference according to the keyIdentifier parameter * 4) Create the CipherValue element structure and insert the encrypted session key */ - protected void createEncryptedKeyElement(X509Certificate remoteCert, Crypto crypto) throws WSSecurityException { + protected void createEncryptedKeyElement(X509Certificate remoteCert, Crypto crypto, KeyAgreementParameters dhSpec) + throws WSSecurityException { encryptedKeyElement = createEncryptedKey(getDocument(), keyEncAlgo); - if (encKeyId == null || encKeyId.length() == 0) { + if (encKeyId == null || encKeyId.isEmpty()) { encKeyId = IDGenerator.generateID("EK-"); } encryptedKeyElement.setAttributeNS(null, "Id", encKeyId); @@ -324,7 +339,7 @@ public class WSSecEncryptedKey extends WSSecBase { refCustd.setValueType(customEKTokenValueType); } else if (WSConstants.WSS_SAML2_KI_VALUE_TYPE.equals(customEKTokenValueType)) { secToken.addTokenType(WSConstants.WSS_SAML2_TOKEN_TYPE); - } else if (WSConstants.WSS_ENC_KEY_VALUE_TYPE.equals(customEKTokenValueType)) { + } else if (WSConstants.WSS_ENC_KEY_VALUE_TYPE.equals(customEKTokenValueType)) { secToken.addTokenType(WSConstants.WSS_ENC_KEY_VALUE_TYPE); refCustd.setValueType(customEKTokenValueType); } else { @@ -358,7 +373,20 @@ public class WSSecEncryptedKey extends WSSecBase { keyInfoElement.setAttributeNS( WSConstants.XMLNS_NS, "xmlns:" + WSConstants.SIG_PREFIX, WSConstants.SIG_NS ); - keyInfoElement.appendChild(secToken.getElement()); + if (WSConstants.AGREEMENT_METHOD_ECDH_ES.equals(keyAgreementMethod)) { + try { + AgreementMethodImpl agreementMethod = new AgreementMethodImpl(getDocument(), dhSpec); + agreementMethod.getRecipientKeyInfo().addUnknownElement(secToken.getElement()); + Element agreementMethodElement = agreementMethod.getElement(); + keyInfoElement.appendChild(agreementMethodElement); + } catch (XMLSecurityException e) { + throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "unsupportedKeyId", + new Object[] {keyIdentifierType}); + } + + } else { + keyInfoElement.appendChild(secToken.getElement()); + } encryptedKeyElement.appendChild(keyInfoElement); } @@ -387,7 +415,7 @@ public class WSSecEncryptedKey extends WSSecBase { */ protected void createEncryptedKeyElement(Key key) throws WSSecurityException { encryptedKeyElement = createEncryptedKey(getDocument(), keyEncAlgo); - if (encKeyId == null || encKeyId.length() == 0) { + if (encKeyId == null || encKeyId.isEmpty()) { encKeyId = IDGenerator.generateID("EK-"); } encryptedKeyElement.setAttributeNS(null, "Id", encKeyId); @@ -426,7 +454,7 @@ public class WSSecEncryptedKey extends WSSecBase { refCustd.setValueType(customEKTokenValueType); } else if (WSConstants.WSS_SAML2_KI_VALUE_TYPE.equals(customEKTokenValueType)) { secToken.addTokenType(WSConstants.WSS_SAML2_TOKEN_TYPE); - } else if (WSConstants.WSS_ENC_KEY_VALUE_TYPE.equals(customEKTokenValueType)) { + } else if (WSConstants.WSS_ENC_KEY_VALUE_TYPE.equals(customEKTokenValueType)) { secToken.addTokenType(WSConstants.WSS_ENC_KEY_VALUE_TYPE); refCustd.setValueType(customEKTokenValueType); } else { @@ -505,35 +533,60 @@ public class WSSecEncryptedKey extends WSSecBase { } } - protected byte[] encryptSymmetricKey(PublicKey encryptingKey, SecretKey keyToBeEncrypted) + /** + * Method builds the KeyAgreementParameterSpec for the ECDH-ES Key Agreement Method using + * the recipient's public key and preconfigured values: keyEncAlgo, digestAlgo and keyAgreementMethod + * + * @param recipientPublicKey the recipient's public key + * @return KeyAgreementParameterSpec the {@link java.security.spec.AlgorithmParameterSpec} for generating the + * key for encrypting transport key and generating XML elements. + * + * @throws WSSecurityException if the KeyAgreementParameterSpec cannot be created + */ + private KeyAgreementParameters buildKeyAgreementParameter(PublicKey recipientPublicKey) + throws WSSecurityException { + KeyAgreementParameters dhSpec; + try { + int keyBitLength = org.apache.xml.security.utils.KeyUtils.getAESKeyBitSizeForWrapAlgorithm(keyEncAlgo); + KeyDerivationParameters kdf = XMLCipherUtil.constructConcatKeyDerivationParameter(keyBitLength, digestAlgo); + KeyPair dhKeyPair = org.apache.xml.security.utils.KeyUtils.generateEphemeralDHKeyPair(recipientPublicKey, null); + dhSpec = XMLCipherUtil.constructAgreementParameters(keyAgreementMethod, + KeyAgreementParameters.ActorType.ORIGINATOR, kdf, null, recipientPublicKey); + dhSpec.setOriginatorKeyPair(dhKeyPair); + } catch (XMLEncryptionException e) { + throw new WSSecurityException( + WSSecurityException.ErrorCode.FAILED_ENCRYPTION, e + ); + } + return dhSpec; + } + + /** + * Method generates the key for encrypting the transport key using the KeyAgreementParameterSpec + * + * @param keyAgreementParameter the {@link KeyAgreementParameters} for generating the secret key + * @return SecretKey the secret key for encrypting the transport key + * @throws WSSecurityException if the secret key cannot be generated + */ + private SecretKey generateEncryptionKey(KeyAgreementParameters keyAgreementParameter) throws WSSecurityException { + try { + // derive the key for encryption of the transport key + return org.apache.xml.security.utils.KeyUtils.aesWrapKeyWithDHGeneratedKey(keyAgreementParameter); + } catch (XMLEncryptionException e) { + throw new WSSecurityException( + WSSecurityException.ErrorCode.FAILED_ENCRYPTION, e + ); + } + } + + private byte[] encryptSymmetricKey(Key encryptingKey, SecretKey keyToBeEncrypted) throws WSSecurityException { Cipher cipher = KeyUtils.getCipherInstance(keyEncAlgo); try { OAEPParameterSpec oaepParameterSpec = null; if (WSConstants.KEYTRANSPORT_RSAOAEP.equals(keyEncAlgo) || WSConstants.KEYTRANSPORT_RSAOAEP_XENC11.equals(keyEncAlgo)) { - String jceDigestAlgorithm = "SHA-1"; - if (digestAlgo != null) { - jceDigestAlgorithm = JCEMapper.translateURItoJCEID(digestAlgo); - } - - MGF1ParameterSpec mgf1ParameterSpec = new MGF1ParameterSpec("SHA-1"); - if (WSConstants.KEYTRANSPORT_RSAOAEP_XENC11.equals(keyEncAlgo)) { - if (WSConstants.MGF_SHA224.equals(mgfAlgo)) { - mgf1ParameterSpec = new MGF1ParameterSpec("SHA-224"); - } else if (WSConstants.MGF_SHA256.equals(mgfAlgo)) { - mgf1ParameterSpec = new MGF1ParameterSpec("SHA-256"); - } else if (WSConstants.MGF_SHA384.equals(mgfAlgo)) { - mgf1ParameterSpec = new MGF1ParameterSpec("SHA-384"); - } else if (WSConstants.MGF_SHA512.equals(mgfAlgo)) { - mgf1ParameterSpec = new MGF1ParameterSpec("SHA-512"); - } - } - - oaepParameterSpec = - new OAEPParameterSpec( - jceDigestAlgorithm, "MGF1", mgf1ParameterSpec, PSource.PSpecified.DEFAULT - ); + oaepParameterSpec = XMLCipherUtil.constructOAEPParameters(keyEncAlgo, digestAlgo, mgfAlgo, null); } if (oaepParameterSpec == null) { cipher.init(Cipher.WRAP_MODE, encryptingKey); @@ -732,8 +785,16 @@ public class WSSecEncryptedKey extends WSSecBase { return keyEncAlgo; } + public String getKeyAgreementMethod() { + return keyAgreementMethod; + } + + public void setKeyAgreementMethod(String keyAgreementMethod) { + this.keyAgreementMethod = keyAgreementMethod; + } + /** - * Get the id of the BSt generated during <code>prepare()</code>. + * Get the id of the BSt generated during <code>prepare()</code>. * * @return Returns the the value of wsu:Id attribute of the * BinaruSecurityToken element. diff --git a/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecSignature.java b/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecSignature.java index 74fbd1b4d..e7384610d 100644 --- a/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecSignature.java +++ b/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecSignature.java @@ -761,7 +761,7 @@ public class WSSecSignature extends WSSecSignatureBase { } /** - * Get the id of the BST generated during <code>prepare()</code>. + * Get the id of the BST generated during <code>prepare()</code>. * * @return Returns the the value of wsu:Id attribute of the * BinaruSecurityToken element. @@ -927,7 +927,7 @@ public class WSSecSignature extends WSSecSignatureBase { decoder.expect(decoder.TYPE_SEQUENCE); decoder.getLength(); decoder.expect(decoder.TYPE_OBJECT_IDENTIFIER); - int size = decoder.getLength(); + int size = decoder.getLength(); if (size != 3) { LOG.debug("Invalid ECDSA Public key OID byte size: [{}]", size); throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, "invalidCert"); diff --git a/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/token/SignatureConfirmation.java b/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/token/SignatureConfirmation.java index 34b57c27c..faebfce67 100644 --- a/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/token/SignatureConfirmation.java +++ b/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/token/SignatureConfirmation.java @@ -74,7 +74,7 @@ public class SignatureConfirmation { element = doc.createElementNS( WSConstants.WSSE11_NS, - WSConstants.WSSE11_PREFIX + ":" + WSConstants.SIGNATURE_CONFIRMATION_LN + WSConstants.WSSE11_PREFIX + ":" + WSConstants.SIGNATURE_CONFIRMATION_LN ); XMLUtils.setNamespace(element, WSConstants.WSSE11_NS, WSConstants.WSSE11_PREFIX); if (signVal != null) { diff --git a/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/CertificateResult.java b/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/CertificateResult.java new file mode 100644 index 000000000..0b62bf79e --- /dev/null +++ b/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/CertificateResult.java @@ -0,0 +1,106 @@ +/** + * 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.wss4j.dom.processor; + +import org.apache.wss4j.common.ext.WSSecurityException; +import org.apache.wss4j.dom.str.STRParser; + +import java.security.PublicKey; +import java.security.cert.X509Certificate; + +/** + * Class contains the result of locating public key using the KeyInfoType element. + * The result is either a PublicKey or/and an X509Certificate chain with the STRParser.REFERENCE_TYPE. + */ +public class CertificateResult { + + /** + * CertificateResult builder class. + */ + static final class Builder { + private X509Certificate[] certs; + private PublicKey publicKey; + private STRParser.REFERENCE_TYPE referenceType; + + private Builder() { + } + + public static Builder create() { + return new Builder(); + } + + public Builder certificates(X509Certificate[] certs) { + this.certs = certs; + return this; + } + + public Builder publicKey(PublicKey publicKey) { + this.publicKey = publicKey; + return this; + } + + public Builder certificatesReferenceType(STRParser.REFERENCE_TYPE referenceType) { + this.referenceType = referenceType; + return this; + } + + /** + * Method to build the CertificateResult object. + * + * @return the CertificateResult object + * @throws WSSecurityException if the result is empty. + */ + public CertificateResult build() throws WSSecurityException { + if (publicKey == null && (certs == null || certs.length < 1 || certs[0] == null)) { + throw new WSSecurityException( + WSSecurityException.ErrorCode.FAILURE, + "noCertsFound", + new Object[] {"decryption (KeyId)"}); + } + if (certs != null && certs.length > 0) { + publicKey = certs[0].getPublicKey(); + } + + return new CertificateResult(certs, publicKey, referenceType); + } + } + + private final X509Certificate[] certs; + private final PublicKey publicKey; + private final STRParser.REFERENCE_TYPE referenceType; + + protected CertificateResult(X509Certificate[] certs, PublicKey publicKey, STRParser.REFERENCE_TYPE referenceType) { + this.certs = certs; + this.publicKey = publicKey; + this.referenceType = referenceType; + } + + public X509Certificate[] getCerts() { + return certs; + } + + public PublicKey getPublicKey() { + return publicKey; + } + + public STRParser.REFERENCE_TYPE getCertificatesReferenceType() { + return referenceType; + } +} diff --git a/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/EncryptedKeyProcessor.java b/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/EncryptedKeyProcessor.java index 93c0d611b..06622e319 100644 --- a/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/EncryptedKeyProcessor.java +++ b/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/EncryptedKeyProcessor.java @@ -22,13 +22,8 @@ package org.apache.wss4j.dom.processor; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.PrivateKey; -import java.security.Provider; -import java.security.PublicKey; +import java.security.*; import java.security.cert.X509Certificate; -import java.security.spec.MGF1ParameterSpec; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -37,9 +32,15 @@ import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.OAEPParameterSpec; -import javax.crypto.spec.PSource; import javax.xml.crypto.dsig.XMLSignatureFactory; +import org.apache.xml.security.encryption.AgreementMethod; +import org.apache.xml.security.encryption.XMLCipherUtil; +import org.apache.xml.security.encryption.keys.RecipientKeyInfo; +import org.apache.xml.security.encryption.keys.content.AgreementMethodImpl; +import org.apache.xml.security.encryption.params.KeyAgreementParameters; +import org.apache.xml.security.exceptions.XMLSecurityException; +import org.apache.xml.security.utils.EncryptionConstants; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -99,7 +100,7 @@ public class EncryptedKeyProcessor implements Processor { // See if this key has already been processed. If so then just return the result String id = elem.getAttributeNS(null, "Id"); - if (id.length() != 0) { + if (!id.isEmpty()) { WSSecurityEngineResult result = data.getWsDocInfo().getResult(id); if (result != null && WSConstants.ENCR == (Integer)result.get(WSSecurityEngineResult.TAG_ACTION) @@ -131,8 +132,10 @@ public class EncryptedKeyProcessor implements Processor { throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY); } + Element keyInfoChildElement = getKeyInfoChildElement(elem, data); + boolean isDHKeyWrap = isDiffieHellmanKeyWrap(keyInfoChildElement); // Check BSP Compliance - checkBSPCompliance(elem, encryptedKeyTransportMethod, data.getBSPEnforcer()); + checkBSPCompliance(elem, encryptedKeyTransportMethod, isDHKeyWrap, data.getBSPEnforcer()); // // Now lookup CipherValue. @@ -142,56 +145,28 @@ public class EncryptedKeyProcessor implements Processor { throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, "noCipher"); } - Element keyInfoChildElement = getKeyInfoChildElement(elem, data); - X509Certificate[] certs = null; STRParser.REFERENCE_TYPE referenceType = null; PublicKey publicKey = null; boolean symmetricKeyWrap = isSymmetricKeyWrap(encryptedKeyTransportMethod); - if (!symmetricKeyWrap) { - if (SecurityTokenReference.SECURITY_TOKEN_REFERENCE.equals(keyInfoChildElement.getLocalName()) - && WSConstants.WSSE_NS.equals(keyInfoChildElement.getNamespaceURI())) { - STRParserParameters parameters = new STRParserParameters(); - parameters.setData(data); - parameters.setStrElement(keyInfoChildElement); - - STRParser strParser = new EncryptedKeySTRParser(); - STRParserResult parserResult = strParser.parseSecurityTokenReference(parameters); - - certs = parserResult.getCertificates(); - publicKey = parserResult.getPublicKey(); - referenceType = parserResult.getCertificatesReferenceType(); - } else { - certs = getCertificatesFromX509Data(keyInfoChildElement, data); - if (certs == null || certs.length == 0) { - XMLSignatureFactory signatureFactory; - if (provider == null) { - // Try to install the Santuario Provider - fall back to the JDK provider if this does - // not work - try { - signatureFactory = XMLSignatureFactory.getInstance("DOM", "ApacheXMLDSig"); - } catch (NoSuchProviderException ex) { - signatureFactory = XMLSignatureFactory.getInstance("DOM"); - } - } else { - signatureFactory = XMLSignatureFactory.getInstance("DOM", provider); - } - - publicKey = X509Util.parseKeyValue((Element)keyInfoChildElement.getParentNode(), - signatureFactory); - } - } - - if (publicKey == null && (certs == null || certs.length < 1 || certs[0] == null)) { + AgreementMethod agreementMethod = null; + if (isDHKeyWrap) { + // get key agreement method value + agreementMethod = getAgreementMethodFromElement(keyInfoChildElement); + // get the recipient key info element + keyInfoChildElement = getRecipientKeyInfoChildElement(agreementMethod); + if (keyInfoChildElement == null) { throw new WSSecurityException( - WSSecurityException.ErrorCode.FAILURE, - "noCertsFound", - new Object[] {"decryption (KeyId)"}); - } - if (certs != null && certs.length > 0) { - publicKey = certs[0].getPublicKey(); + WSSecurityException.ErrorCode.INVALID_SECURITY, "noRecipientSecTokRef" + ); } } + if (!symmetricKeyWrap || isDHKeyWrap) { + CertificateResult certificateResult = getPublicKey(keyInfoChildElement, data); + certs = certificateResult.getCerts(); + publicKey = certificateResult.getPublicKey(); + referenceType = certificateResult.getCertificatesReferenceType(); + } // Check for compliance against the defined AlgorithmSuite if (algorithmSuite != null) { @@ -204,6 +179,11 @@ public class EncryptedKeyProcessor implements Processor { algorithmSuiteValidator.checkEncryptionKeyWrapAlgorithm( encryptedKeyTransportMethod ); + if (agreementMethod != null) { + algorithmSuiteValidator.checkKeyAgreementMethodAlgorithm( + agreementMethod.getAlgorithm() + ); + } } byte[] encryptedEphemeralKey = null; @@ -219,7 +199,11 @@ public class EncryptedKeyProcessor implements Processor { encryptedEphemeralKey = EncryptionUtils.getDecodedBase64EncodedData(xencCipherValue); } - if (symmetricKeyWrap) { + if (isDHKeyWrap) { + PrivateKey privateKey = getPrivateKey(data, certs, publicKey); + decryptedBytes = getDiffieHellmanDecryptedBytes(data, agreementMethod, + encryptedKeyTransportMethod, encryptedEphemeralKey, privateKey); + } else if (symmetricKeyWrap) { decryptedBytes = getSymmetricDecryptedBytes(data, data.getWsDocInfo(), keyInfoChildElement, refList); } else { PrivateKey privateKey = getPrivateKey(data, certs, publicKey); @@ -257,6 +241,54 @@ public class EncryptedKeyProcessor implements Processor { return Collections.singletonList(result); } + /** + * Resolve the KeyInfoType child element to locate the public key (with the X509Certificate chain if given ) + * to use to decrypt the EncryptedKey. + * + * @param keyValueElement The element to get the child element from + * @param data The RequestData context + * @return The CertificateResult object containing the public key and optionally X509Certificate chain + * @throws WSSecurityException an error occurred when trying to resolve the key info + */ + private CertificateResult getPublicKey(Element keyValueElement, RequestData data) throws WSSecurityException { + CertificateResult.Builder builder = CertificateResult.Builder.create(); + + if (SecurityTokenReference.SECURITY_TOKEN_REFERENCE.equals(keyValueElement.getLocalName()) + && WSConstants.WSSE_NS.equals(keyValueElement.getNamespaceURI())) { + STRParserParameters parameters = new STRParserParameters(); + parameters.setData(data); + parameters.setStrElement(keyValueElement); + + STRParser strParser = new EncryptedKeySTRParser(); + STRParserResult result = strParser.parseSecurityTokenReference(parameters); + builder.certificates(result.getCertificates()); + builder.publicKey(result.getPublicKey()); + builder.certificatesReferenceType(result.getCertificatesReferenceType()); + } else { + X509Certificate[] certs = getCertificatesFromX509Data(keyValueElement, data); + builder.certificates(certs); + if (certs == null || certs.length == 0) { + XMLSignatureFactory signatureFactory; + if (provider == null) { + // Try to install the Santuario Provider - fall back to the JDK provider if this does + // not work + try { + signatureFactory = XMLSignatureFactory.getInstance("DOM", "ApacheXMLDSig"); + } catch (NoSuchProviderException ex) { + signatureFactory = XMLSignatureFactory.getInstance("DOM"); + } + } else { + signatureFactory = XMLSignatureFactory.getInstance("DOM", provider); + } + + PublicKey publicKey = X509Util.parseKeyValue((Element) keyValueElement.getParentNode(), + signatureFactory); + builder.publicKey(publicKey); + } + } + return builder.build(); + } + private PrivateKey getPrivateKey( RequestData data, X509Certificate[] certs, PublicKey publicKey ) throws WSSecurityException { @@ -307,35 +339,10 @@ public class EncryptedKeyProcessor implements Processor { || WSConstants.KEYTRANSPORT_RSAOAEP_XENC11.equals(encryptedKeyTransportMethod)) { // Get the DigestMethod if it exists String digestAlgorithm = EncryptionUtils.getDigestAlgorithm(encryptedKeyElement); - String jceDigestAlgorithm = "SHA-1"; - if (digestAlgorithm != null && digestAlgorithm.length() != 0) { - jceDigestAlgorithm = JCEMapper.translateURItoJCEID(digestAlgorithm); - } - - MGF1ParameterSpec mgfParameterSpec = new MGF1ParameterSpec("SHA-1"); - if (WSConstants.KEYTRANSPORT_RSAOAEP_XENC11.equals(encryptedKeyTransportMethod)) { - String mgfAlgorithm = EncryptionUtils.getMGFAlgorithm(encryptedKeyElement); - if (WSConstants.MGF_SHA224.equals(mgfAlgorithm)) { - mgfParameterSpec = new MGF1ParameterSpec("SHA-224"); - } else if (WSConstants.MGF_SHA256.equals(mgfAlgorithm)) { - mgfParameterSpec = new MGF1ParameterSpec("SHA-256"); - } else if (WSConstants.MGF_SHA384.equals(mgfAlgorithm)) { - mgfParameterSpec = new MGF1ParameterSpec("SHA-384"); - } else if (WSConstants.MGF_SHA512.equals(mgfAlgorithm)) { - mgfParameterSpec = new MGF1ParameterSpec("SHA-512"); - } - } - - PSource.PSpecified pSource = PSource.PSpecified.DEFAULT; + String mgfAlgorithm = EncryptionUtils.getMGFAlgorithm(encryptedKeyElement); byte[] pSourceBytes = EncryptionUtils.getPSource(encryptedKeyElement); - if (pSourceBytes != null && pSourceBytes.length > 0) { - pSource = new PSource.PSpecified(pSourceBytes); - } - - oaepParameterSpec = - new OAEPParameterSpec( - jceDigestAlgorithm, "MGF1", mgfParameterSpec, pSource - ); + oaepParameterSpec = XMLCipherUtil.constructOAEPParameters(encryptedKeyTransportMethod, + digestAlgorithm, mgfAlgorithm, pSourceBytes); } if (oaepParameterSpec == null) { @@ -357,6 +364,111 @@ public class EncryptedKeyProcessor implements Processor { } } + /** + * Method decrypts encryptedEphemeralKey using Key Agreement algorithm to derive symmetric key + * for decryption of the key. + * + * @param data RequestData context + * @param agreementMethod AgreementMethod element + * @param encryptedKeyTransportMethod Algorithm used to encrypt the key + * @param encryptedEphemeralKey Encrypted ephemeral/transport key + * @param privateKey Private key of the recipient + * @return Decrypted bytes of the ephemeral/transport key + * @throws WSSecurityException if the key decryption fails + */ + private static byte[] getDiffieHellmanDecryptedBytes( + RequestData data, + AgreementMethod agreementMethod, + String encryptedKeyTransportMethod, + byte[] encryptedEphemeralKey, + PrivateKey privateKey + ) throws WSSecurityException { + + SecretKey kek; + try { + KeyAgreementParameters parameterSpec = XMLCipherUtil.constructRecipientKeyAgreementParameters( + encryptedKeyTransportMethod, agreementMethod, privateKey); + + kek = org.apache.xml.security.utils.KeyUtils.aesWrapKeyWithDHGeneratedKey(parameterSpec); + } catch (XMLSecurityException ex) { + LOG.debug("Error occurred while resolving the Diffie Hellman key: " + ex.getMessage()); + throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK, ex); + } + + String cryptoProvider = data.getDecCrypto().getCryptoProvider(); + Cipher cipher = KeyUtils.getCipherInstance(encryptedKeyTransportMethod, cryptoProvider); + + try { + cipher.init(Cipher.UNWRAP_MODE, kek); + String keyAlgorithm = JCEMapper.translateURItoJCEID(encryptedKeyTransportMethod); + return cipher.unwrap(encryptedEphemeralKey, keyAlgorithm, Cipher.SECRET_KEY).getEncoded(); + } catch (InvalidKeyException | NoSuchAlgorithmException ex) { + throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK, ex); + } + } + + /** + * if keyInfo element contains AgreementMethod element then check if it is supported EC Diffie-Hellman key agreement algorithm + * + * @param keyInfoChildElement The KeyInfo child element + * @return true if AgreementMethod element is present and DH algorithm supported and false if AgreementMethod element is not present + * @throws WSSecurityException if AgreementMethod element is present but DH algorithm is not supported + */ + private boolean isDiffieHellmanKeyWrap(Element keyInfoChildElement) throws WSSecurityException { + if (EncryptionConstants._TAG_AGREEMENTMETHOD.equals(keyInfoChildElement.getLocalName()) + && WSConstants.ENC_NS.equals(keyInfoChildElement.getNamespaceURI())) { + String algorithmURI = keyInfoChildElement.getAttributeNS(null, "Algorithm"); + // Only ECDH_ES is supported for AgreementMethod + if (!WSConstants.AGREEMENT_METHOD_ECDH_ES.equals(algorithmURI)) { + throw new WSSecurityException( + WSSecurityException.ErrorCode.UNSUPPORTED_ALGORITHM, + "unknownAlgorithm", new Object[]{algorithmURI}); + } + return true; + } + return false; + } + + /** + * Parse keyInfo content to AgreementMethod object. + * + * @param keyInfoChildElement The KeyInfo child element containing AgreementMethod data. + * @return the {@link AgreementMethod} object. + * @throws WSSecurityException if AgreementMethod element is invalid. + */ + private AgreementMethod getAgreementMethodFromElement(Element keyInfoChildElement) throws WSSecurityException { + try { + return new AgreementMethodImpl(keyInfoChildElement); + } catch (XMLSecurityException ex) { + throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, ex); + } + } + + /** + * Get the RecipientKeyInfo child element from the AgreementMethod element. + * + * @param agreementMethod The AgreementMethod element + * @return the RecipientKeyInfo child element which contains the recipient's public key. + * @throws WSSecurityException if the agreementMethod is null or RecipientKeyInfo element can not be retrieved. + */ + private Element getRecipientKeyInfoChildElement(AgreementMethod agreementMethod) throws WSSecurityException { + if (agreementMethod == null) { + throw new WSSecurityException( + WSSecurityException.ErrorCode.INVALID_SECURITY, "noAgreementMethod" + ); + } + try { + RecipientKeyInfo recipientKeyInfo = agreementMethod.getRecipientKeyInfo(); + if (recipientKeyInfo == null) { + throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, "noRecipientKeyInfo"); + } + Element receiverKeyInfoElement = recipientKeyInfo.getElement(); + return getFirstElement(receiverKeyInfoElement); + } catch (XMLSecurityException ex) { + throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, ex); + } + } + private static boolean isSymmetricKeyWrap(String transportAlgorithm) { return XMLCipher.AES_128_KeyWrap.equals(transportAlgorithm) || XMLCipher.AES_192_KeyWrap.equals(transportAlgorithm) @@ -602,34 +714,44 @@ public class EncryptedKeyProcessor implements Processor { /** * A method to check that the EncryptedKey is compliant with the BSP spec. - * @throws WSSecurityException + * @throws WSSecurityException if the EncryptedKey is not BSP compliant */ private void checkBSPCompliance( - Element elem, String encAlgo, BSPEnforcer bspEnforcer + Element elem, String encAlgo, + boolean useKeyWrap, + BSPEnforcer bspEnforcer ) throws WSSecurityException { String attribute = elem.getAttributeNS(null, "Type"); - if (attribute != null && attribute.length() != 0) { + if (attribute != null && !attribute.isEmpty()) { bspEnforcer.handleBSPRule(BSPRule.R3209); } attribute = elem.getAttributeNS(null, "MimeType"); - if (attribute != null && attribute.length() != 0) { + if (attribute != null && !attribute.isEmpty()) { bspEnforcer.handleBSPRule(BSPRule.R5622); } attribute = elem.getAttributeNS(null, "Encoding"); - if (attribute != null && attribute.length() != 0) { + if (attribute != null && !attribute.isEmpty()) { bspEnforcer.handleBSPRule(BSPRule.R5623); } attribute = elem.getAttributeNS(null, "Recipient"); - if (attribute != null && attribute.length() != 0) { + if (attribute != null && !attribute.isEmpty()) { bspEnforcer.handleBSPRule(BSPRule.R5602); } - // EncryptionAlgorithm must be RSA15, or RSAOEP. - if (!(WSConstants.KEYTRANSPORT_RSA15.equals(encAlgo) - || WSConstants.KEYTRANSPORT_RSAOAEP.equals(encAlgo) - || WSConstants.KEYTRANSPORT_RSAOAEP_XENC11.equals(encAlgo))) { - bspEnforcer.handleBSPRule(BSPRule.R5621); + if (useKeyWrap) { + if (!(WSConstants.KEYWRAP_AES128.equals(encAlgo) + || WSConstants.KEYWRAP_AES192.equals(encAlgo) + || WSConstants.KEYWRAP_AES256.equals(encAlgo) + || WSConstants.KEYWRAP_TRIPLEDES.equals(encAlgo))) { + bspEnforcer.handleBSPRule(BSPRule.R5625); + } + } else { + // EncryptionAlgorithm must be RSA15, or RSAOEP. + if (!(WSConstants.KEYTRANSPORT_RSA15.equals(encAlgo) + || WSConstants.KEYTRANSPORT_RSAOAEP.equals(encAlgo) + || WSConstants.KEYTRANSPORT_RSAOAEP_XENC11.equals(encAlgo))) { + bspEnforcer.handleBSPRule(BSPRule.R5621); + } } } - } diff --git a/ws-security-dom/src/main/java/org/apache/wss4j/dom/saml/DOMSAMLUtil.java b/ws-security-dom/src/main/java/org/apache/wss4j/dom/saml/DOMSAMLUtil.java index 68be6eabd..9ebaadad8 100644 --- a/ws-security-dom/src/main/java/org/apache/wss4j/dom/saml/DOMSAMLUtil.java +++ b/ws-security-dom/src/main/java/org/apache/wss4j/dom/saml/DOMSAMLUtil.java @@ -41,7 +41,7 @@ import org.w3c.dom.Element; /** * Some SAML Utility methods only for use in the DOM code. */ -public final class DOMSAMLUtil { +public final class DOMSAMLUtil { private static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(DOMSAMLUtil.class); diff --git a/ws-security-dom/src/test/java/org/apache/wss4j/dom/common/KeystoreCallbackHandler.java b/ws-security-dom/src/test/java/org/apache/wss4j/dom/common/KeystoreCallbackHandler.java index 29b1aa0cc..252952ed6 100644 --- a/ws-security-dom/src/test/java/org/apache/wss4j/dom/common/KeystoreCallbackHandler.java +++ b/ws-security-dom/src/test/java/org/apache/wss4j/dom/common/KeystoreCallbackHandler.java @@ -35,7 +35,7 @@ import java.util.Map; */ public class KeystoreCallbackHandler implements CallbackHandler { - private Map<String, String> users = new HashMap<>(); + private final Map<String, String> users = new HashMap<>(); public KeystoreCallbackHandler() { users.put("wss86", "security"); @@ -43,6 +43,11 @@ public class KeystoreCallbackHandler implements CallbackHandler { users.put("wss40rev", "security"); users.put("16c73ab6-b892-458f-abf5-2f875f74882e", "security"); users.put("regexp", "security"); + users.put("x448", "security"); + users.put("x25519", "security"); + users.put("secp256r1", "security"); + users.put("secp384r1", "security"); + users.put("secp521r1", "security"); } public void handle(Callback[] callbacks) diff --git a/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/EncryptionTest.java b/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/EncryptionTest.java index 4c23720bb..2c65b2116 100644 --- a/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/EncryptionTest.java +++ b/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/EncryptionTest.java @@ -19,6 +19,7 @@ package org.apache.wss4j.dom.message; +import java.security.Security; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collections; @@ -28,15 +29,13 @@ import javax.crypto.SecretKey; import javax.security.auth.callback.CallbackHandler; import org.apache.wss4j.common.WSEncryptionPart; +import org.apache.wss4j.common.WSS4JConstants; import org.apache.wss4j.common.bsp.BSPRule; import org.apache.wss4j.common.crypto.Crypto; import org.apache.wss4j.common.crypto.CryptoFactory; import org.apache.wss4j.common.crypto.CryptoType; import org.apache.wss4j.common.ext.WSSecurityException; -import org.apache.wss4j.common.util.DOM2Writer; -import org.apache.wss4j.common.util.KeyUtils; -import org.apache.wss4j.common.util.SOAPUtil; -import org.apache.wss4j.common.util.XMLUtils; +import org.apache.wss4j.common.util.*; import org.apache.wss4j.dom.SOAPConstants; import org.apache.wss4j.dom.WSConstants; import org.apache.wss4j.dom.WSDataRef; @@ -54,8 +53,11 @@ import org.apache.wss4j.dom.handler.WSHandlerResult; import org.apache.wss4j.dom.str.STRParser.REFERENCE_TYPE; import org.apache.wss4j.dom.util.WSSecurityUtil; +import org.apache.xml.security.utils.EncryptionConstants; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -313,6 +315,70 @@ public class EncryptionTest { verify(encryptedEncryptedDoc, encCrypto, keystoreCallbackHandler); } + /** + * Test that encrypt and decrypt a WS-Security envelope. + * This test uses the ECDSA-ES algorithm to (wrap) the symmetric key. + * <p/> + * + * @throws Exception Thrown when there is any problem in signing or verification + */ + @ParameterizedTest + @CsvSource({"xdh, X25519", + "xdh, X448", + "ec, secp256r1", + "ec, secp384r1", + "ec, secp521r1", + }) + public void testEncryptionDecryptionECDSA_ES(String algorithm, String certAlias) throws Exception { + try { + if (!JDKTestUtils.isAlgorithmSupportedByJDK(algorithm)) { + LOG.info("Add AuxiliaryProvider to execute test with algorithm [{}] and cert alias [{}]", algorithm, certAlias); + Security.addProvider(JDKTestUtils.getAuxiliaryProvider()); + } + Crypto encCrypto = CryptoFactory.getInstance("wss-ecdh.properties"); + + Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG); + WSSecHeader secHeader = new WSSecHeader(doc); + secHeader.insertSecurityHeader(); + + WSSecEncrypt builder = new WSSecEncrypt(secHeader); + builder.setUserInfo(certAlias); + builder.setKeyEncAlgo(WSConstants.KEYWRAP_AES128); + builder.setKeyAgreementMethod(WSConstants.AGREEMENT_METHOD_ECDH_ES); + builder.setDigestAlgorithm(WSS4JConstants.SHA256); + builder.setKeyIdentifierType(WSConstants.SKI_KEY_IDENTIFIER); + + LOG.info("Before Encryption ..."); + KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128_GCM); + SecretKey symmetricKey = keyGen.generateKey(); + + Document encryptedDoc = builder.build(encCrypto, symmetricKey); + LOG.info("After Encryption ...."); + + String outputString = + XMLUtils.prettyDocumentToString(encryptedDoc); + if (LOG.isDebugEnabled()) { + LOG.debug("Encrypted message:"); + LOG.debug(outputString); + } + assertFalse(outputString.contains("counter_port_type")); + // Check for algorithms and agreement method element + assertTrue(outputString.contains(EncryptionConstants._TAG_AGREEMENTMETHOD)); + assertTrue(outputString.contains(WSConstants.KEYWRAP_AES128)); + assertTrue(outputString.contains(WSConstants.AGREEMENT_METHOD_ECDH_ES)); + + WSSecurityEngine newEngine = new WSSecurityEngine(); + WSHandlerResult results = + newEngine.processSecurityHeader(encryptedDoc, null, keystoreCallbackHandler, encCrypto); + + WSSecurityEngineResult actionResult = + results.getActionResults().get(WSConstants.ENCR).get(0); + assertNotNull(actionResult); + } finally { + Security.removeProvider(JDKTestUtils.getAuxiliaryProvider().getName()); + } + } + /** * Test that encrypts and decrypts a WS-Security envelope. * The test uses the ThumbprintSHA1 key identifier type. @@ -810,4 +876,4 @@ public class EncryptionTest { return results; } -} \ No newline at end of file +} diff --git a/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/SignatureCertTest.java b/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/SignatureCertTest.java index 81c1777c3..d4950488e 100644 --- a/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/SignatureCertTest.java +++ b/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/SignatureCertTest.java @@ -38,6 +38,8 @@ import org.apache.wss4j.dom.handler.WSHandlerResult; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.w3c.dom.Document; import javax.security.auth.x500.X500Principal; @@ -349,10 +351,14 @@ public class SignatureCertTest { } /** - * The Ed25519 KeyValue test. + * The EdDSA KeyValue test. */ - @Test - public void testED25519SignatureDirectReference() throws Exception { + @ParameterizedTest + @CsvSource({ + "ed25519, 'Algorithm=\"http://www.w3.org/2021/04/xmldsig-more#eddsa-ed25519\"', 'CN=ed25519, OU=eDeliveryAS4-2.0, OU=wss4j, O=apache, C=EU'", + "ed448, 'Algorithm=\"http://www.w3.org/2021/04/xmldsig-more#eddsa-ed448\"', 'CN=ed448, OU=eDeliveryAS4-2.0, OU=wss4j, O=apache, C=EU'", + }) + public void testEdDSASignatureDirectReference(String alias, String algorithm, X500Principal certSubjectDN) throws Exception { try { // not needed after JDK 16 if (!isJDK16up) { @@ -366,7 +372,7 @@ public class SignatureCertTest { Crypto ed_crypto = CryptoFactory.getInstance("wss-eddsa.properties"); WSSecSignature builder = new WSSecSignature(secHeader); - builder.setUserInfo("ed25519", "security"); + builder.setUserInfo(alias, "security"); builder.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE); Document signedDoc = builder.build(ed_crypto); // test the algorithm attribute @@ -376,7 +382,7 @@ public class SignatureCertTest { LOG.debug(outputString); } - assertTrue(outputString.contains("Algorithm=\"http://www.w3.org/2021/04/xmldsig-more#eddsa-ed25519\"")); + assertTrue(outputString.contains(algorithm)); final WSHandlerResult results = verify(signedDoc, ed_crypto); @@ -388,56 +394,8 @@ public class SignatureCertTest { (java.security.Principal) actionResult.get(WSSecurityEngineResult.TAG_PRINCIPAL); assertTrue(principal instanceof X500Principal); X500Principal x500Principal = (X500Principal) principal; - assertEquals(new X500Principal("CN=ED25519,O=EDELIVERY,C=EU"), x500Principal); - - } finally { - if (!isJDK16up) { - Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); - } - } - } - - /** - * Successful ECKeyValue test. - */ - @Test - public void testED448KeyValue() throws Exception { - try { - // not needed after JDK 16 - if (!isJDK16up) { - Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); - } + assertEquals(certSubjectDN, x500Principal); - Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG); - WSSecHeader secHeader = new WSSecHeader(doc); - secHeader.insertSecurityHeader(); - - Crypto ed_crypto = CryptoFactory.getInstance("wss-eddsa.properties"); - - WSSecSignature builder = new WSSecSignature(secHeader); - builder.setUserInfo("ed448", "security"); - builder.setKeyIdentifierType(WSConstants.X509_KEY_IDENTIFIER); - Document signedDoc = builder.build(ed_crypto); - - String outputString = - XMLUtils.prettyDocumentToString(signedDoc); - if (LOG.isDebugEnabled()) { - LOG.debug(outputString); - } - - assertTrue(outputString.contains("Algorithm=\"http://www.w3.org/2021/04/xmldsig-more#eddsa-ed448\"")); - - final WSHandlerResult results = verify(signedDoc, ed_crypto); - - WSSecurityEngineResult actionResult = - results.getActionResults().get(WSConstants.SIGN).get(0); - assertNotNull(actionResult); - - java.security.Principal principal = - (java.security.Principal) actionResult.get(WSSecurityEngineResult.TAG_PRINCIPAL); - assertTrue(principal instanceof X500Principal); - X500Principal x500Principal = (X500Principal) principal; - assertEquals(new X500Principal("CN=ED448, O=EDELIVERY, C=EU"), x500Principal); } finally { if (!isJDK16up) { Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); diff --git a/ws-security-policy-stax/src/main/java/org/apache/wss4j/policy/stax/assertionStates/AlgorithmSuiteAssertionState.java b/ws-security-policy-stax/src/main/java/org/apache/wss4j/policy/stax/assertionStates/AlgorithmSuiteAssertionState.java index ae296f93e..c5c52758d 100644 --- a/ws-security-policy-stax/src/main/java/org/apache/wss4j/policy/stax/assertionStates/AlgorithmSuiteAssertionState.java +++ b/ws-security-policy-stax/src/main/java/org/apache/wss4j/policy/stax/assertionStates/AlgorithmSuiteAssertionState.java @@ -92,7 +92,7 @@ public class AlgorithmSuiteAssertionState extends AssertionState implements Asse && (algorithmSuite.getAlgorithmSuiteType().getMinimumSymmetricKeyLength() > keyLength || algorithmSuite.getAlgorithmSuiteType().getMaximumSymmetricKeyLength() < keyLength)) { setAsserted(false); - setErrorMessage("Symmetric signature algorithm key length " + keyLength + " does not meet policy"); + setErrorMessage("Symmetric signature algorithm key length " + keyLength + " does not meet policy"); policyAsserter.unassertPolicy(getAssertion(), getErrorMessage()); } else if (algorithmSuiteSecurityEvent.isDerivedKey() && algorithmSuite.getAlgorithmSuiteType().getSignatureDerivedKeyLength() != keyLength) { @@ -130,12 +130,12 @@ public class AlgorithmSuiteAssertionState extends AssertionState implements Asse && (algorithmSuite.getAlgorithmSuiteType().getMinimumSymmetricKeyLength() > keyLength || algorithmSuite.getAlgorithmSuiteType().getMaximumSymmetricKeyLength() < keyLength)) { setAsserted(false); - setErrorMessage("Symmetric encryption algorithm key length " + keyLength + " does not meet policy"); + setErrorMessage("Symmetric encryption algorithm key length " + keyLength + " does not meet policy"); policyAsserter.unassertPolicy(getAssertion(), getErrorMessage()); } else if (algorithmSuiteSecurityEvent.isDerivedKey() && algorithmSuite.getAlgorithmSuiteType().getEncryptionDerivedKeyLength() != keyLength) { setAsserted(false); - setErrorMessage("Symmetric encryption algorithm derived key length " + keyLength + " does not meet policy"); + setErrorMessage("Symmetric encryption algorithm derived key length " + keyLength + " does not meet policy"); policyAsserter.unassertPolicy(getAssertion(), getErrorMessage()); } } else if (WSSConstants.Sym_Key_Wrap.equals(keyUsage)) { @@ -147,7 +147,7 @@ public class AlgorithmSuiteAssertionState extends AssertionState implements Asse if (algorithmSuite.getAlgorithmSuiteType().getMinimumSymmetricKeyLength() > keyLength || algorithmSuite.getAlgorithmSuiteType().getMaximumSymmetricKeyLength() < keyLength) { setAsserted(false); - setErrorMessage("Symmetric key wrap algorithm key length " + keyLength + " does not meet policy"); + setErrorMessage("Symmetric key wrap algorithm key length " + keyLength + " does not meet policy"); policyAsserter.unassertPolicy(getAssertion(), getErrorMessage()); } } else if (WSSConstants.Asym_Key_Wrap.equals(keyUsage)) { @@ -209,7 +209,7 @@ public class AlgorithmSuiteAssertionState extends AssertionState implements Asse if (algorithmSuite.getStrType() != null && !algorithmSuite.getStrType().getValue().equals(algorithmURI)) { setAsserted(false); - setErrorMessage("STR transformation algorithm " + algorithmURI + " does not meet policy"); + setErrorMessage("STR transformation algorithm " + algorithmURI + " does not meet policy"); policyAsserter.unassertPolicy(getAssertion(), getErrorMessage()); } } else if (WSSConstants.XPATH.equals(keyUsage) && algorithmSuite.getXPathType() != null diff --git a/ws-security-stax/src/test/java/org/apache/wss4j/stax/test/EncDecryptionTest.java b/ws-security-stax/src/test/java/org/apache/wss4j/stax/test/EncDecryptionTest.java index d62bf439d..94148b59c 100644 --- a/ws-security-stax/src/test/java/org/apache/wss4j/stax/test/EncDecryptionTest.java +++ b/ws-security-stax/src/test/java/org/apache/wss4j/stax/test/EncDecryptionTest.java @@ -2361,7 +2361,7 @@ public class EncDecryptionTest extends AbstractTestBase { try { doInboundSecurity(securityProperties, xmlInputFactory.createXMLStreamReader(new ByteArrayInputStream(baos.toByteArray())), null); fail("Failure expected on the wrong key transport algorithm"); - } catch (XMLStreamException e) { + } catch (XMLStreamException e) { assertTrue(e.getCause() instanceof WSSecurityException); } } @@ -2375,7 +2375,7 @@ public class EncDecryptionTest extends AbstractTestBase { try { doInboundSecurity(securityProperties, xmlInputFactory.createXMLStreamReader(new ByteArrayInputStream(baos.toByteArray())), null); fail("Failure expected on the wrong key transport algorithm"); - } catch (XMLStreamException e) { + } catch (XMLStreamException e) { assertTrue(e.getCause() instanceof WSSecurityException); } } diff --git a/ws-security-stax/src/test/java/org/apache/wss4j/stax/test/HeaderOrderingTest.java b/ws-security-stax/src/test/java/org/apache/wss4j/stax/test/HeaderOrderingTest.java index e3be2699e..7655414c2 100644 --- a/ws-security-stax/src/test/java/org/apache/wss4j/stax/test/HeaderOrderingTest.java +++ b/ws-security-stax/src/test/java/org/apache/wss4j/stax/test/HeaderOrderingTest.java @@ -521,7 +521,7 @@ public class HeaderOrderingTest extends AbstractTestBase { //done UsernameToken; now verification: { - String action = WSHandlerConstants.SIGNATURE + " " + WSHandlerConstants.USERNAME_TOKEN + " " + WSHandlerConstants.TIMESTAMP + " " + WSHandlerConstants.ENCRYPTION; + String action = WSHandlerConstants.SIGNATURE + " " + WSHandlerConstants.USERNAME_TOKEN + " " + WSHandlerConstants.TIMESTAMP + " " + WSHandlerConstants.ENCRYPTION; doInboundSecurityWithWSS4J(documentBuilderFactory.newDocumentBuilder().parse(new ByteArrayInputStream(baos.toByteArray())), action); } } @@ -592,7 +592,7 @@ public class HeaderOrderingTest extends AbstractTestBase { //done UsernameToken; now verification: { - String action = WSHandlerConstants.SIGNATURE + " " + WSHandlerConstants.USERNAME_TOKEN + " " + WSHandlerConstants.TIMESTAMP + " " + WSHandlerConstants.ENCRYPTION; + String action = WSHandlerConstants.SIGNATURE + " " + WSHandlerConstants.USERNAME_TOKEN + " " + WSHandlerConstants.TIMESTAMP + " " + WSHandlerConstants.ENCRYPTION; doInboundSecurityWithWSS4J(documentBuilderFactory.newDocumentBuilder().parse(new ByteArrayInputStream(baos.toByteArray())), action); } } @@ -696,7 +696,7 @@ public class HeaderOrderingTest extends AbstractTestBase { //verify SigConf response: { - String action = WSHandlerConstants.SIGNATURE + " " + WSHandlerConstants.USERNAME_TOKEN + " " + WSHandlerConstants.TIMESTAMP; + String action = WSHandlerConstants.SIGNATURE + " " + WSHandlerConstants.USERNAME_TOKEN + " " + WSHandlerConstants.TIMESTAMP; Properties properties = new Properties(); properties.put(WSHandlerConstants.SEND_SIGV, sigv); doInboundSecurityWithWSS4J_1(documentBuilderFactory.newDocumentBuilder().parse(new ByteArrayInputStream(baos.toByteArray())), action, properties, true); @@ -772,8 +772,8 @@ public class HeaderOrderingTest extends AbstractTestBase { //done UsernameToken; now verification: { - String action = WSHandlerConstants.SIGNATURE + " " + WSHandlerConstants.USERNAME_TOKEN + " " + WSHandlerConstants.TIMESTAMP + " " + WSHandlerConstants.ENCRYPTION; + String action = WSHandlerConstants.SIGNATURE + " " + WSHandlerConstants.USERNAME_TOKEN + " " + WSHandlerConstants.TIMESTAMP + " " + WSHandlerConstants.ENCRYPTION; doInboundSecurityWithWSS4J(documentBuilderFactory.newDocumentBuilder().parse(new ByteArrayInputStream(baos.toByteArray())), action); } } -} \ No newline at end of file +} diff --git a/ws-security-stax/src/test/java/org/apache/wss4j/stax/test/SecurityContextTokenTest.java b/ws-security-stax/src/test/java/org/apache/wss4j/stax/test/SecurityContextTokenTest.java index 300452dca..55fb08199 100644 --- a/ws-security-stax/src/test/java/org/apache/wss4j/stax/test/SecurityContextTokenTest.java +++ b/ws-security-stax/src/test/java/org/apache/wss4j/stax/test/SecurityContextTokenTest.java @@ -225,7 +225,7 @@ public class SecurityContextTokenTest extends AbstractTestBase { Properties properties = new Properties(); CallbackHandlerImpl callbackHandler = new CallbackHandlerImpl(tempSecret); - properties.put(WSHandlerConstants.PW_CALLBACK_REF, callbackHandler); + properties.put(WSHandlerConstants.PW_CALLBACK_REF, callbackHandler); properties.put(WSHandlerConstants.DERIVED_TOKEN_REFERENCE, "SecurityContextToken"); if (version == ConversationConstants.VERSION_05_02) { properties.put(WSHandlerConstants.USE_2005_12_NAMESPACE, "false"); @@ -444,7 +444,7 @@ public class SecurityContextTokenTest extends AbstractTestBase { Properties properties = new Properties(); CallbackHandlerImpl callbackHandler = new CallbackHandlerImpl(tempSecret); - properties.put(WSHandlerConstants.PW_CALLBACK_REF, callbackHandler); + properties.put(WSHandlerConstants.PW_CALLBACK_REF, callbackHandler); properties.put(WSHandlerConstants.DERIVED_TOKEN_REFERENCE, "SecurityContextToken"); if (version == ConversationConstants.VERSION_05_02) { properties.put(WSHandlerConstants.USE_2005_12_NAMESPACE, "false"); @@ -706,7 +706,7 @@ public class SecurityContextTokenTest extends AbstractTestBase { Properties properties = new Properties(); CallbackHandlerImpl callbackHandler = new CallbackHandlerImpl(tempSecret); - properties.put(WSHandlerConstants.PW_CALLBACK_REF, callbackHandler); + properties.put(WSHandlerConstants.PW_CALLBACK_REF, callbackHandler); properties.put(WSHandlerConstants.DERIVED_TOKEN_REFERENCE, "SecurityContextToken"); if (version == ConversationConstants.VERSION_05_02) { properties.put(WSHandlerConstants.USE_2005_12_NAMESPACE, "false"); @@ -930,7 +930,7 @@ public class SecurityContextTokenTest extends AbstractTestBase { Properties properties = new Properties(); CallbackHandlerImpl callbackHandler = new CallbackHandlerImpl(tempSecret); - properties.put(WSHandlerConstants.PW_CALLBACK_REF, callbackHandler); + properties.put(WSHandlerConstants.PW_CALLBACK_REF, callbackHandler); properties.put(WSHandlerConstants.DERIVED_TOKEN_REFERENCE, "SecurityContextToken"); if (version == ConversationConstants.VERSION_05_02) { properties.put(WSHandlerConstants.USE_2005_12_NAMESPACE, "false"); diff --git a/ws-security-stax/src/test/java/org/apache/wss4j/stax/test/saml/SAMLTokenTest.java b/ws-security-stax/src/test/java/org/apache/wss4j/stax/test/saml/SAMLTokenTest.java index 87161d205..82d082ad5 100644 --- a/ws-security-stax/src/test/java/org/apache/wss4j/stax/test/saml/SAMLTokenTest.java +++ b/ws-security-stax/src/test/java/org/apache/wss4j/stax/test/saml/SAMLTokenTest.java @@ -1026,7 +1026,7 @@ public class SAMLTokenTest extends AbstractTestBase { try { StAX2DOM.readDoc(documentBuilderFactory.newDocumentBuilder(), xmlStreamReader); fail("Failure expected on a Bearer assertion"); - } catch (XMLStreamException e) { + } catch (XMLStreamException e) { assertTrue(e.getCause() instanceof XMLSecurityException); } } @@ -1070,7 +1070,7 @@ public class SAMLTokenTest extends AbstractTestBase { try { StAX2DOM.readDoc(documentBuilderFactory.newDocumentBuilder(), xmlStreamReader); fail("Failure expected on an unknown subject confirmation method"); - } catch (XMLStreamException e) { + } catch (XMLStreamException e) { assertTrue(e.getCause() instanceof XMLSecurityException); } } @@ -1131,7 +1131,7 @@ public class SAMLTokenTest extends AbstractTestBase { try { StAX2DOM.readDoc(documentBuilderFactory.newDocumentBuilder(), xmlStreamReader); fail("Failure expected on an unsigned bearer token"); - } catch (XMLStreamException e) { + } catch (XMLStreamException e) { assertTrue(e.getCause() instanceof XMLSecurityException); } } @@ -1236,4 +1236,4 @@ public class SAMLTokenTest extends AbstractTestBase { cipher.doFinal(document, elementToEncrypt, content); } -} \ No newline at end of file +}