Skip to content
This repository has been archived by the owner on May 26, 2020. It is now read-only.

Commit

Permalink
SANTUARIO-532 User-defined and delayed evaluation of which XML elemen…
Browse files Browse the repository at this point in the history
…ts need to be secured
  • Loading branch information
peterdemaeyer committed Apr 20, 2020
1 parent 282cc09 commit e9b0747
Show file tree
Hide file tree
Showing 53 changed files with 2,033 additions and 385 deletions.
13 changes: 13 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,7 @@
<targetJdk>1.8</targetJdk>
<clirr.version>2.8</clirr.version>
<maven-owasp-plugin-version>5.2.4</maven-owasp-plugin-version>
<mockito.version>3.3.0</mockito.version>

<!-- Allow Clirr severity to be overriden by the command-line option -DminSeverity=level -->
<minSeverity>info</minSeverity>
Expand Down Expand Up @@ -559,6 +560,12 @@
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.xmlunit</groupId>
<artifactId>xmlunit-core</artifactId>
Expand Down Expand Up @@ -628,6 +635,12 @@
<version>0.7.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<distributionManagement>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,11 +189,13 @@ stax.unsupportedKeyTransp = Der public-key Algorithmus ist zu kurz um den symmet
stax.recursiveKeyReference = Rekursive Schl\u00fcssel referenzierung detektiert.
stax.ecParametersNotSupported = ECParameters werden nicht unterst\u00fctzt.
stax.namedCurveMissing = NamedCurve fehlt.
stax.encryption.securePartNotFound = Part zum Verschl\u00fcsseln nicht gefunden: {0}
stax.signature.securePartNotFound = Part zum Signieren nicht gefunden: {0}
stax.multipleSignaturesNotSupported = Mehrere Signaturen werden nicht unterstützt.
stax.encryption.tooFewOccurrences = Zu wenige ({0}/{1}) Elemente gefunden zum Verschl\u00fcsslen: {2}
stax.encryption.tooManyOccurrences = Zu viele ({0}/{1}) Elemente gefunden zum Verschl\u00fcsslen: {2}
stax.signature.tooFewOccurrences = Zu wenige ({0}/{1}) Elemente gefunden zum Signieren: {2}
stax.signature.tooManyOccurrences = Zu viele ({0}/{1}) Elemente gefunden zum Signieren: {2}
stax.multipleSignaturesNotSupported = Mehrere Signaturen werden nicht unterst\u00fctzt.
stax.signature.keyNameMissing = KeyName nicht konfiguriert.
stax.keyNotFoundForName = Kein Schl\u00fcssel für Schl\u00fcsselname konfiguriert: {0}
stax.keyNotFoundForName = Kein Schl\u00fcssel f\u00fcr Schl\u00fcsselname konfiguriert: {0}
stax.keyTypeNotSupported = Key vom Typ {0} nicht f\u00fcr einen Key-Namenssuche unterst\u00fctzt
stax.idsetbutnotgenerated = An Id attribute is specified, but Id generation is disabled
stax.idgenerationdisablewithmultipleparts = Id generation must not be disabled when multiple parts need signing
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,10 @@ stax.unsupportedKeyTransp = public key algorithm too weak to encrypt symmetric k
stax.recursiveKeyReference = Recursive key reference detected.
stax.ecParametersNotSupported = ECParameters not supported.
stax.namedCurveMissing = NamedCurve is missing.
stax.encryption.securePartNotFound = Part to encrypt not found: {0}
stax.signature.securePartNotFound = Part to sign not found: {0}
stax.encryption.tooFewOccurrences = Too few ({0}/{1}) elements found to encrypt: {2}
stax.encryption.tooManyOccurrences = Too many ({0}/{1}) elements found to encrypt: {2}
stax.signature.tooFewOccurrences = Too few ({0}/{1}) elements found to sign: {2}
stax.signature.tooManyOccurrences = Too many ({0}/{1}) elements found to sign: {2}
stax.multipleSignaturesNotSupported = Multiple signatures are not supported.
stax.signature.keyNameMissing = KeyName not configured.
stax.keyNotFoundForName = No key configured for KeyName: {0}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ protected Deque<XMLSecEvent> getXmlSecEventBuffer() {
}

@Override
public void processNextEvent(XMLSecEvent xmlSecEvent, OutputProcessorChain outputProcessorChain)
public void processEvent(XMLSecEvent xmlSecEvent, OutputProcessorChain outputProcessorChain)
throws XMLStreamException, XMLSecurityException {
xmlSecEventBuffer.offer(xmlSecEvent);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,10 @@
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Attribute;

import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.stax.ext.stax.XMLSecAttribute;
Expand All @@ -37,6 +35,7 @@
import org.apache.xml.security.stax.ext.stax.XMLSecEventFactory;
import org.apache.xml.security.stax.ext.stax.XMLSecNamespace;
import org.apache.xml.security.stax.ext.stax.XMLSecStartElement;
import org.apache.xml.security.utils.KeyValue;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
Expand Down Expand Up @@ -230,24 +229,24 @@ protected void outputAsEvent(OutputProcessorChain outputProcessorChain, XMLSecEv
outputProcessorChain.processEvent(xmlSecEvent);
}

protected SecurePart securePartMatches(XMLSecStartElement xmlSecStartElement,
OutputProcessorChain outputProcessorChain, String dynamicParts) {
Map<Object, SecurePart> dynamicSecureParts = outputProcessorChain.getSecurityContext().getAsMap(dynamicParts);
return securePartMatches(xmlSecStartElement, dynamicSecureParts);
protected KeyValue<SecurePartSelector, SecurePart> securePartMatches(XMLSecStartElement xmlSecStartElement,
OutputProcessorChain outputProcessorChain, String dynamicPartSelectors) {
OutboundSecurityContext securityContext = outputProcessorChain.getSecurityContext();
List<SecurePartSelector> dynamicSecurePartSelectors = securityContext.get(dynamicPartSelectors);
Element currentElement = securityContext.get(XMLSecurityConstants.CURRENT_ELEMENT);
return securePartMatches(xmlSecStartElement, dynamicSecurePartSelectors, currentElement);
}

protected SecurePart securePartMatches(XMLSecStartElement xmlSecStartElement, Map<Object, SecurePart> secureParts) {
SecurePart securePart = null;
if (secureParts != null) {
securePart = secureParts.get(xmlSecStartElement.getName());
if (securePart == null) {
Attribute attribute = xmlSecStartElement.getAttributeByName(securityProperties.getIdAttributeNS());
if (attribute != null) {
securePart = secureParts.get(attribute.getValue());
protected KeyValue<SecurePartSelector, SecurePart> securePartMatches(XMLSecStartElement xmlSecStartElement, List<SecurePartSelector> securePartSelectors, Element currentElement) {
if (securePartSelectors != null) {
for (SecurePartSelector securePartSelector : securePartSelectors) {
SecurePart securePart = securePartSelector.select(xmlSecStartElement.getName(), currentElement);
if (securePart != null) {
return new KeyValue<>(securePartSelector, securePart);
}
}
}
return securePart;
return null;
}

protected void outputDOMElement(Element element, OutputProcessorChain outputProcessorChain)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* 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.stax.ext;

import org.w3c.dom.Element;

import javax.xml.namespace.QName;

/**
* This interface allows implementors to select <i>which</i> elements to secure, based on an element's qualified name
* and skeleton DOM element.
*/
public interface ElementSelector {

/**
* Selects a given element for securing.
* The given element is a combination of qualified name and skeleton DOM element.
* The skeleton DOM element has no content nor comments, it only has:
* <ol>
* <li>local name, namespace URI and prefix;</li>
* <li>attributes;</li>
* <li>namespace declarations (which are just a special type of attributes).</li>
* </ol>
* The hierarchical structure of the skeleton DOM element depends on the
* {@link org.apache.xml.security.stax.ext.XMLSecurityProperties.ElementModifier} set on
* {@link XMLSecurityProperties}.
* The qualified name and/or skeleton DOM element may be {@code null}
* The skeleton DOM element may be {@code null}, if an only if the qualified name is {@code null} or the element
* modifier is set to {@code null} using
* {@link XMLSecurityProperties#setElementModifier(XMLSecurityProperties.ElementModifier)}.
* <table>
* <tr>
* <th>Name</th>
* <th>Element</th>
* <th>Meaning</th>
* </tr>
* <tr>
* <td>Not {@code null}</td>
* <td>Not {@code null}</td>
* <td>
* A regular element, when the element modifier has a regular non-null value.
* The framework calls this method for every element in the document.
* </td>
* </tr>
* <tr>
* <td>Not {@code null}</td>
* <td>{@code null}</td>
* <td>
* A regular element, when the element modifier has explicitly been set to {@code null} using
* {@link XMLSecurityProperties#setElementModifier(XMLSecurityProperties.ElementModifier)}.
* The framework calls this method for every element in the document.
* </td>
* </tr>
* <tr>
* <td>{@code null}</td>
* <td>{@code null}</td>
* <td>
* The document element.
* The framework may call this method once for the whole document, even if it has no elements.
* When the qualified name is {@code null}, the skeleton DOM element is also {@code null}.
* In practice, it is only used by the signature action when finalizing processing, to resolve any
* external references which are global to the document but not bound to any specific element.
* </td>
* </tr>
* </table>
*
* @param name The qualified name, possibly {@code null}.
* @param element The skeleton DOM element, possibly {@code null}.
* @return {@code true} to select the given skeleton element for securing, {@code false} otherwise.
*/
boolean select(QName name, Element element);
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public XMLStreamReader processInMessage(
requestSecurityEvents = Collections.emptyList();
}

final InboundSecurityContextImpl inboundSecurityContext = new InboundSecurityContextImpl();
final InboundSecurityContextImpl inboundSecurityContext = new InboundSecurityContextImpl(securityProperties);
inboundSecurityContext.putList(SecurityEvent.class, requestSecurityEvents);
inboundSecurityContext.addSecurityEventListener(securityEventListener);

Expand Down
65 changes: 12 additions & 53 deletions src/main/java/org/apache/xml/security/stax/ext/OutboundXMLSec.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public XMLStreamWriter processOutMessage(XMLStreamWriter xmlStreamWriter, String

private XMLStreamWriter processOutMessage(
Object output, String encoding, SecurityEventListener eventListener) throws XMLSecurityException {
final OutboundSecurityContextImpl outboundSecurityContext = new OutboundSecurityContextImpl();
final OutboundSecurityContextImpl outboundSecurityContext = new OutboundSecurityContextImpl(securityProperties);

if (eventListener != null) {
outboundSecurityContext.addSecurityEventListener(eventListener);
Expand All @@ -105,64 +105,27 @@ private XMLStreamWriter processOutMessage(

OutputProcessorChainImpl outputProcessorChain = new OutputProcessorChainImpl(outboundSecurityContext, documentContext);

SecurePart signEntireRequestPart = null;
SecurePart encryptEntireRequestPart = null;

for (XMLSecurityConstants.Action action : securityProperties.getActions()) {
if (XMLSecurityConstants.SIGNATURE.equals(action)) {
XMLSignatureOutputProcessor signatureOutputProcessor = new XMLSignatureOutputProcessor();
initializeOutputProcessor(outputProcessorChain, signatureOutputProcessor, action);

configureSignatureKeys(outboundSecurityContext);
List<SecurePart> signatureParts = securityProperties.getSignatureSecureParts();
for (SecurePart securePart : signatureParts) {
if (securePart.getIdToSecure() == null && securePart.getName() != null) {
outputProcessorChain.getSecurityContext().putAsMap(
XMLSecurityConstants.SIGNATURE_PARTS,
securePart.getName(),
securePart
);
} else if (securePart.getIdToSecure() != null) {
outputProcessorChain.getSecurityContext().putAsMap(
XMLSecurityConstants.SIGNATURE_PARTS,
securePart.getIdToSecure(),
securePart
);
} else if (securePart.getExternalReference() != null) {
outputProcessorChain.getSecurityContext().putAsMap(
XMLSecurityConstants.SIGNATURE_PARTS,
securePart.getExternalReference(),
securePart
);
} else if (securePart.isSecureEntireRequest()) {
// Special functionality to sign the first element in the request
signEntireRequestPart = securePart;
}
}
List<SecurePartSelector> signaturePartSelectors = securityProperties.getSignaturePartSelectors();
outputProcessorChain.getSecurityContext().put(
XMLSecurityConstants.SIGNATURE_PART_SELECTORS,
signaturePartSelectors
);
} else if (XMLSecurityConstants.ENCRYPTION.equals(action)) {
XMLEncryptOutputProcessor encryptOutputProcessor = new XMLEncryptOutputProcessor();
initializeOutputProcessor(outputProcessorChain, encryptOutputProcessor, action);

configureEncryptionKeys(outboundSecurityContext);
List<SecurePart> encryptionParts = securityProperties.getEncryptionSecureParts();
for (SecurePart securePart : encryptionParts) {
if (securePart.getIdToSecure() == null && securePart.getName() != null) {
outputProcessorChain.getSecurityContext().putAsMap(
XMLSecurityConstants.ENCRYPTION_PARTS,
securePart.getName(),
securePart
);
} else if (securePart.getIdToSecure() != null) {
outputProcessorChain.getSecurityContext().putAsMap(
XMLSecurityConstants.ENCRYPTION_PARTS,
securePart.getIdToSecure(),
securePart
);
} else if (securePart.isSecureEntireRequest()) {
// Special functionality to encrypt the first element in the request
encryptEntireRequestPart = securePart;
}
}
List<SecurePartSelector> encryptionPartSelectors = securityProperties.getEncryptionPartSelectors();
outputProcessorChain.getSecurityContext().put(
XMLSecurityConstants.ENCRYPTION_PART_SELECTORS,
encryptionPartSelectors
);
}
}
if (output instanceof OutputStream) {
Expand All @@ -177,11 +140,7 @@ private XMLStreamWriter processOutMessage(
throw new IllegalArgumentException(output + " is not supported as output");
}

XMLSecurityStreamWriter streamWriter = new XMLSecurityStreamWriter(outputProcessorChain);
streamWriter.setSignEntireRequestPart(signEntireRequestPart);
streamWriter.setEncryptEntireRequestPart(encryptEntireRequestPart);

return streamWriter;
return new XMLSecurityStreamWriter(outputProcessorChain);
}

private void initializeOutputProcessor(OutputProcessorChainImpl outputProcessorChain, OutputProcessor outputProcessor, XMLSecurityConstants.Action action) throws XMLSecurityException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public interface OutputProcessor {
* @throws XMLStreamException thrown when a streaming error occurs
* @throws XMLSecurityException thrown when a Security failure occurs
*/
void processNextEvent(XMLSecEvent xmlSecEvent, OutputProcessorChain outputProcessorChain) throws XMLStreamException, XMLSecurityException;
void processEvent(XMLSecEvent xmlSecEvent, OutputProcessorChain outputProcessorChain) throws XMLStreamException, XMLSecurityException;

/**
* Will be called when the whole document is processed.
Expand Down
Loading

0 comments on commit e9b0747

Please sign in to comment.