问题
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