blob: 2fc1b757d41dec8d920105651e6779ba07ea7d3b [file] [log] [blame]
/**
* 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.output;
import org.apache.xml.security.stax.impl.transformer.canonicalizer.Canonicalizer20010315_Excl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.stax.config.JCEAlgorithmMapper;
import org.apache.xml.security.stax.config.ResourceResolverMapper;
import org.apache.xml.security.stax.ext.*;
import org.apache.xml.security.stax.ext.stax.XMLSecEvent;
import org.apache.xml.security.stax.ext.stax.XMLSecStartElement;
import org.apache.xml.security.stax.impl.SignaturePartDef;
import org.apache.xml.security.stax.impl.transformer.TransformIdentity;
import org.apache.xml.security.stax.impl.util.DigestOutputStream;
import org.apache.xml.security.utils.UnsyncBufferedOutputStream;
import org.apache.xml.security.utils.XMLUtils;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.*;
/**
*/
public abstract class AbstractSignatureOutputProcessor extends AbstractOutputProcessor {
private static final transient Logger LOG = LoggerFactory.getLogger(AbstractSignatureOutputProcessor.class);
private final List<SignaturePartDef> signaturePartDefList = new ArrayList<>();
private InternalSignatureOutputProcessor activeInternalSignatureOutputProcessor;
public AbstractSignatureOutputProcessor() throws XMLSecurityException {
super();
}
public List<SignaturePartDef> getSignaturePartDefList() {
return signaturePartDefList;
}
@Override
public abstract void processEvent(XMLSecEvent xmlSecEvent, OutputProcessorChain outputProcessorChain)
throws XMLStreamException, XMLSecurityException;
@Override
public void doFinal(OutputProcessorChain outputProcessorChain) throws XMLStreamException, XMLSecurityException {
doFinalInternal(outputProcessorChain);
super.doFinal(outputProcessorChain);
}
protected void doFinalInternal(OutputProcessorChain outputProcessorChain) throws XMLSecurityException, XMLStreamException {
Map<Object, SecurePart> dynamicSecureParts =
outputProcessorChain.getSecurityContext().getAsMap(XMLSecurityConstants.SIGNATURE_PARTS);
if (dynamicSecureParts != null) {
Iterator<Map.Entry<Object, SecurePart>> securePartsMapIterator = dynamicSecureParts.entrySet().iterator();
while (securePartsMapIterator.hasNext()) {
Map.Entry<Object, SecurePart> securePartEntry = securePartsMapIterator.next();
final SecurePart securePart = securePartEntry.getValue();
if (securePart.getExternalReference() != null) {
digestExternalReference(outputProcessorChain, securePart);
}
}
}
verifySignatureParts(outputProcessorChain);
}
protected void digestExternalReference(
OutputProcessorChain outputProcessorChain, SecurePart securePart)
throws XMLSecurityException, XMLStreamException {
final String externalReference = securePart.getExternalReference();
ResourceResolver resourceResolver =
ResourceResolverMapper.getResourceResolver(
externalReference, outputProcessorChain.getDocumentContext().getBaseURI());
String digestAlgo = securePart.getDigestMethod();
if (digestAlgo == null) {
digestAlgo = getSecurityProperties().getSignatureDigestAlgorithm();
}
DigestOutputStream digestOutputStream = createMessageDigestOutputStream(digestAlgo);
InputStream inputStream = resourceResolver.getInputStreamFromExternalReference();
SignaturePartDef signaturePartDef = new SignaturePartDef();
signaturePartDef.setSecurePart(securePart);
signaturePartDef.setSigRefId(externalReference);
signaturePartDef.setExternalResource(true);
signaturePartDef.setTransforms(securePart.getTransforms());
signaturePartDef.setDigestAlgo(digestAlgo);
try {
if (securePart.getTransforms() != null) {
signaturePartDef.setExcludeVisibleC14Nprefixes(true);
Transformer transformer = buildTransformerChain(digestOutputStream, signaturePartDef, null);
transformer.transform(inputStream);
transformer.doFinal();
} else {
XMLSecurityUtils.copy(inputStream, digestOutputStream);
}
digestOutputStream.close();
} catch (IOException e) {
throw new XMLSecurityException(e);
}
String calculatedDigest =
XMLUtils.encodeToString(digestOutputStream.getDigestValue());
LOG.debug("Calculated Digest: {}", calculatedDigest);
signaturePartDef.setDigestValue(calculatedDigest);
getSignaturePartDefList().add(signaturePartDef);
}
protected void verifySignatureParts(OutputProcessorChain outputProcessorChain) throws XMLSecurityException {
List<SignaturePartDef> signaturePartDefs = getSignaturePartDefList();
Map<Object, SecurePart> dynamicSecureParts = outputProcessorChain.getSecurityContext().getAsMap(XMLSecurityConstants.SIGNATURE_PARTS);
if (dynamicSecureParts != null) {
Iterator<Map.Entry<Object, SecurePart>> securePartsMapIterator = dynamicSecureParts.entrySet().iterator();
loop:
while (securePartsMapIterator.hasNext()) {
Map.Entry<Object, SecurePart> securePartEntry = securePartsMapIterator.next();
final SecurePart securePart = securePartEntry.getValue();
if (securePart.isRequired()) {
for (int i = 0; i < signaturePartDefs.size(); i++) {
SignaturePartDef signaturePartDef = signaturePartDefs.get(i);
if (signaturePartDef.getSecurePart() == securePart) {
continue loop;
}
}
throw new XMLSecurityException("stax.signature.securePartNotFound",
new Object[] {securePart.getName()});
}
}
}
}
protected InternalSignatureOutputProcessor getActiveInternalSignatureOutputProcessor() {
return activeInternalSignatureOutputProcessor;
}
protected void setActiveInternalSignatureOutputProcessor(
InternalSignatureOutputProcessor activeInternalSignatureOutputProcessor) {
this.activeInternalSignatureOutputProcessor = activeInternalSignatureOutputProcessor;
}
protected DigestOutputStream createMessageDigestOutputStream(String digestAlgorithm)
throws XMLSecurityException {
String jceName = JCEAlgorithmMapper.translateURItoJCEID(digestAlgorithm);
String jceProvider = JCEAlgorithmMapper.getJCEProviderFromURI(digestAlgorithm);
if (jceName == null) {
throw new XMLSecurityException("algorithms.NoSuchMap",
new Object[] {digestAlgorithm});
}
MessageDigest messageDigest;
try {
if (jceProvider != null) {
messageDigest = MessageDigest.getInstance(jceName, jceProvider);
} else {
messageDigest = MessageDigest.getInstance(jceName);
}
} catch (NoSuchAlgorithmException e) {
throw new XMLSecurityException(e);
} catch (NoSuchProviderException e) {
throw new XMLSecurityException(e);
}
return new DigestOutputStream(messageDigest);
}
protected Transformer buildTransformerChain(OutputStream outputStream,
SignaturePartDef signaturePartDef,
XMLSecStartElement xmlSecStartElement)
throws XMLSecurityException {
String[] transforms = signaturePartDef.getTransforms();
if (transforms == null || transforms.length == 0) {
Transformer transformer = new TransformIdentity();
transformer.setOutputStream(outputStream);
return transformer;
}
Transformer parentTransformer = null;
for (int i = transforms.length - 1; i >= 0; i--) {
String transform = transforms[i];
Map<String, Object> transformerProperties = null;
if (getSecurityProperties().isAddExcC14NInclusivePrefixes() &&
XMLSecurityConstants.NS_C14N_EXCL_OMIT_COMMENTS.equals(transform)) {
Set<String> prefixSet = XMLSecurityUtils.getExcC14NInclusiveNamespacePrefixes(
xmlSecStartElement, signaturePartDef.isExcludeVisibleC14Nprefixes()
);
StringBuilder prefixes = new StringBuilder();
for (Iterator<String> iterator = prefixSet.iterator(); iterator.hasNext(); ) {
String prefix = iterator.next();
if (prefixes.length() != 0) {
prefixes.append(" ");
}
prefixes.append(prefix);
}
signaturePartDef.setInclusiveNamespacesPrefixes(prefixes.toString());
List<String> inclusiveNamespacePrefixes = new ArrayList<>(prefixSet);
transformerProperties = new HashMap<>();
transformerProperties.put(
Canonicalizer20010315_Excl.INCLUSIVE_NAMESPACES_PREFIX_LIST, inclusiveNamespacePrefixes);
}
if (parentTransformer != null) {
parentTransformer = XMLSecurityUtils.getTransformer(
parentTransformer, null, transformerProperties, transform, XMLSecurityConstants.DIRECTION.OUT);
} else {
parentTransformer = XMLSecurityUtils.getTransformer(
null, outputStream, transformerProperties, transform, XMLSecurityConstants.DIRECTION.OUT);
}
}
return parentTransformer;
}
public class InternalSignatureOutputProcessor extends AbstractOutputProcessor {
private SignaturePartDef signaturePartDef;
private XMLSecStartElement xmlSecStartElement;
private int elementCounter;
private OutputStream bufferedDigestOutputStream;
private DigestOutputStream digestOutputStream;
private Transformer transformer;
public InternalSignatureOutputProcessor(SignaturePartDef signaturePartDef, XMLSecStartElement xmlSecStartElement)
throws XMLSecurityException {
super();
this.addBeforeProcessor(InternalSignatureOutputProcessor.class.getName());
this.signaturePartDef = signaturePartDef;
this.xmlSecStartElement = xmlSecStartElement;
}
@Override
public void init(OutputProcessorChain outputProcessorChain) throws XMLSecurityException {
this.digestOutputStream = createMessageDigestOutputStream(signaturePartDef.getDigestAlgo());
this.bufferedDigestOutputStream = new UnsyncBufferedOutputStream(digestOutputStream);
this.transformer = buildTransformerChain(this.bufferedDigestOutputStream, signaturePartDef, xmlSecStartElement);
super.init(outputProcessorChain);
}
@Override
public void processEvent(XMLSecEvent xmlSecEvent, OutputProcessorChain outputProcessorChain)
throws XMLStreamException, XMLSecurityException {
transformer.transform(xmlSecEvent);
switch (xmlSecEvent.getEventType()) {
case XMLStreamConstants.START_ELEMENT:
elementCounter++;
break;
case XMLStreamConstants.END_ELEMENT:
elementCounter--;
if (elementCounter == 0 &&
xmlSecEvent.asEndElement().getName().equals(this.xmlSecStartElement.getName())) {
transformer.doFinal();
try {
bufferedDigestOutputStream.close();
} catch (IOException e) {
throw new XMLSecurityException(e);
}
String calculatedDigest =
XMLUtils.encodeToString(this.digestOutputStream.getDigestValue());
LOG.debug("Calculated Digest: {}", calculatedDigest);
signaturePartDef.setDigestValue(calculatedDigest);
outputProcessorChain.removeProcessor(this);
//from now on signature is possible again
setActiveInternalSignatureOutputProcessor(null);
}
break;
}
outputProcessorChain.processEvent(xmlSecEvent);
}
}
}