Skip to content

Commit 5ac3cf1

Browse files
author
Joze RIHTARSIC
committed
[SANTUARIO-615] Implements XAdES using pre/post signature processors
1 parent b24341b commit 5ac3cf1

16 files changed

Lines changed: 2569 additions & 20 deletions

src/main/java/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
exports org.apache.jcp.xml.dsig.internal.dom;
3434
exports org.apache.xml.security;
3535
exports org.apache.xml.security.extension;
36+
exports org.apache.xml.security.extension.xades;
3637
exports org.apache.xml.security.algorithms;
3738
exports org.apache.xml.security.algorithms.implementations;
3839
exports org.apache.xml.security.c14n;

src/main/java/org/apache/jcp/xml/dsig/internal/dom/DOMUtils.java

Lines changed: 61 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
package org.apache.jcp.xml.dsig.internal.dom;
2323

2424
import java.security.spec.AlgorithmParameterSpec;
25+
import java.util.Arrays;
2526
import java.util.List;
2627

2728
import javax.xml.XMLConstants;
@@ -39,6 +40,7 @@
3940
import org.w3c.dom.Document;
4041
import org.w3c.dom.Element;
4142
import org.w3c.dom.Node;
43+
import org.w3c.dom.NodeList;
4244

4345
/**
4446
* Useful static DOM utility methods.
@@ -49,6 +51,9 @@ public final class DOMUtils {
4951
// class cannot be instantiated
5052
private DOMUtils() {}
5153

54+
/** Attribute names treated as XML ID attributes when scanning for ID declarations. */
55+
private static final List<String> ID_ATTRIBUTE_NAMES = Arrays.asList("Id", "ID", "id");
56+
5257
/**
5358
* Returns the owner document of the specified node.
5459
*
@@ -92,7 +97,7 @@ public static Element createElement(Document doc, String tag,
9297
String nsURI, String prefix)
9398
{
9499
String qName = (prefix == null || prefix.length() == 0)
95-
? tag : prefix + ":" + tag;
100+
? tag : prefix + ":" + tag;
96101
return doc.createElementNS(nsURI, qName);
97102
}
98103

@@ -158,23 +163,23 @@ public static Element getFirstChildElement(Node node) {
158163
* equal to {@code localName}
159164
*/
160165
public static Element getFirstChildElement(Node node, String localName, String namespaceURI)
161-
throws MarshalException
166+
throws MarshalException
162167
{
163168
return verifyElement(getFirstChildElement(node), localName, namespaceURI);
164169
}
165170

166171
private static Element verifyElement(Element elem, String localName, String namespaceURI)
167-
throws MarshalException
172+
throws MarshalException
168173
{
169174
if (elem == null) {
170175
throw new MarshalException("Missing " + localName + " element");
171176
}
172177
String name = elem.getLocalName();
173178
String namespace = elem.getNamespaceURI();
174179
if (!name.equals(localName) || namespace == null && namespaceURI != null
175-
|| namespace != null && !namespace.equals(namespaceURI)) {
180+
|| namespace != null && !namespace.equals(namespaceURI)) {
176181
throw new MarshalException("Invalid element name: " +
177-
namespace + ":" + name + ", expected " + namespaceURI + ":" + localName);
182+
namespace + ":" + name + ", expected " + namespaceURI + ":" + localName);
178183
}
179184
return elem;
180185
}
@@ -225,7 +230,7 @@ public static Element getNextSiblingElement(Node node) {
225230
* equal to {@code localName}
226231
*/
227232
public static Element getNextSiblingElement(Node node, String localName, String namespaceURI)
228-
throws MarshalException
233+
throws MarshalException
229234
{
230235
return verifyElement(getNextSiblingElement(node), localName, namespaceURI);
231236
}
@@ -282,7 +287,7 @@ public static <N> String getIdAttributeValue(Element elem, String name) {
282287
public static String getNSPrefix(XMLCryptoContext context, String nsURI) {
283288
if (context != null) {
284289
return context.getNamespacePrefix
285-
(nsURI, context.getDefaultNamespacePrefix());
290+
(nsURI, context.getDefaultNamespacePrefix());
286291
} else {
287292
return null;
288293
}
@@ -335,29 +340,29 @@ public static void appendChild(Node parent, Node child) {
335340
}
336341

337342
public static boolean paramsEqual(AlgorithmParameterSpec spec1,
338-
AlgorithmParameterSpec spec2) {
343+
AlgorithmParameterSpec spec2) {
339344
if (spec1 == spec2) {
340345
return true;
341346
}
342347
if (spec1 instanceof XPathFilter2ParameterSpec &&
343-
spec2 instanceof XPathFilter2ParameterSpec) {
348+
spec2 instanceof XPathFilter2ParameterSpec) {
344349
return paramsEqual((XPathFilter2ParameterSpec)spec1,
345-
(XPathFilter2ParameterSpec)spec2);
350+
(XPathFilter2ParameterSpec)spec2);
346351
}
347352
if (spec1 instanceof ExcC14NParameterSpec &&
348-
spec2 instanceof ExcC14NParameterSpec) {
353+
spec2 instanceof ExcC14NParameterSpec) {
349354
return paramsEqual((ExcC14NParameterSpec) spec1,
350-
(ExcC14NParameterSpec)spec2);
355+
(ExcC14NParameterSpec)spec2);
351356
}
352357
if (spec1 instanceof XPathFilterParameterSpec &&
353-
spec2 instanceof XPathFilterParameterSpec) {
358+
spec2 instanceof XPathFilterParameterSpec) {
354359
return paramsEqual((XPathFilterParameterSpec)spec1,
355-
(XPathFilterParameterSpec)spec2);
360+
(XPathFilterParameterSpec)spec2);
356361
}
357362
if (spec1 instanceof XSLTTransformParameterSpec &&
358-
spec2 instanceof XSLTTransformParameterSpec) {
363+
spec2 instanceof XSLTTransformParameterSpec) {
359364
return paramsEqual((XSLTTransformParameterSpec)spec1,
360-
(XSLTTransformParameterSpec)spec2);
365+
(XSLTTransformParameterSpec)spec2);
361366
}
362367
return false;
363368
}
@@ -377,8 +382,8 @@ private static boolean paramsEqual(XPathFilter2ParameterSpec spec1,
377382
XPathType type = types.get(i);
378383
XPathType otype = otypes.get(i);
379384
if (!type.getExpression().equals(otype.getExpression()) ||
380-
!type.getNamespaceMap().equals(otype.getNamespaceMap()) ||
381-
type.getFilter() != otype.getFilter()) {
385+
!type.getNamespaceMap().equals(otype.getNamespaceMap()) ||
386+
type.getFilter() != otype.getFilter()) {
382387
return false;
383388
}
384389
}
@@ -407,10 +412,10 @@ private static boolean paramsEqual(XSLTTransformParameterSpec spec1,
407412
return false;
408413
}
409414
Node ostylesheetElem =
410-
((javax.xml.crypto.dom.DOMStructure) ostylesheet).getNode();
415+
((javax.xml.crypto.dom.DOMStructure) ostylesheet).getNode();
411416
XMLStructure stylesheet = spec1.getStylesheet();
412417
Node stylesheetElem =
413-
((javax.xml.crypto.dom.DOMStructure) stylesheet).getNode();
418+
((javax.xml.crypto.dom.DOMStructure) stylesheet).getNode();
414419
return nodesEqual(stylesheetElem, ostylesheetElem);
415420
}
416421

@@ -422,4 +427,40 @@ public static boolean isNamespace(Node node)
422427
}
423428
return false;
424429
}
430+
431+
/**
432+
* Recursively walks the DOM tree rooted at {@code node} and calls
433+
* {@link Element#setIdAttribute(String, boolean)} for every attribute whose local name
434+
* is one of {@code Id}, {@code ID}, or {@code id}. This is required so that
435+
* {@link Document#getElementById} correctly resolves same-document ID references in
436+
* XAdES-generated structures.
437+
*
438+
* @param node the root node to start from
439+
*/
440+
public static void setIdFlagToIdAttributes(Node node) {
441+
setIdFlagToIdAttributes(node, ID_ATTRIBUTE_NAMES);
442+
}
443+
444+
/**
445+
* Recursively walks the DOM tree rooted at {@code node} and calls
446+
* {@link Element#setIdAttribute(String, boolean)} for every attribute whose local name
447+
* is in {@code idAttributeNames}.
448+
*
449+
* @param node the root node to start from
450+
* @param idAttributeNames the list of attribute local names to treat as Id attributes
451+
*/
452+
public static void setIdFlagToIdAttributes(Node node, List<String> idAttributeNames) {
453+
if (node.getNodeType() == Node.ELEMENT_NODE) {
454+
Element element = (Element) node;
455+
for (String idName : idAttributeNames) {
456+
if (element.hasAttribute(idName)) {
457+
element.setIdAttribute(idName, true);
458+
}
459+
}
460+
NodeList children = element.getChildNodes();
461+
for (int i = 0; i < children.getLength(); i++) {
462+
setIdFlagToIdAttributes(children.item(i), idAttributeNames);
463+
}
464+
}
465+
}
425466
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.xml.security.extension.xades;
20+
21+
import org.apache.xml.security.utils.XMLUtils;
22+
import org.w3c.dom.Document;
23+
import org.w3c.dom.Element;
24+
25+
import java.math.BigInteger;
26+
27+
/**
28+
* Proxy for the {@code xades132:Cert} element.
29+
*
30+
* <p>Holds certificate identification data:
31+
* <ul>
32+
* <li>{@code CertDigest} — digest of the DER-encoded certificate,
33+
* using {@code ds:DigestMethod} and {@code ds:DigestValue}</li>
34+
* <li>{@code IssuerSerial} — issuer distinguished name and serial number,
35+
* using {@code ds:X509IssuerName} and {@code ds:X509SerialNumber}</li>
36+
* </ul>
37+
*
38+
* <pre>{@code
39+
* <xades132:Cert>
40+
* <xades132:CertDigest>
41+
* <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
42+
* <ds:DigestValue>base64...</ds:DigestValue>
43+
* </xades132:CertDigest>
44+
* <xades132:IssuerSerial>
45+
* <ds:X509IssuerName>CN=...</ds:X509IssuerName>
46+
* <ds:X509SerialNumber>12345</ds:X509SerialNumber>
47+
* </xades132:IssuerSerial>
48+
* </xades132:Cert>
49+
* }</pre>
50+
*/
51+
public class Cert extends XAdESElementProxy {
52+
53+
public Cert(Document doc) {
54+
super(doc);
55+
}
56+
57+
@Override
58+
public String getBaseLocalName() {
59+
return "Cert";
60+
}
61+
62+
/**
63+
* Appends a {@code <xades132:CertDigest>} child containing the digest algorithm
64+
* and the base64-encoded digest value of the DER-encoded certificate.
65+
*
66+
* @param digestAlgorithmURI W3C URI of the digest algorithm (e.g. {@code XMLCipher.SHA256})
67+
* @param digestValue the raw digest bytes
68+
*/
69+
public void setCertDigest(String digestAlgorithmURI, byte[] digestValue) {
70+
Element certDigest = createXAdESChild("CertDigest");
71+
72+
Element digestMethod = createDsChild("DigestMethod");
73+
digestMethod.setAttributeNS(null, "Algorithm", digestAlgorithmURI);
74+
certDigest.appendChild(digestMethod);
75+
76+
Element digestValueEl = createDsChild("DigestValue");
77+
digestValueEl.setTextContent(XMLUtils.encodeToString(digestValue));
78+
certDigest.appendChild(digestValueEl);
79+
80+
appendSelf(certDigest);
81+
}
82+
83+
/**
84+
* Appends a {@code <xades132:IssuerSerial>} child containing the certificate's
85+
* issuer distinguished name and serial number.
86+
*
87+
* @param issuerName RFC 2253 issuer distinguished name
88+
* @param serialNumber certificate serial number
89+
*/
90+
public void setIssuerSerial(String issuerName, BigInteger serialNumber) {
91+
Element issuerSerial = createXAdESChild("IssuerSerial");
92+
93+
Element issuerNameEl = createDsChild("X509IssuerName");
94+
issuerNameEl.setTextContent(issuerName);
95+
issuerSerial.appendChild(issuerNameEl);
96+
97+
Element serialEl = createDsChild("X509SerialNumber");
98+
serialEl.setTextContent(serialNumber.toString());
99+
issuerSerial.appendChild(serialEl);
100+
101+
appendSelf(issuerSerial);
102+
}
103+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.xml.security.extension.xades;
20+
21+
import org.apache.xml.security.utils.Constants;
22+
import org.w3c.dom.Document;
23+
24+
/**
25+
* Proxy for the {@code xades132:QualifyingProperties} element.
26+
*
27+
* <p>The root element of the XAdES qualifying properties tree. Carries the
28+
* {@code Target} attribute pointing to the enclosing {@code ds:Signature} Id.
29+
* Also declares {@code xmlns:ds} so that descendant {@code ds:DigestMethod} and
30+
* related elements are well-formed even when this sub-tree is serialised in isolation.
31+
*
32+
* <pre>{@code
33+
* <xades132:QualifyingProperties
34+
* xmlns:xades132="http://uri.etsi.org/01903/v1.3.2#"
35+
* xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
36+
* Target="#sig-xxx">
37+
* ...
38+
* </xades132:QualifyingProperties>
39+
* }</pre>
40+
*/
41+
public class QualifyingProperties extends XAdESElementProxy {
42+
43+
public QualifyingProperties(Document doc, String target) {
44+
super(doc);
45+
getElement().setAttributeNS(null, "Target", target);
46+
getElement().setAttributeNS(Constants.NamespaceSpecNS, "xmlns:ds", Constants.SignatureSpecNS);
47+
}
48+
49+
@Override
50+
public String getBaseLocalName() {
51+
return XAdESConstants.TAG_QUALIFYING_PROPERTIES;
52+
}
53+
54+
public void setSignedProperties(SignedProperties sp) {
55+
appendSelf(sp);
56+
}
57+
}

0 commit comments

Comments
 (0)