seanjmullan commented on code in PR #234:
URL: 
https://github.com/apache/santuario-xml-security-java/pull/234#discussion_r1431443884


##########
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.

Review Comment:
   Can throw this for other reasons than null.



##########
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("Error occurred while reading the 
input stream.", ex);
+        }
+    }
+
+
+    public static String getAlgorithmIdFromPublicKey(PublicKey publicKey) 
throws DERDecodingException {

Review Comment:
   Missing javadoc description.



##########
src/main/java/org/apache/xml/security/utils/KeyUtils.java:
##########
@@ -0,0 +1,286 @@
+/**
+ * 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.encryption.keys.content.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"),
+        RSASSA_PSS("RSASSA-PSS", "1.2.840.113549.1.1.10"),
+        DH("DiffieHellman", "1.2.840.113549.1.3.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 {
+        DSA("DSA", "RFC 8017", KeyAlgorithmType.DSA, "1.2.840.10040.4.1"),
+        RSA("RSA", "RFC 8017", KeyAlgorithmType.RSA, "1.2.840.113549.1.1.1"),
+        RSASSA_PSS("RSASSA-PSS", "RFC 3447", KeyAlgorithmType.RSASSA_PSS, 
"1.2.840.113549.1.1.10"),
+        DH("DH", "PKCS #3", KeyAlgorithmType.DH, "1.2.840.113549.1.3.1"),
+        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) {

Review Comment:
   if digestAlgorithm should never be null, it is better to check for this in 
the `ConcatKeyDerivationParameter` ctor and then you don't have to check for 
null here.



##########
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("Error occurred while reading the 
input stream.", ex);
+        }
+    }
+
+
+    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 ex) {
+            throw new DERDecodingException("Error reading public key", ex);
+        }
+    }
+
+    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

Review Comment:
   can also throw `IOException`.



##########
src/main/java/org/apache/xml/security/utils/XMLUtils.java:
##########
@@ -706,6 +706,30 @@ public static Element selectXencNode(Node sibling, String 
nodeName, int number)
         return null;
     }
 
+    /**
+     * Helper method to get the "number"-th element for a given local
+     * name and namespace: http://www.w3.org/2009/xmlenc11#. If element with 
given search parameters is not found,
+     * null is returned.
+     *
+     * @param sibling the sibling node from which to start searching
+     * @param nodeName the local name of the element to search for
+     * @param number the index of the element to search for
+     * @return node with the given node name or null if not found.
+     */
+    public static Element selectXenc11Node(Node sibling, String nodeName, int 
number) {

Review Comment:
   Can you make the namespace a parameter, so this is not specific to XML enc?



##########
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.

Review Comment:
   can throw this for other reasons than null.



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