Skip to content

Instantly share code, notes, and snippets.

@markscottwright
Created December 12, 2018 14:48
Show Gist options
  • Save markscottwright/8af03ee2951da6dd138a3a3db222efe1 to your computer and use it in GitHub Desktop.
Save markscottwright/8af03ee2951da6dd138a3a3db222efe1 to your computer and use it in GitHub Desktop.
How to retrieve the signing certs and any attached chain of trust from a signed Office document.
package scratch;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.bouncycastle.util.encoders.Base64;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
/**
* Parse the signature in a signed office document
*
* @author mwright
*
*/
public class SignedOfficeDocumentCerts {
private final File signedOfficeFile;
/**
* SAX document parser that parses and retains X509Certificate and
* EncapsulatedX509Certificate entries.
*
* @author mwright
*
*/
public static class CertificateFinder extends DefaultHandler {
boolean inCert = false;
ArrayList<X509Certificate> certs = new ArrayList<>();
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
if (isCertificateElement(uri, localName)) {
inCert = true;
}
}
private boolean isCertificateElement(String uri, String localName) {
return (uri.equals("http://www.w3.org/2000/09/xmldsig#")
&& localName.equals("X509Certificate"))
|| (uri.equals("http://uri.etsi.org/01903/v1.3.2#")
&& localName.equals("EncapsulatedX509Certificate"));
}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
inCert = false;
}
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
if (inCert) {
byte[] certData = Base64
.decode(String.copyValueOf(ch, start, length));
try {
X509Certificate cert = (X509Certificate) CertificateFactory
.getInstance("X509").generateCertificate(
new ByteArrayInputStream(certData));
certs.add(cert);
} catch (CertificateException e) {
throw new SAXException(e);
}
}
}
public ArrayList<X509Certificate> getCerts() {
return certs;
}
}
public SignedOfficeDocumentCerts(File signedOfficeFile) throws ZipException,
IOException,
ParserConfigurationException,
SAXException {
this.signedOfficeFile = signedOfficeFile;
}
public ArrayList<X509Certificate> findCerts() throws ZipException,
IOException,
ParserConfigurationException,
SAXException {
try (ZipFile zipFile = new ZipFile(signedOfficeFile)) {
ZipEntry signatureEntry = zipFile
.getEntry("_xmlsignatures/sig1.xml");
// document isn't signed
if (signatureEntry == null)
return new ArrayList<>();
try (InputStream signatureContents = zipFile
.getInputStream(signatureEntry)) {
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setNamespaceAware(true);
SAXParser saxParser = spf.newSAXParser();
XMLReader xmlReader = saxParser.getXMLReader();
CertificateFinder certFinder = new CertificateFinder();
xmlReader.setContentHandler(certFinder);
xmlReader.parse(new InputSource(signatureContents));
return certFinder.getCerts();
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment