How to programmatically sign a binary MS office document with Java?

我与影子孤独终老i 提交于 2020-12-27 07:16:24

问题


How can we digitally sign a legacy binary MS-Office document (doc, xls, ppt) in Apache POI, or any other open source library?

The Open XML formats are covered at How to programatically sign an MS office XML document with Java?


回答1:


I was able to sign .doc file by creating detached xml signature, then adding it under root directory using POIFSFileSystem, example is below :

public class OfficeDocumentSigner2 {


    public static void main(String[] args) {
        signClassicOfficeDocuments();
    }

    private static String sign() {

        // First, create a DOM XMLSignatureFactory that will be used to
        // generate the XMLSignature and marshal it to DOM.
        XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
        OutputStream os = null;
        String signedDoc = "C:\\Users\\Desktop\\digitalSign\\signdoc_signed.xml";
        try {
            // Create a Reference to an external URI that will be digested
            // using the SHA1 digest algorithm

            Reference ref = fac.newReference(officeFilePath, fac.newDigestMethod(DigestMethod.SHA1, null));
            // Create the SignedInfo
            SignedInfo si = fac.newSignedInfo(fac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS, (C14NMethodParameterSpec) null), fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null),
                    Collections.singletonList(ref));

            // Create the Document that will hold the resulting XMLSignature --> detached
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true); // must be set
            Document doc = dbf.newDocumentBuilder().newDocument();

            // certificate info
            File file = new File("C:\\Users\\Desktop\\digitalSign\\test101\\KeyStore.jks");
            // extracting private key and certificate
            String alias = "certAlias";
            X509Certificate x509 = null;
            // loading the keystore
            KeyStore keystore = KeyStore.getInstance("JKS");
            FileInputStream fis = new FileInputStream(file);
            keystore.load(fis, password);
            fis.close();
            x509 = (X509Certificate) keystore.getCertificate(alias);
            KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry) keystore.getEntry(alias, new KeyStore.PasswordProtection(password));

            // Create the KeyInfo containing the X509Data.
            // ref : http://www.oracle.com/technetwork/articles/javase/dig-signature-api-140772.html
            KeyInfoFactory kif = fac.getKeyInfoFactory();
            List x509Content = new ArrayList();
            x509Content.add(x509.getSubjectX500Principal().getName());
            x509Content.add(x509);
            X509Data xd = kif.newX509Data(x509Content);
            KeyInfo ki = kif.newKeyInfo(Collections.singletonList(xd));

            // Create a DOMSignContext and specify the DSA PrivateKey and
            // location of the resulting XMLSignature's parent element
            DOMSignContext dsc = new DOMSignContext(keyEntry.getPrivateKey(), doc);
            dsc.setBaseURI(baseURI);
            // Create the XMLSignature (but don't sign it yet)
            XMLSignature signature = fac.newXMLSignature(si, ki);

            // Marshal, generate (and sign) the enveloped signature
            signature.sign(dsc);

            // Output the resulting document.

            os = new FileOutputStream(signedDoc);
            TransformerFactory tf = TransformerFactory.newInstance();
            Transformer trans = tf.newTransformer();
            trans.transform(new DOMSource(doc), new StreamResult(os));


        } catch (Exception e) {
            e.printStackTrace();
        }
        return signedDoc;
    }

    public static void signClassicOfficeDocuments() {

        InputStream is;
        try {
            //sign document
            String signaturePath = sign();
            InputStream signatureAsIS = new FileInputStream(signaturePath);

            is = new FileInputStream(fileName);
            POIFSFileSystem poifs = new POIFSFileSystem(is);
            DirectoryEntry dirEntry =  poifs.createDirectory("_xmlsignatures"); // create a new DirectoryEntry in the root directory
            dirEntry.createDocument("9149", signatureAsIS);

            String destPath = "C://Users//Desktop//digitalSign//test_11_24_signedByMe.doc";
            OutputStream os = new FileOutputStream(destPath);
            poifs.writeFilesystem(os);

        } catch (Exception e) {
            e.printStackTrace();
        }

    }


}


来源:https://stackoverflow.com/questions/33891618/how-to-programmatically-sign-a-binary-ms-office-document-with-java

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!