github-advanced-security[bot] commented on code in PR #234:
URL: 
https://github.com/apache/santuario-xml-security-java/pull/234#discussion_r1378570296


##########
src/main/java/org/apache/xml/security/keys/derivedKey/ConcatKDF.java:
##########
@@ -0,0 +1,232 @@
+/**
+ * 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.keys.derivedKey;
+
+import org.apache.xml.security.algorithms.MessageDigestAlgorithm;
+import org.apache.xml.security.encryption.XMLEncryptionException;
+import org.apache.xml.security.exceptions.XMLSecurityException;
+
+import java.lang.System.Logger.Level;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * Key DerivationAlgorithm implementation, defined in Section 5.8.1 of NIST SP 
800-56A [SP800-56A], and is equivalent
+ * to the KDF3 function defined in ANSI X9.44-2007 [ANSI-X9-44-2007] when the 
contents of the OtherInfo parameter
+ * is structured as in NIST SP 800-56A.
+ * <p>
+ * Identifier:  http://www.w3.org/2009/xmlenc11#ConcatKDF
+ */
+public class ConcatKDF implements DerivationAlgorithm {
+
+    private static final System.Logger LOG = 
System.getLogger(ConcatKDF.class.getName());
+
+    String algorithmURI;
+
+    /**
+     * Constructor ConcatKDF with digest algorithmURI parameter such as 
http://www.w3.org/2001/04/xmlenc#sha256,
+     * http://www.w3.org/2001/04/xmlenc#sha512, etc.
+     */
+    public ConcatKDF(String algorithmURI) {
+        this.algorithmURI = algorithmURI;
+    }
+
+
+    /**
+     * Default Constructor which sets the default digest algorithmURI 
parameter:  http://www.w3.org/2001/04/xmlenc#sha256,
+     */
+    public ConcatKDF() {
+        this(MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA256);
+    }
+
+
+
+    /**
+     * Key DerivationAlgorithm implementation as defined in Section 5.8.1 of 
NIST SP 800-56A [SP800-56A]
+     * <ul>
+     * <li> reps = ⎡ keydatalen / hashlen⎤.</li>
+     * <li> If reps > (2>32 −1), then ABORT: output an error indicator and 
stop.</li>
+     * <li> Initialize a 32-bit, big-endian bit string counter as 
0000000116.</li>
+     * <li> If counter || Z || OtherInfo is more than max_hash_inputlen bits 
long, then ABORT: output an error indicator and stop.
+     * <li> For i = 1 to reps by 1, do the following:<ul>
+     *     <li> Compute Hashi = H(counter || Z || OtherInfo).</li>
+     *     <li> Increment counter (modulo 232), treating it as an unsigned 
32-bit integer.</li>
+     * </ul></li>
+     * <li> Let Hhash be set to Hashreps if (keydatalen / hashlen) is an 
integer; otherwise, let Hhash  be set to the
+     * (keydatalen mod hashlen) leftmost bits of Hashreps.</li>
+     * <li>Set DerivedKeyingMaterial = Hash1 || Hash2 || ... || Hashreps-1 || 
Hhash</li>
+     * </ul>
+     *
+     * @param secret    The "shared" secret to use for key derivation (e.g. 
the secret key)
+     * @param otherInfo as specified in [SP800-56A] the optional  attributes:  
AlgorithmID, PartyUInfo, PartyVInfo, SuppPubInfo and SuppPrivInfo attributes  
are concatenated to form a bit string “OtherInfo” that is used with the key 
derivation function.
+     * @param offset    the offset parameter is ignored by this implementation.
+     * @param keyLength The length of the key to derive
+     * @return The derived key
+     * @throws XMLEncryptionException
+     */
+    @Override
+    public byte[] deriveKey(byte[] secret, byte[] otherInfo, int offset, long 
keyLength) throws XMLSecurityException {
+
+        MessageDigest digest = 
MessageDigestAlgorithm.getDigestInstance(algorithmURI);
+
+        long genKeyLength = offset+keyLength;
+
+        int iDigestLength = digest.getDigestLength();
+        if (genKeyLength / iDigestLength > (long) Integer.MAX_VALUE) {
+            LOG.log(Level.DEBUG, "Key size is to long to be derived with hash 
algorithm [{0}]", algorithmURI);
+            throw new XMLEncryptionException("errorInKeyDerivation");
+        }
+        int toGenerateSize = (int) genKeyLength;
+
+        digest.reset();
+        ByteBuffer indexBuffer = 
ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN);
+
+        ByteBuffer result = ByteBuffer.allocate(toGenerateSize);
+
+        int counter = 1;
+        while (toGenerateSize > 0) {
+            indexBuffer.position(0);
+            indexBuffer.putInt(counter++);
+            indexBuffer.position(0);
+            digest.update(indexBuffer);
+            digest.update(secret);
+            if (otherInfo != null && otherInfo.length > 0) {
+                digest.update(otherInfo);
+            }
+            result.put(digest.digest(), 0, toGenerateSize < iDigestLength ? 
toGenerateSize : iDigestLength);
+            toGenerateSize -= iDigestLength;
+        }
+        if (offset > 0) {
+            result.position(offset);
+            return result.slice().array();
+        }
+        return result.array();
+    }
+
+    /**
+     * Method concatenate the bitstrings in following order {@code algID || 
partyUInfo || partyVInfo || suppPubInfo || suppPrivInfo}.
+     * to crate otherInfo as key derivation function input.
+     * If named parameters are null the value is ignored.
+     * Method parses the bitstring firs {@See 
https://www.w3.org/TR/xmlenc-core1/#sec-ConcatKDF} and then concatenates them 
to a byte array.
+     *
+     * @param sharedSecret The "shared" secret to use for key derivation (e.g. 
the secret key)
+     * @param algID        A bit string that indicates how the derived keying 
material will be parsed and for which
+     *                     algorithm(s) the derived secret keying material 
will be used.
+     * @param partyUInfo   A bit string containing public information that is 
required by the
+     *                     application using this KDF to be contributed by 
party U to the key derivation
+     *                     process. At a minimum, PartyUInfo shall include 
IDU, the identifier of party U. See
+     *                     the notes below..
+     * @param partyVInfo   A bit string containing public information that is 
required by the
+     *                     application using this KDF to be contributed by 
party V to the key derivation
+     *                     process. At a minimum, PartyVInfo shall include 
IDV, the identifier of party V. See
+     *                     the notes below.
+     * @param suppPubInfo  bit string containing additional, mutually-known 
public information.
+     * @param suppPrivInfo The suppPrivInfo A bit string containing 
additional, mutually-known public Information.
+     * @param keyLength    The length of the key to derive
+     * @return The resulting other info.
+     */
+    public byte[] deriveKey(final byte[] sharedSecret,
+                            final String algID,
+                            final String partyUInfo,
+                            final String partyVInfo,
+                            final String suppPubInfo,
+                            final String suppPrivInfo,
+                            final long keyLength)
+            throws XMLSecurityException {
+
+        final byte[] otherInfo = concatParameters(algID, partyUInfo, 
partyVInfo, suppPubInfo, suppPrivInfo);
+
+        return deriveKey(sharedSecret, otherInfo, keyLength);
+    }
+
+
+    /**
+     * Simple method to concatenate non-padded bitstream ConcatKDF parameters.
+     * If parameters are null the value is ignored.
+     *
+     * @param parameters the parameters to concatenate
+     * @return the concatenated parameters as byte array
+     */
+    public static byte[] concatParameters(final String... parameters) throws 
XMLEncryptionException {
+
+        List<byte[]> byteParams = new ArrayList<>();
+        for (String parameter : parameters) {
+            byte[] bytes = parseBitString(parameter);
+            byteParams.add(bytes);
+        }
+        // get bytearrays size
+        int iSize = byteParams.stream().map(ConcatKDF::getSize).reduce(0, (a, 
b) -> a + b);
+
+        ByteBuffer buffer = ByteBuffer
+                .allocate(iSize);
+        byteParams.stream().forEach(buffer::put);
+        return buffer.array();
+    }
+
+
+    /**
+     * The method validates the bitstring parameter structure and returns byte 
array of the parameter.
+     * <p/>
+     * The bitstring is divided into octets using big-endian encoding. 
Parameter starts with two characters (hex number) defining the number of 
padding bits followed by hex-string.
+     * the length of the bitstring is not a multiple of 8 then add padding 
bits (value 0) as necessary to the last octet to make it a multiple of 8.
+     *
+     * @param kdfParameter the parameter to parse
+     * @return the parsed parameter as byte array
+     */
+    private static byte[] parseBitString(final String kdfParameter) throws 
XMLEncryptionException {
+        // ignore empty parameters
+        if (kdfParameter == null || kdfParameter.isEmpty()) {
+            return new byte[0];
+        }
+        String kdfP = kdfParameter.trim();
+        int paramLen = kdfP.length();
+        // bit string mus have two chars following by first byte defining the 
number of padding bits
+        if (paramLen < 4) {
+            LOG.log(Level.ERROR, "ConcatKDF parameter is to short");
+            throw new XMLEncryptionException( "errorInKeyDerivation");
+        }
+        if (paramLen % 2 != 0) {
+            LOG.log(Level.ERROR, "Invalid length of ConcatKDF parameter 
[{}]!", kdfP);
+            throw new XMLEncryptionException( "errorInKeyDerivation");
+        }
+        int iPadding = Integer.parseInt(kdfP.substring(0, 2), 16);

Review Comment:
   ## Missing catch of NumberFormatException
   
   Potential uncaught 'java.lang.NumberFormatException'.
   
   [Show more 
details](https://github.com/apache/santuario-xml-security-java/security/code-scanning/1134)



##########
src/main/java/org/apache/xml/security/utils/DERDecoderUtils.java:
##########
@@ -0,0 +1,253 @@
+/**
+ * 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.
+     * @throws IOException

Review Comment:
   ## Javadoc has impossible 'throws' tag
   
   Javadoc for readObjectIdentifier claims to throw IOException but this is 
impossible.
   
   [Show more 
details](https://github.com/apache/santuario-xml-security-java/security/code-scanning/1135)



##########
src/main/java/org/apache/xml/security/utils/DERDecoderUtils.java:
##########
@@ -0,0 +1,253 @@
+/**
+ * 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.
+     * @throws IOException
+     */
+    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");
+            byte[] oidBytes = new byte[length];
+            derEncodedIS.read(oidBytes);
+            return oidBytes;
+        } catch (IOException e) {
+            throw new DERDecodingException(e, "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");
+        }
+    }
+
+    private static void validateType(int iType, byte expectedType) throws 
DERDecodingException {
+        validateType((byte) (iType & 0xFF), expectedType);
+    }
+
+    private static void validateType(byte type, byte expectedType) throws 
DERDecodingException {
+        if (type != expectedType) {
+            throw new DERDecodingException("DER decoding error: Expected type 
[" + expectedType + "] but got [" + type + "]");
+        }
+    }
+
+    /**
+     * Get the DER length at the current position.
+     * <p>
+     * DER length is encoded as
+     * <ul>
+     * <li>If the first byte is 0x00 to 0x7F, it describes the actual length.
+     * <li>If the first byte is 0x80 + n with 0<n<0x7F, the actual length is
+     * described in the following 'n' bytes.
+     * <li>The length value 0x80, used only in constructed types, is
+     * defined as "indefinite length".
+     * </ul>
+     *
+     * @return the length, -1 for indefinite length.
+     * @throws DERDecodingException if the current position is at the end of 
the array or there is
+     *                              an incomplete length specification.
+     */
+    public static int readLength(InputStream derEncodedIs) throws 
DERDecodingException, IOException {
+        if (derEncodedIs.available() <= 0) {
+            throw new DERDecodingException("Invalid DER format");
+        }
+
+        int value = derEncodedIs.read();
+
+        if ((value & 0x080) == 0x00) { // short form, 1 byte size
+            return value;
+        }
+        // number of bytes used to encode length
+        int byteCount = value & 0x07f;
+        //byteCount == 0 indicates indefinite length encoded data.
+        if (byteCount == 0)
+            return -1;
+
+        // byteCount> 4 not able to handle more than 4Gb of data (max int 
size) tmp > 4 indicates.
+        if (byteCount < 0 || byteCount > 4) {
+            throw new DERDecodingException("Data length byte size: [" + 
byteCount + "] is incorrect/too big");
+        }
+        byte[] intSizeBytes = new byte[byteCount];
+        derEncodedIs.read(intSizeBytes);

Review Comment:
   ## Ignored error status of call
   
   Method readLength ignores exceptional return value of InputStream.read.
   
   [Show more 
details](https://github.com/apache/santuario-xml-security-java/security/code-scanning/1133)



##########
src/main/java/org/apache/xml/security/utils/DERDecoderUtils.java:
##########
@@ -0,0 +1,253 @@
+/**
+ * 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.
+     * @throws IOException
+     */
+    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");
+            byte[] oidBytes = new byte[length];
+            derEncodedIS.read(oidBytes);

Review Comment:
   ## Ignored error status of call
   
   Method readObjectIdentifier ignores exceptional return value of 
InputStream.read.
   
   [Show more 
details](https://github.com/apache/santuario-xml-security-java/security/code-scanning/1132)



##########
src/main/java/org/apache/xml/security/keys/derivedKey/KeyDerivationMethodImpl.java:
##########
@@ -0,0 +1,98 @@
+/**
+ * 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;
+
+/**
+ * Class KeyDerivationMethodImpl is an DOM implementation of the 
KeyDerivationMethod
+ *
+ */
+public class KeyDerivationMethodImpl extends Encryption11ElementProxy 
implements KeyDerivationMethod {
+    ConcatKDFParamsImpl concatKDFParams;
+
+    /**
+     * Constructor KeyDerivationMethodImpl creates a new 
KeyDerivationMethodImpl instance.
+     *
+     * @param doc the Document in which to create the DOM tree
+     */
+    public KeyDerivationMethodImpl(Document doc) {
+        super(doc);
+    }
+
+    /**
+     * Constructor KeyDerivationMethodImpl from existing XML element
+     *
+     * @param element the element to use as source
+     * @param baseURI the URI of the resource where the XML instance was stored
+     * @throws XMLSecurityException
+     */
+    public KeyDerivationMethodImpl(Element element, String baseURI) throws 
XMLSecurityException {
+        super(element, baseURI);
+    }
+
+    /**
+     * Sets the <code>Algorithm</code> attribute
+     *
+     * @param algorithm ID
+     */
+    public void setAlgorithm(String algorithm) {
+        if (algorithm != null) {
+            setLocalIdAttribute(EncryptionConstants._ATT_ALGORITHM, algorithm);
+        }
+    }
+
+    public String getAlgorithm() {

Review Comment:
   ## Missing Override annotation
   
   This method overrides [KeyDerivationMethod.getAlgorithm](1); it is advisable 
to add an Override annotation.
   
   [Show more 
details](https://github.com/apache/santuario-xml-security-java/security/code-scanning/1136)



##########
src/main/java/org/apache/xml/security/utils/DERDecoderUtils.java:
##########
@@ -0,0 +1,253 @@
+/**
+ * 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.
+     * @throws IOException
+     */
+    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");
+            byte[] oidBytes = new byte[length];
+            derEncodedIS.read(oidBytes);
+            return oidBytes;
+        } catch (IOException e) {
+            throw new DERDecodingException(e, "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");
+        }
+    }
+
+    private static void validateType(int iType, byte expectedType) throws 
DERDecodingException {
+        validateType((byte) (iType & 0xFF), expectedType);
+    }
+
+    private static void validateType(byte type, byte expectedType) throws 
DERDecodingException {
+        if (type != expectedType) {
+            throw new DERDecodingException("DER decoding error: Expected type 
[" + expectedType + "] but got [" + type + "]");
+        }
+    }
+
+    /**
+     * Get the DER length at the current position.
+     * <p>
+     * DER length is encoded as
+     * <ul>
+     * <li>If the first byte is 0x00 to 0x7F, it describes the actual length.
+     * <li>If the first byte is 0x80 + n with 0<n<0x7F, the actual length is
+     * described in the following 'n' bytes.
+     * <li>The length value 0x80, used only in constructed types, is
+     * defined as "indefinite length".
+     * </ul>
+     *
+     * @return the length, -1 for indefinite length.
+     * @throws DERDecodingException if the current position is at the end of 
the array or there is
+     *                              an incomplete length specification.
+     */
+    public static int readLength(InputStream derEncodedIs) throws 
DERDecodingException, IOException {
+        if (derEncodedIs.available() <= 0) {
+            throw new DERDecodingException("Invalid DER format");
+        }
+
+        int value = derEncodedIs.read();
+
+        if ((value & 0x080) == 0x00) { // short form, 1 byte size
+            return value;
+        }
+        // number of bytes used to encode length
+        int byteCount = value & 0x07f;
+        //byteCount == 0 indicates indefinite length encoded data.
+        if (byteCount == 0)
+            return -1;
+
+        // byteCount> 4 not able to handle more than 4Gb of data (max int 
size) tmp > 4 indicates.
+        if (byteCount < 0 || byteCount > 4) {

Review Comment:
   ## Useless comparison test
   
   Test is always false, because of [this condition](1).
   
   [Show more 
details](https://github.com/apache/santuario-xml-security-java/security/code-scanning/1137)



##########
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", ""),
+        EdDSA("EdDSA", "");
+        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"),
+        C2TNB191V1("c2tnb191v1", "X9.62", KeyAlgorithmType.EC, 
"1.2.840.10045.3.0.5"),
+        C2TNB191V3("c2tnb191v3", "X9.62", KeyAlgorithmType.EC, 
"1.2.840.10045.3.0.7"),
+        C2TNB239V1("c2tnb239v1", "X9.62", KeyAlgorithmType.EC, 
"1.2.840.10045.3.0.11"),
+        C2TNB239V2("c2tnb239v2", "X9.62", KeyAlgorithmType.EC, 
"1.2.840.10045.3.0.12"),
+        C2TNB239V3("c2tnb239v3", "X9.62", KeyAlgorithmType.EC, 
"1.2.840.10045.3.0.13"),
+        C2TNB359V1("c2tnb359v1", "X9.62", KeyAlgorithmType.EC, 
"1.2.840.10045.3.0.18"),
+        C2TNB431R1("c2tnb431r1", "X9.62", KeyAlgorithmType.EC, 
"1.2.840.10045.3.0.20"),
+        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
+     */
+    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
+     */
+    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
+     */
+    public static SecretKey 
aesWrapKeyWithDHGeneratedKey(KeyAgreementParameterSpec parameterSpec)
+            throws XMLEncryptionException {
+        try {
+            PublicKey publicKey = parameterSpec.getAgreementPublicKey();
+            PrivateKey privateKey = parameterSpec.getAgreementPrivateKey();
+
+            String algorithm = publicKey.getAlgorithm();
+            // java 11 uses ECDH instead of EC
+            algorithm = algorithm.equalsIgnoreCase("EC") ? "ECDH" : algorithm;
+            KeyAgreement keyAgreement = KeyAgreement.getInstance(algorithm);

Review Comment:
   ## Use of a potentially broken or risky cryptographic algorithm
   
   Cryptographic algorithm [ECDH](1) may not be secure, consider using a 
different algorithm.
   
   [Show more 
details](https://github.com/apache/santuario-xml-security-java/security/code-scanning/1138)



-- 
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]


Reply via email to