Skip to content

Commit 953f800

Browse files
author
Joze RIHTARSIC
committed
[SANTUARIO-615] Implements secure digest algorithm validation for XAdES
1 parent 5ac3cf1 commit 953f800

3 files changed

Lines changed: 57 additions & 24 deletions

File tree

src/main/java/org/apache/xml/security/extension/xades/XAdESBBValidator.java

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
*/
1919
package org.apache.xml.security.extension.xades;
2020

21-
import org.apache.xml.security.algorithms.JCEMapper;
21+
import org.apache.xml.security.algorithms.MessageDigestAlgorithm;
2222
import org.apache.xml.security.exceptions.XMLSecurityException;
2323
import org.apache.xml.security.signature.Reference;
2424
import org.apache.xml.security.signature.SignedInfo;
@@ -40,8 +40,6 @@
4040
import java.io.IOException;
4141
import java.io.InputStream;
4242
import java.io.Reader;
43-
import java.security.MessageDigest;
44-
import java.security.NoSuchAlgorithmException;
4543
import java.security.cert.CertificateEncodingException;
4644
import java.security.cert.X509Certificate;
4745
import java.util.ArrayList;
@@ -87,7 +85,9 @@
8785
*/
8886
public final class XAdESBBValidator {
8987

90-
private static final String XADES_SCHEMA_RESOURCE ="bindings/schemas/XAdES01903v141-202107.xsd";
88+
private static final String XADES_SCHEMA_RESOURCE =
89+
"bindings/schemas/XAdES01903v132-201601.xsd";
90+
9191

9292
/**
9393
* Schema is thread-safe once constructed; load once and share.
@@ -106,7 +106,7 @@ private static Schema loadSchema() {
106106
XADES_SCHEMA_RESOURCE, XAdESBBValidator.class)) {
107107
return sf.newSchema(new StreamSource(xadesIs, xadesUri));
108108
}
109-
} catch (SAXException | IOException e) {
109+
} catch (SAXException | IOException e) {
110110
// Logged here; validate() reports the violation rather than crashing callers
111111
System.getLogger(XAdESBBValidator.class.getName())
112112
.log(System.Logger.Level.ERROR,
@@ -123,6 +123,17 @@ private static String resourceUri(String path) {
123123
return url.toExternalForm();
124124
}
125125

126+
127+
private final boolean secureValidation;
128+
129+
public XAdESBBValidator() {
130+
this(true);
131+
}
132+
133+
public XAdESBBValidator(boolean secureValidation) {
134+
this.secureValidation = secureValidation;
135+
}
136+
126137
/**
127138
* Validates XAdES-B-B properties in {@code signature}.
128139
*
@@ -259,9 +270,8 @@ private void validateCertDigest(Element qualifyingProps,
259270
return;
260271
}
261272

262-
String jceAlgorithm = JCEMapper.translateURItoJCEID(algorithmURI);
263-
if (jceAlgorithm == null) {
264-
violations.add("Unknown digest algorithm URI in CertDigest: " + algorithmURI);
273+
if (secureValidation && !XAdESConstants.APPROVED_CERT_DIGEST_ALGORITHM_URIS.contains(algorithmURI)) {
274+
violations.add("CertDigest uses a weak or disallowed digest algorithm: " + algorithmURI);
265275
return;
266276
}
267277

@@ -276,8 +286,8 @@ private void validateCertDigest(Element qualifyingProps,
276286
byte[] actualDigest;
277287
try {
278288
byte[] certDer = signingCertificate.getEncoded();
279-
actualDigest = MessageDigest.getInstance(jceAlgorithm).digest(certDer);
280-
} catch (CertificateEncodingException | NoSuchAlgorithmException e) {
289+
actualDigest = MessageDigestAlgorithm.getDigestInstance(algorithmURI).digest(certDer);
290+
} catch (CertificateEncodingException | XMLSecurityException e) {
281291
violations.add("Cannot compute signing certificate digest: " + e.getMessage());
282292
return;
283293
}

src/main/java/org/apache/xml/security/extension/xades/XAdESConstants.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,12 @@
1818
*/
1919
package org.apache.xml.security.extension.xades;
2020

21+
import org.apache.xml.security.algorithms.MessageDigestAlgorithm;
22+
23+
import java.util.Set;
24+
2125
/**
22-
* Namespace URIs, prefixes, and element tag constants for XAdES v1.3.2 and v1.4.1.
26+
* Namespace URIs, prefixes, element tag constants, and approved algorithm sets for XAdES v1.3.2 and v1.4.1.
2327
*
2428
* @see <a href="https://www.etsi.org/deliver/etsi_en/319100_319199/31913201/01.03.01_60/en_31913201v010301p.pdf">
2529
* ETSI EN 319 132-1 (XAdES)</a>
@@ -29,6 +33,20 @@ public final class XAdESConstants {
2933
private XAdESConstants() {
3034
}
3135

36+
/**
37+
* Algorithm URIs accepted for certificate digest in XAdES {@code CertDigest}.
38+
* Only SHA-2 and SHA-3 variants with at least 256-bit output are permitted;
39+
* MD5, SHA-1, SHA-224, and other weak/deprecated algorithms are rejected.
40+
* Used by both {@link XAdESSignatureProcessor} (signing) and {@link XAdESBBValidator} (validation)
41+
*/
42+
public static final Set<String> APPROVED_CERT_DIGEST_ALGORITHM_URIS = Set.of(
43+
MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA256,
44+
MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA384,
45+
MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA512,
46+
MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA3_256,
47+
MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA3_384,
48+
MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA3_512);
49+
3250
public static final String XADES_V132_NS = "http://uri.etsi.org/01903/v1.3.2#";
3351
public static final String XADES_V141_NS = "http://uri.etsi.org/01903/v1.4.1#";
3452

src/main/java/org/apache/xml/security/extension/xades/XAdESSignatureProcessor.java

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@
1818
*/
1919
package org.apache.xml.security.extension.xades;
2020

21-
import org.apache.xml.security.algorithms.JCEMapper;
21+
import org.apache.xml.security.algorithms.MessageDigestAlgorithm;
2222
import org.apache.xml.security.encryption.XMLCipher;
23+
import org.apache.xml.security.exceptions.XMLSecurityException;
2324
import org.apache.xml.security.extension.SignatureExtensionException;
2425
import org.apache.xml.security.extension.SignatureProcessor;
2526
import org.apache.xml.security.signature.ObjectContainer;
@@ -30,8 +31,6 @@
3031
import org.apache.xml.security.transforms.Transforms;
3132
import org.w3c.dom.Document;
3233

33-
import java.security.MessageDigest;
34-
import java.security.NoSuchAlgorithmException;
3534
import java.security.cert.CertificateEncodingException;
3635
import java.security.cert.X509Certificate;
3736
import java.time.OffsetDateTime;
@@ -143,12 +142,6 @@ private SignedSignatureProperties buildSignedSignatureProperties(Document doc)
143142
}
144143

145144
private SigningCertificate buildSigningCertificate(Document doc) throws XMLSignatureException {
146-
String jceAlgorithm = JCEMapper.translateURItoJCEID(certificateDigestAlgorithmURI);
147-
if (jceAlgorithm == null) {
148-
throw new SignatureExtensionException(
149-
"Unknown digest algorithm URI: " + certificateDigestAlgorithmURI);
150-
}
151-
152145
byte[] certDer;
153146
try {
154147
certDer = certificate.getEncoded();
@@ -158,8 +151,8 @@ private SigningCertificate buildSigningCertificate(Document doc) throws XMLSigna
158151

159152
byte[] digest;
160153
try {
161-
digest = MessageDigest.getInstance(jceAlgorithm).digest(certDer);
162-
} catch (NoSuchAlgorithmException e) {
154+
digest = MessageDigestAlgorithm.getDigestInstance(certificateDigestAlgorithmURI).digest(certDer);
155+
} catch (XMLSecurityException e) {
163156
throw new SignatureExtensionException(
164157
"Digest algorithm not available: " + certificateDigestAlgorithmURI, e);
165158
}
@@ -175,13 +168,13 @@ private SigningCertificate buildSigningCertificate(Document doc) throws XMLSigna
175168
return sc;
176169
}
177170

178-
private void ensureSignatureId(XMLSignature signature) throws XMLSignatureException {
171+
private void ensureSignatureId(XMLSignature signature) {
179172
if (isBlank(signature.getId())) {
180173
signature.setId(IDGenerator.generateID(ID_PREFIX_SIG));
181174
}
182175
}
183176

184-
private void ensureSignatureValueId(XMLSignature signature) throws XMLSignatureException {
177+
private void ensureSignatureValueId(XMLSignature signature){
185178
if (isBlank(signature.getSignatureValueId())) {
186179
signature.setSignatureValueId(IDGenerator.generateID(ID_PREFIX_SIG_VAL));
187180
}
@@ -220,6 +213,7 @@ private static boolean isBlank(String value) {
220213
*/
221214
public static final class Builder {
222215

216+
private boolean allowWeakAlgorithms = false;
223217
private final X509Certificate certificate;
224218
private String certificateDigestAlgorithmURI = XMLCipher.SHA256;
225219
private boolean signaturePolicyImplied = false;
@@ -261,6 +255,12 @@ public Builder withSignatureCountryName(String countryName) {
261255
return this;
262256
}
263257

258+
/** When {@code true}, allows weak digest algorithms (e.g. SHA-1) to be used for certificate digest. */
259+
public Builder withAllowWeakAlgorithms(boolean allowWeakAlgorithms) {
260+
this.allowWeakAlgorithms = allowWeakAlgorithms;
261+
return this;
262+
}
263+
264264
/**
265265
* Adds a canonicalization or transform algorithm URI to apply to the
266266
* {@code SignedProperties} reference before digesting. Algorithms are applied in
@@ -273,6 +273,11 @@ public Builder addReferenceTransformAlgorithm(String algorithm) {
273273

274274
public XAdESSignatureProcessor build() {
275275
Objects.requireNonNull(certificateDigestAlgorithmURI, "certificateDigestAlgorithmURI");
276+
if (!this.allowWeakAlgorithms && !XAdESConstants.APPROVED_CERT_DIGEST_ALGORITHM_URIS.contains(certificateDigestAlgorithmURI)) {
277+
throw new IllegalArgumentException(
278+
"certificateDigestAlgorithmURI uses a weak or disallowed digest algorithm: "
279+
+ certificateDigestAlgorithmURI);
280+
}
276281
return new XAdESSignatureProcessor(this);
277282
}
278283
}

0 commit comments

Comments
 (0)