Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Support for the ECDH KeyAgreement method algorithm #264

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -48,7 +49,7 @@
<wsdl4j.version>1.6.3</wsdl4j.version>
<woodstox.version>6.5.1</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 />
Expand Down Expand Up @@ -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>
Expand Down
3 changes: 2 additions & 1 deletion policy/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ public static final class AlgorithmSuiteType {
private String encryptionDigest;
private String symmetricSignature = SPConstants.HMAC_SHA1;
private String asymmetricSignature = SPConstants.RSA_SHA1;
private String encryptionKeyAgreementMethod;

public AlgorithmSuiteType(String name, String digest, String encryption, String symmetricKeyWrap, //NOPMD
String asymmetricKeyWrap, String encryptionKeyDerivation,
Expand Down Expand Up @@ -268,6 +269,7 @@ public AlgorithmSuiteType(AlgorithmSuiteType algorithmSuiteType) {
this.symmetricSignature = algorithmSuiteType.symmetricSignature;
this.asymmetricSignature = algorithmSuiteType.asymmetricSignature;
this.encryptionDerivedKeyLength = algorithmSuiteType.encryptionDerivedKeyLength;
this.encryptionKeyAgreementMethod = algorithmSuiteType.encryptionKeyAgreementMethod;
this.signatureDerivedKeyLength = algorithmSuiteType.signatureDerivedKeyLength;
this.minimumSymmetricKeyLength = algorithmSuiteType.minimumSymmetricKeyLength;
this.maximumSymmetricKeyLength = algorithmSuiteType.maximumSymmetricKeyLength;
Expand Down Expand Up @@ -311,6 +313,10 @@ public boolean equals(Object object) {
|| encryptionKeyDerivation == null && that.encryptionKeyDerivation != null) {
return false;
}
if (encryptionKeyAgreementMethod != null && !encryptionKeyAgreementMethod.equals(that.encryptionKeyAgreementMethod)
|| encryptionKeyAgreementMethod == null && that.encryptionKeyAgreementMethod != null) {
return false;
}
if (signatureKeyDerivation != null && !signatureKeyDerivation.equals(that.signatureKeyDerivation)
|| signatureKeyDerivation == null && that.signatureKeyDerivation != null) {
return false;
Expand Down Expand Up @@ -365,6 +371,9 @@ public int hashCode() {
if (encryptionKeyDerivation != null) {
result = 31 * result + encryptionKeyDerivation.hashCode();
}
if (encryptionKeyAgreementMethod != null) {
result = 31 * result + encryptionKeyAgreementMethod.hashCode();
}
if (signatureKeyDerivation != null) {
result = 31 * result + signatureKeyDerivation.hashCode();
}
Expand Down Expand Up @@ -419,6 +428,14 @@ public String getEncryptionKeyDerivation() {
return encryptionKeyDerivation;
}

public void setEncryptionKeyAgreementMethod(String encryptionKeyAgreementMethod) {
this.encryptionKeyAgreementMethod = encryptionKeyAgreementMethod;
}

public String getEncryptionKeyAgreementMethod() {
return encryptionKeyAgreementMethod;
}

public String getSignatureKeyDerivation() {
return signatureKeyDerivation;
}
Expand Down
3 changes: 2 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,21 @@ protected 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/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -54,6 +55,12 @@ public String getKeyTransportAlgorithm() {
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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,18 @@ protected 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 =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -116,6 +116,17 @@ public Set<String> getKeyWrapAlgorithms() {
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<>();
Expand Down Expand Up @@ -191,4 +202,4 @@ public void setMinimumEllipticCurveKeyLength(int minimumEllipticCurveKeyLength)
this.minimumEllipticCurveKeyLength = minimumEllipticCurveKeyLength;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,19 @@ public void checkEncryptionKeyWrapAlgorithm(
}
}

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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}

Binary file not shown.
4 changes: 4 additions & 0 deletions ws-security-common/src/test/resources/wss-ecdh.properties
Original file line number Diff line number Diff line change
@@ -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
8 changes: 7 additions & 1 deletion ws-security-dom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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>
Expand Down
Loading