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

Commit 4f7f5c3

Browse files
committed
SANTUARIO-532 User-defined and delayed evaluation of which XML elements need to be secured
1 parent cc40b75 commit 4f7f5c3

File tree

52 files changed

+2007
-420
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+2007
-420
lines changed

pom.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,7 @@
523523
<targetJdk>1.8</targetJdk>
524524
<clirr.version>2.8</clirr.version>
525525
<maven-owasp-plugin-version>5.2.4</maven-owasp-plugin-version>
526+
<mockito.version>3.3.0</mockito.version>
526527

527528
<!-- Allow Clirr severity to be overriden by the command-line option -DminSeverity=level -->
528529
<minSeverity>info</minSeverity>
@@ -559,6 +560,12 @@
559560
<version>${junit.version}</version>
560561
<scope>test</scope>
561562
</dependency>
563+
<dependency>
564+
<groupId>org.junit.jupiter</groupId>
565+
<artifactId>junit-jupiter-params</artifactId>
566+
<version>${junit.version}</version>
567+
<scope>test</scope>
568+
</dependency>
562569
<dependency>
563570
<groupId>org.xmlunit</groupId>
564571
<artifactId>xmlunit-core</artifactId>
@@ -628,6 +635,12 @@
628635
<version>0.7.2</version>
629636
<scope>test</scope>
630637
</dependency>
638+
<dependency>
639+
<groupId>org.mockito</groupId>
640+
<artifactId>mockito-junit-jupiter</artifactId>
641+
<version>${mockito.version}</version>
642+
<scope>test</scope>
643+
</dependency>
631644
</dependencies>
632645

633646
<distributionManagement>

src/main/java/org/apache/xml/security/resource/xmlsecurity_de.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,8 @@ stax.unsupportedKeyTransp = Der public-key Algorithmus ist zu kurz um den symmet
189189
stax.recursiveKeyReference = Rekursive Schl\u00fcssel referenzierung detektiert.
190190
stax.ecParametersNotSupported = ECParameters werden nicht unterst\u00fctzt.
191191
stax.namedCurveMissing = NamedCurve fehlt.
192-
stax.encryption.securePartNotFound = Part zum Verschl\u00fcsseln nicht gefunden: {0}
193-
stax.signature.securePartNotFound = Part zum Signieren nicht gefunden: {0}
192+
stax.encryption.securePartNotFound = {0}/{1} Part(e) zum Verschl\u00fcsseln nicht gefunden: {2}
193+
stax.signature.securePartNotFound = {0}/{1} Part(e) zum Signieren nicht gefunden: {2}
194194
stax.multipleSignaturesNotSupported = Mehrere Signaturen werden nicht unterstützt.
195195
stax.signature.keyNameMissing = KeyName nicht konfiguriert.
196196
stax.keyNotFoundForName = Kein Schl\u00fcssel für Schl\u00fcsselname konfiguriert: {0}

src/main/java/org/apache/xml/security/resource/xmlsecurity_en.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,8 @@ stax.unsupportedKeyTransp = public key algorithm too weak to encrypt symmetric k
189189
stax.recursiveKeyReference = Recursive key reference detected.
190190
stax.ecParametersNotSupported = ECParameters not supported.
191191
stax.namedCurveMissing = NamedCurve is missing.
192-
stax.encryption.securePartNotFound = Part to encrypt not found: {0}
193-
stax.signature.securePartNotFound = Part to sign not found: {0}
192+
stax.encryption.securePartNotFound = {0}/{1} part(s) to encrypt not found: {2}
193+
stax.signature.securePartNotFound = {0}/{1} part(s) to sign not found: {2}
194194
stax.multipleSignaturesNotSupported = Multiple signatures are not supported.
195195
stax.signature.keyNameMissing = KeyName not configured.
196196
stax.keyNotFoundForName = No key configured for KeyName: {0}

src/main/java/org/apache/xml/security/stax/ext/AbstractOutputProcessor.java

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,10 @@
2222
import java.util.Collections;
2323
import java.util.HashSet;
2424
import java.util.List;
25-
import java.util.Map;
2625
import java.util.Set;
2726

2827
import javax.xml.namespace.QName;
2928
import javax.xml.stream.XMLStreamException;
30-
import javax.xml.stream.events.Attribute;
3129

3230
import org.apache.xml.security.exceptions.XMLSecurityException;
3331
import org.apache.xml.security.stax.ext.stax.XMLSecAttribute;
@@ -37,6 +35,7 @@
3735
import org.apache.xml.security.stax.ext.stax.XMLSecEventFactory;
3836
import org.apache.xml.security.stax.ext.stax.XMLSecNamespace;
3937
import org.apache.xml.security.stax.ext.stax.XMLSecStartElement;
38+
import org.apache.xml.security.utils.KeyValue;
4039
import org.w3c.dom.Attr;
4140
import org.w3c.dom.Element;
4241
import org.w3c.dom.NamedNodeMap;
@@ -124,15 +123,6 @@ public XMLSecurityConstants.Action getAction() {
124123
return action;
125124
}
126125

127-
public abstract void processEvent(XMLSecEvent xmlSecEvent, OutputProcessorChain outputProcessorChain)
128-
throws XMLStreamException, XMLSecurityException;
129-
130-
@Override
131-
public void processNextEvent(XMLSecEvent xmlSecEvent, OutputProcessorChain outputProcessorChain)
132-
throws XMLStreamException, XMLSecurityException {
133-
processEvent(xmlSecEvent, outputProcessorChain);
134-
}
135-
136126
@Override
137127
public void doFinal(OutputProcessorChain outputProcessorChain) throws XMLStreamException, XMLSecurityException {
138128
outputProcessorChain.doFinal();
@@ -239,24 +229,24 @@ protected void outputAsEvent(OutputProcessorChain outputProcessorChain, XMLSecEv
239229
outputProcessorChain.processEvent(xmlSecEvent);
240230
}
241231

242-
protected SecurePart securePartMatches(XMLSecStartElement xmlSecStartElement,
243-
OutputProcessorChain outputProcessorChain, String dynamicParts) {
244-
Map<Object, SecurePart> dynamicSecureParts = outputProcessorChain.getSecurityContext().getAsMap(dynamicParts);
245-
return securePartMatches(xmlSecStartElement, dynamicSecureParts);
232+
protected KeyValue<SecurePartSelector, SecurePart> securePartMatches(XMLSecStartElement xmlSecStartElement,
233+
OutputProcessorChain outputProcessorChain, String dynamicPartSelectors) {
234+
OutboundSecurityContext securityContext = outputProcessorChain.getSecurityContext();
235+
List<SecurePartSelector> dynamicSecurePartSelectors = securityContext.get(dynamicPartSelectors);
236+
Element currentElement = securityContext.get(XMLSecurityConstants.CURRENT_ELEMENT);
237+
return securePartMatches(xmlSecStartElement, dynamicSecurePartSelectors, currentElement);
246238
}
247239

248-
protected SecurePart securePartMatches(XMLSecStartElement xmlSecStartElement, Map<Object, SecurePart> secureParts) {
249-
SecurePart securePart = null;
250-
if (secureParts != null) {
251-
securePart = secureParts.get(xmlSecStartElement.getName());
252-
if (securePart == null) {
253-
Attribute attribute = xmlSecStartElement.getAttributeByName(securityProperties.getIdAttributeNS());
254-
if (attribute != null) {
255-
securePart = secureParts.get(attribute.getValue());
240+
protected KeyValue<SecurePartSelector, SecurePart> securePartMatches(XMLSecStartElement xmlSecStartElement, List<SecurePartSelector> securePartSelectors, Element currentElement) {
241+
if (securePartSelectors != null) {
242+
for (SecurePartSelector securePartSelector : securePartSelectors) {
243+
SecurePart securePart = securePartSelector.select(xmlSecStartElement.getName(), currentElement);
244+
if (securePart != null) {
245+
return new KeyValue<>(securePartSelector, securePart);
256246
}
257247
}
258248
}
259-
return securePart;
249+
return null;
260250
}
261251

262252
protected void outputDOMElement(Element element, OutputProcessorChain outputProcessorChain)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package org.apache.xml.security.stax.ext;
2+
3+
import org.w3c.dom.Element;
4+
5+
import javax.xml.namespace.QName;
6+
7+
/**
8+
* This interface allows implementors to select secure parts based on an element's qualified name and skeleton DOM
9+
* element, returning a {@link SecurePart} upon match or {@code null} otherwise.
10+
*
11+
* @author Peter De Maeyer
12+
*/
13+
public interface ElementSelector {
14+
15+
/**
16+
* Gets the required number of occurrences, or {@code -1} for no such requirement.
17+
* The number of occurrences is verified after processing the entire XML document.
18+
* Processing will fail when the number of occurrences mismatches the required number.
19+
* Use {@code 0} to verify that a secure part <i>never</i> occurs.
20+
* Use {@code -1} to disable verification altogether.
21+
* The recommended default for all implementations is {@code -1}
22+
*
23+
* @return a number of required secure parts, or {@code -1} for no particular number of required secure parts.
24+
*/
25+
int getRequiredNumOccurrences();
26+
27+
/**
28+
* Selects the given element for encryption or signing when the return value is non-{@code null},
29+
* or does not select it when the return value is {@code null}.
30+
* The given element is a combination of qualified name and skeleton DOM element.
31+
* The structure of the skeleton DOM element depends on the
32+
* {@link org.apache.xml.security.stax.ext.XMLSecurityProperties.ElementModifier} set on
33+
* {@link XMLSecurityProperties}, and is possibly {@code null}.
34+
* The skeleton DOM element has no content, it only has:
35+
* <ol>
36+
* <li>local name, namespace URI and prefix;</li>
37+
* <li>attributes;</li>
38+
* <li>namespace declarations (which are just a special type of attributes).</li>
39+
* </ol>
40+
*
41+
* @param name the qualified name, never {@code null}.
42+
* @param element the skeleton DOM element, possibly {@code null}.
43+
* @return {@code true} to select the given skeleton element for encryption or signing, {@code false} otherwise
44+
*/
45+
boolean select(QName name, Element element);
46+
}

src/main/java/org/apache/xml/security/stax/ext/InboundXMLSec.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public XMLStreamReader processInMessage(
115115
requestSecurityEvents = Collections.emptyList();
116116
}
117117

118-
final InboundSecurityContextImpl inboundSecurityContext = new InboundSecurityContextImpl();
118+
final InboundSecurityContextImpl inboundSecurityContext = new InboundSecurityContextImpl(securityProperties);
119119
inboundSecurityContext.putList(SecurityEvent.class, requestSecurityEvents);
120120
inboundSecurityContext.addSecurityEventListener(securityEventListener);
121121

src/main/java/org/apache/xml/security/stax/ext/OutboundXMLSec.java

Lines changed: 37 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,9 @@
1818
*/
1919
package org.apache.xml.security.stax.ext;
2020

21-
import java.io.OutputStream;
22-
import java.security.Key;
23-
import java.security.NoSuchAlgorithmException;
24-
import java.security.PrivateKey;
25-
import java.security.PublicKey;
26-
import java.security.cert.X509Certificate;
27-
import java.util.List;
28-
29-
import javax.crypto.KeyGenerator;
30-
import javax.xml.stream.XMLStreamWriter;
31-
3221
import org.apache.xml.security.exceptions.XMLSecurityException;
3322
import org.apache.xml.security.stax.config.JCEAlgorithmMapper;
23+
import org.apache.xml.security.stax.ext.XMLSecurityProperties.SecurePartElementSelector;
3424
import org.apache.xml.security.stax.impl.DocumentContextImpl;
3525
import org.apache.xml.security.stax.impl.OutboundSecurityContextImpl;
3626
import org.apache.xml.security.stax.impl.OutputProcessorChainImpl;
@@ -45,6 +35,18 @@
4535
import org.apache.xml.security.stax.securityToken.SecurityTokenConstants;
4636
import org.apache.xml.security.stax.securityToken.SecurityTokenProvider;
4737

38+
import javax.crypto.KeyGenerator;
39+
import javax.xml.stream.XMLStreamWriter;
40+
import java.io.OutputStream;
41+
import java.security.Key;
42+
import java.security.NoSuchAlgorithmException;
43+
import java.security.PrivateKey;
44+
import java.security.PublicKey;
45+
import java.security.cert.X509Certificate;
46+
import java.util.List;
47+
48+
import static java.util.stream.Collectors.toList;
49+
4850
/**
4951
* Outbound Streaming-XML-Security
5052
* An instance of this class can be retrieved over the XMLSec class
@@ -94,7 +96,7 @@ public XMLStreamWriter processOutMessage(XMLStreamWriter xmlStreamWriter, String
9496

9597
private XMLStreamWriter processOutMessage(
9698
Object output, String encoding, SecurityEventListener eventListener) throws XMLSecurityException {
97-
final OutboundSecurityContextImpl outboundSecurityContext = new OutboundSecurityContextImpl();
99+
final OutboundSecurityContextImpl outboundSecurityContext = new OutboundSecurityContextImpl(securityProperties);
98100

99101
if (eventListener != null) {
100102
outboundSecurityContext.addSecurityEventListener(eventListener);
@@ -105,58 +107,27 @@ private XMLStreamWriter processOutMessage(
105107

106108
OutputProcessorChainImpl outputProcessorChain = new OutputProcessorChainImpl(outboundSecurityContext, documentContext);
107109

108-
SecurePart signEntireRequestPart = null;
109-
SecurePart encryptEntireRequestPart = null;
110-
111110
for (XMLSecurityConstants.Action action : securityProperties.getActions()) {
112111
if (XMLSecurityConstants.SIGNATURE.equals(action)) {
113112
XMLSignatureOutputProcessor signatureOutputProcessor = new XMLSignatureOutputProcessor();
114113
initializeOutputProcessor(outputProcessorChain, signatureOutputProcessor, action);
115114

116115
configureSignatureKeys(outboundSecurityContext);
117-
List<SecurePart> signatureParts = securityProperties.getSignatureSecureParts();
118-
for (SecurePart securePart : signatureParts) {
119-
if (securePart.getIdToSign() == null && securePart.getName() != null) {
120-
outputProcessorChain.getSecurityContext().putAsMap(
121-
XMLSecurityConstants.SIGNATURE_PARTS,
122-
securePart.getName(),
123-
securePart
124-
);
125-
} else if (securePart.getIdToSign() != null) {
126-
outputProcessorChain.getSecurityContext().putAsMap(
127-
XMLSecurityConstants.SIGNATURE_PARTS,
128-
securePart.getIdToSign(),
129-
securePart
130-
);
131-
} else if (securePart.isSecureEntireRequest()) {
132-
// Special functionality to sign the first element in the request
133-
signEntireRequestPart = securePart;
134-
}
135-
}
116+
List<SecurePartSelector> signaturePartSelectors = securityProperties.getSignaturePartSelectors();
117+
outputProcessorChain.getSecurityContext().put(
118+
XMLSecurityConstants.SIGNATURE_PART_SELECTORS,
119+
filter(signaturePartSelectors)
120+
);
136121
} else if (XMLSecurityConstants.ENCRYPT.equals(action)) {
137122
XMLEncryptOutputProcessor encryptOutputProcessor = new XMLEncryptOutputProcessor();
138123
initializeOutputProcessor(outputProcessorChain, encryptOutputProcessor, action);
139124

140125
configureEncryptionKeys(outboundSecurityContext);
141-
List<SecurePart> encryptionParts = securityProperties.getEncryptionSecureParts();
142-
for (SecurePart securePart : encryptionParts) {
143-
if (securePart.getIdToSign() == null && securePart.getName() != null) {
144-
outputProcessorChain.getSecurityContext().putAsMap(
145-
XMLSecurityConstants.ENCRYPTION_PARTS,
146-
securePart.getName(),
147-
securePart
148-
);
149-
} else if (securePart.getIdToSign() != null) {
150-
outputProcessorChain.getSecurityContext().putAsMap(
151-
XMLSecurityConstants.ENCRYPTION_PARTS,
152-
securePart.getIdToSign(),
153-
securePart
154-
);
155-
} else if (securePart.isSecureEntireRequest()) {
156-
// Special functionality to encrypt the first element in the request
157-
encryptEntireRequestPart = securePart;
158-
}
159-
}
126+
List<SecurePartSelector> encryptionPartSelectors = securityProperties.getEncryptionPartSelectors();
127+
outputProcessorChain.getSecurityContext().put(
128+
XMLSecurityConstants.ENCRYPTION_PART_SELECTORS,
129+
filter(encryptionPartSelectors)
130+
);
160131
}
161132
}
162133
if (output instanceof OutputStream) {
@@ -171,11 +142,20 @@ private XMLStreamWriter processOutMessage(
171142
throw new IllegalArgumentException(output + " is not supported as output");
172143
}
173144

174-
XMLSecurityStreamWriter streamWriter = new XMLSecurityStreamWriter(outputProcessorChain);
175-
streamWriter.setSignEntireRequestPart(signEntireRequestPart);
176-
streamWriter.setEncryptEntireRequestPart(encryptEntireRequestPart);
145+
return new XMLSecurityStreamWriter(outputProcessorChain);
146+
}
177147

178-
return streamWriter;
148+
private static List<SecurePartSelector> filter(List<SecurePartSelector> securePartSelectors) {
149+
return securePartSelectors.stream().filter(securePartSelector -> {
150+
ElementSelector elementSelector = securePartSelector.getElementSelector();
151+
if (elementSelector instanceof SecurePartElementSelector) {
152+
SecurePart securePart = ((SecurePartElementSelector) elementSelector).getSecurePart();
153+
if (securePart.getName() == null && securePart.getIdToSign() == null && !securePart.isSecureEntireRequest()) {
154+
return false;
155+
}
156+
}
157+
return true;
158+
}).collect(toList());
179159
}
180160

181161
private void initializeOutputProcessor(OutputProcessorChainImpl outputProcessorChain, OutputProcessor outputProcessor, XMLSecurityConstants.Action action) throws XMLSecurityException {

src/main/java/org/apache/xml/security/stax/ext/OutputProcessor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public interface OutputProcessor {
9292
* @throws XMLStreamException thrown when a streaming error occurs
9393
* @throws XMLSecurityException thrown when a Security failure occurs
9494
*/
95-
void processNextEvent(XMLSecEvent xmlSecEvent, OutputProcessorChain outputProcessorChain) throws XMLStreamException, XMLSecurityException;
95+
void processEvent(XMLSecEvent xmlSecEvent, OutputProcessorChain outputProcessorChain) throws XMLStreamException, XMLSecurityException;
9696

9797
/**
9898
* Will be called when the whole document is processed.

src/main/java/org/apache/xml/security/stax/ext/SecurePart.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,4 +200,16 @@ public boolean isSecureEntireRequest() {
200200
public void setSecureEntireRequest(boolean secureEntireRequest) {
201201
this.secureEntireRequest = secureEntireRequest;
202202
}
203+
204+
@Override
205+
public String toString() {
206+
if (idToSign != null) {
207+
return idToSign;
208+
} else if (name != null) {
209+
return name.toString();
210+
} else if (externalReference != null) {
211+
return externalReference;
212+
}
213+
return super.toString();
214+
}
203215
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package org.apache.xml.security.stax.ext;
2+
3+
import org.w3c.dom.Element;
4+
5+
import javax.xml.namespace.QName;
6+
7+
/**
8+
* @author Peter De Maeyer
9+
*/
10+
public interface SecurePartFactory {
11+
12+
SecurePart create(QName name, Element element);
13+
}

0 commit comments

Comments
 (0)