Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
exports org.apache.jcp.xml.dsig.internal.dom;
exports org.apache.xml.security;
exports org.apache.xml.security.extension;
exports org.apache.xml.security.extension.xades;
exports org.apache.xml.security.algorithms;
exports org.apache.xml.security.algorithms.implementations;
exports org.apache.xml.security.c14n;
Expand Down
81 changes: 61 additions & 20 deletions src/main/java/org/apache/jcp/xml/dsig/internal/dom/DOMUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
package org.apache.jcp.xml.dsig.internal.dom;

import java.security.spec.AlgorithmParameterSpec;
import java.util.Arrays;
import java.util.List;

import javax.xml.XMLConstants;
Expand All @@ -39,6 +40,7 @@
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

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

/** Attribute names treated as XML ID attributes when scanning for ID declarations. */
private static final List<String> ID_ATTRIBUTE_NAMES = Arrays.asList("Id", "ID", "id");

/**
* Returns the owner document of the specified node.
*
Expand Down Expand Up @@ -92,7 +97,7 @@ public static Element createElement(Document doc, String tag,
String nsURI, String prefix)
{
String qName = (prefix == null || prefix.length() == 0)
? tag : prefix + ":" + tag;
? tag : prefix + ":" + tag;
return doc.createElementNS(nsURI, qName);
}

Expand Down Expand Up @@ -158,23 +163,23 @@ public static Element getFirstChildElement(Node node) {
* equal to {@code localName}
*/
public static Element getFirstChildElement(Node node, String localName, String namespaceURI)
throws MarshalException
throws MarshalException
{
return verifyElement(getFirstChildElement(node), localName, namespaceURI);
}

private static Element verifyElement(Element elem, String localName, String namespaceURI)
throws MarshalException
throws MarshalException
{
if (elem == null) {
throw new MarshalException("Missing " + localName + " element");
}
String name = elem.getLocalName();
String namespace = elem.getNamespaceURI();
if (!name.equals(localName) || namespace == null && namespaceURI != null
|| namespace != null && !namespace.equals(namespaceURI)) {
|| namespace != null && !namespace.equals(namespaceURI)) {
throw new MarshalException("Invalid element name: " +
namespace + ":" + name + ", expected " + namespaceURI + ":" + localName);
namespace + ":" + name + ", expected " + namespaceURI + ":" + localName);
}
return elem;
}
Expand Down Expand Up @@ -225,7 +230,7 @@ public static Element getNextSiblingElement(Node node) {
* equal to {@code localName}
*/
public static Element getNextSiblingElement(Node node, String localName, String namespaceURI)
throws MarshalException
throws MarshalException
{
return verifyElement(getNextSiblingElement(node), localName, namespaceURI);
}
Expand Down Expand Up @@ -282,7 +287,7 @@ public static <N> String getIdAttributeValue(Element elem, String name) {
public static String getNSPrefix(XMLCryptoContext context, String nsURI) {
if (context != null) {
return context.getNamespacePrefix
(nsURI, context.getDefaultNamespacePrefix());
(nsURI, context.getDefaultNamespacePrefix());
} else {
return null;
}
Expand Down Expand Up @@ -335,29 +340,29 @@ public static void appendChild(Node parent, Node child) {
}

public static boolean paramsEqual(AlgorithmParameterSpec spec1,
AlgorithmParameterSpec spec2) {
AlgorithmParameterSpec spec2) {
if (spec1 == spec2) {
return true;
}
if (spec1 instanceof XPathFilter2ParameterSpec &&
spec2 instanceof XPathFilter2ParameterSpec) {
spec2 instanceof XPathFilter2ParameterSpec) {
return paramsEqual((XPathFilter2ParameterSpec)spec1,
(XPathFilter2ParameterSpec)spec2);
(XPathFilter2ParameterSpec)spec2);
}
if (spec1 instanceof ExcC14NParameterSpec &&
spec2 instanceof ExcC14NParameterSpec) {
spec2 instanceof ExcC14NParameterSpec) {
return paramsEqual((ExcC14NParameterSpec) spec1,
(ExcC14NParameterSpec)spec2);
(ExcC14NParameterSpec)spec2);
}
if (spec1 instanceof XPathFilterParameterSpec &&
spec2 instanceof XPathFilterParameterSpec) {
spec2 instanceof XPathFilterParameterSpec) {
return paramsEqual((XPathFilterParameterSpec)spec1,
(XPathFilterParameterSpec)spec2);
(XPathFilterParameterSpec)spec2);
}
if (spec1 instanceof XSLTTransformParameterSpec &&
spec2 instanceof XSLTTransformParameterSpec) {
spec2 instanceof XSLTTransformParameterSpec) {
return paramsEqual((XSLTTransformParameterSpec)spec1,
(XSLTTransformParameterSpec)spec2);
(XSLTTransformParameterSpec)spec2);
}
return false;
}
Expand All @@ -377,8 +382,8 @@ private static boolean paramsEqual(XPathFilter2ParameterSpec spec1,
XPathType type = types.get(i);
XPathType otype = otypes.get(i);
if (!type.getExpression().equals(otype.getExpression()) ||
!type.getNamespaceMap().equals(otype.getNamespaceMap()) ||
type.getFilter() != otype.getFilter()) {
!type.getNamespaceMap().equals(otype.getNamespaceMap()) ||
type.getFilter() != otype.getFilter()) {
return false;
}
}
Expand Down Expand Up @@ -407,10 +412,10 @@ private static boolean paramsEqual(XSLTTransformParameterSpec spec1,
return false;
}
Node ostylesheetElem =
((javax.xml.crypto.dom.DOMStructure) ostylesheet).getNode();
((javax.xml.crypto.dom.DOMStructure) ostylesheet).getNode();
XMLStructure stylesheet = spec1.getStylesheet();
Node stylesheetElem =
((javax.xml.crypto.dom.DOMStructure) stylesheet).getNode();
((javax.xml.crypto.dom.DOMStructure) stylesheet).getNode();
return nodesEqual(stylesheetElem, ostylesheetElem);
}

Expand All @@ -422,4 +427,40 @@ public static boolean isNamespace(Node node)
}
return false;
}

/**
* Recursively walks the DOM tree rooted at {@code node} and calls
* {@link Element#setIdAttribute(String, boolean)} for every attribute whose local name
* is one of {@code Id}, {@code ID}, or {@code id}. This is required so that
* {@link Document#getElementById} correctly resolves same-document ID references in
* XAdES-generated structures.
*
* @param node the root node to start from
*/
public static void setIdFlagToIdAttributes(Node node) {
setIdFlagToIdAttributes(node, ID_ATTRIBUTE_NAMES);
}

/**
* Recursively walks the DOM tree rooted at {@code node} and calls
* {@link Element#setIdAttribute(String, boolean)} for every attribute whose local name
* is in {@code idAttributeNames}.
*
* @param node the root node to start from
* @param idAttributeNames the list of attribute local names to treat as Id attributes
*/
public static void setIdFlagToIdAttributes(Node node, List<String> idAttributeNames) {
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) node;
for (String idName : idAttributeNames) {
if (element.hasAttribute(idName)) {
element.setIdAttribute(idName, true);
}
}
NodeList children = element.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
setIdFlagToIdAttributes(children.item(i), idAttributeNames);
}
}
}
}
103 changes: 103 additions & 0 deletions src/main/java/org/apache/xml/security/extension/xades/Cert.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/**
* 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.utils.XMLUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import java.math.BigInteger;

/**
* Proxy for the {@code xades132:Cert} element.
*
* <p>Holds certificate identification data:
* <ul>
* <li>{@code CertDigest} — digest of the DER-encoded certificate,
* using {@code ds:DigestMethod} and {@code ds:DigestValue}</li>
* <li>{@code IssuerSerial} — issuer distinguished name and serial number,
* using {@code ds:X509IssuerName} and {@code ds:X509SerialNumber}</li>
* </ul>
*
* <pre>{@code
* <xades132:Cert>
* <xades132:CertDigest>
* <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
* <ds:DigestValue>base64...</ds:DigestValue>
* </xades132:CertDigest>
* <xades132:IssuerSerial>
* <ds:X509IssuerName>CN=...</ds:X509IssuerName>
* <ds:X509SerialNumber>12345</ds:X509SerialNumber>
* </xades132:IssuerSerial>
* </xades132:Cert>
* }</pre>
*/
public class Cert extends XAdESElementProxy {

public Cert(Document doc) {
super(doc);
}

@Override
public String getBaseLocalName() {
return "Cert";
}

/**
* Appends a {@code <xades132:CertDigest>} child containing the digest algorithm
* and the base64-encoded digest value of the DER-encoded certificate.
*
* @param digestAlgorithmURI W3C URI of the digest algorithm (e.g. {@code XMLCipher.SHA256})
* @param digestValue the raw digest bytes
*/
public void setCertDigest(String digestAlgorithmURI, byte[] digestValue) {
Element certDigest = createXAdESChild("CertDigest");

Element digestMethod = createDsChild("DigestMethod");
digestMethod.setAttributeNS(null, "Algorithm", digestAlgorithmURI);
certDigest.appendChild(digestMethod);

Element digestValueEl = createDsChild("DigestValue");
digestValueEl.setTextContent(XMLUtils.encodeToString(digestValue));
certDigest.appendChild(digestValueEl);

appendSelf(certDigest);
}

/**
* Appends a {@code <xades132:IssuerSerial>} child containing the certificate's
* issuer distinguished name and serial number.
*
* @param issuerName RFC 2253 issuer distinguished name
* @param serialNumber certificate serial number
*/
public void setIssuerSerial(String issuerName, BigInteger serialNumber) {
Element issuerSerial = createXAdESChild("IssuerSerial");

Element issuerNameEl = createDsChild("X509IssuerName");
issuerNameEl.setTextContent(issuerName);
issuerSerial.appendChild(issuerNameEl);

Element serialEl = createDsChild("X509SerialNumber");
serialEl.setTextContent(serialNumber.toString());
issuerSerial.appendChild(serialEl);

appendSelf(issuerSerial);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* 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.utils.Constants;
import org.w3c.dom.Document;

/**
* Proxy for the {@code xades132:QualifyingProperties} element.
*
* <p>The root element of the XAdES qualifying properties tree. Carries the
* {@code Target} attribute pointing to the enclosing {@code ds:Signature} Id.
* Also declares {@code xmlns:ds} so that descendant {@code ds:DigestMethod} and
* related elements are well-formed even when this sub-tree is serialised in isolation.
*
* <pre>{@code
* <xades132:QualifyingProperties
* xmlns:xades132="http://uri.etsi.org/01903/v1.3.2#"
* xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
* Target="#sig-xxx">
* ...
* </xades132:QualifyingProperties>
* }</pre>
*/
public class QualifyingProperties extends XAdESElementProxy {

public QualifyingProperties(Document doc, String target) {
super(doc);
getElement().setAttributeNS(null, "Target", target);
getElement().setAttributeNS(Constants.NamespaceSpecNS, "xmlns:ds", Constants.SignatureSpecNS);
}

@Override
public String getBaseLocalName() {
return XAdESConstants.TAG_QUALIFYING_PROPERTIES;
}

public void setSignedProperties(SignedProperties sp) {
appendSelf(sp);
}
}
Loading