in src/main/java/org/apache/xml/security/stax/impl/processor/input/AbstractDecryptInputProcessor.java [176:362]
private XMLSecEvent processEvent(InputProcessorChain inputProcessorChain, boolean isSecurityHeaderEvent)
throws XMLStreamException, XMLSecurityException {
if (!tmpXmlEventList.isEmpty()) {
return tmpXmlEventList.pollLast();
}
XMLSecEvent xmlSecEvent = isSecurityHeaderEvent
? inputProcessorChain.processHeaderEvent()
: inputProcessorChain.processEvent();
boolean encryptedHeader = false;
if (xmlSecEvent.getEventType() == XMLStreamConstants.START_ELEMENT) {
XMLSecStartElement xmlSecStartElement = xmlSecEvent.asStartElement();
//buffer the events until the EncryptedData Element appears and discard it if we found the reference inside it
//otherwise replay it
if (xmlSecStartElement.getName().equals(XMLSecurityConstants.TAG_wsse11_EncryptedHeader)) {
xmlSecEvent = readAndBufferEncryptedHeader(inputProcessorChain, isSecurityHeaderEvent, xmlSecEvent);
xmlSecStartElement = xmlSecEvent.asStartElement();
encryptedHeader = true;
}
//check if the current start-element has the name EncryptedData and an Id attribute
if (xmlSecStartElement.getName().equals(XMLSecurityConstants.TAG_xenc_EncryptedData)) {
ReferenceType referenceType = null;
if (references != null) {
referenceType = matchesReferenceId(xmlSecStartElement);
if (referenceType == null) {
//if the events were not for us (no matching reference-id the we have to replay the EncryptedHeader elements)
if (!tmpXmlEventList.isEmpty()) {
return tmpXmlEventList.pollLast();
}
return xmlSecEvent;
}
//duplicate id's are forbidden
if (processedReferences.contains(referenceType)) {
throw new XMLSecurityException("signature.Verification.MultipleIDs");
}
processedReferences.add(referenceType);
}
tmpXmlEventList.clear();
//the following LOGic reads the encryptedData structure and doesn't pass them further
//through the chain
InputProcessorChain subInputProcessorChain = inputProcessorChain.createSubChain(this);
EncryptedDataType encryptedDataType =
parseEncryptedDataStructure(isSecurityHeaderEvent, xmlSecEvent, subInputProcessorChain);
if (encryptedDataType.getId() == null) {
encryptedDataType.setId(IDGenerator.generateID(null));
}
InboundSecurityToken inboundSecurityToken =
getSecurityToken(inputProcessorChain, xmlSecStartElement, encryptedDataType);
handleSecurityToken(inboundSecurityToken, inputProcessorChain.getSecurityContext(), encryptedDataType);
final String algorithmURI = encryptedDataType.getEncryptionMethod().getAlgorithm();
final int ivLength = JCEMapper.getIVLengthFromURI(algorithmURI) / 8;
Cipher symCipher = getCipher(algorithmURI);
if (encryptedDataType.getCipherData().getCipherReference() != null) {
handleCipherReference(inputProcessorChain, encryptedDataType, symCipher, inboundSecurityToken);
subInputProcessorChain.reset();
return isSecurityHeaderEvent
? subInputProcessorChain.processHeaderEvent()
: subInputProcessorChain.processEvent();
}
XMLSecStartElement parentXMLSecStartElement = xmlSecStartElement.getParentXMLSecStartElement();
if (encryptedHeader) {
parentXMLSecStartElement = parentXMLSecStartElement.getParentXMLSecStartElement();
}
AbstractDecryptedEventReaderInputProcessor decryptedEventReaderInputProcessor =
newDecryptedEventReaderInputProcessor(
encryptedHeader, parentXMLSecStartElement, encryptedDataType, inboundSecurityToken,
inputProcessorChain.getSecurityContext()
);
//add the new created EventReader processor to the chain.
inputProcessorChain.addProcessor(decryptedEventReaderInputProcessor);
inputProcessorChain.getDocumentContext().setIsInEncryptedContent(
inputProcessorChain.getProcessors().indexOf(decryptedEventReaderInputProcessor),
decryptedEventReaderInputProcessor);
//fire here only ContentEncryptedElementEvents
//the other ones will be fired later, because we don't know the encrypted element name yet
//important: this must occur after setIsInEncryptedContent!
if (SecurePart.Modifier.Content.getModifier().equals(encryptedDataType.getType())) {
handleEncryptedContent(inputProcessorChain, xmlSecStartElement.getParentXMLSecStartElement(),
inboundSecurityToken, encryptedDataType);
}
// Process the next event - we need to do this in case it's an xop:Include tag as we handle this
// differently compared to the usual inlined bytes. Note this only works if we have a single xop:Include
// chile element
XMLSecEvent nextEvent = null;
subInputProcessorChain.reset();
if (isSecurityHeaderEvent) {
nextEvent = subInputProcessorChain.processHeaderEvent();
} else {
nextEvent = subInputProcessorChain.processEvent();
}
InputStream decryptInputStream = null; //NOPMD
if (nextEvent.isStartElement() && nextEvent.asStartElement().getName().equals(XMLSecurityConstants.TAG_XOP_INCLUDE)) {
try {
// Unmarshal the XOP Include Element
Deque<XMLSecEvent> xmlSecEvents = new ArrayDeque<>();
xmlSecEvents.push(nextEvent);
xmlSecEvents.push(XMLSecEventFactory.createXmlSecEndElement(XMLSecurityConstants.TAG_XOP_INCLUDE));
Unmarshaller unmarshaller =
XMLSecurityConstants.getJaxbUnmarshaller(getSecurityProperties().isDisableSchemaValidation());
@SuppressWarnings("unchecked")
JAXBElement<Include> includeJAXBElement =
(JAXBElement<Include>) unmarshaller.unmarshal(new XMLSecurityEventReader(xmlSecEvents, 0));
Include include = includeJAXBElement.getValue();
String href = include.getHref();
decryptInputStream =
handleXOPInclude(inputProcessorChain, encryptedDataType, href, symCipher, inboundSecurityToken);
} catch (JAXBException e) {
throw new XMLSecurityException(e);
}
} else {
//create a new Thread for streaming decryption
DecryptionThread decryptionThread = new DecryptionThread(subInputProcessorChain, isSecurityHeaderEvent, nextEvent);
Key decryptionKey =
inboundSecurityToken.getSecretKey(algorithmURI, XMLSecurityConstants.Enc, encryptedDataType.getId());
decryptionKey = XMLSecurityUtils.prepareSecretKey(algorithmURI, decryptionKey.getEncoded());
decryptionThread.setSecretKey(decryptionKey);
decryptionThread.setSymmetricCipher(symCipher);
decryptionThread.setIvLength(ivLength);
Thread thread = new Thread(decryptionThread);
thread.setPriority(Thread.NORM_PRIORITY + 1);
thread.setName("decryption thread");
//when an exception in the decryption thread occurs, we want to forward them:
thread.setUncaughtExceptionHandler(decryptedEventReaderInputProcessor);
decryptedEventReaderInputProcessor.setDecryptionThread(thread);
//we have to start the thread before we call decryptionThread.getPipedInputStream().
//Otherwise we will end in a deadlock, because the StAX reader expects already data.
//@See some lines below:
LOG.log(Level.DEBUG, "Starting decryption thread");
thread.start();
decryptInputStream = decryptionThread.getPipedInputStream();
}
InputStream prologInputStream; //NOPMD
InputStream epilogInputStream; //NOPMD
try {
prologInputStream = writeWrapperStartElement(xmlSecStartElement);
epilogInputStream = writeWrapperEndElement();
} catch (IOException e) {
throw new XMLSecurityException(e);
}
decryptInputStream = applyTransforms(referenceType, decryptInputStream);
//spec says (4.2): "The cleartext octet sequence obtained in step 3 is
//interpreted as UTF-8 encoded character data."
XMLStreamReader xmlStreamReader =
inputProcessorChain.getSecurityContext().<XMLInputFactory>get(
XMLSecurityConstants.XMLINPUTFACTORY).createXMLStreamReader(
new MultiInputStream(prologInputStream, decryptInputStream, epilogInputStream), StandardCharsets.UTF_8.name());
//forward to wrapper element
forwardToWrapperElement(xmlStreamReader);
decryptedEventReaderInputProcessor.setXmlStreamReader(xmlStreamReader);
if (isSecurityHeaderEvent) {
return decryptedEventReaderInputProcessor.processHeaderEvent(inputProcessorChain);
} else {
return decryptedEventReaderInputProcessor.processEvent(inputProcessorChain);
}
}
}
return xmlSecEvent;
}