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 <[email protected]>
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 <[email protected]>
---
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
+}