package org.apache.poi.poifs.crypt.dsig;
import static org.apache.poi.ooxml.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
import static org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet.MS_DIGSIG_NS;
import static org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet.XML_DIGSIG_NS;
import java.io.IOException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import javax.xml.crypto.MarshalException;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureException;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.namespace.NamespaceContext;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.ooxml.util.DocumentHelper;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
import org.apache.xmlbeans.XmlException;
import org.w3.x2000.x09.xmldsig.SignatureDocument;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class SignaturePart {
private static final POILogger LOG = POILogFactory.getLogger(SignaturePart.class);
private static final String XMLSEC_VALIDATE_MANIFEST = "org.jcp.xml.dsig.validateManifests";
private final PackagePart signaturePart;
private final SignatureConfig signatureConfig;
private X509Certificate signer;
private List<X509Certificate> certChain;
SignaturePart(final PackagePart signaturePart, final SignatureConfig signatureConfig) {
this.signaturePart = signaturePart;
this.signatureConfig = signatureConfig;
}
public PackagePart getPackagePart() {
return signaturePart;
}
public X509Certificate getSigner() {
return signer;
}
public List<X509Certificate> getCertChain() {
return certChain;
}
public SignatureDocument getSignatureDocument() throws IOException, XmlException {
return SignatureDocument.Factory.parse(signaturePart.getInputStream(), DEFAULT_XML_OPTIONS);
}
public boolean validate() {
KeyInfoKeySelector keySelector = new KeyInfoKeySelector();
XPath xpath = XPathFactory.newInstance().newXPath();
xpath.setNamespaceContext(new XPathNSContext());
try {
Document doc = DocumentHelper.readDocument(signaturePart.getInputStream());
NodeList nl = (NodeList)xpath.compile("//*[@Id]").evaluate(doc, XPathConstants.NODESET);
final int length = nl.getLength();
for (int i=0; i<length; i++) {
((Element)nl.item(i)).setIdAttribute("Id", true);
}
DOMValidateContext domValidateContext = new DOMValidateContext(keySelector, doc);
domValidateContext.setProperty(XMLSEC_VALIDATE_MANIFEST, Boolean.TRUE);
domValidateContext.setURIDereferencer(signatureConfig.getUriDereferencer());
XMLSignatureFactory xmlSignatureFactory = signatureConfig.getSignatureFactory();
XMLSignature xmlSignature = xmlSignatureFactory.unmarshalXMLSignature(domValidateContext);
boolean valid = xmlSignature.validate(domValidateContext);
if (valid) {
signer = keySelector.getSigner();
certChain = keySelector.getCertChain();
extractConfig(doc, xmlSignature);
}
return valid;
} catch (IOException e) {
String s = "error in reading document";
LOG.log(POILogger.ERROR, s, e);
throw new EncryptedDocumentException(s, e);
} catch (SAXException e) {
String s = "error in parsing document";
LOG.log(POILogger.ERROR, s, e);
throw new EncryptedDocumentException(s, e);
} catch (XPathExpressionException e) {
String s = "error in searching document with xpath expression";
LOG.log(POILogger.ERROR, s, e);
throw new EncryptedDocumentException(s, e);
} catch (MarshalException e) {
String s = "error in unmarshalling the signature";
LOG.log(POILogger.ERROR, s, e);
throw new EncryptedDocumentException(s, e);
} catch (XMLSignatureException e) {
String s = "error in validating the signature";
LOG.log(POILogger.ERROR, s, e);
throw new EncryptedDocumentException(s, e);
}
}
private void (final Document doc, final XMLSignature xmlSignature) throws XPathExpressionException {
if (!signatureConfig.isUpdateConfigOnValidate()) {
return;
}
signatureConfig.setSigningCertificateChain(certChain);
signatureConfig.setSignatureMethodFromUri(xmlSignature.getSignedInfo().getSignatureMethod().getAlgorithm());
final XPath xpath = XPathFactory.newInstance().newXPath();
xpath.setNamespaceContext(new XPathNSContext());
final Map<String,Consumer<String>> m = new HashMap();
m.put("//mdssi:SignatureTime/mdssi:Value", signatureConfig::setExecutionTime);
m.put("//xd:ClaimedRole", signatureConfig::setXadesRole);
m.put("//dsss:SignatureComments", signatureConfig::setSignatureDescription);
m.put("//xd:QualifyingProperties//xd:SignedSignatureProperties//ds:DigestMethod/@Algorithm", signatureConfig::setXadesDigestAlgo);
m.put("//ds:CanonicalizationMethod", signatureConfig::setCanonicalizationMethod);
for (Map.Entry<String,Consumer<String>> me : m.entrySet()) {
String val = (String)xpath.compile(me.getKey()).evaluate(doc, XPathConstants.STRING);
me.getValue().accept(val);
}
}
private class XPathNSContext implements NamespaceContext {
final Map<String,String> nsMap = new HashMap<>();
{
signatureConfig.getNamespacePrefixes().forEach((k,v) -> nsMap.put(v,k));
nsMap.put("dsss", MS_DIGSIG_NS);
nsMap.put("ds", XML_DIGSIG_NS);
}
public String getNamespaceURI(String prefix) {
return nsMap.get(prefix);
}
public Iterator getPrefixes(String val) {
return null;
}
public String getPrefix(String uri) {
return null;
}
}
}