Re: [PR] Feature/santuario 615 xades extension [santuario- xml-security-java]
github-advanced-security[bot] (via GitHub) <[email protected]> Mon, 15 Jun 2026 12:52:55 -0000
| Newsgroups | gmane.text.xml.security.devel |
|---|---|
| Message-ID | <PR_kwDOD7oF987misly-4c9778f8-fa50-4cc5-a4e6-82574cde839a@gitbox.apache.org> |
github-advanced-security[bot] commented on code in PR #617:
URL: https://github.com/apache/santuario-xml-security-java/pull/617#discussion_r3413581736
##########
src/main/java/org/apache/xml/security/extension/xades/XAdESBBValidator.java:
##########
@@ -0,0 +1,383 @@
+/**
+ * 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.extension.xades;
+
+import org.apache.xml.security.algorithms.JCEMapper;
+import org.apache.xml.security.exceptions.XMLSecurityException;
+import org.apache.xml.security.signature.Reference;
+import org.apache.xml.security.signature.SignedInfo;
+import org.apache.xml.security.signature.XMLSignature;
+import org.apache.xml.security.utils.ClassLoaderUtils;
+import org.apache.xml.security.utils.Constants;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.ls.LSInput;
+import org.w3c.dom.ls.LSResourceResolver;
+import org.xml.sax.SAXException;
+
+import javax.xml.XMLConstants;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import javax.xml.validation.Validator;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.List;
+
+/**
+ * Validates XAdES-B-B (Basic Electronic Signature) qualifying properties embedded in an
+ * {@link XMLSignature}.
+ *
+ * <h3>Validation performed</h3>
+ * <ol>
+ * <li><b>Presence check</b> — determines whether {@code xades132:QualifyingProperties}
+ * is present in the signature's {@code ds:Object} elements. If not present the
+ * result is reported as {@link XAdESValidationResult#isXAdESPresent()} == {@code false}
+ * and no further checks are run.</li>
+ * <li><b>XSD structural validation</b> — validates the {@code QualifyingProperties} subtree
+ * against the bundled XAdES v1.3.2 schema ({@code XAdES01903v132-201601.xsd}).</li>
+ * <li><b>Target attribute</b> — {@code QualifyingProperties/@Target} must equal
+ * {@code "#"} + the signature element {@code Id}.</li>
+ * <li><b>SignedProperties reference</b> — the signature must contain a
+ * {@code ds:Reference} whose {@code @Type} equals
+ * {@link XAdESConstants#REFERENCE_TYPE_SIGNEDPROPERTIES}.</li>
+ * <li><b>Signing certificate digest</b> — the {@code CertDigest} value inside
+ * {@code SigningCertificate/Cert} must match the SHA-256 (or configured algorithm)
+ * digest of the provided signing certificate.</li>
+ * </ol>
+ *
+ * <h3>Usage</h3>
+ * <pre>{@code
+ * XAdESBBValidator validator = new XAdESBBValidator();
+ * XAdESValidationResult result = validator.validate(signature, signingCertificate);
+ * if (result.isXAdESPresent() && !result.isValid()) {
+ * result.getViolations().forEach(System.out::println);
+ * }
+ * }</pre>
+ *
+ * <p>The schema is loaded once at class-load time and reused across instances.
+ *
+ * @see <a href="https://www.etsi.org/deliver/etsi_en/319100_319199/31913201/01.03.01_60/en_31913201v010301p.pdf">
+ * ETSI EN 319 132-1 (XAdES)</a>
+ */
+public final class XAdESBBValidator {
+
+ private static final String XADES_SCHEMA_RESOURCE ="bindings/schemas/XAdES01903v141-202107.xsd";
+
+ /**
+ * Schema is thread-safe once constructed; load once and share.
+ * Null if schema loading failed at class init time.
+ */
+ private static final Schema XADES_SCHEMA = loadSchema();
+
+ private static Schema loadSchema() {
+ try {
+ SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+ sf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+ sf.setResourceResolver(new SchemeResourceResolver());
+ // load all schema resources from classpath and combine into a single schema
+ String xadesUri = resourceUri(XADES_SCHEMA_RESOURCE);
+ try (InputStream xadesIs = ClassLoaderUtils.getResourceAsStream(
+ XADES_SCHEMA_RESOURCE, XAdESBBValidator.class)) {
+ return sf.newSchema(new StreamSource(xadesIs, xadesUri));
+ }
+ } catch (SAXException | IOException e) {
+ // Logged here; validate() reports the violation rather than crashing callers
+ System.getLogger(XAdESBBValidator.class.getName())
+ .log(System.Logger.Level.ERROR,
+ "Failed to load XAdES schema — XSD validation will be skipped", e);
+ return null;
+ }
+ }
+
+ private static String resourceUri(String path) {
+ java.net.URL url = ClassLoaderUtils.getResource(path, XAdESBBValidator.class);
+ if (url == null) {
+ throw new IllegalStateException("XAdES schema not found on classpath: " + path);
+ }
+ return url.toExternalForm();
+ }
+
+ /**
+ * Validates XAdES-B-B properties in {@code signature}.
+ *
+ * @param signature the cryptographically verified {@link XMLSignature}
+ * (core verification must have already succeeded)
+ * @param signingCertificate the certificate used to create the signature;
+ * used to check the {@code CertDigest} value
+ * @return validation result; never {@code null}
+ */
+ public XAdESValidationResult validate(XMLSignature signature,
+ X509Certificate signingCertificate) {
+ List<String> violations = new ArrayList<>();
+
+ Element qualifyingProps = findQualifyingProperties(signature);
+ if (qualifyingProps == null) {
+ return XAdESValidationResult.notPresent();
+ }
+
+ validateSchema(qualifyingProps, violations);
+ validateTarget(qualifyingProps, signature, violations);
+ validateSignedPropertiesReference(signature, violations);
+ if (signingCertificate != null) {
+ validateCertDigest(qualifyingProps, signingCertificate, violations);
+ }
+
+ return new XAdESValidationResult(true, violations);
+ }
+
+ // -------------------------------------------------------------------------
+ // XAdES element discovery
+ // -------------------------------------------------------------------------
+
+ private Element findQualifyingProperties(XMLSignature signature) {
+ Element sigElement = signature.getElement();
+ NodeList objects = sigElement.getElementsByTagNameNS(
+ Constants.SignatureSpecNS, "Object");
+ for (int i = 0; i < objects.getLength(); i++) {
+ Element object = (Element) objects.item(i);
+ NodeList qpList = object.getElementsByTagNameNS(
+ XAdESConstants.XADES_V132_NS,
+ XAdESConstants.TAG_QUALIFYING_PROPERTIES);
+ if (qpList.getLength() > 0) {
+ return (Element) qpList.item(0);
+ }
+ }
+ return null;
+ }
+
+ // -------------------------------------------------------------------------
+ // XSD validation
+ // -------------------------------------------------------------------------
+
+ private void validateSchema(Element qualifyingProps, List<String> violations) {
+ if (XADES_SCHEMA == null) {
+ violations.add("XAdES schema not available — XSD validation skipped");
+ return;
+ }
+ try {
+ Validator validator = XADES_SCHEMA.newValidator();
+ validator.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+ // Collect all schema violations rather than stopping at first error
+ List<String> schemaViolations = new ArrayList<>();
+ validator.setErrorHandler(new SchemaErrorCollector(schemaViolations));
+ validator.validate(new DOMSource(qualifyingProps));
+ violations.addAll(schemaViolations);
+ } catch (SAXException | IOException e) {
+ violations.add("XSD validation error: " + e.getMessage());
+ }
+ }
+
+ // -------------------------------------------------------------------------
+ // Semantic checks
+ // -------------------------------------------------------------------------
+
+ private void validateTarget(Element qualifyingProps,
+ XMLSignature signature,
+ List<String> violations) {
+ String target = qualifyingProps.getAttribute("Target");
+ String signatureId = signature.getId();
+ if (signatureId == null || signatureId.isBlank()) {
+ violations.add("QualifyingProperties/@Target validation skipped: " +
+ "ds:Signature has no Id attribute");
+ return;
+ }
+ String expected = "#" + signatureId;
+ if (!expected.equals(target)) {
+ violations.add("QualifyingProperties/@Target '" + target +
+ "' does not match expected '" + expected + "'");
+ }
+ }
+
+ private void validateSignedPropertiesReference(XMLSignature signature,
+ List<String> violations) {
+ try {
+ SignedInfo si = signature.getSignedInfo();
+ for (int i = 0; i < si.getLength(); i++) {
+ Reference ref = si.item(i);
+ if (XAdESConstants.REFERENCE_TYPE_SIGNEDPROPERTIES.equals(ref.getType())) {
+ return; // found
+ }
+ }
+ } catch (XMLSecurityException e) {
+ violations.add("Cannot read ds:SignedInfo references: " + e.getMessage());
+ return;
+ }
+ violations.add("No ds:Reference with @Type='" +
+ XAdESConstants.REFERENCE_TYPE_SIGNEDPROPERTIES +
+ "' found — SignedProperties is not covered by the signature");
+ }
+
+ private void validateCertDigest(Element qualifyingProps,
+ X509Certificate signingCertificate,
+ List<String> violations) {
+ // Find the first CertDigest inside SigningCertificate/Cert
+ NodeList certDigestNodes = qualifyingProps.getElementsByTagNameNS(
+ XAdESConstants.XADES_V132_NS, "CertDigest");
+ if (certDigestNodes.getLength() == 0) {
+ violations.add("No xades132:CertDigest element found in QualifyingProperties");
+ return;
+ }
+ Element certDigest = (Element) certDigestNodes.item(0);
+
+ String algorithmURI = getChildTextContent(certDigest,
+ Constants.SignatureSpecNS, "DigestMethod", "Algorithm");
+ String digestValueB64 = getChildTextContent(certDigest,
+ Constants.SignatureSpecNS, "DigestValue", null);
+
+ if (algorithmURI == null || algorithmURI.isBlank()) {
+ violations.add("CertDigest/ds:DigestMethod/@Algorithm is missing or empty");
+ return;
+ }
+ if (digestValueB64 == null || digestValueB64.isBlank()) {
+ violations.add("CertDigest/ds:DigestValue is missing or empty");
+ return;
+ }
+
+ String jceAlgorithm = JCEMapper.translateURItoJCEID(algorithmURI);
+ if (jceAlgorithm == null) {
+ violations.add("Unknown digest algorithm URI in CertDigest: " + algorithmURI);
+ return;
+ }
+
+ byte[] reportedDigest;
+ try {
+ reportedDigest = Base64.getDecoder().decode(digestValueB64.trim());
+ } catch (IllegalArgumentException e) {
+ violations.add("CertDigest/ds:DigestValue is not valid Base64: " + e.getMessage());
+ return;
+ }
+
+ byte[] actualDigest;
+ try {
+ byte[] certDer = signingCertificate.getEncoded();
+ actualDigest = MessageDigest.getInstance(jceAlgorithm).digest(certDer);
Review Comment:
## CodeQL / Use of a broken or risky cryptographic algorithm
Cryptographic algorithm [DESede/CBC/ISO10126Padding](1) is insecure. It has a short key length of 56 bits, making it vulnerable to brute-force attacks. Consider using AES instead.
Cryptographic algorithm [DESedeWrap](2) is insecure. It has a short key length of 56 bits, making it vulnerable to brute-force attacks. Consider using AES instead.
[Show more details](https://github.com/apache/santuario-xml-security-java/security/code-scanning/1253)
##########
src/main/java/org/apache/xml/security/extension/xades/XAdESBBValidator.java:
##########
@@ -0,0 +1,383 @@
+/**
+ * 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.extension.xades;
+
+import org.apache.xml.security.algorithms.JCEMapper;
+import org.apache.xml.security.exceptions.XMLSecurityException;
+import org.apache.xml.security.signature.Reference;
+import org.apache.xml.security.signature.SignedInfo;
+import org.apache.xml.security.signature.XMLSignature;
+import org.apache.xml.security.utils.ClassLoaderUtils;
+import org.apache.xml.security.utils.Constants;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.ls.LSInput;
+import org.w3c.dom.ls.LSResourceResolver;
+import org.xml.sax.SAXException;
+
+import javax.xml.XMLConstants;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import javax.xml.validation.Validator;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.List;
+
+/**
+ * Validates XAdES-B-B (Basic Electronic Signature) qualifying properties embedded in an
+ * {@link XMLSignature}.
+ *
+ * <h3>Validation performed</h3>
+ * <ol>
+ * <li><b>Presence check</b> — determines whether {@code xades132:QualifyingProperties}
+ * is present in the signature's {@code ds:Object} elements. If not present the
+ * result is reported as {@link XAdESValidationResult#isXAdESPresent()} == {@code false}
+ * and no further checks are run.</li>
+ * <li><b>XSD structural validation</b> — validates the {@code QualifyingProperties} subtree
+ * against the bundled XAdES v1.3.2 schema ({@code XAdES01903v132-201601.xsd}).</li>
+ * <li><b>Target attribute</b> — {@code QualifyingProperties/@Target} must equal
+ * {@code "#"} + the signature element {@code Id}.</li>
+ * <li><b>SignedProperties reference</b> — the signature must contain a
+ * {@code ds:Reference} whose {@code @Type} equals
+ * {@link XAdESConstants#REFERENCE_TYPE_SIGNEDPROPERTIES}.</li>
+ * <li><b>Signing certificate digest</b> — the {@code CertDigest} value inside
+ * {@code SigningCertificate/Cert} must match the SHA-256 (or configured algorithm)
+ * digest of the provided signing certificate.</li>
+ * </ol>
+ *
+ * <h3>Usage</h3>
+ * <pre>{@code
+ * XAdESBBValidator validator = new XAdESBBValidator();
+ * XAdESValidationResult result = validator.validate(signature, signingCertificate);
+ * if (result.isXAdESPresent() && !result.isValid()) {
+ * result.getViolations().forEach(System.out::println);
+ * }
+ * }</pre>
+ *
+ * <p>The schema is loaded once at class-load time and reused across instances.
+ *
+ * @see <a href="https://www.etsi.org/deliver/etsi_en/319100_319199/31913201/01.03.01_60/en_31913201v010301p.pdf">
+ * ETSI EN 319 132-1 (XAdES)</a>
+ */
+public final class XAdESBBValidator {
+
+ private static final String XADES_SCHEMA_RESOURCE ="bindings/schemas/XAdES01903v141-202107.xsd";
+
+ /**
+ * Schema is thread-safe once constructed; load once and share.
+ * Null if schema loading failed at class init time.
+ */
+ private static final Schema XADES_SCHEMA = loadSchema();
+
+ private static Schema loadSchema() {
+ try {
+ SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+ sf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+ sf.setResourceResolver(new SchemeResourceResolver());
+ // load all schema resources from classpath and combine into a single schema
+ String xadesUri = resourceUri(XADES_SCHEMA_RESOURCE);
+ try (InputStream xadesIs = ClassLoaderUtils.getResourceAsStream(
+ XADES_SCHEMA_RESOURCE, XAdESBBValidator.class)) {
+ return sf.newSchema(new StreamSource(xadesIs, xadesUri));
+ }
+ } catch (SAXException | IOException e) {
+ // Logged here; validate() reports the violation rather than crashing callers
+ System.getLogger(XAdESBBValidator.class.getName())
+ .log(System.Logger.Level.ERROR,
+ "Failed to load XAdES schema — XSD validation will be skipped", e);
+ return null;
+ }
+ }
+
+ private static String resourceUri(String path) {
+ java.net.URL url = ClassLoaderUtils.getResource(path, XAdESBBValidator.class);
+ if (url == null) {
+ throw new IllegalStateException("XAdES schema not found on classpath: " + path);
+ }
+ return url.toExternalForm();
+ }
+
+ /**
+ * Validates XAdES-B-B properties in {@code signature}.
+ *
+ * @param signature the cryptographically verified {@link XMLSignature}
+ * (core verification must have already succeeded)
+ * @param signingCertificate the certificate used to create the signature;
+ * used to check the {@code CertDigest} value
+ * @return validation result; never {@code null}
+ */
+ public XAdESValidationResult validate(XMLSignature signature,
+ X509Certificate signingCertificate) {
+ List<String> violations = new ArrayList<>();
+
+ Element qualifyingProps = findQualifyingProperties(signature);
+ if (qualifyingProps == null) {
+ return XAdESValidationResult.notPresent();
+ }
+
+ validateSchema(qualifyingProps, violations);
+ validateTarget(qualifyingProps, signature, violations);
+ validateSignedPropertiesReference(signature, violations);
+ if (signingCertificate != null) {
+ validateCertDigest(qualifyingProps, signingCertificate, violations);
+ }
+
+ return new XAdESValidationResult(true, violations);
+ }
+
+ // -------------------------------------------------------------------------
+ // XAdES element discovery
+ // -------------------------------------------------------------------------
+
+ private Element findQualifyingProperties(XMLSignature signature) {
+ Element sigElement = signature.getElement();
+ NodeList objects = sigElement.getElementsByTagNameNS(
+ Constants.SignatureSpecNS, "Object");
+ for (int i = 0; i < objects.getLength(); i++) {
+ Element object = (Element) objects.item(i);
+ NodeList qpList = object.getElementsByTagNameNS(
+ XAdESConstants.XADES_V132_NS,
+ XAdESConstants.TAG_QUALIFYING_PROPERTIES);
+ if (qpList.getLength() > 0) {
+ return (Element) qpList.item(0);
+ }
+ }
+ return null;
+ }
+
+ // -------------------------------------------------------------------------
+ // XSD validation
+ // -------------------------------------------------------------------------
+
+ private void validateSchema(Element qualifyingProps, List<String> violations) {
+ if (XADES_SCHEMA == null) {
+ violations.add("XAdES schema not available — XSD validation skipped");
+ return;
+ }
+ try {
+ Validator validator = XADES_SCHEMA.newValidator();
+ validator.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+ // Collect all schema violations rather than stopping at first error
+ List<String> schemaViolations = new ArrayList<>();
+ validator.setErrorHandler(new SchemaErrorCollector(schemaViolations));
+ validator.validate(new DOMSource(qualifyingProps));
+ violations.addAll(schemaViolations);
+ } catch (SAXException | IOException e) {
+ violations.add("XSD validation error: " + e.getMessage());
+ }
+ }
+
+ // -------------------------------------------------------------------------
+ // Semantic checks
+ // -------------------------------------------------------------------------
+
+ private void validateTarget(Element qualifyingProps,
+ XMLSignature signature,
+ List<String> violations) {
+ String target = qualifyingProps.getAttribute("Target");
+ String signatureId = signature.getId();
+ if (signatureId == null || signatureId.isBlank()) {
+ violations.add("QualifyingProperties/@Target validation skipped: " +
+ "ds:Signature has no Id attribute");
+ return;
+ }
+ String expected = "#" + signatureId;
+ if (!expected.equals(target)) {
+ violations.add("QualifyingProperties/@Target '" + target +
+ "' does not match expected '" + expected + "'");
+ }
+ }
+
+ private void validateSignedPropertiesReference(XMLSignature signature,
+ List<String> violations) {
+ try {
+ SignedInfo si = signature.getSignedInfo();
+ for (int i = 0; i < si.getLength(); i++) {
+ Reference ref = si.item(i);
+ if (XAdESConstants.REFERENCE_TYPE_SIGNEDPROPERTIES.equals(ref.getType())) {
+ return; // found
+ }
+ }
+ } catch (XMLSecurityException e) {
+ violations.add("Cannot read ds:SignedInfo references: " + e.getMessage());
+ return;
+ }
+ violations.add("No ds:Reference with @Type='" +
+ XAdESConstants.REFERENCE_TYPE_SIGNEDPROPERTIES +
+ "' found — SignedProperties is not covered by the signature");
+ }
+
+ private void validateCertDigest(Element qualifyingProps,
+ X509Certificate signingCertificate,
+ List<String> violations) {
+ // Find the first CertDigest inside SigningCertificate/Cert
+ NodeList certDigestNodes = qualifyingProps.getElementsByTagNameNS(
+ XAdESConstants.XADES_V132_NS, "CertDigest");
+ if (certDigestNodes.getLength() == 0) {
+ violations.add("No xades132:CertDigest element found in QualifyingProperties");
+ return;
+ }
+ Element certDigest = (Element) certDigestNodes.item(0);
+
+ String algorithmURI = getChildTextContent(certDigest,
+ Constants.SignatureSpecNS, "DigestMethod", "Algorithm");
+ String digestValueB64 = getChildTextContent(certDigest,
+ Constants.SignatureSpecNS, "DigestValue", null);
+
+ if (algorithmURI == null || algorithmURI.isBlank()) {
+ violations.add("CertDigest/ds:DigestMethod/@Algorithm is missing or empty");
+ return;
+ }
+ if (digestValueB64 == null || digestValueB64.isBlank()) {
+ violations.add("CertDigest/ds:DigestValue is missing or empty");
+ return;
+ }
+
+ String jceAlgorithm = JCEMapper.translateURItoJCEID(algorithmURI);
+ if (jceAlgorithm == null) {
+ violations.add("Unknown digest algorithm URI in CertDigest: " + algorithmURI);
+ return;
+ }
+
+ byte[] reportedDigest;
+ try {
+ reportedDigest = Base64.getDecoder().decode(digestValueB64.trim());
+ } catch (IllegalArgumentException e) {
+ violations.add("CertDigest/ds:DigestValue is not valid Base64: " + e.getMessage());
+ return;
+ }
+
+ byte[] actualDigest;
+ try {
+ byte[] certDer = signingCertificate.getEncoded();
+ actualDigest = MessageDigest.getInstance(jceAlgorithm).digest(certDer);
Review Comment:
## CodeQL / Use of a potentially broken or risky cryptographic algorithm
Cryptographic algorithm [MD5](1) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [RIPEMD160](2) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [SHA-1](3) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [SHA-224](4) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [WHIRLPOOL](5) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [SHA3-224](6) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [SHA1withDSA](7) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [RSASSA-PSS](8) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [HmacMD5](9) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [HMACRIPEMD160](10) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [HmacSHA224](11) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [SEED/CBC/ISO10126Padding](12) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [Camellia/CBC/ISO10126Padding](13) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [Camellia/CBC/ISO10126Padding](14) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [Camellia/CBC/ISO10126Padding](15) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [AESWrap](16) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [AESWrap](17) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [AESWrap](18) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [CamelliaWrap](19) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [CamelliaWrap](20) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [CamelliaWrap](21) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [SEEDWrap](22) may not be secure. Consider using a different algorithm.
[Show more details](https://github.com/apache/santuario-xml-security-java/security/code-scanning/1255)
##########
src/main/java/org/apache/xml/security/extension/xades/XAdESSignatureProcessor.java:
##########
@@ -0,0 +1,279 @@
+/**
+ * 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.extension.xades;
+
+import org.apache.xml.security.algorithms.JCEMapper;
+import org.apache.xml.security.encryption.XMLCipher;
+import org.apache.xml.security.extension.SignatureExtensionException;
+import org.apache.xml.security.extension.SignatureProcessor;
+import org.apache.xml.security.signature.ObjectContainer;
+import org.apache.xml.security.signature.XMLSignature;
+import org.apache.xml.security.signature.XMLSignatureException;
+import org.apache.xml.security.stax.impl.util.IDGenerator;
+import org.apache.xml.security.transforms.TransformationException;
+import org.apache.xml.security.transforms.Transforms;
+import org.w3c.dom.Document;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.time.OffsetDateTime;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Pre-processor that adds XAdES-B-B (Basic Electronic Signature) qualifying properties
+ * to an XML signature before digests are computed.
+ *
+ * <p>The processor:
+ * <ol>
+ * <li>Assigns an {@code Id} to the {@code ds:Signature} element if one is not already set.</li>
+ * <li>Assigns an {@code Id} to the {@code ds:SignatureValue} element (enables XAdES-T extension).</li>
+ * <li>Builds an XAdES {@code QualifyingProperties} structure containing {@code SignedProperties}
+ * using DOM-based {@link XAdESElementProxy} classes — no JAXB dependency.</li>
+ * <li>Wraps it in a {@code ds:Object} and appends it to the signature.</li>
+ * <li>Adds a {@code ds:Reference} with type {@code SignedProperties} so that
+ * {@code SignedProperties} is covered by the signature digest.</li>
+ * </ol>
+ *
+ * <p>Create an instance using the {@link Builder}:
+ * <pre>{@code
+ * XAdESSignatureProcessor xades = XAdESSignatureProcessor.builder(certificate)
+ * .withSignaturePolicyImplied(true)
+ * .withSignatureCity("Brussels")
+ * .build();
+ * sig.addPreProcessor(xades);
+ * }</pre>
+ *
+ * @see <a href="https://www.etsi.org/deliver/etsi_en/319100_319199/31913201/01.03.01_60/en_31913201v010301p.pdf">
+ * ETSI EN 319 132-1 (XAdES)</a>
+ */
+public final class XAdESSignatureProcessor implements SignatureProcessor {
+
+ private static final String ID_PREFIX_SIG = "sig-";
+ private static final String ID_PREFIX_SIG_VAL = "sig-val-";
+ private static final String ID_PREFIX_SIG_PROP = "sig-prop-";
+
+ private final X509Certificate certificate;
+ private final String certificateDigestAlgorithmURI;
+ private final boolean signaturePolicyImplied;
+ private final String signatureCity;
+ private final String signatureCountryName;
+ private final List<String> referenceTransformAlgorithms;
+
+ private XAdESSignatureProcessor(Builder builder) {
+ this.certificate = builder.certificate;
+ this.certificateDigestAlgorithmURI = builder.certificateDigestAlgorithmURI;
+ this.signaturePolicyImplied = builder.signaturePolicyImplied;
+ this.signatureCity = builder.signatureCity;
+ this.signatureCountryName = builder.signatureCountryName;
+ this.referenceTransformAlgorithms = new ArrayList<>(builder.referenceTransformAlgorithms);
+ }
+
+ /**
+ * Creates a builder for configuring an {@link XAdESSignatureProcessor}.
+ *
+ * @param certificate the signing certificate; must not be {@code null}
+ */
+ public static Builder builder(X509Certificate certificate) {
+ return new Builder(certificate);
+ }
+
+ @Override
+ public void processSignature(XMLSignature signature) throws XMLSignatureException {
+ ensureSignatureId(signature);
+ ensureSignatureValueId(signature);
+
+ String signatureId = signature.getId();
+ String signedPropertiesId = IDGenerator.generateID(ID_PREFIX_SIG_PROP);
+ Document doc = signature.getElement().getOwnerDocument();
+
+ SignedSignatureProperties ssp = buildSignedSignatureProperties(doc);
+
+ SignedProperties sp = new SignedProperties(doc, signedPropertiesId);
+ sp.setSignedSignatureProperties(ssp);
+
+ QualifyingProperties qp = new QualifyingProperties(doc, "#" + signatureId);
+ qp.setSignedProperties(sp);
+
+ ObjectContainer objectContainer = new ObjectContainer(doc);
+ objectContainer.appendChild(qp.getElement());
+ signature.appendObject(objectContainer);
+
+ Transforms transforms = buildReferenceTransforms(doc);
+ signature.addDocument(
+ "#" + signedPropertiesId,
+ transforms,
+ XMLCipher.SHA256,
+ null,
+ XAdESConstants.REFERENCE_TYPE_SIGNEDPROPERTIES);
+ }
+
+ private SignedSignatureProperties buildSignedSignatureProperties(Document doc)
+ throws XMLSignatureException {
+ SignedSignatureProperties ssp = new SignedSignatureProperties(doc);
+ ssp.setSigningTime(OffsetDateTime.now());
+ ssp.setSigningCertificate(buildSigningCertificate(doc));
+ if (signaturePolicyImplied) {
+ ssp.setSignaturePolicyImplied();
+ }
+ if (signatureCity != null || signatureCountryName != null) {
+ ssp.setSignatureProductionPlace(signatureCity, signatureCountryName);
+ }
+ return ssp;
+ }
+
+ private SigningCertificate buildSigningCertificate(Document doc) throws XMLSignatureException {
+ String jceAlgorithm = JCEMapper.translateURItoJCEID(certificateDigestAlgorithmURI);
+ if (jceAlgorithm == null) {
+ throw new SignatureExtensionException(
+ "Unknown digest algorithm URI: " + certificateDigestAlgorithmURI);
+ }
+
+ byte[] certDer;
+ try {
+ certDer = certificate.getEncoded();
+ } catch (CertificateEncodingException e) {
+ throw new SignatureExtensionException("Cannot encode signing certificate", e);
+ }
+
+ byte[] digest;
+ try {
+ digest = MessageDigest.getInstance(jceAlgorithm).digest(certDer);
Review Comment:
## CodeQL / Use of a broken or risky cryptographic algorithm
Cryptographic algorithm [DESede/CBC/ISO10126Padding](1) is insecure. It has a short key length of 56 bits, making it vulnerable to brute-force attacks. Consider using AES instead.
Cryptographic algorithm [DESedeWrap](2) is insecure. It has a short key length of 56 bits, making it vulnerable to brute-force attacks. Consider using AES instead.
[Show more details](https://github.com/apache/santuario-xml-security-java/security/code-scanning/1252)
##########
src/main/java/org/apache/xml/security/extension/xades/XAdESSignatureProcessor.java:
##########
@@ -0,0 +1,279 @@
+/**
+ * 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.extension.xades;
+
+import org.apache.xml.security.algorithms.JCEMapper;
+import org.apache.xml.security.encryption.XMLCipher;
+import org.apache.xml.security.extension.SignatureExtensionException;
+import org.apache.xml.security.extension.SignatureProcessor;
+import org.apache.xml.security.signature.ObjectContainer;
+import org.apache.xml.security.signature.XMLSignature;
+import org.apache.xml.security.signature.XMLSignatureException;
+import org.apache.xml.security.stax.impl.util.IDGenerator;
+import org.apache.xml.security.transforms.TransformationException;
+import org.apache.xml.security.transforms.Transforms;
+import org.w3c.dom.Document;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.time.OffsetDateTime;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Pre-processor that adds XAdES-B-B (Basic Electronic Signature) qualifying properties
+ * to an XML signature before digests are computed.
+ *
+ * <p>The processor:
+ * <ol>
+ * <li>Assigns an {@code Id} to the {@code ds:Signature} element if one is not already set.</li>
+ * <li>Assigns an {@code Id} to the {@code ds:SignatureValue} element (enables XAdES-T extension).</li>
+ * <li>Builds an XAdES {@code QualifyingProperties} structure containing {@code SignedProperties}
+ * using DOM-based {@link XAdESElementProxy} classes — no JAXB dependency.</li>
+ * <li>Wraps it in a {@code ds:Object} and appends it to the signature.</li>
+ * <li>Adds a {@code ds:Reference} with type {@code SignedProperties} so that
+ * {@code SignedProperties} is covered by the signature digest.</li>
+ * </ol>
+ *
+ * <p>Create an instance using the {@link Builder}:
+ * <pre>{@code
+ * XAdESSignatureProcessor xades = XAdESSignatureProcessor.builder(certificate)
+ * .withSignaturePolicyImplied(true)
+ * .withSignatureCity("Brussels")
+ * .build();
+ * sig.addPreProcessor(xades);
+ * }</pre>
+ *
+ * @see <a href="https://www.etsi.org/deliver/etsi_en/319100_319199/31913201/01.03.01_60/en_31913201v010301p.pdf">
+ * ETSI EN 319 132-1 (XAdES)</a>
+ */
+public final class XAdESSignatureProcessor implements SignatureProcessor {
+
+ private static final String ID_PREFIX_SIG = "sig-";
+ private static final String ID_PREFIX_SIG_VAL = "sig-val-";
+ private static final String ID_PREFIX_SIG_PROP = "sig-prop-";
+
+ private final X509Certificate certificate;
+ private final String certificateDigestAlgorithmURI;
+ private final boolean signaturePolicyImplied;
+ private final String signatureCity;
+ private final String signatureCountryName;
+ private final List<String> referenceTransformAlgorithms;
+
+ private XAdESSignatureProcessor(Builder builder) {
+ this.certificate = builder.certificate;
+ this.certificateDigestAlgorithmURI = builder.certificateDigestAlgorithmURI;
+ this.signaturePolicyImplied = builder.signaturePolicyImplied;
+ this.signatureCity = builder.signatureCity;
+ this.signatureCountryName = builder.signatureCountryName;
+ this.referenceTransformAlgorithms = new ArrayList<>(builder.referenceTransformAlgorithms);
+ }
+
+ /**
+ * Creates a builder for configuring an {@link XAdESSignatureProcessor}.
+ *
+ * @param certificate the signing certificate; must not be {@code null}
+ */
+ public static Builder builder(X509Certificate certificate) {
+ return new Builder(certificate);
+ }
+
+ @Override
+ public void processSignature(XMLSignature signature) throws XMLSignatureException {
+ ensureSignatureId(signature);
+ ensureSignatureValueId(signature);
+
+ String signatureId = signature.getId();
+ String signedPropertiesId = IDGenerator.generateID(ID_PREFIX_SIG_PROP);
+ Document doc = signature.getElement().getOwnerDocument();
+
+ SignedSignatureProperties ssp = buildSignedSignatureProperties(doc);
+
+ SignedProperties sp = new SignedProperties(doc, signedPropertiesId);
+ sp.setSignedSignatureProperties(ssp);
+
+ QualifyingProperties qp = new QualifyingProperties(doc, "#" + signatureId);
+ qp.setSignedProperties(sp);
+
+ ObjectContainer objectContainer = new ObjectContainer(doc);
+ objectContainer.appendChild(qp.getElement());
+ signature.appendObject(objectContainer);
+
+ Transforms transforms = buildReferenceTransforms(doc);
+ signature.addDocument(
+ "#" + signedPropertiesId,
+ transforms,
+ XMLCipher.SHA256,
+ null,
+ XAdESConstants.REFERENCE_TYPE_SIGNEDPROPERTIES);
+ }
+
+ private SignedSignatureProperties buildSignedSignatureProperties(Document doc)
+ throws XMLSignatureException {
+ SignedSignatureProperties ssp = new SignedSignatureProperties(doc);
+ ssp.setSigningTime(OffsetDateTime.now());
+ ssp.setSigningCertificate(buildSigningCertificate(doc));
+ if (signaturePolicyImplied) {
+ ssp.setSignaturePolicyImplied();
+ }
+ if (signatureCity != null || signatureCountryName != null) {
+ ssp.setSignatureProductionPlace(signatureCity, signatureCountryName);
+ }
+ return ssp;
+ }
+
+ private SigningCertificate buildSigningCertificate(Document doc) throws XMLSignatureException {
+ String jceAlgorithm = JCEMapper.translateURItoJCEID(certificateDigestAlgorithmURI);
+ if (jceAlgorithm == null) {
+ throw new SignatureExtensionException(
+ "Unknown digest algorithm URI: " + certificateDigestAlgorithmURI);
+ }
+
+ byte[] certDer;
+ try {
+ certDer = certificate.getEncoded();
+ } catch (CertificateEncodingException e) {
+ throw new SignatureExtensionException("Cannot encode signing certificate", e);
+ }
+
+ byte[] digest;
+ try {
+ digest = MessageDigest.getInstance(jceAlgorithm).digest(certDer);
Review Comment:
## CodeQL / Use of a potentially broken or risky cryptographic algorithm
Cryptographic algorithm [MD5](1) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [RIPEMD160](2) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [SHA-1](3) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [SHA-224](4) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [WHIRLPOOL](5) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [SHA3-224](6) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [SHA1withDSA](7) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [RSASSA-PSS](8) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [HmacMD5](9) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [HMACRIPEMD160](10) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [HmacSHA224](11) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [SEED/CBC/ISO10126Padding](12) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [Camellia/CBC/ISO10126Padding](13) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [Camellia/CBC/ISO10126Padding](14) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [Camellia/CBC/ISO10126Padding](15) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [AESWrap](16) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [AESWrap](17) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [AESWrap](18) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [CamelliaWrap](19) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [CamelliaWrap](20) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [CamelliaWrap](21) may not be secure. Consider using a different algorithm.
Cryptographic algorithm [SEEDWrap](22) may not be secure. Consider using a different algorithm.
[Show more details](https://github.com/apache/santuario-xml-security-java/security/code-scanning/1254)
--
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]