1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.wss4j.stax.impl.processor.input;
20
21 import java.security.Key;
22 import java.security.PublicKey;
23 import java.security.cert.X509Certificate;
24 import java.util.ArrayList;
25 import java.util.Deque;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29
30 import javax.crypto.spec.SecretKeySpec;
31 import jakarta.xml.bind.JAXBElement;
32 import jakarta.xml.bind.JAXBException;
33 import jakarta.xml.bind.Unmarshaller;
34 import javax.xml.namespace.QName;
35 import javax.xml.parsers.ParserConfigurationException;
36 import javax.xml.stream.XMLStreamConstants;
37 import javax.xml.stream.XMLStreamException;
38 import javax.xml.stream.events.Attribute;
39 import javax.xml.stream.events.Comment;
40 import javax.xml.stream.events.Namespace;
41 import javax.xml.stream.events.ProcessingInstruction;
42
43 import org.apache.wss4j.binding.wss10.ObjectFactory;
44 import org.apache.wss4j.binding.wss10.SecurityTokenReferenceType;
45 import org.apache.wss4j.common.ext.WSSecurityException;
46 import org.apache.wss4j.common.saml.OpenSAMLUtil;
47 import org.apache.wss4j.common.saml.SamlAssertionWrapper;
48 import org.apache.wss4j.stax.ext.WSInboundSecurityContext;
49 import org.apache.wss4j.stax.ext.WSSConstants;
50 import org.apache.wss4j.stax.ext.WSSSecurityProperties;
51 import org.apache.wss4j.stax.securityEvent.SamlTokenSecurityEvent;
52 import org.apache.wss4j.stax.securityEvent.SignedPartSecurityEvent;
53 import org.apache.wss4j.stax.securityEvent.WSSecurityEventConstants;
54 import org.apache.wss4j.stax.securityToken.SamlSecurityToken;
55 import org.apache.wss4j.stax.securityToken.WSSecurityTokenConstants;
56 import org.apache.wss4j.stax.utils.WSSUtils;
57 import org.apache.wss4j.stax.validate.SamlTokenValidator;
58 import org.apache.wss4j.stax.validate.SamlTokenValidatorImpl;
59 import org.apache.wss4j.stax.validate.TokenContext;
60 import org.apache.xml.security.binding.xmldsig.KeyInfoType;
61 import org.apache.xml.security.binding.xmldsig.KeyValueType;
62 import org.apache.xml.security.binding.xmldsig.X509DataType;
63 import org.apache.xml.security.binding.xmlenc.EncryptedKeyType;
64 import org.apache.xml.security.exceptions.XMLSecurityException;
65 import org.apache.xml.security.stax.config.JCEAlgorithmMapper;
66 import org.apache.xml.security.stax.ext.AbstractInputProcessor;
67 import org.apache.xml.security.stax.ext.AbstractInputSecurityHeaderHandler;
68 import org.apache.xml.security.stax.ext.InputProcessorChain;
69 import org.apache.xml.security.stax.ext.XMLSecurityConstants;
70 import org.apache.xml.security.stax.ext.XMLSecurityProperties;
71 import org.apache.xml.security.stax.ext.stax.XMLSecAttribute;
72 import org.apache.xml.security.stax.ext.stax.XMLSecEvent;
73 import org.apache.xml.security.stax.ext.stax.XMLSecNamespace;
74 import org.apache.xml.security.stax.ext.stax.XMLSecStartElement;
75 import org.apache.xml.security.stax.impl.XMLSecurityEventReader;
76 import org.apache.xml.security.stax.impl.securityToken.AbstractInboundSecurityToken;
77 import org.apache.xml.security.stax.impl.util.IDGenerator;
78 import org.apache.xml.security.stax.securityEvent.SecurityEvent;
79 import org.apache.xml.security.stax.securityEvent.SecurityEventListener;
80 import org.apache.xml.security.stax.securityEvent.SignedElementSecurityEvent;
81 import org.apache.xml.security.stax.securityToken.InboundSecurityToken;
82 import org.apache.xml.security.stax.securityToken.SecurityToken;
83 import org.apache.xml.security.stax.securityToken.SecurityTokenConstants.TokenUsage;
84 import org.apache.xml.security.utils.XMLUtils;
85 import org.apache.xml.security.stax.securityToken.SecurityTokenFactory;
86 import org.apache.xml.security.stax.securityToken.SecurityTokenProvider;
87 import org.opensaml.security.credential.BasicCredential;
88 import org.opensaml.security.x509.BasicX509Credential;
89 import org.opensaml.xmlsec.signature.Signature;
90 import org.opensaml.xmlsec.signature.support.SignatureException;
91 import org.opensaml.xmlsec.signature.support.SignatureValidator;
92 import org.w3c.dom.Attr;
93 import org.w3c.dom.Document;
94 import org.w3c.dom.Element;
95 import org.w3c.dom.Node;
96
97
98
99
100 public class SAMLTokenInputHandler extends AbstractInputSecurityHeaderHandler {
101
102 @Override
103 public void handle(final InputProcessorChain inputProcessorChain, final XMLSecurityProperties securityProperties,
104 Deque<XMLSecEvent> eventQueue, Integer index) throws XMLSecurityException {
105
106 final Document samlTokenDocument = (Document) parseStructure(eventQueue, index, securityProperties);
107
108 final WSSSecurityProperties wssSecurityProperties = (WSSSecurityProperties) securityProperties;
109 final WSInboundSecurityContext wsInboundSecurityContext = (WSInboundSecurityContext) inputProcessorChain.getSecurityContext();
110 final Element samlElement = samlTokenDocument.getDocumentElement();
111 final SamlAssertionWrapper samlAssertionWrapper = new SamlAssertionWrapper(samlElement);
112
113 SamlTokenValidator samlTokenValidator =
114 wssSecurityProperties.getValidator(new QName(samlElement.getNamespaceURI(), samlElement.getLocalName()));
115 if (samlTokenValidator == null) {
116 samlTokenValidator = new SamlTokenValidatorImpl();
117 }
118
119
120 if (samlAssertionWrapper.isSigned()) {
121 Signature signature = samlAssertionWrapper.getSignature();
122 if (signature == null) {
123 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN,
124 "empty", new Object[] {"no signature to validate"});
125 }
126
127 int sigKeyInfoIdx = getSignatureKeyInfoIndex(eventQueue);
128 if (sigKeyInfoIdx < 0) {
129 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN, "noKeyInSAMLToken");
130 }
131 InboundSecurityToken sigSecurityToken = parseKeyInfo(inputProcessorChain, securityProperties, eventQueue, sigKeyInfoIdx);
132
133 if (sigSecurityToken == null) {
134 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN, "noKeyInSAMLToken");
135 }
136
137 samlTokenValidator.validate(sigSecurityToken, wssSecurityProperties);
138
139 BasicCredential credential = null;
140 if (sigSecurityToken.getX509Certificates() != null) {
141 credential = new BasicX509Credential(sigSecurityToken.getX509Certificates()[0]);
142 } else if (sigSecurityToken.getPublicKey() != null) {
143 credential = new BasicCredential(sigSecurityToken.getPublicKey());
144 } else {
145 throw new WSSecurityException(
146 WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity",
147 new Object[] {"cannot get certificate or key"}
148 );
149 }
150 try {
151 SignatureValidator.validate(signature, credential);
152 } catch (SignatureException ex) {
153 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE,
154 ex, "empty", new Object[] {"SAML signature validation failed"});
155 }
156 }
157
158 final InboundSecurityToken subjectSecurityToken;
159
160 List<String> methods = samlAssertionWrapper.getConfirmationMethods();
161 boolean holderOfKey = false;
162 if (methods != null) {
163 for (String method : methods) {
164 if (OpenSAMLUtil.isMethodHolderOfKey(method)) {
165 holderOfKey = true;
166 break;
167 }
168 }
169 }
170
171 if (holderOfKey) {
172 int subjectKeyInfoIndex = getSubjectKeyInfoIndex(eventQueue);
173 if (subjectKeyInfoIndex < 0) {
174 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN, "noKeyInSAMLToken");
175 }
176
177 subjectSecurityToken = parseKeyInfo(inputProcessorChain, securityProperties, eventQueue, subjectKeyInfoIndex);
178 if (subjectSecurityToken == null) {
179 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN, "noKeyInSAMLToken");
180 }
181 } else {
182 subjectSecurityToken = null;
183 }
184
185 final List<XMLSecEvent> xmlSecEvents = getResponsibleXMLSecEvents(eventQueue, index);
186 final List<QName> elementPath = getElementPath(eventQueue);
187 final TokenContext tokenContext =
188 new TokenContext(wssSecurityProperties, wsInboundSecurityContext, xmlSecEvents, elementPath);
189
190 final SamlSecurityToken samlSecurityToken =
191 samlTokenValidator.validate(samlAssertionWrapper, subjectSecurityToken, tokenContext);
192
193 SecurityTokenProvider<InboundSecurityToken> subjectSecurityTokenProvider =
194 new SecurityTokenProvider<InboundSecurityToken>() {
195
196 @Override
197 public InboundSecurityToken getSecurityToken() throws XMLSecurityException {
198 return (InboundSecurityToken)samlSecurityToken;
199 }
200
201 @Override
202 public String getId() {
203 return samlAssertionWrapper.getId();
204 }
205 };
206
207 wsInboundSecurityContext.registerSecurityTokenProvider(samlAssertionWrapper.getId(), subjectSecurityTokenProvider);
208
209
210 SamlTokenSecurityEvent samlTokenSecurityEvent = new SamlTokenSecurityEvent();
211 samlTokenSecurityEvent.setSecurityToken((SamlSecurityToken)subjectSecurityTokenProvider.getSecurityToken());
212 samlTokenSecurityEvent.setCorrelationID(samlAssertionWrapper.getId());
213 wsInboundSecurityContext.registerSecurityEvent(samlTokenSecurityEvent);
214
215 if (wssSecurityProperties.isValidateSamlSubjectConfirmation()) {
216 boolean soap12 = false;
217 if (elementPath.get(0) != null && WSSConstants.NS_SOAP12.equals(elementPath.get(0).getNamespaceURI())) {
218 soap12 = true;
219 }
220 SAMLTokenVerifierInputProcessor samlTokenVerifierInputProcessor =
221 new SAMLTokenVerifierInputProcessor(
222 securityProperties, samlAssertionWrapper, subjectSecurityTokenProvider, subjectSecurityToken,
223 soap12);
224 wsInboundSecurityContext.addSecurityEventListener(samlTokenVerifierInputProcessor);
225 inputProcessorChain.addProcessor(samlTokenVerifierInputProcessor);
226 }
227 }
228
229 private int getSubjectKeyInfoIndex(Deque<XMLSecEvent> eventQueue) {
230 int idx = -1;
231 Iterator<XMLSecEvent> xmlSecEventIterator = eventQueue.descendingIterator();
232 while (xmlSecEventIterator.hasNext()) {
233 XMLSecEvent xmlSecEvent = xmlSecEventIterator.next();
234 idx++;
235 if (XMLStreamConstants.START_ELEMENT == xmlSecEvent.getEventType()) {
236 QName elementName = xmlSecEvent.asStartElement().getName();
237 if (WSSConstants.TAG_dsig_KeyInfo.equals(elementName)) {
238 List<QName> elementPath = xmlSecEvent.asStartElement().getElementPath();
239 if (elementPath.size() >= 4) {
240 int lastIndex = elementPath.size() - 2;
241 if ("SubjectConfirmationData".equals(elementPath.get(lastIndex).getLocalPart())
242 && "SubjectConfirmation".equals(elementPath.get(lastIndex - 1).getLocalPart())
243 && "Subject".equals(elementPath.get(lastIndex - 2).getLocalPart())) {
244 return idx;
245 } else if ("SubjectConfirmation".equals(elementPath.get(lastIndex).getLocalPart())
246 && "Subject".equals(elementPath.get(lastIndex - 1).getLocalPart())) {
247 return idx;
248 }
249 }
250 }
251 }
252 }
253 return idx;
254 }
255
256 private int getSignatureKeyInfoIndex(Deque<XMLSecEvent> eventQueue) {
257 int idx = -1;
258 Iterator<XMLSecEvent> xmlSecEventIterator = eventQueue.descendingIterator();
259 while (xmlSecEventIterator.hasNext()) {
260 XMLSecEvent xmlSecEvent = xmlSecEventIterator.next();
261 idx++;
262 if (XMLStreamConstants.START_ELEMENT == xmlSecEvent.getEventType()) {
263 QName elementName = xmlSecEvent.asStartElement().getName();
264 if (WSSConstants.TAG_dsig_KeyInfo.equals(elementName)) {
265 List<QName> elementPath = xmlSecEvent.asStartElement().getElementPath();
266 if (elementPath.size() >= 4) {
267 int lastIndex = elementPath.size() - 2;
268 if ("Signature".equals(elementPath.get(lastIndex).getLocalPart())
269 && "Assertion".equals(elementPath.get(lastIndex - 1).getLocalPart())) {
270 return idx;
271 }
272 }
273 }
274 }
275 }
276 return idx;
277 }
278
279 private InboundSecurityToken parseKeyInfo(InputProcessorChain inputProcessorChain, XMLSecurityProperties securityProperties,
280 Deque<XMLSecEvent> eventQueue, int index) throws XMLSecurityException {
281 XMLSecEvent xmlSecEvent = null;
282 int idx = 0;
283 Iterator<XMLSecEvent> xmlSecEventIterator = eventQueue.descendingIterator();
284 while (xmlSecEventIterator.hasNext() && idx <= index) {
285 xmlSecEvent = xmlSecEventIterator.next();
286 idx++;
287 }
288
289 while (xmlSecEventIterator.hasNext()) {
290 xmlSecEvent = xmlSecEventIterator.next();
291 if (xmlSecEvent.isStartElement()) {
292 break;
293 }
294 idx++;
295 }
296 if (xmlSecEvent == null || !xmlSecEvent.isStartElement()) {
297 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN, "noKeyInSAMLToken");
298 }
299
300 final XMLSecStartElement xmlSecStartElement = xmlSecEvent.asStartElement();
301 final QName elementName = xmlSecStartElement.getName();
302 if (WSSConstants.TAG_WST_BINARY_SECRET.equals(elementName)
303 || WSSConstants.TAG_WST0512_BINARY_SECRET.equals(elementName)) {
304
305 final StringBuilder stringBuilder = new StringBuilder();
306 loop:
307 while (xmlSecEventIterator.hasNext()) {
308 xmlSecEvent = xmlSecEventIterator.next();
309 switch (xmlSecEvent.getEventType()) {
310 case XMLStreamConstants.END_ELEMENT:
311 if (xmlSecEvent.asEndElement().getName().equals(elementName)) {
312 break loop;
313 }
314 break;
315 case XMLStreamConstants.CHARACTERS:
316 stringBuilder.append(xmlSecEvent.asCharacters().getText());
317 break;
318 }
319 }
320
321 return new AbstractInboundSecurityToken(
322 inputProcessorChain.getSecurityContext(), IDGenerator.generateID(null),
323 WSSecurityTokenConstants.KeyIdentifier_NoKeyInfo, true) {
324 @Override
325 public WSSecurityTokenConstants.TokenType getTokenType() {
326 return WSSecurityTokenConstants.DefaultToken;
327 }
328
329 @Override
330 public boolean isAsymmetric() throws XMLSecurityException {
331 return false;
332 }
333
334 @Override
335 protected Key getKey(String algorithmURI, XMLSecurityConstants.AlgorithmUsage algorithmUsage, String correlationID)
336 throws XMLSecurityException {
337 Key key = super.getKey(algorithmURI, algorithmUsage, correlationID);
338 if (key == null) {
339 String algoFamily = JCEAlgorithmMapper.getJCEKeyAlgorithmFromURI(algorithmURI);
340 key = new SecretKeySpec(XMLUtils.decode(stringBuilder.toString()), algoFamily);
341 setSecretKey(algorithmURI, key);
342 }
343 return key;
344 }
345 };
346 } else {
347 Object object = null;
348 try {
349 Unmarshaller unmarshaller = WSSConstants.getJaxbUnmarshaller(securityProperties.isDisableSchemaValidation());
350 object = unmarshaller.unmarshal(new XMLSecurityEventReader(eventQueue, idx));
351 } catch (JAXBException e) {
352 throw new WSSecurityException(WSSecurityException.ErrorCode.UNSUPPORTED_SECURITY_TOKEN, e);
353 }
354
355 if (object instanceof JAXBElement) {
356 object = ((JAXBElement<?>) object).getValue();
357 }
358
359 KeyInfoType keyInfoType = null;
360 if (object instanceof X509DataType) {
361 JAXBElement<X509DataType> x509DataTypeJAXBElement =
362 new org.apache.xml.security.binding.xmldsig.ObjectFactory().createX509Data((X509DataType) object);
363 keyInfoType = new KeyInfoType();
364 SecurityTokenReferenceType securityTokenReferenceType = new SecurityTokenReferenceType();
365 securityTokenReferenceType.getAny().add(x509DataTypeJAXBElement);
366 JAXBElement<SecurityTokenReferenceType> securityTokenReferenceTypeJAXBElement =
367 new ObjectFactory().createSecurityTokenReference(securityTokenReferenceType);
368 keyInfoType.getContent().add(securityTokenReferenceTypeJAXBElement);
369 } else if (object instanceof EncryptedKeyType) {
370 EncryptedKeyType encryptedKeyType = (EncryptedKeyType) object;
371
372 WSSEncryptedKeyInputHandler encryptedKeyInputHandler = new WSSEncryptedKeyInputHandler();
373 encryptedKeyInputHandler.handle(inputProcessorChain, encryptedKeyType, xmlSecStartElement, securityProperties);
374
375 SecurityTokenProvider<? extends InboundSecurityToken> securityTokenProvider =
376 inputProcessorChain.getSecurityContext().getSecurityTokenProvider(encryptedKeyType.getId());
377 if (securityTokenProvider != null) {
378 return securityTokenProvider.getSecurityToken();
379 }
380
381 } else if (object instanceof SecurityTokenReferenceType) {
382 JAXBElement<SecurityTokenReferenceType> securityTokenReferenceTypeJAXBElement =
383 new ObjectFactory().createSecurityTokenReference((SecurityTokenReferenceType) object);
384 keyInfoType = new KeyInfoType();
385 keyInfoType.getContent().add(securityTokenReferenceTypeJAXBElement);
386 } else if (object instanceof KeyValueType) {
387 JAXBElement<KeyValueType> keyValueTypeJAXBElement =
388 new org.apache.xml.security.binding.xmldsig.ObjectFactory().createKeyValue((KeyValueType) object);
389 keyInfoType = new KeyInfoType();
390 keyInfoType.getContent().add(keyValueTypeJAXBElement);
391 } else {
392 throw new WSSecurityException(WSSecurityException.ErrorCode.UNSUPPORTED_SECURITY_TOKEN, "unsupportedKeyInfo");
393 }
394
395 return SecurityTokenFactory.getInstance().getSecurityToken(
396 keyInfoType, WSSecurityTokenConstants.KeyUsage_Signature_Verification,
397 securityProperties, inputProcessorChain.getSecurityContext());
398 }
399 }
400
401 @SuppressWarnings("unchecked")
402 @Override
403 protected <T> T parseStructure(Deque<XMLSecEvent> eventDeque, int index, XMLSecurityProperties securityProperties)
404 throws XMLSecurityException {
405 Document document = null;
406 try {
407 document = ((WSSSecurityProperties) securityProperties).getDocumentCreator().newDocument();
408 } catch (ParserConfigurationException e) {
409 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN, e);
410 }
411
412 Iterator<XMLSecEvent> xmlSecEventIterator = eventDeque.descendingIterator();
413 int curIdx = 0;
414 while (curIdx++ < index) {
415 xmlSecEventIterator.next();
416 }
417
418 Node currentNode = document;
419 while (xmlSecEventIterator.hasNext()) {
420 XMLSecEvent next = xmlSecEventIterator.next();
421 currentNode = parseXMLEvent(next, currentNode, document);
422 }
423 return (T) document;
424 }
425
426
427 public Node parseXMLEvent(XMLSecEvent xmlSecEvent, Node currentNode, Document document) throws WSSecurityException {
428 switch (xmlSecEvent.getEventType()) {
429 case XMLStreamConstants.START_ELEMENT:
430 XMLSecStartElement xmlSecStartElement = xmlSecEvent.asStartElement();
431 Element element = document.createElementNS(xmlSecStartElement.getName().getNamespaceURI(),
432 xmlSecStartElement.getName().getLocalPart());
433 if (xmlSecStartElement.getName().getPrefix() != null && !xmlSecStartElement.getName().getPrefix().isEmpty()) {
434 element.setPrefix(xmlSecStartElement.getName().getPrefix());
435 }
436 currentNode = currentNode.appendChild(element);
437 @SuppressWarnings("unchecked")
438 Iterator<Namespace> namespaceIterator = xmlSecStartElement.getNamespaces();
439 while (namespaceIterator.hasNext()) {
440 XMLSecNamespace next = (XMLSecNamespace)namespaceIterator.next();
441 parseXMLEvent(next, currentNode, document);
442 }
443 @SuppressWarnings("unchecked")
444 Iterator<Attribute> attributesIterator = xmlSecStartElement.getAttributes();
445 while (attributesIterator.hasNext()) {
446 XMLSecAttribute next = (XMLSecAttribute)attributesIterator.next();
447 parseXMLEvent(next, currentNode, document);
448 }
449
450 String elementNs = document.lookupNamespaceURI(xmlSecStartElement.getName().getPrefix());
451 if (elementNs == null) {
452 parseXMLEvent(xmlSecStartElement.getElementNamespace(), currentNode, document);
453 }
454 break;
455 case XMLStreamConstants.END_ELEMENT:
456 if (currentNode.getParentNode() != null) {
457 currentNode = currentNode.getParentNode();
458 }
459 break;
460 case XMLStreamConstants.PROCESSING_INSTRUCTION:
461 Node piNode = document.createProcessingInstruction(
462 ((ProcessingInstruction) xmlSecEvent).getTarget(),
463 ((ProcessingInstruction) xmlSecEvent).getTarget()
464 );
465 currentNode.appendChild(piNode);
466 break;
467 case XMLStreamConstants.CHARACTERS:
468 Node characterNode = document.createTextNode(xmlSecEvent.asCharacters().getData());
469 currentNode.appendChild(characterNode);
470 break;
471 case XMLStreamConstants.COMMENT:
472 Node commentNode = document.createComment(((Comment) xmlSecEvent).getText());
473 currentNode.appendChild(commentNode);
474 break;
475 case XMLStreamConstants.START_DOCUMENT:
476 break;
477 case XMLStreamConstants.END_DOCUMENT:
478 return currentNode;
479 case XMLStreamConstants.ATTRIBUTE:
480 final XMLSecAttribute xmlSecAttribute = (XMLSecAttribute) xmlSecEvent;
481 Attr attributeNode = document.createAttributeNS(
482 xmlSecAttribute.getName().getNamespaceURI(),
483 xmlSecAttribute.getName().getLocalPart());
484 attributeNode.setPrefix(xmlSecAttribute.getName().getPrefix());
485 attributeNode.setValue(xmlSecAttribute.getValue());
486 ((Element) currentNode).setAttributeNodeNS(attributeNode);
487
488
489 String attrNs = document.lookupNamespaceURI(xmlSecAttribute.getName().getPrefix());
490 if (attrNs == null) {
491 parseXMLEvent(xmlSecAttribute.getAttributeNamespace(), currentNode, document);
492 }
493 break;
494 case XMLStreamConstants.DTD:
495
496
497
498
499
500
501 break;
502 case XMLStreamConstants.NAMESPACE:
503 Namespace namespace = (Namespace) xmlSecEvent;
504 Attr namespaceNode;
505 String prefix = namespace.getPrefix();
506 if (prefix == null || prefix.isEmpty()) {
507 namespaceNode = document.createAttributeNS(WSSConstants.NS_XML, "xmlns");
508 } else {
509 namespaceNode = document.createAttributeNS(WSSConstants.NS_XML, "xmlns:" + prefix);
510 }
511 namespaceNode.setValue(namespace.getNamespaceURI());
512 ((Element) currentNode).setAttributeNodeNS(namespaceNode);
513 break;
514 default:
515 throw new WSSecurityException(
516 WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN,
517 "empty",
518 new Object[] {"Illegal XMLEvent received: " + xmlSecEvent.getEventType()});
519 }
520 return currentNode;
521 }
522
523
524
525
526
527
528 static class SAMLTokenVerifierInputProcessor extends AbstractInputProcessor implements SecurityEventListener {
529
530 private SamlAssertionWrapper samlAssertionWrapper;
531 private SecurityTokenProvider<InboundSecurityToken> securityTokenProvider;
532 private InboundSecurityToken subjectSecurityToken;
533 private List<SignedElementSecurityEvent> samlTokenSignedElementSecurityEvents = new ArrayList<>();
534 private SignedPartSecurityEvent bodySignedPartSecurityEvent;
535
536 private final boolean soap12;
537 private final List<QName> saml1TokenPath;
538 private final List<QName> saml2TokenPath;
539
540 SAMLTokenVerifierInputProcessor(XMLSecurityProperties securityProperties,
541 SamlAssertionWrapper samlAssertionWrapper,
542 SecurityTokenProvider<InboundSecurityToken> securityTokenProvider,
543 InboundSecurityToken subjectSecurityToken,
544 boolean soap12) {
545 super(securityProperties);
546 this.setPhase(XMLSecurityConstants.Phase.POSTPROCESSING);
547 this.addAfterProcessor(OperationInputProcessor.class.getName());
548 this.samlAssertionWrapper = samlAssertionWrapper;
549 this.securityTokenProvider = securityTokenProvider;
550 this.subjectSecurityToken = subjectSecurityToken;
551
552 this.soap12 = soap12;
553 if (soap12) {
554 saml1TokenPath = new ArrayList<>(WSSConstants.SOAP_12_WSSE_SECURITY_HEADER_PATH);
555 saml1TokenPath.add(WSSConstants.TAG_SAML_ASSERTION);
556 saml2TokenPath = new ArrayList<>(WSSConstants.SOAP_12_WSSE_SECURITY_HEADER_PATH);
557 saml2TokenPath.add(WSSConstants.TAG_SAML2_ASSERTION);
558 } else {
559 saml1TokenPath = new ArrayList<>(WSSConstants.SOAP_11_WSSE_SECURITY_HEADER_PATH);
560 saml1TokenPath.add(WSSConstants.TAG_SAML_ASSERTION);
561 saml2TokenPath = new ArrayList<>(WSSConstants.SOAP_11_WSSE_SECURITY_HEADER_PATH);
562 saml2TokenPath.add(WSSConstants.TAG_SAML2_ASSERTION);
563 }
564 }
565
566 @Override
567 public void registerSecurityEvent(SecurityEvent securityEvent) throws XMLSecurityException {
568 if (WSSecurityEventConstants.SIGNED_PART.equals(securityEvent.getSecurityEventType())) {
569 SignedPartSecurityEvent signedPartSecurityEvent = (SignedPartSecurityEvent) securityEvent;
570
571 List<QName> elementPath = signedPartSecurityEvent.getElementPath();
572 if (soap12 && WSSUtils.pathMatches(WSSConstants.SOAP_12_BODY_PATH, elementPath)
573 || !soap12 && WSSUtils.pathMatches(WSSConstants.SOAP_11_BODY_PATH, elementPath)) {
574 bodySignedPartSecurityEvent = signedPartSecurityEvent;
575 }
576 } else if (WSSecurityEventConstants.SignedElement.equals(securityEvent.getSecurityEventType())) {
577 SignedElementSecurityEvent signedPartSecurityEvent = (SignedElementSecurityEvent) securityEvent;
578
579 List<QName> elementPath = signedPartSecurityEvent.getElementPath();
580 if (WSSUtils.pathMatches(saml2TokenPath, elementPath)
581 || WSSUtils.pathMatches(saml1TokenPath, elementPath)) {
582 samlTokenSignedElementSecurityEvents.add(signedPartSecurityEvent);
583 }
584 }
585 }
586
587 @Override
588 public XMLSecEvent processHeaderEvent(InputProcessorChain inputProcessorChain)
589 throws XMLStreamException, XMLSecurityException {
590 return inputProcessorChain.processHeaderEvent();
591 }
592
593 @Override
594 public XMLSecEvent processEvent(InputProcessorChain inputProcessorChain)
595 throws XMLStreamException, XMLSecurityException {
596
597 XMLSecEvent xmlSecEvent = inputProcessorChain.processEvent();
598 if (xmlSecEvent.getEventType() == XMLStreamConstants.START_ELEMENT) {
599 XMLSecStartElement xmlSecStartElement = xmlSecEvent.asStartElement();
600 List<QName> elementPath = xmlSecStartElement.getElementPath();
601 if (elementPath.size() == 3 && WSSUtils.isInSOAPBody(elementPath)) {
602 inputProcessorChain.removeProcessor(this);
603 checkPossessionOfKey(inputProcessorChain, samlAssertionWrapper, subjectSecurityToken);
604 }
605 }
606 return xmlSecEvent;
607 }
608
609 private void checkPossessionOfKey(
610 InputProcessorChain inputProcessorChain, SamlAssertionWrapper samlAssertionWrapper,
611 InboundSecurityToken subjectSecurityToken) throws WSSecurityException {
612
613 boolean methodNotSatisfied = false;
614 try {
615 SecurityToken httpsSecurityToken = getHttpsSecurityToken(inputProcessorChain);
616
617 List<SecurityTokenProvider<? extends InboundSecurityToken>> securityTokenProviders =
618 inputProcessorChain.getSecurityContext().getRegisteredSecurityTokenProviders();
619
620 List<String> confirmationMethods = samlAssertionWrapper.getConfirmationMethods();
621 for (int i = 0; i < confirmationMethods.size(); i++) {
622 String confirmationMethod = confirmationMethods.get(i);
623 if (OpenSAMLUtil.isMethodHolderOfKey(confirmationMethod)) {
624
625 X509Certificate[] subjectCertificates = subjectSecurityToken.getX509Certificates();
626 PublicKey subjectPublicKey = subjectSecurityToken.getPublicKey();
627 Key subjectSecretKey = null;
628 Map<String, Key> subjectKeyMap = subjectSecurityToken.getSecretKey();
629 if (!subjectKeyMap.isEmpty()) {
630 subjectSecretKey = subjectKeyMap.values().toArray(new Key[subjectKeyMap.size()])[0];
631 }
632
633
634
635
636
637
638
639
640
641
642 if (httpsSecurityToken != null
643 && httpsSecurityToken.getX509Certificates() != null
644 && httpsSecurityToken.getX509Certificates().length > 0) {
645
646 X509Certificate httpsCertificate = httpsSecurityToken.getX509Certificates()[0];
647
648
649 if (subjectCertificates != null && subjectCertificates.length > 0
650 && httpsCertificate.equals(subjectCertificates[0])) {
651 return;
652
653 } else if (httpsCertificate.getPublicKey().equals(subjectPublicKey)) {
654 return;
655 }
656 }
657
658
659 for (int j = 0; j < securityTokenProviders.size(); j++) {
660 SecurityTokenProvider<? extends InboundSecurityToken> securityTokenProvider = securityTokenProviders.get(j);
661 InboundSecurityToken securityToken = securityTokenProvider.getSecurityToken();
662
663 if (securityToken == httpsSecurityToken || securityToken == subjectSecurityToken
664 || !containsSignature(securityToken.getTokenUsages())) {
665 continue;
666 }
667 X509Certificate[] x509Certificates = securityToken.getX509Certificates();
668 PublicKey publicKey = securityToken.getPublicKey();
669 Map<String, Key> keyMap = securityToken.getSecretKey();
670 if (x509Certificates != null && x509Certificates.length > 0
671 && subjectCertificates != null && subjectCertificates.length > 0
672 && subjectCertificates[0].equals(x509Certificates[0])) {
673 return;
674 }
675 if (publicKey != null && publicKey.equals(subjectPublicKey)) {
676 return;
677 }
678 Iterator<Map.Entry<String, Key>> iterator = keyMap.entrySet().iterator();
679 while (iterator.hasNext()) {
680 Map.Entry<String, Key> next = iterator.next();
681 if (next.getValue().equals(subjectSecretKey)) {
682 return;
683 }
684 }
685 }
686 methodNotSatisfied = true;
687 } else if (OpenSAMLUtil.isMethodSenderVouches(confirmationMethod)) {
688
689
690
691
692
693
694
695
696 if (httpsSecurityToken != null
697 && httpsSecurityToken.getX509Certificates() != null
698 && httpsSecurityToken.getX509Certificates().length > 0) {
699 return;
700 }
701
702 SignedElementSecurityEvent samlTokenSignedElementSecurityEvent = null;
703 for (int j = 0; j < samlTokenSignedElementSecurityEvents.size(); j++) {
704 SignedElementSecurityEvent signedElementSecurityEvent = samlTokenSignedElementSecurityEvents.get(j);
705 if (securityTokenProvider.getSecurityToken().getXMLSecEvent()
706 == signedElementSecurityEvent.getXmlSecEvent()) {
707
708 samlTokenSignedElementSecurityEvent = signedElementSecurityEvent;
709 }
710 }
711 if (bodySignedPartSecurityEvent != null
712 && samlTokenSignedElementSecurityEvent != null
713 && bodySignedPartSecurityEvent.getSecurityToken()
714 == samlTokenSignedElementSecurityEvent.getSecurityToken()) {
715 return;
716 }
717 methodNotSatisfied = true;
718 }
719 }
720 } catch (XMLSecurityException e) {
721 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, e);
722 }
723 if (methodNotSatisfied) {
724 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION,
725 "empty",
726 new Object[] {"SAML proof-of-possession of the private/secret key failed"});
727 }
728 }
729
730 private SecurityToken getHttpsSecurityToken(InputProcessorChain inputProcessorChain) throws XMLSecurityException {
731 List<SecurityTokenProvider<? extends InboundSecurityToken>> securityTokenProviders =
732 inputProcessorChain.getSecurityContext().getRegisteredSecurityTokenProviders();
733 for (int i = 0; i < securityTokenProviders.size(); i++) {
734 SecurityTokenProvider<? extends InboundSecurityToken> securityTokenProvider = securityTokenProviders.get(i);
735 SecurityToken securityToken = securityTokenProvider.getSecurityToken();
736 if (WSSecurityTokenConstants.HTTPS_TOKEN.equals(securityToken.getTokenType())) {
737 return securityToken;
738 }
739 }
740 return null;
741 }
742
743 private boolean containsSignature(List<TokenUsage> tokenUses) {
744 return tokenUses.contains(WSSecurityTokenConstants.TOKENUSAGE_MAIN_SIGNATURE)
745 || tokenUses.contains(WSSecurityTokenConstants.TokenUsage_Signature)
746 || tokenUses.contains(WSSecurityTokenConstants.TOKENUSAGE_ENDORSING_ENCRYPTED_SUPPORTING_TOKENS)
747 || tokenUses.contains(WSSecurityTokenConstants.TOKENUSAGE_ENDORSING_SUPPORTING_TOKENS)
748 || tokenUses.contains(WSSecurityTokenConstants.TOKENUSAGE_SIGNED_ENDORSING_ENCRYPTED_SUPPORTING_TOKENS)
749 || tokenUses.contains(WSSecurityTokenConstants.TOKENUSAGE_SIGNED_ENDORSING_SUPPORTING_TOKENS);
750 }
751 }
752 }