Created
December 12, 2018 14:48
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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