phax commented on code in PR #234:
URL:
https://github.com/apache/santuario-xml-security-java/pull/234#discussion_r1384765287
##########
src/main/java/org/apache/xml/security/encryption/XMLCipherUtil.java:
##########
@@ -81,4 +94,212 @@ private static AlgorithmParameterSpec
constructBlockCipherParametersForGCMAlgori
LOG.log(Level.DEBUG, "Successfully created GCMParameterSpec");
return gcmSpec;
}
+
+ /**
+ * Method buildOAEPParameters from given parameters and returns
OAEPParameterSpec. If encryptionAlgorithmURI is
+ * not RSA_OAEP or RSA_OAEP_11, null is returned.
+ *
+ * @param encryptionAlgorithmURI the encryption algorithm URI (RSA_OAEP or
RSA_OAEP_11)
+ * @param digestAlgorithmURI the digest algorithm URI
+ * @param mgfAlgorithmURI the MGF algorithm URI if
encryptionAlgorithmURI is RSA_OAEP_11, otherwise parameter is ignored
+ * @param oaepParams the OAEP parameters bytes
+ * @return OAEPParameterSpec or null if encryptionAlgorithmURI is not
RSA_OAEP or RSA_OAEP_11
+ */
+ public static OAEPParameterSpec constructOAEPParameters(
+ String encryptionAlgorithmURI,
+ String digestAlgorithmURI,
+ String mgfAlgorithmURI,
+ byte[] oaepParams
+ ) {
+ if (XMLCipher.RSA_OAEP.equals(encryptionAlgorithmURI)
+ || XMLCipher.RSA_OAEP_11.equals(encryptionAlgorithmURI)) {
+
+ String jceDigestAlgorithm = "SHA-1";
+ if (digestAlgorithmURI != null) {
+ jceDigestAlgorithm =
JCEMapper.translateURItoJCEID(digestAlgorithmURI);
+ }
+
+ PSource.PSpecified pSource = oaepParams == null ?
+ PSource.PSpecified.DEFAULT : new
PSource.PSpecified(oaepParams);
+
+ MGF1ParameterSpec mgfParameterSpec = new
MGF1ParameterSpec("SHA-1");
+ if (XMLCipher.RSA_OAEP_11.equals(encryptionAlgorithmURI)) {
+ mgfParameterSpec = constructMGF1Parameter(mgfAlgorithmURI);
+ }
+ return new OAEPParameterSpec(jceDigestAlgorithm, "MGF1",
mgfParameterSpec, pSource);
+ }
+ return null;
+ }
+
+ /**
+ * Create MGF1ParameterSpec for the given algorithm URI
+ *
+ * @param mgh1AlgorithmURI the algorithm URI. If null or empty, SHA-1 is
used as default MGF1 digest algorithm.
+ * @return the MGF1ParameterSpec for the given algorithm URI
+ */
+ public static MGF1ParameterSpec constructMGF1Parameter(String
mgh1AlgorithmURI) {
+ LOG.log(Level.DEBUG, "Creating MGF1ParameterSpec for [{0}]",
mgh1AlgorithmURI);
Review Comment:
Some `LOG.isLoggingEnabled (...)` should be added to all debug logs,
shouldn't it?
##########
src/main/java/org/apache/xml/security/encryption/XMLCipherUtil.java:
##########
@@ -81,4 +94,212 @@ private static AlgorithmParameterSpec
constructBlockCipherParametersForGCMAlgori
LOG.log(Level.DEBUG, "Successfully created GCMParameterSpec");
return gcmSpec;
}
+
+ /**
+ * Method buildOAEPParameters from given parameters and returns
OAEPParameterSpec. If encryptionAlgorithmURI is
+ * not RSA_OAEP or RSA_OAEP_11, null is returned.
+ *
+ * @param encryptionAlgorithmURI the encryption algorithm URI (RSA_OAEP or
RSA_OAEP_11)
+ * @param digestAlgorithmURI the digest algorithm URI
+ * @param mgfAlgorithmURI the MGF algorithm URI if
encryptionAlgorithmURI is RSA_OAEP_11, otherwise parameter is ignored
+ * @param oaepParams the OAEP parameters bytes
+ * @return OAEPParameterSpec or null if encryptionAlgorithmURI is not
RSA_OAEP or RSA_OAEP_11
+ */
+ public static OAEPParameterSpec constructOAEPParameters(
+ String encryptionAlgorithmURI,
+ String digestAlgorithmURI,
+ String mgfAlgorithmURI,
+ byte[] oaepParams
+ ) {
+ if (XMLCipher.RSA_OAEP.equals(encryptionAlgorithmURI)
+ || XMLCipher.RSA_OAEP_11.equals(encryptionAlgorithmURI)) {
+
+ String jceDigestAlgorithm = "SHA-1";
+ if (digestAlgorithmURI != null) {
+ jceDigestAlgorithm =
JCEMapper.translateURItoJCEID(digestAlgorithmURI);
+ }
+
+ PSource.PSpecified pSource = oaepParams == null ?
+ PSource.PSpecified.DEFAULT : new
PSource.PSpecified(oaepParams);
+
+ MGF1ParameterSpec mgfParameterSpec = new
MGF1ParameterSpec("SHA-1");
+ if (XMLCipher.RSA_OAEP_11.equals(encryptionAlgorithmURI)) {
+ mgfParameterSpec = constructMGF1Parameter(mgfAlgorithmURI);
+ }
+ return new OAEPParameterSpec(jceDigestAlgorithm, "MGF1",
mgfParameterSpec, pSource);
+ }
+ return null;
+ }
+
+ /**
+ * Create MGF1ParameterSpec for the given algorithm URI
+ *
+ * @param mgh1AlgorithmURI the algorithm URI. If null or empty, SHA-1 is
used as default MGF1 digest algorithm.
+ * @return the MGF1ParameterSpec for the given algorithm URI
+ */
+ public static MGF1ParameterSpec constructMGF1Parameter(String
mgh1AlgorithmURI) {
+ LOG.log(Level.DEBUG, "Creating MGF1ParameterSpec for [{0}]",
mgh1AlgorithmURI);
+ if (mgh1AlgorithmURI == null || mgh1AlgorithmURI.isEmpty()) {
+ LOG.log(Level.WARNING,"MGF1 algorithm URI is null or empty. Using
SHA-1 as default.");
+ return new MGF1ParameterSpec("SHA-1");
+ }
+
+ switch (mgh1AlgorithmURI) {
+ case EncryptionConstants.MGF1_SHA1:
+ return new MGF1ParameterSpec("SHA-1");
+ case EncryptionConstants.MGF1_SHA224:
+ return new MGF1ParameterSpec("SHA-224");
+ case EncryptionConstants.MGF1_SHA256:
+ return new MGF1ParameterSpec("SHA-256");
+ case EncryptionConstants.MGF1_SHA384:
+ return new MGF1ParameterSpec("SHA-384");
+ case EncryptionConstants.MGF1_SHA512:
+ return new MGF1ParameterSpec("SHA-512");
+ default:
+ LOG.log(Level.WARNING, "Unsupported MGF algorithm: [{0}] Using
SHA-1 as default.", mgh1AlgorithmURI);
+ return new MGF1ParameterSpec("SHA-1");
Review Comment:
Is `SHA-1` really a reasonable default? It is e.g. disabled for XMLDsig in
Java 17. Wouldn't `SHA-256` be better?
##########
src/main/java/org/apache/xml/security/encryption/XMLCipherUtil.java:
##########
@@ -81,4 +94,212 @@ private static AlgorithmParameterSpec
constructBlockCipherParametersForGCMAlgori
LOG.log(Level.DEBUG, "Successfully created GCMParameterSpec");
return gcmSpec;
}
+
+ /**
+ * Method buildOAEPParameters from given parameters and returns
OAEPParameterSpec. If encryptionAlgorithmURI is
+ * not RSA_OAEP or RSA_OAEP_11, null is returned.
+ *
+ * @param encryptionAlgorithmURI the encryption algorithm URI (RSA_OAEP or
RSA_OAEP_11)
+ * @param digestAlgorithmURI the digest algorithm URI
+ * @param mgfAlgorithmURI the MGF algorithm URI if
encryptionAlgorithmURI is RSA_OAEP_11, otherwise parameter is ignored
+ * @param oaepParams the OAEP parameters bytes
+ * @return OAEPParameterSpec or null if encryptionAlgorithmURI is not
RSA_OAEP or RSA_OAEP_11
+ */
+ public static OAEPParameterSpec constructOAEPParameters(
+ String encryptionAlgorithmURI,
+ String digestAlgorithmURI,
+ String mgfAlgorithmURI,
+ byte[] oaepParams
+ ) {
+ if (XMLCipher.RSA_OAEP.equals(encryptionAlgorithmURI)
+ || XMLCipher.RSA_OAEP_11.equals(encryptionAlgorithmURI)) {
+
+ String jceDigestAlgorithm = "SHA-1";
+ if (digestAlgorithmURI != null) {
+ jceDigestAlgorithm =
JCEMapper.translateURItoJCEID(digestAlgorithmURI);
+ }
+
+ PSource.PSpecified pSource = oaepParams == null ?
+ PSource.PSpecified.DEFAULT : new
PSource.PSpecified(oaepParams);
+
+ MGF1ParameterSpec mgfParameterSpec = new
MGF1ParameterSpec("SHA-1");
+ if (XMLCipher.RSA_OAEP_11.equals(encryptionAlgorithmURI)) {
+ mgfParameterSpec = constructMGF1Parameter(mgfAlgorithmURI);
+ }
+ return new OAEPParameterSpec(jceDigestAlgorithm, "MGF1",
mgfParameterSpec, pSource);
+ }
+ return null;
+ }
+
+ /**
+ * Create MGF1ParameterSpec for the given algorithm URI
+ *
+ * @param mgh1AlgorithmURI the algorithm URI. If null or empty, SHA-1 is
used as default MGF1 digest algorithm.
+ * @return the MGF1ParameterSpec for the given algorithm URI
+ */
+ public static MGF1ParameterSpec constructMGF1Parameter(String
mgh1AlgorithmURI) {
+ LOG.log(Level.DEBUG, "Creating MGF1ParameterSpec for [{0}]",
mgh1AlgorithmURI);
+ if (mgh1AlgorithmURI == null || mgh1AlgorithmURI.isEmpty()) {
+ LOG.log(Level.WARNING,"MGF1 algorithm URI is null or empty. Using
SHA-1 as default.");
+ return new MGF1ParameterSpec("SHA-1");
+ }
+
+ switch (mgh1AlgorithmURI) {
+ case EncryptionConstants.MGF1_SHA1:
+ return new MGF1ParameterSpec("SHA-1");
+ case EncryptionConstants.MGF1_SHA224:
+ return new MGF1ParameterSpec("SHA-224");
+ case EncryptionConstants.MGF1_SHA256:
+ return new MGF1ParameterSpec("SHA-256");
+ case EncryptionConstants.MGF1_SHA384:
+ return new MGF1ParameterSpec("SHA-384");
+ case EncryptionConstants.MGF1_SHA512:
+ return new MGF1ParameterSpec("SHA-512");
+ default:
+ LOG.log(Level.WARNING, "Unsupported MGF algorithm: [{0}] Using
SHA-1 as default.", mgh1AlgorithmURI);
+ return new MGF1ParameterSpec("SHA-1");
+ }
+ }
+
+ /**
+ * Get the MGF1 algorithm URI for the given MGF1ParameterSpec
+ *
+ * @param parameterSpec the MGF1ParameterSpec
+ * @return the MGF1 algorithm URI for the given MGF1ParameterSpec
+ */
+ public static String getMgf1URIForParameter(MGF1ParameterSpec
parameterSpec) {
+ String digestAlgorithm = parameterSpec.getDigestAlgorithm();
+ LOG.log(Level.DEBUG, "Get MGF1 URI for digest algorithm [{0}]",
digestAlgorithm);
+ switch (digestAlgorithm) {
+ case "SHA-1":
+ return EncryptionConstants.MGF1_SHA1;
+ case "SHA-224":
+ return EncryptionConstants.MGF1_SHA224;
+ case "SHA-256":
+ return EncryptionConstants.MGF1_SHA256;
+ case "SHA-384":
+ return EncryptionConstants.MGF1_SHA384;
+ case "SHA-512":
+ return EncryptionConstants.MGF1_SHA512;
+ default:
+ LOG.log(Level.WARNING, "Unknown hash algorithm: [{0}] for
MGF1", digestAlgorithm);
+ return EncryptionConstants.MGF1_SHA1;
Review Comment:
Same as above
##########
src/main/java/org/apache/xml/security/encryption/params/ConcatKeyDerivationParameter.java:
##########
@@ -0,0 +1,105 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.xml.security.encryption.params;
+
+import org.apache.xml.security.algorithms.MessageDigestAlgorithm;
+import org.apache.xml.security.utils.EncryptionConstants;
+
+/**
+ * Class ConcatKeyDerivationParameter is used to specify parameters for the
ConcatKDF key derivation algorithm.
+ * @see <A HREF="https://www.w3.org/TR/xmlenc-core1/#sec-ConcatKDF">XML
Encryption Syntax and Processing Version 1.1, 5.8.1
+ * The ConcatKDF Key Derivation Algorithm</A>
+ */
+public class ConcatKeyDerivationParameter extends KeyDerivationParameter {
+
+ private String digestAlgorithm;
+ private String algorithmID;
+ private String partyUInfo;
+ private String partyVInfo;
+ private String suppPubInfo;
+ private String suppPrivInfo;
+
+ /**
+ * Constructor ConcatKeyDerivationParameter with default SHA256 digest
algorithm
+ *
+ * @param keyBitLength the length of the derived key in bits
+ */
+ public ConcatKeyDerivationParameter(int keyBitLength) {
Review Comment:
Is it sensible to have a default constructor with a default algorithm? Is
that a common case?
Alternatively you could add some `DEFAULT_DIGEST_ALGORITHM` constant that
uses `MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA256`
##########
src/test/java/org/apache/xml/security/testutils/JDKTestUtils.java:
##########
@@ -0,0 +1,149 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.xml.security.testutils;
+
+import java.lang.System.Logger.Level;
+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 XMLSEC 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
+ * xmlsec functionality ...
+ *
+ */
+public class JDKTestUtils {
+
+
+ // 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 final System.Logger LOG =
System.getLogger(JDKTestUtils.class.getName());
+
+ private static Provider auxiliaryProvider;
+ private static boolean auxiliaryProviderInitialized = false;
+ private static Set<String> supportedAuxiliaryProviderAlgorithms = null;
+
+ private static 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 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) {
+ // ignore
+ }
+ return 0;
+ }
+
+ public static synchronized Provider getAuxiliaryProvider() {
+ if (auxiliaryProviderInitialized) {
+ return auxiliaryProvider;
+ }
+
+ Constructor<?> cons;
Review Comment:
could be moved inside the block
##########
src/test/java/org/apache/xml/security/testutils/JDKTestUtils.java:
##########
@@ -0,0 +1,149 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.xml.security.testutils;
+
+import java.lang.System.Logger.Level;
+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 XMLSEC 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
+ * xmlsec functionality ...
+ *
+ */
+public class JDKTestUtils {
+
+
+ // 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 final System.Logger LOG =
System.getLogger(JDKTestUtils.class.getName());
+
+ private static Provider auxiliaryProvider;
+ private static boolean auxiliaryProviderInitialized = false;
+ private static Set<String> supportedAuxiliaryProviderAlgorithms = null;
+
+ private static 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 Set<String> SUPPORTED_ALGORITHMS =
Stream.of(Security.getProviders())
Review Comment:
could be `final`
##########
src/main/java/org/apache/xml/security/keys/derivedKey/KeyDerivationMethodImpl.java:
##########
@@ -0,0 +1,108 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.xml.security.keys.derivedKey;
+
+import org.apache.xml.security.encryption.KeyDerivationMethod;
+import org.apache.xml.security.exceptions.XMLSecurityException;
+import org.apache.xml.security.utils.Encryption11ElementProxy;
+import org.apache.xml.security.utils.EncryptionConstants;
+import org.apache.xml.security.utils.XMLUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.util.StringJoiner;
+
+/**
+ * Class KeyDerivationMethodImpl is an DOM implementation of the
KeyDerivationMethod
+ *
+ */
+public class KeyDerivationMethodImpl extends Encryption11ElementProxy
implements KeyDerivationMethod {
+ ConcatKDFParamsImpl concatKDFParams;
Review Comment:
Please make `private`
##########
src/main/java/org/apache/xml/security/utils/DERDecoderUtils.java:
##########
@@ -0,0 +1,250 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.xml.security.utils;
+
+import org.apache.xml.security.exceptions.DERDecodingException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.security.PublicKey;
+
+/**
+ * Provides the means to navigate through a DER-encoded byte array, to help
+ * in decoding the contents.
+ * <p>
+ * It maintains a "current position" in the array that advances with each
+ * operation, providing a simple means to handle the type-length-value
+ * encoding of DER. For example
+ * <pre>
+ * decoder.expect(TYPE);
+ * int length = decoder.getLength();
+ * byte[] value = decoder.getBytes(len);
+ * </pre>
+ */
+public class DERDecoderUtils {
+ private static final System.Logger LOG =
System.getLogger(DERDecoderUtils.class.getName());
+
+ /**
+ * DER type identifier for a bit string value
+ */
+ public static final byte TYPE_BIT_STRING = 0x03;
+ /**
+ * DER type identifier for a octet string value
+ */
+ public static final byte TYPE_OCTET_STRING = 0x04;
+ /**
+ * DER type identifier for a sequence value
+ */
+ public static final byte TYPE_SEQUENCE = 0x30;
+ /**
+ * DER type identifier for ASN.1 "OBJECT IDENTIFIER" value.
+ */
+ public static final byte TYPE_OBJECT_IDENTIFIER = 0x06;
+
+ /**
+ * Simple method parses an ASN.1 encoded byte array. The encoding uses
"DER", a BER/1 subset, that means a triple { typeId, length, data }.
+ * with the following structure:
+ * <p>
+ * PublicKeyInfo ::= SEQUENCE {
+ * algorithm AlgorithmIdentifier,
+ * PublicKey BIT STRING
+ * }
+ * <p>
+ * Where AlgorithmIdentifier is formatted as:
+ * AlgorithmIdentifier ::= SEQUENCE {
+ * algorithm OBJECT IDENTIFIER,
+ * parameters ANY DEFINED BY algorithm OPTIONAL
+ * }
+ *
+ * @param derEncodedIS the DER-encoded input stream to decode.
+ * @throws DERDecodingException if the given array is null.
+ */
+ public static byte[] getAlgorithmIdBytes(InputStream derEncodedIS) throws
DERDecodingException, IOException {
+ if (derEncodedIS == null || derEncodedIS.available() <= 0) {
+ throw new DERDecodingException("DER decoding error: Null data");
+ }
+
+ validateType(derEncodedIS.read(), TYPE_SEQUENCE);
+ readLength(derEncodedIS);
+ validateType(derEncodedIS.read(), TYPE_SEQUENCE);
+ readLength(derEncodedIS);
+
+ return readObjectIdentifier(derEncodedIS);
+ }
+
+ /**
+ * Read the next object identifier from the given DER-encoded input stream.
+ * <p>
+ * @param derEncodedIS the DER-encoded input stream to decode.
+ * @return the object identifier as a byte array.
+ * @throws DERDecodingException if the given array is null.
+ */
+ public static byte[] readObjectIdentifier(InputStream derEncodedIS) throws
DERDecodingException {
+ try {
+ validateType(derEncodedIS.read(), TYPE_OBJECT_IDENTIFIER);
+ int length = readLength(derEncodedIS);
+ LOG.log(System.Logger.Level.DEBUG, "DER decoding algorithm id
bytes");
Review Comment:
see above
##########
src/main/java/org/apache/xml/security/utils/EncryptionElementProxy.java:
##########
@@ -0,0 +1,71 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.xml.security.utils;
+
+import org.apache.xml.security.exceptions.XMLSecurityException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * Class EncryptionElementProxy
+ *
+ */
+public abstract class EncryptionElementProxy extends ElementProxy {
+
+ protected EncryptionElementProxy() {
+ }
+
+ /**
+ * Constructor EncryptionElementProxy
+ *
+ * @param doc
+ */
+ public EncryptionElementProxy(Document doc) {
+ if (doc == null) {
+ throw new RuntimeException("Document is null");
Review Comment:
Please use `IllegalArgumentException` - never throw `RuntimeException`
directly
##########
src/main/java/org/apache/xml/security/encryption/params/ConcatKeyDerivationParameter.java:
##########
@@ -0,0 +1,105 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.xml.security.encryption.params;
+
+import org.apache.xml.security.algorithms.MessageDigestAlgorithm;
+import org.apache.xml.security.utils.EncryptionConstants;
+
+/**
+ * Class ConcatKeyDerivationParameter is used to specify parameters for the
ConcatKDF key derivation algorithm.
+ * @see <A HREF="https://www.w3.org/TR/xmlenc-core1/#sec-ConcatKDF">XML
Encryption Syntax and Processing Version 1.1, 5.8.1
+ * The ConcatKDF Key Derivation Algorithm</A>
+ */
+public class ConcatKeyDerivationParameter extends KeyDerivationParameter {
+
+ private String digestAlgorithm;
+ private String algorithmID;
+ private String partyUInfo;
+ private String partyVInfo;
+ private String suppPubInfo;
+ private String suppPrivInfo;
+
+ /**
+ * Constructor ConcatKeyDerivationParameter with default SHA256 digest
algorithm
+ *
+ * @param keyBitLength the length of the derived key in bits
+ */
+ public ConcatKeyDerivationParameter(int keyBitLength) {
+ this(keyBitLength, MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA256);
+ }
+
+ /**
+ * Constructor ConcatKeyDerivationParameter with specified digest algorithm
+ *
+ * @param keyBitLength the length of the derived key in bits
+ * @param digestAlgorithm the digest algorithm to use
+ */
+ public ConcatKeyDerivationParameter(int keyBitLength, String
digestAlgorithm) {
+ super(EncryptionConstants.ALGO_ID_KEYDERIVATION_CONCATKDF,
keyBitLength);
+ this.digestAlgorithm = digestAlgorithm == null?
MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA256 : digestAlgorithm;
+ }
+
+ public String getDigestAlgorithm() {
+ return digestAlgorithm;
+ }
+
+ public void setDigestAlgorithm(String digestAlgorithm) {
+ this.digestAlgorithm = digestAlgorithm;
Review Comment:
In the constructor you used a special `null` handling that is missing in the
setter - I think unification would be good
##########
src/main/java/org/apache/xml/security/utils/DERDecoderUtils.java:
##########
@@ -0,0 +1,250 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.xml.security.utils;
+
+import org.apache.xml.security.exceptions.DERDecodingException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.security.PublicKey;
+
+/**
+ * Provides the means to navigate through a DER-encoded byte array, to help
+ * in decoding the contents.
+ * <p>
+ * It maintains a "current position" in the array that advances with each
+ * operation, providing a simple means to handle the type-length-value
+ * encoding of DER. For example
+ * <pre>
+ * decoder.expect(TYPE);
+ * int length = decoder.getLength();
+ * byte[] value = decoder.getBytes(len);
+ * </pre>
+ */
+public class DERDecoderUtils {
+ private static final System.Logger LOG =
System.getLogger(DERDecoderUtils.class.getName());
+
+ /**
+ * DER type identifier for a bit string value
+ */
+ public static final byte TYPE_BIT_STRING = 0x03;
+ /**
+ * DER type identifier for a octet string value
+ */
+ public static final byte TYPE_OCTET_STRING = 0x04;
+ /**
+ * DER type identifier for a sequence value
+ */
+ public static final byte TYPE_SEQUENCE = 0x30;
+ /**
+ * DER type identifier for ASN.1 "OBJECT IDENTIFIER" value.
+ */
+ public static final byte TYPE_OBJECT_IDENTIFIER = 0x06;
+
+ /**
+ * Simple method parses an ASN.1 encoded byte array. The encoding uses
"DER", a BER/1 subset, that means a triple { typeId, length, data }.
+ * with the following structure:
+ * <p>
+ * PublicKeyInfo ::= SEQUENCE {
+ * algorithm AlgorithmIdentifier,
+ * PublicKey BIT STRING
+ * }
+ * <p>
+ * Where AlgorithmIdentifier is formatted as:
+ * AlgorithmIdentifier ::= SEQUENCE {
+ * algorithm OBJECT IDENTIFIER,
+ * parameters ANY DEFINED BY algorithm OPTIONAL
+ * }
+ *
+ * @param derEncodedIS the DER-encoded input stream to decode.
+ * @throws DERDecodingException if the given array is null.
+ */
+ public static byte[] getAlgorithmIdBytes(InputStream derEncodedIS) throws
DERDecodingException, IOException {
+ if (derEncodedIS == null || derEncodedIS.available() <= 0) {
+ throw new DERDecodingException("DER decoding error: Null data");
+ }
+
+ validateType(derEncodedIS.read(), TYPE_SEQUENCE);
+ readLength(derEncodedIS);
+ validateType(derEncodedIS.read(), TYPE_SEQUENCE);
+ readLength(derEncodedIS);
+
+ return readObjectIdentifier(derEncodedIS);
+ }
+
+ /**
+ * Read the next object identifier from the given DER-encoded input stream.
+ * <p>
+ * @param derEncodedIS the DER-encoded input stream to decode.
+ * @return the object identifier as a byte array.
+ * @throws DERDecodingException if the given array is null.
+ */
+ public static byte[] readObjectIdentifier(InputStream derEncodedIS) throws
DERDecodingException {
+ try {
+ validateType(derEncodedIS.read(), TYPE_OBJECT_IDENTIFIER);
+ int length = readLength(derEncodedIS);
+ LOG.log(System.Logger.Level.DEBUG, "DER decoding algorithm id
bytes");
+ return derEncodedIS.readNBytes(length);
+ } catch (IOException ex) {
+ throw new DERDecodingException(ex, "Error occurred while reading
the input stream.");
+ }
+ }
+
+
+ public static String getAlgorithmIdFromPublicKey(PublicKey publicKey)
throws DERDecodingException {
+ String keyFormat = publicKey.getFormat();
+ if (!("X.509".equalsIgnoreCase(keyFormat)
+ || "X509".equalsIgnoreCase(keyFormat))) {
+ throw new DERDecodingException("Unknown key format [" + keyFormat
+ "]! Support for X.509-encoded public keys only!");
+ }
+ try (InputStream inputStream = new
ByteArrayInputStream(publicKey.getEncoded())) {
+ byte[] keyAlgOidBytes = getAlgorithmIdBytes(inputStream);
+ String alg = decodeOID(keyAlgOidBytes);
+ if (alg.equals(KeyUtils.KeyAlgorithmType.EC.getOid())) {
+ keyAlgOidBytes = readObjectIdentifier(inputStream);
+ alg = decodeOID(keyAlgOidBytes);
+ }
+ return alg;
+ } catch (IOException e) {
+ throw new DERDecodingException("Error reading public key");
Review Comment:
I think it would be good to pass the source exception through
##########
src/main/java/org/apache/xml/security/encryption/XMLCipherUtil.java:
##########
@@ -81,4 +94,212 @@ private static AlgorithmParameterSpec
constructBlockCipherParametersForGCMAlgori
LOG.log(Level.DEBUG, "Successfully created GCMParameterSpec");
return gcmSpec;
}
+
+ /**
+ * Method buildOAEPParameters from given parameters and returns
OAEPParameterSpec. If encryptionAlgorithmURI is
+ * not RSA_OAEP or RSA_OAEP_11, null is returned.
+ *
+ * @param encryptionAlgorithmURI the encryption algorithm URI (RSA_OAEP or
RSA_OAEP_11)
+ * @param digestAlgorithmURI the digest algorithm URI
+ * @param mgfAlgorithmURI the MGF algorithm URI if
encryptionAlgorithmURI is RSA_OAEP_11, otherwise parameter is ignored
+ * @param oaepParams the OAEP parameters bytes
+ * @return OAEPParameterSpec or null if encryptionAlgorithmURI is not
RSA_OAEP or RSA_OAEP_11
+ */
+ public static OAEPParameterSpec constructOAEPParameters(
+ String encryptionAlgorithmURI,
+ String digestAlgorithmURI,
+ String mgfAlgorithmURI,
+ byte[] oaepParams
+ ) {
+ if (XMLCipher.RSA_OAEP.equals(encryptionAlgorithmURI)
+ || XMLCipher.RSA_OAEP_11.equals(encryptionAlgorithmURI)) {
+
+ String jceDigestAlgorithm = "SHA-1";
+ if (digestAlgorithmURI != null) {
+ jceDigestAlgorithm =
JCEMapper.translateURItoJCEID(digestAlgorithmURI);
+ }
+
+ PSource.PSpecified pSource = oaepParams == null ?
+ PSource.PSpecified.DEFAULT : new
PSource.PSpecified(oaepParams);
+
+ MGF1ParameterSpec mgfParameterSpec = new
MGF1ParameterSpec("SHA-1");
+ if (XMLCipher.RSA_OAEP_11.equals(encryptionAlgorithmURI)) {
+ mgfParameterSpec = constructMGF1Parameter(mgfAlgorithmURI);
+ }
+ return new OAEPParameterSpec(jceDigestAlgorithm, "MGF1",
mgfParameterSpec, pSource);
+ }
+ return null;
+ }
+
+ /**
+ * Create MGF1ParameterSpec for the given algorithm URI
+ *
+ * @param mgh1AlgorithmURI the algorithm URI. If null or empty, SHA-1 is
used as default MGF1 digest algorithm.
+ * @return the MGF1ParameterSpec for the given algorithm URI
+ */
+ public static MGF1ParameterSpec constructMGF1Parameter(String
mgh1AlgorithmURI) {
+ LOG.log(Level.DEBUG, "Creating MGF1ParameterSpec for [{0}]",
mgh1AlgorithmURI);
+ if (mgh1AlgorithmURI == null || mgh1AlgorithmURI.isEmpty()) {
+ LOG.log(Level.WARNING,"MGF1 algorithm URI is null or empty. Using
SHA-1 as default.");
+ return new MGF1ParameterSpec("SHA-1");
+ }
+
+ switch (mgh1AlgorithmURI) {
+ case EncryptionConstants.MGF1_SHA1:
+ return new MGF1ParameterSpec("SHA-1");
+ case EncryptionConstants.MGF1_SHA224:
+ return new MGF1ParameterSpec("SHA-224");
+ case EncryptionConstants.MGF1_SHA256:
+ return new MGF1ParameterSpec("SHA-256");
+ case EncryptionConstants.MGF1_SHA384:
+ return new MGF1ParameterSpec("SHA-384");
+ case EncryptionConstants.MGF1_SHA512:
+ return new MGF1ParameterSpec("SHA-512");
+ default:
+ LOG.log(Level.WARNING, "Unsupported MGF algorithm: [{0}] Using
SHA-1 as default.", mgh1AlgorithmURI);
+ return new MGF1ParameterSpec("SHA-1");
+ }
+ }
+
+ /**
+ * Get the MGF1 algorithm URI for the given MGF1ParameterSpec
+ *
+ * @param parameterSpec the MGF1ParameterSpec
+ * @return the MGF1 algorithm URI for the given MGF1ParameterSpec
+ */
+ public static String getMgf1URIForParameter(MGF1ParameterSpec
parameterSpec) {
+ String digestAlgorithm = parameterSpec.getDigestAlgorithm();
+ LOG.log(Level.DEBUG, "Get MGF1 URI for digest algorithm [{0}]",
digestAlgorithm);
Review Comment:
Same as above
##########
src/test/java/org/apache/xml/security/testutils/JDKTestUtils.java:
##########
@@ -0,0 +1,149 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.xml.security.testutils;
+
+import java.lang.System.Logger.Level;
+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 XMLSEC 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
+ * xmlsec functionality ...
+ *
+ */
+public class JDKTestUtils {
+
+
+ // 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 final System.Logger LOG =
System.getLogger(JDKTestUtils.class.getName());
+
+ private static Provider auxiliaryProvider;
+ private static boolean auxiliaryProviderInitialized = false;
+ private static Set<String> supportedAuxiliaryProviderAlgorithms = null;
+
+ private static Map<String, Integer> javaAlgSupportFrom = Stream.of(
Review Comment:
could be `final`
##########
src/test/java/org/apache/xml/security/testutils/JDKTestUtils.java:
##########
@@ -0,0 +1,149 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.xml.security.testutils;
+
+import java.lang.System.Logger.Level;
+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 XMLSEC 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
+ * xmlsec functionality ...
+ *
+ */
+public class JDKTestUtils {
+
+
+ // 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 final System.Logger LOG =
System.getLogger(JDKTestUtils.class.getName());
+
+ private static Provider auxiliaryProvider;
+ private static boolean auxiliaryProviderInitialized = false;
+ private static Set<String> supportedAuxiliaryProviderAlgorithms = null;
+
+ private static 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 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) {
+ // ignore
+ }
+ return 0;
+ }
+
+ public static synchronized Provider getAuxiliaryProvider() {
+ if (auxiliaryProviderInitialized) {
+ return auxiliaryProvider;
+ }
+
+ Constructor<?> cons;
+ try {
+ String providerClassName =
System.getProperty(TEST_PROVIDER_CLASSNAME_PROPERTY,
TEST_PROVIDER_CLASSNAME_DEFAULT);
+ LOG.log(Level.INFO, "Initialize the auxiliary security provider:
[{0}]", providerClassName);
+ Class<?> c = Class.forName(providerClassName);
+ cons = c.getConstructor(new Class[] {});
+ auxiliaryProvider = (Provider)cons.newInstance();
+ supportedAuxiliaryProviderAlgorithms =
auxiliaryProvider.getServices().stream()
+ .map(Provider.Service::getAlgorithm)
+ .map(String::toLowerCase)
+ .collect(Collectors.toSet());
+ } catch (Exception e) {
+ LOG.log(Level.WARNING, "Failed to initialize the auxiliary
security provider: [{0}]", e.getMessage());
+ }
+ auxiliaryProviderInitialized = true;
+ return auxiliaryProvider;
+ }
+
+ public static void registerAuxiliaryProvider() {
+ // init provider if needed
+ Provider provider = getAuxiliaryProvider();
+ if (provider == null) {
+ LOG.log(Level.WARNING, "Auxiliary security provider is not
initialized. Cannot register it.");
+ return;
+ }
+ Security.addProvider(provider);
+ }
+
+ public static void unregisterAuxiliaryProvider() {
+ if (auxiliaryProvider == null) {
+ LOG.log(Level.DEBUG, "Auxiliary security provider is not
initialized. Cannot unregister it.");
+ return;
+ }
+ LOG.log(Level.DEBUG, "Unregister auxiliary security provider [{0}]",
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.log(Level.DEBUG, "Algorithm [{0}] is supported by JDK version
[{1}]", alg, iJDKVersion);
+ return true;
+ }
+ Provider provider = getAuxiliaryProvider();
+ if (useAuxiliaryProvider
+ && provider!=null
+ && supportedAuxiliaryProviderAlgorithms.contains(alg)){
+ LOG.log(Level.DEBUG, "Algorithm [{0}] is supported by auxiliary
Provider [{1}].",
+ alg, provider.getName());
+ return true;
+ }
+ // double check in all supported algorithms ...
+ LOG.log(Level.INFO, "Algorithm [{0}] is NOT supported", alg,
iJDKVersion);
Review Comment:
One parameter too much
##########
src/main/java/org/apache/xml/security/utils/KeyUtils.java:
##########
@@ -0,0 +1,280 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.xml.security.utils;
+
+import org.apache.xml.security.algorithms.implementations.ECDSAUtils;
+import org.apache.xml.security.encryption.XMLEncryptionException;
+import org.apache.xml.security.encryption.params.ConcatKeyDerivationParameter;
+import org.apache.xml.security.encryption.params.KeyAgreementParameterSpec;
+import org.apache.xml.security.encryption.params.KeyDerivationParameter;
+import org.apache.xml.security.exceptions.DERDecodingException;
+import org.apache.xml.security.exceptions.XMLSecurityException;
+import org.apache.xml.security.keys.derivedKey.ConcatKDF;
+
+import javax.crypto.*;
+import javax.crypto.spec.SecretKeySpec;
+import java.lang.System.Logger.Level;
+import java.security.*;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECGenParameterSpec;
+import java.util.Arrays;
+
+/**
+ * A set of utility methods to handle keys.
+ */
+public class KeyUtils {
+ private static final System.Logger LOG =
System.getLogger(KeyUtils.class.getName());
+ /**
+ * Enumeration of Supported key algorithm types.
+ */
+ public enum KeyAlgorithmType {
+ EC("EC", "1.2.840.10045.2.1"),
+ DSA("DSA", "1.2.840.10040.4.1"),
+ RSA("RSA", "1.2.840.113549.1.1.1"),
+ XDH("XDH", null),
+ EdDSA("EdDSA", null);
+ private final String jceName;
+ private final String oid;
+
+ KeyAlgorithmType(String jceName, String oid) {
+ this.jceName = jceName;
+ this.oid = oid;
+ }
+
+ public String getJceName() {
+ return jceName;
+ }
+
+ public String getOid() {
+ return oid;
+ }
+
+ }
+
+ /**
+ * Enumeration of specific key types.
+ */
+ public enum KeyType {
+ SECT163K1("sect163k1", "NIST K-163", KeyAlgorithmType.EC,
"1.3.132.0.1"),
+ SECT163R1("sect163r1", "", KeyAlgorithmType.EC, "1.3.132.0.2"),
+ SECT163R2("sect163r2", "NIST B-163", KeyAlgorithmType.EC,
"1.3.132.0.15"),
+ SECT193R1("sect193r1", "", KeyAlgorithmType.EC, "1.3.132.0.24"),
+ SECT193R2("sect193r2", "", KeyAlgorithmType.EC, "1.3.132.0.25"),
+ SECT233K1("sect233k1", "NIST K-233", KeyAlgorithmType.EC,
"1.3.132.0.26"),
+ SECT233R1("sect233r1", "NIST B-233", KeyAlgorithmType.EC,
"1.3.132.0.27"),
+ SECT239K1("sect239k1", "", KeyAlgorithmType.EC, "1.3.132.0.3"),
+ SECT283K1("sect283k1", "NIST K-283", KeyAlgorithmType.EC,
"1.3.132.0.16"),
+ SECT283R1("sect283r1", "", KeyAlgorithmType.EC, "1.3.132.0.17"),
+ SECT409K1("sect409k1", "NIST K-409", KeyAlgorithmType.EC,
"1.3.132.0.36"),
+ SECT409R1("sect409r1", "NIST B-409", KeyAlgorithmType.EC,
"1.3.132.0.37"),
+ SECT571K1("sect571k1", "NIST K-571", KeyAlgorithmType.EC,
"1.3.132.0.38"),
+ SECT571R1("sect571r1", "NIST B-571", KeyAlgorithmType.EC,
"1.3.132.0.39"),
+ SECP160K1("secp160k1", "", KeyAlgorithmType.EC, "1.3.132.0.9"),
+ SECP160R1("secp160r1", "", KeyAlgorithmType.EC, "1.3.132.0.8"),
+ SECP160R2("secp160r2", "", KeyAlgorithmType.EC, "1.3.132.0.30"),
+ SECP192K1("secp192k1", "", KeyAlgorithmType.EC, "1.3.132.0.31"),
+ SECP192R1("secp192r1", "NIST P-192,X9.62 prime192v1",
KeyAlgorithmType.EC, "1.2.840.10045.3.1.1"),
+ SECP224K1("secp224k1", "", KeyAlgorithmType.EC, "1.3.132.0.32"),
+ SECP224R1("secp224r1", "NIST P-224", KeyAlgorithmType.EC,
"1.3.132.0.33"),
+ SECP256K1("secp256k1", "", KeyAlgorithmType.EC, "1.3.132.0.10"),
+ SECP256R1("secp256r1", "NIST P-256,X9.62 prime256v1",
KeyAlgorithmType.EC, "1.2.840.10045.3.1.7"),
+ SECP384R1("secp384r1", "NIST P-384", KeyAlgorithmType.EC,
"1.3.132.0.34"),
+ SECP521R1("secp521r1", "NIST P-521", KeyAlgorithmType.EC,
"1.3.132.0.35"),
+ BRAINPOOLP256R1("brainpoolP256r1", "RFC 5639", KeyAlgorithmType.EC,
"1.3.36.3.3.2.8.1.1.7"),
+ BRAINPOOLP384R1("brainpoolP384r1", "RFC 5639", KeyAlgorithmType.EC,
"1.3.36.3.3.2.8.1.1.11"),
+ BRAINPOOLP512R1("brainpoolP512r1", "RFC 5639", KeyAlgorithmType.EC,
"1.3.36.3.3.2.8.1.1.13"),
+ X25519("x25519", "RFC 7748", KeyAlgorithmType.XDH, "1.3.101.110"),
+ X448("x448", "RFC 7748", KeyAlgorithmType.XDH, "1.3.101.111"),
+ ED25519("ed25519", "RFC 8032", KeyAlgorithmType.EdDSA, "1.3.101.112"),
+ ED448("ed448", "RFC 8032", KeyAlgorithmType.EdDSA, "1.3.101.113"),
+ ;
+
+ private final String name;
+ private final String origin;
+ private final KeyAlgorithmType algorithm;
+ private final String oid;
+
+ KeyType(String name, String origin, KeyAlgorithmType algorithm, String
oid) {
+ this.name = name;
+ this.origin = origin;
+ this.algorithm = algorithm;
+ this.oid = oid;
+ }
+
+
+ public String getName() {
+ return name;
+ }
+
+ public KeyAlgorithmType getAlgorithm() {
+ return algorithm;
+ }
+
+ public String getOid() {
+ return oid;
+ }
+
+ public String getOrigin() {
+ return origin;
+ }
+
+ public static KeyType getByOid(String oid) {
+ return Arrays.stream(KeyType.values())
+ .filter(keyType -> keyType.getOid().equals(oid))
+ .findFirst().orElse(null);
+ }
+ }
+
+ /**
+ * Method generates DH keypair which match the type of given public key
type.
+ *
+ * @param recipientPublicKey public key of recipient
+ * @param provider provider to use for key generation
+ * @return generated keypair
+ * @throws XMLEncryptionException if the keys cannot be generated
+ */
+ public static KeyPair generateEphemeralDHKeyPair(PublicKey
recipientPublicKey, Provider provider) throws XMLEncryptionException {
+ String algorithm = recipientPublicKey.getAlgorithm();
+ KeyPairGenerator keyPairGenerator;
+ try {
+
+ if (recipientPublicKey instanceof ECPublicKey) {
+ keyPairGenerator = createKeyPairGenerator(algorithm, provider);
+ ECPublicKey exchangePublicKey = (ECPublicKey)
recipientPublicKey;
+ String keyOId =
ECDSAUtils.getOIDFromPublicKey(exchangePublicKey);
+ if (keyOId == null) {
+ keyOId =
DERDecoderUtils.getAlgorithmIdFromPublicKey(recipientPublicKey);
+ }
+ ECGenParameterSpec kpgparams = new ECGenParameterSpec(keyOId);
+ keyPairGenerator.initialize(kpgparams);
+ } else {
+ String keyOId =
DERDecoderUtils.getAlgorithmIdFromPublicKey(recipientPublicKey);
+ KeyType keyType = KeyType.getByOid(keyOId);
+ keyPairGenerator =
createKeyPairGenerator(keyType==null?keyOId: keyType.getName() , provider);
+ }
+
+ return keyPairGenerator.generateKeyPair();
+ } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException
| DERDecodingException e) {
+ throw new XMLEncryptionException(e);
+ }
+ }
+
+ /**
+ * Create a KeyPairGenerator for the given algorithm and provider.
+ *
+ * @param algorithm the key JCE algorithm name
+ * @param provider the provider to use or null if default JCE provider
should be used
+ * @return the KeyPairGenerator
+ * @throws NoSuchAlgorithmException if the algorithm is not supported
+ */
+ public static KeyPairGenerator createKeyPairGenerator(String algorithm,
Provider provider) throws NoSuchAlgorithmException {
+ return provider == null ? KeyPairGenerator.getInstance(algorithm)
+ : KeyPairGenerator.getInstance(algorithm, provider);
+ }
+
+
+ /**
+ * Method generates a secret key for given KeyAgreementParameterSpec.
+ *
+ * @param parameterSpec KeyAgreementParameterSpec which defines algorithm
to derive key
+ * @return generated secret key
+ * @throws XMLEncryptionException if the secret key cannot be generated
as: Key agreement is not supported,
+ * wrong key types, etc.
+ */
+ public static SecretKey
aesWrapKeyWithDHGeneratedKey(KeyAgreementParameterSpec parameterSpec)
+ throws XMLEncryptionException {
+ try {
+ PublicKey publicKey = parameterSpec.getAgreementPublicKey();
+ PrivateKey privateKey = parameterSpec.getAgreementPrivateKey();
+
+ String algorithm = publicKey.getAlgorithm();
+ if ("EC".equalsIgnoreCase(algorithm)) {
+ LOG.log(Level.WARNING, "EC keys are detected for key agreement
algorithm! " +
+ "Cryptographic algorithm may not be secure, consider
using a different algorithm (and keys). ");
+ }
+ algorithm = algorithm + (algorithm.equalsIgnoreCase("EC") ? "DH" :
"");
+ KeyAgreement keyAgreement = KeyAgreement.getInstance(algorithm);
+ keyAgreement.init(privateKey);
+ keyAgreement.doPhase(publicKey, true);
+ byte[] secret = keyAgreement.generateSecret();
+ byte[] kek = deriveKeyEncryptionKey(secret,
parameterSpec.getKeyDerivationParameter());
+ return new SecretKeySpec(kek, "AES");
+
+
+ } catch (XMLSecurityException | NoSuchAlgorithmException |
InvalidKeyException e) {
+ throw new XMLEncryptionException(e);
+ }
+ }
+
+ /**
+ * Defines the key size for the encrypting algorithm.
+ *
+ * @param keyWrapAlg the key wrap algorithm URI
+ * @return the key size in bits
+ * @throws XMLEncryptionException if the key wrap algorithm is not
supported
+ */
+ public static int getAESKeyBitSizeForWrapAlgorithm(String keyWrapAlg)
throws XMLEncryptionException {
+ switch (keyWrapAlg) {
+ case EncryptionConstants.ALGO_ID_KEYWRAP_AES128:
+ return 128;
+ case EncryptionConstants.ALGO_ID_KEYWRAP_AES192:
+ return 192;
+ case EncryptionConstants.ALGO_ID_KEYWRAP_AES256:
+ return 256;
+ default:
+ throw new XMLEncryptionException("Unsupported KeyWrap
Algorithm");
+ }
+ }
+
+
+ /**
+ * Derive a key encryption key from a shared secret and
keyDerivationParameter. Currently only the ConcatKDF is supported.
+ * @param sharedSecret the shared secret
+ * @param keyDerivationParameter the key derivation parameters
+ * @return the derived key encryption key
+ * @throws XMLSecurityException if the key derivation algorithm is not
supported
+ */
+ public static byte[] deriveKeyEncryptionKey(byte[] sharedSecret,
KeyDerivationParameter keyDerivationParameter)
+ throws XMLSecurityException {
+ int iKeySize = keyDerivationParameter.getKeyBitLength()/8;
+ String keyDerivationAlgorithm = keyDerivationParameter.getAlgorithm();
+ if
(!EncryptionConstants.ALGO_ID_KEYDERIVATION_CONCATKDF.equals(keyDerivationAlgorithm))
{
+ throw new XMLEncryptionException( "unknownAlgorithm",
+ keyDerivationAlgorithm);
+ }
+ ConcatKeyDerivationParameter ckdfParameter =
(ConcatKeyDerivationParameter) keyDerivationParameter;
+
+
+ // get parameters
+ String digestAlgorithm =ckdfParameter.getDigestAlgorithm();
+ if (digestAlgorithm == null) {
+ LOG.log(Level.DEBUG, "Missing ConcatKDFParams Digest algorithm for
key derivation with algorithm [{0}]", keyDerivationAlgorithm);
Review Comment:
see above
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]