java - 使用 Java 的 XLSX 和 XAdES-T 数字签名
问题描述
我正在尝试在 Office 2016 中签署 XLSX 文件并添加时间戳服务器,我使用了 apache poi 的代码,但它似乎不起作用,每个签名结果都返回 XAdES-EPES。我期望的结果是 XAdES-T。这是带有时间戳服务器签名的 apache poi 的源代码:
void testSignEnvelopingDocument() throws Exception {
String testFile = "hello-world-unsigned.xlsx";
File sigCopy = testdata.getFile(testFile);
UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream(50000);
final String execTimestr;
try (OPCPackage pkg = OPCPackage.open(copy(sigCopy), PackageAccess.READ_WRITE)) {
initKeyPair();
final X509CRL crl = generateCrl(x509, keyPair.getPrivate());
// setup
SignatureConfig signatureConfig = new SignatureConfig();
signatureConfig.setKey(keyPair.getPrivate());
/*
* We need at least 2 certificates for the XAdES-C complete certificate
* refs construction.
*/
List<X509Certificate> certificateChain = new ArrayList<>();
certificateChain.add(x509);
certificateChain.add(x509);
signatureConfig.setSigningCertificateChain(certificateChain);
signatureConfig.addSignatureFacet(new OOXMLSignatureFacet());
signatureConfig.addSignatureFacet(new EnvelopedSignatureFacet());
signatureConfig.addSignatureFacet(new KeyInfoSignatureFacet());
signatureConfig.addSignatureFacet(new XAdESSignatureFacet());
signatureConfig.addSignatureFacet(new XAdESXLSignatureFacet());
// check for internet, no error means it works
boolean mockTsp = (getAccessError("http://timestamp.comodoca.com/rfc3161", true, 10000) != null);
// http://timestamping.edelweb.fr/service/tsp
// http://tsa.belgium.be/connect
// http://timestamp.comodoca.com/authenticode
// http://timestamp.comodoca.com/rfc3161
// http://services.globaltrustfinder.com/adss/tsa
signatureConfig.setTspUrl("http://timestamp.comodoca.com/rfc3161");
signatureConfig.setTspRequestPolicy(null); // comodoca request fails, if default policy is set ...
signatureConfig.setTspOldProtocol(false);
signatureConfig.setXadesDigestAlgo(HashAlgorithm.sha512);
signatureConfig.setXadesRole("Xades Reviewer");
signatureConfig.setSignatureDescription("test xades signature");
execTimestr = signatureConfig.formatExecutionTime();
//set proxy info if any
String proxy = System.getProperty("http_proxy");
if (proxy != null && proxy.trim().length() > 0) {
signatureConfig.setProxyUrl(proxy);
}
if (mockTsp) {
TimeStampService tspService = (signatureInfo, data, revocationData) -> {
revocationData.addCRL(crl);
return "time-stamp-token".getBytes(LocaleUtil.CHARSET_1252);
};
signatureConfig.setTspService(tspService);
} else {
TimeStampServiceValidator tspValidator = (validateChain, revocationData) -> {
for (X509Certificate certificate : validateChain) {
LOG.atDebug().log("certificate: {}", certificate.getSubjectX500Principal());
LOG.atDebug().log("validity: {} - {}", certificate.getNotBefore(), certificate.getNotAfter());
}
};
signatureConfig.setTspValidator(tspValidator);
signatureConfig.setTspOldProtocol(signatureConfig.getTspUrl().contains("edelweb"));
}
final RevocationData revocationData = new RevocationData();
revocationData.addCRL(crl);
OCSPResp ocspResp = createOcspResp(x509, x509, x509, keyPair.getPrivate(), cal.getTimeInMillis());
revocationData.addOCSP(ocspResp.getEncoded());
RevocationDataService revocationDataService = revocationChain -> revocationData;
signatureConfig.setRevocationDataService(revocationDataService);
// operate
SignatureInfo si = new SignatureInfo();
si.setOpcPackage(pkg);
si.setSignatureConfig(signatureConfig);
try {
si.confirmSignature();
} catch (RuntimeException e) {
// only allow a ConnectException because of timeout, we see this in Jenkins from time to time...
if (e.getCause() == null) {
throw e;
}
if ((e.getCause() instanceof ConnectException) || (e.getCause() instanceof SocketTimeoutException)) {
assumeFalse(e.getCause().getMessage().contains("timed out"),
"Only allowing ConnectException with 'timed out' as message here, but had: " + e);
} else if (e.getCause() instanceof IOException) {
assumeFalse(e.getCause().getMessage().contains("Error contacting TSP server"),
"Only allowing IOException with 'Error contacting TSP server' as message here, but had: " + e);
} else if (e.getCause() instanceof RuntimeException) {
assumeFalse(e.getCause().getMessage().contains("This site is cur"),
"Only allowing RuntimeException with 'This site is cur' as message here, but had: " + e);
}
throw e;
}
}
解决方案
- 问题在猕猴桃热心专业的帮助下得到了解决
- 如果有人和我有同样的问题,你可以按照 kwiwing 在评论中的说明使用他描述的 2 个链接:
作为参考,我添加了很快将包含修复的bugzilla 条目。对于 POI 5.0.0 的用户,您可以复制 XAdESXLSignatureFacet 源并在您的用户包中提供它。对应的测试用例是TestSignatureInfo中的“createXAdES_T_65623 ”
推荐阅读
- ios - 故事板虚拟值
- c - 返回 char 字符串指针之间的差异时,强制转换和取消引用的顺序有多重要?
- java - 有没有一种简单的方法来比较二叉树的路径?
- mysql - 如何使用 ActiveRecord 忽略 MySQL 索引?
- sql - 如何在 SQL (Postgres) 中查找子字符串
- javascript - Leaflet.Draw 多边形的坐标
- firebase - 未经身份验证的最终用户正在调用 Firebase HTTPS 可调用函数
- active-directory - 我可以拥有 2 个具有相同域扩展名的独立 Active Directory 林吗?
- python - Anaconda 环境不会激活
- java - 如何检查用户是否在不和谐中扮演角色