| /** |
| * 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.impl.processor.input; |
| |
| import org.apache.xml.security.exceptions.XMLSecurityException; |
| import org.apache.xml.security.stax.ext.AbstractInputProcessor; |
| import org.apache.xml.security.stax.ext.InputProcessorChain; |
| import org.apache.xml.security.stax.ext.XMLSecurityConstants; |
| import org.apache.xml.security.stax.ext.XMLSecurityProperties; |
| import org.apache.xml.security.stax.ext.stax.XMLSecEndElement; |
| import org.apache.xml.security.stax.ext.stax.XMLSecEvent; |
| import org.apache.xml.security.stax.ext.stax.XMLSecStartElement; |
| |
| import javax.xml.stream.XMLStreamConstants; |
| import javax.xml.stream.XMLStreamException; |
| import java.util.ArrayDeque; |
| |
| /** |
| * Processor for XML Security. |
| * |
| */ |
| public class XMLSecurityInputProcessor extends AbstractInputProcessor { |
| |
| private int startIndexForProcessor; |
| private InternalBufferProcessor internalBufferProcessor; |
| private boolean signatureElementFound = false; |
| private boolean encryptedDataElementFound = false; |
| private boolean decryptOnly = false; |
| |
| public XMLSecurityInputProcessor(XMLSecurityProperties securityProperties) { |
| super(securityProperties); |
| setPhase(XMLSecurityConstants.Phase.POSTPROCESSING); |
| |
| // For decrypt only mode we misuse the actions that are normally only used for outbound processing. |
| // In decrypt only mode we can save a lot of memory because we don't have to buffer anything and |
| // can process the document sequentially. |
| // for backward compatibility: |
| // If no actions are set (default behaviour) we do signature and decryption processing |
| // If the only action is XMLSecurityConstants.ENCRYPT then we only do decryption and skip signature processing |
| decryptOnly = securityProperties.getActions().size() == 1 && |
| securityProperties.getActions().contains(XMLSecurityConstants.ENCRYPT); |
| } |
| |
| @Override |
| public XMLSecEvent processNextHeaderEvent(InputProcessorChain inputProcessorChain) |
| throws XMLStreamException, XMLSecurityException { |
| return null; |
| } |
| |
| @Override |
| public XMLSecEvent processNextEvent(InputProcessorChain inputProcessorChain) |
| throws XMLStreamException, XMLSecurityException { |
| |
| //add the buffer processor (for signature) when this processor is called for the first time |
| if (!decryptOnly && internalBufferProcessor == null) { |
| internalBufferProcessor = new InternalBufferProcessor(getSecurityProperties()); |
| inputProcessorChain.addProcessor(internalBufferProcessor); |
| } |
| |
| XMLSecEvent xmlSecEvent = inputProcessorChain.processEvent(); |
| switch (xmlSecEvent.getEventType()) { |
| case XMLStreamConstants.START_ELEMENT: |
| final XMLSecStartElement xmlSecStartElement = xmlSecEvent.asStartElement(); |
| |
| if (!decryptOnly && xmlSecStartElement.getName().equals(XMLSecurityConstants.TAG_dsig_Signature)) { |
| if (signatureElementFound) { |
| throw new XMLSecurityException("stax.multipleSignaturesNotSupported"); |
| } |
| signatureElementFound = true; |
| startIndexForProcessor = internalBufferProcessor.getXmlSecEventList().size() - 1; |
| } else if (xmlSecStartElement.getName().equals(XMLSecurityConstants.TAG_xenc_EncryptedData)) { |
| encryptedDataElementFound = true; |
| |
| XMLDecryptInputProcessor decryptInputProcessor = new XMLDecryptInputProcessor(getSecurityProperties()); |
| decryptInputProcessor.setPhase(XMLSecurityConstants.Phase.PREPROCESSING); |
| decryptInputProcessor.addAfterProcessor(XMLEventReaderInputProcessor.class.getName()); |
| decryptInputProcessor.addBeforeProcessor(XMLSecurityInputProcessor.class.getName()); |
| decryptInputProcessor.addBeforeProcessor(XMLSecurityInputProcessor.InternalBufferProcessor.class.getName()); |
| inputProcessorChain.addProcessor(decryptInputProcessor); |
| |
| if (!decryptOnly) { |
| final ArrayDeque<XMLSecEvent> xmlSecEventList = internalBufferProcessor.getXmlSecEventList(); |
| //remove the last event (EncryptedData) |
| xmlSecEventList.pollFirst(); |
| } |
| |
| // temporary processor to return the EncryptedData element for the DecryptionProcessor |
| AbstractInputProcessor abstractInputProcessor = new AbstractInputProcessor(getSecurityProperties()) { |
| @Override |
| public XMLSecEvent processNextHeaderEvent(InputProcessorChain inputProcessorChain) |
| throws XMLStreamException, XMLSecurityException { |
| return processNextEvent(inputProcessorChain); |
| } |
| |
| @Override |
| public XMLSecEvent processNextEvent(InputProcessorChain inputProcessorChain) |
| throws XMLStreamException, XMLSecurityException { |
| inputProcessorChain.removeProcessor(this); |
| return xmlSecStartElement; |
| } |
| }; |
| abstractInputProcessor.setPhase(XMLSecurityConstants.Phase.PREPROCESSING); |
| abstractInputProcessor.addBeforeProcessor(decryptInputProcessor); |
| inputProcessorChain.addProcessor(abstractInputProcessor); |
| |
| //fetch the next event from the original chain |
| inputProcessorChain.reset(); |
| xmlSecEvent = inputProcessorChain.processEvent(); |
| |
| // no need to catch a possible signature element here because the decrypt processor |
| // is installed before this processor and therefore the decrypted signature element will |
| // flow as normal through this processor. |
| // for safety we do a check if this really true |
| //check if the decrypted element is a Signature element |
| if (!decryptOnly && xmlSecEvent.isStartElement() && |
| xmlSecEvent.asStartElement().getName().equals(XMLSecurityConstants.TAG_dsig_Signature) && |
| !signatureElementFound) { |
| throw new XMLSecurityException("Internal error"); |
| } |
| } |
| break; |
| case XMLStreamConstants.END_ELEMENT: |
| XMLSecEndElement xmlSecEndElement = xmlSecEvent.asEndElement(); |
| // Handle the signature |
| if (signatureElementFound |
| && xmlSecEndElement.getName().equals(XMLSecurityConstants.TAG_dsig_Signature)) { |
| XMLSignatureInputHandler inputHandler = new XMLSignatureInputHandler(); |
| |
| final ArrayDeque<XMLSecEvent> xmlSecEventList = internalBufferProcessor.getXmlSecEventList(); |
| inputHandler.handle(inputProcessorChain, getSecurityProperties(), |
| xmlSecEventList, startIndexForProcessor); |
| |
| inputProcessorChain.removeProcessor(internalBufferProcessor); |
| |
| //add the replay processor to the chain... |
| InternalReplayProcessor internalReplayProcessor = |
| new InternalReplayProcessor(getSecurityProperties(), xmlSecEventList); |
| internalReplayProcessor.addBeforeProcessor(XMLSignatureReferenceVerifyInputProcessor.class.getName()); |
| inputProcessorChain.addProcessor(internalReplayProcessor); |
| |
| //...and let the SignatureVerificationProcessor process the buffered events (enveloped signature). |
| InputProcessorChain subInputProcessorChain = inputProcessorChain.createSubChain(this, false); |
| while (!xmlSecEventList.isEmpty()) { |
| subInputProcessorChain.reset(); |
| subInputProcessorChain.processEvent(); |
| } |
| |
| // copy all processor back to main chain for finalization |
| inputProcessorChain.getProcessors().clear(); |
| inputProcessorChain.getProcessors().addAll(subInputProcessorChain.getProcessors()); |
| } |
| break; |
| } |
| |
| return xmlSecEvent; |
| } |
| |
| @Override |
| public void doFinal(InputProcessorChain inputProcessorChain) throws XMLStreamException, XMLSecurityException { |
| if (!signatureElementFound && !encryptedDataElementFound) { |
| throw new XMLSecurityException("stax.unsecuredMessage"); |
| } |
| super.doFinal(inputProcessorChain); |
| } |
| |
| /** |
| * Temporary Processor to buffer all events until the end of the required actions |
| */ |
| public class InternalBufferProcessor extends AbstractInputProcessor { |
| |
| private final ArrayDeque<XMLSecEvent> xmlSecEventList = new ArrayDeque<XMLSecEvent>(); |
| |
| InternalBufferProcessor(XMLSecurityProperties securityProperties) { |
| super(securityProperties); |
| setPhase(XMLSecurityConstants.Phase.POSTPROCESSING); |
| addBeforeProcessor(XMLSecurityInputProcessor.class.getName()); |
| } |
| |
| public ArrayDeque<XMLSecEvent> getXmlSecEventList() { |
| return xmlSecEventList; |
| } |
| |
| @Override |
| public XMLSecEvent processNextHeaderEvent(InputProcessorChain inputProcessorChain) |
| throws XMLStreamException, XMLSecurityException { |
| return null; |
| } |
| |
| @Override |
| public XMLSecEvent processNextEvent(InputProcessorChain inputProcessorChain) |
| throws XMLStreamException, XMLSecurityException { |
| XMLSecEvent xmlSecEvent = inputProcessorChain.processEvent(); |
| xmlSecEventList.push(xmlSecEvent); |
| return xmlSecEvent; |
| } |
| } |
| |
| /** |
| * Temporary processor to replay the buffered events |
| */ |
| public static class InternalReplayProcessor extends AbstractInputProcessor { |
| |
| private final ArrayDeque<XMLSecEvent> xmlSecEventList; |
| |
| public InternalReplayProcessor(XMLSecurityProperties securityProperties, ArrayDeque<XMLSecEvent> xmlSecEventList) { |
| super(securityProperties); |
| this.xmlSecEventList = xmlSecEventList; |
| } |
| |
| @Override |
| public XMLSecEvent processNextHeaderEvent(InputProcessorChain inputProcessorChain) |
| throws XMLStreamException, XMLSecurityException { |
| return null; |
| } |
| |
| @Override |
| public XMLSecEvent processNextEvent(InputProcessorChain inputProcessorChain) |
| throws XMLStreamException, XMLSecurityException { |
| |
| if (!xmlSecEventList.isEmpty()) { |
| return xmlSecEventList.pollLast(); |
| } else { |
| inputProcessorChain.removeProcessor(this); |
| return inputProcessorChain.processEvent(); |
| } |
| } |
| } |
| } |