【问题标题】:Add dss dictionary添加 dss 字典
【发布时间】:2015-08-17 12:46:10
【问题描述】:

我正在使用 pdfBox 来完成一个包含撤销信息的 pades 文档。目标是文档启用 LTV。

我正在使用的文档有 1 个签名和一个时间戳。我有必要的 crl/ocsp 令牌。

我的问题是如何构建一个 COSDictionary 来获得 CRL 和 OCSP 的响应?

InMemoryDocument doc = new InMemoryDocument(getFile());
PDDocument document = PDDocument.loadNonSeq(doc.openStream(), null);

COSDictionary cosDictionary = document.getDocumentCatalog().getCOSDictionary();
...

cosDictionary.setNeedToBeUpdate(true);

谢谢

【问题讨论】:

  • 您使用InMemoryDocument 似乎表明您使用欧盟加入sd-dss。该项目的当前 4.5-SNAPSHOT 开发版本使用 PDFBox 1.8.8。除了添加签名之外,我还没有看到一种使用 PDFBox 1.8.x 增量更新机制的干净方法。但是,如果我理解正确,您希望在 DSS 字典中添加验证相关信息而不添加文档时间戳。因此,您需要增量更新。恐怕没有简单的方法可以做到这一点,您可能需要为此改进一下 PDFBox。
  • 是的,我正在使用 sd-dss 将文档扩展到 LTA 级别。例如,我有一份来自 TSA 的具有 1 个签名和 1 个时间戳的文档。在这种情况下,做签名的人没有包括撤销信息。所以 adobe 会说签名没有启用 LTV,但时间戳是。我的问题是如何将有关签名的撤销信息添加到 DSS 字典中。
  • 我会在长周末尝试调查这个问题,现在就开始吧。如上所述,我还没有看到一种简单的方法来实现所需要的,即使用 PDFBox 增量更新签名以外的其他内容。
  • 谢谢。我已经向 DSS 添加了必要的信息,但这不是一个优雅的解决方案。我不得不稍微更改 dss-documenbt 库。
  • 我已经向 DSS 添加了必要的信息,但这不是一个优雅的解决方案。 - 如果您添加了代码,则更容易判断您的解决方案是否(尽管可能不优雅)是使用 PDFBox 或不使用的最佳解决方案。

标签: java pdf pdf-generation pdfbox


【解决方案1】:

假设您有必要的证书、CRL 和 OCSP 响应以字节数组的形式提供,您可以使用 PDFBox 将它们添加到像这样的文档安全存储:

COSDictionary dss = createDssDictionary(certificates, crls, ocspResponses);

利用这些辅助方法:

COSDictionary createDssDictionary(Iterable<byte[]> certificates, Iterable<byte[]> crls, Iterable<byte[]> ocspResponses) throws IOException
{
    final COSDictionary dssDictionary = new COSDictionary();
    dssDictionary.setNeedToBeUpdate(true);
    dssDictionary.setName(COSName.TYPE, "DSS");

    if (certificates != null)
        dssDictionary.setItem(COSName.getPDFName("Certs"), createArray(certificates));
    if (crls != null)
        dssDictionary.setItem(COSName.getPDFName("CRLs"), createArray(crls));
    if (ocspResponses != null)
        dssDictionary.setItem(COSName.getPDFName("OCSPs"), createArray(ocspResponses));

    return dssDictionary;
}

COSArray createArray(Iterable<byte[]> datas) throws IOException
{
    COSArray array = new COSArray();
    array.setNeedToBeUpdate(true);

    if (datas != null)
    {
        for (byte[] data: datas)
            array.add(createStream(data));
    }

    return array;
}

COSStream createStream(byte[] data) throws IOException
{
    RandomAccessBuffer storage = new RandomAccessBuffer();
    COSStream stream = new COSStream(storage);
    stream.setNeedToBeUpdate(true);
    final OutputStream unfilteredStream = stream.createUnfilteredStream();
    unfilteredStream.write(data);
    unfilteredStream.flush();
    return stream;
}

您可以将这样的 DSS 字典作为增量更新添加到源文档中,如下所示:

try (   InputStream source = ...;
        FileOutputStream fos = new FileOutputStream(resultFile);
        FileInputStream fis = new FileInputStream(resultFile);
        )
{
    byte[] inputBytes = IOUtils.toByteArray(source);

    PDDocument pdDocument = PDDocument.load(new ByteArrayInputStream(inputBytes));
    PDDocumentCatalog catalog = pdDocument.getDocumentCatalog();
    catalog.getCOSObject().setNeedToBeUpdate(true);
    catalog.getCOSDictionary().setItem(COSName.getPDFName("DSS"), dss);

    fos.write(inputBytes);
    pdDocument.saveIncremental(fis, fos);
    pdDocument.close();
}

【讨论】:

  • 谢谢,我试过这种方法,但最后签名被破坏了。 Adobe 说“签名有问题”
  • 嗯,这令人惊讶。你能分享一个失败的输出示例吗?
  • 很抱歉回复晚了,今天我不在办公室,但明天我会发布一个样本。谢谢。
  • @Hugo 你有时间找样品吗?
  • 嗨。抱歉耽搁了。与此同时,我开始在另一个项目中工作,这个主题变得不那么重要了。谢谢你的帮助。通过您的代码和一些小的更改,我已将必要的信息添加到 DSS 字典中。现在 Adob​​e 阅读器说签名支持 LTV。我已经添加了解决方案的答案。
【解决方案2】:

再次分析代码后,我发现证书编码中存在一些错误。我认为这是文件损坏的原因。 下面是我用来将链证书添加到 DSS 字典的代码。我重用了你的代码。 再次感谢。

private byte[] addCertificates(byte[] pdfFile) throws IOException, PDFLTVEnablerException {

ByteArrayInputStream stream = new ByteArrayInputStream(pdfFile);
PDDocument document = PDDocument.load(stream);

PDDocumentCatalog catalog = document.getDocumentCatalog();
COSDictionary catalogDictionary = catalog.getCOSDictionary();

COSDictionary dssDictionary = (COSDictionary) catalogDictionary.getDictionaryObject("DSS");
dssDictionary.setNeedToBeUpdate(true);

COSArray certDSS = getTrustedCertificatesChain();

dssDictionary.setItem(COSName.getPDFName("Certs"), certDSS);

catalog.getCOSObject().setNeedToBeUpdate(true);
catalogDictionary.setItem(COSName.getPDFName("DSS"), dssDictionary);

File resultFile = File.createTempFile("dss", null);
FileOutputStream fos = new FileOutputStream(resultFile);
FileInputStream fis = new FileInputStream(resultFile);
fos.write(pdfFile);
try {
    document.saveIncremental(fis, fos);
} catch (COSVisitorException e) {
    logger.debug(e.getMessage());
    throw new PDFLTVEnablerException("Couldn't access PDF DSS dictionary");
}

FileInputStream out = new FileInputStream(resultFile);
byte[] byteArray = IOUtils.toByteArray(out);
resultFile.delete();
document.close();
return byteArray;
}

private byte[] addRevocationInfo(byte[] pdfFile) throws IOException, PDFLTVEnablerException {

ByteArrayInputStream stream = new ByteArrayInputStream(pdfFile);
PDDocument document = PDDocument.load(stream);

PDDocumentCatalog catalog = document.getDocumentCatalog();
COSDictionary catalogDictionary = catalog.getCOSDictionary();

COSDictionary dssDictionary = (COSDictionary) catalogDictionary.getDictionaryObject("DSS");
dssDictionary.setNeedToBeUpdate(true);

COSArray crlsDss = loadCrlsForCerts(pdfFile);

dssDictionary.setItem(COSName.getPDFName("CRLs"), crlsDss);
dssDictionary.setItem(COSName.getPDFName("OCSPs"), new COSArray());

catalog.getCOSObject().setNeedToBeUpdate(true);
catalogDictionary.setItem(COSName.getPDFName("DSS"), dssDictionary);

File resultFile = File.createTempFile("dss", null);
FileOutputStream fos = new FileOutputStream(resultFile);
FileInputStream fis = new FileInputStream(resultFile);
fos.write(pdfFile);
try {
    document.saveIncremental(fis, fos);
} catch (COSVisitorException e) {
    logger.debug(e.getMessage());
    throw new PDFLTVEnablerException("Couldn't access PDF DSS dictionary");

}

FileInputStream out = new FileInputStream(resultFile);
byte[] byteArray = IOUtils.toByteArray(out);
resultFile.delete();
document.close();
return byteArray;
}


private COSArray getTrustedCertificatesChain() throws IOException {

COSArray certs = new COSArray();
certs.setNeedToBeUpdate(true);

List < X509Certificate > trustedCerts = trustedCertsStore.getTrustedCerts();

for (X509Certificate cert: trustedCerts) {
    try {
        COSStream certStream = createStream(cert.getEncoded());
        certs.add(certStream);
    } catch (CertificateException e) {
        logger.error("Could not import trusted certificate: " + e.getMessage());
        throw new IOException("The certificate in the truststore is invalid");
    }
}

return certs;
}

private COSArray loadCrlsForCerts(byte[] pdfFile) throws IOException, PDFLTVEnablerException {
COSArray crls = new COSArray();
crls.setNeedToBeUpdate(true);

List < X509Certificate > trustedCerts = trustedCertsStore.getTrustedCerts();

for (X509Certificate cert: trustedCerts) {
    try {
        X509CRL crl = crlDownloader.verifyCertificateCRLs(cert);
        if (crl != null) {
            COSStream crlStream = createStream(crl.getEncoded());
            crls.add(crlStream);
        }
    } catch (CertificateException e) {
        logger.error("Could not import trusted certificate: " + e.getMessage());
        throw new IOException("The certificate in the truststore is invalid");
    } catch (CertificateVerificationException e) {
        logger.error("Could not import trusted certificate: " + e.getMessage());
        throw new IOException("The certificate in the truststore is invalid");
    } catch (CRLException e) {
        logger.debug("Error downloading crl " + e.getMessage());
        throw new Exception("Couldn't retrive CRL");
    } catch (NamingException e) {
        logger.debug(e.getMessage());
        throw new Exception("Couldn't retrive CRL");
    }
}
return crls;
}

public COSStream createStream(byte[] data) throws IOException {
RandomAccessBuffer storage = new RandomAccessBuffer();
COSStream stream = new COSStream(storage);
stream.setNeedToBeUpdate(true);
final OutputStream unfilteredStream = stream.createUnfilteredStream();
unfilteredStream.write(data);
unfilteredStream.flush();
return stream;
}

【讨论】:

  • 如果我正确理解您的代码,您将获得有效的 LTV 签名,而无需将 OCSP 信息添加到 DSS?
  • 是的,但是我将 CRL 添加到 DSS 字典中。
【解决方案3】:

其实我没有直接用过PDFbox。

我从 PAdESService 类型的对象开始,我的目标是为 PDF 添加时间戳。

我已经这样构建了 DSS 字典:

private PdfDict createDSSDictionary() throws IOException, CertificateEncodingException {

final PdfDict dssDictionary = factory.newDict("DSS");

for (X509Certificate x509Certificate : certs) {
    PdfStream stream = factory.newStream(x509Certificate.getEncoded());
    certArray.add(stream);
}

if (certArray.size() > 0) {
    dssDictionary.add("Certs", certArray);
}

if (crlArray.size() > 0) {
    dssDictionary.add("CRLs", crlArray);
}

if (ocspArray.size() > 0) {
    dssDictionary.add("OCSPs", ocspArray);
}
return dssDictionary;
}

此方法仅展示 Certs 流的构建,OCSP 和 CRL 的过程类似。

然后是丑陋的部分。

我已经更改了 eu.europa.ec.markt.dss.parameter.SignatureParameters 类。 我添加了参数 PdfDict,其中包含我要在 PDF 中插入的 DSS 字典(DSS 是从上一个函数返回的)。

然后在类 eu.europa.ec.markt.dss.signature.pades.PAdESLevelBaselineLT 我从 signatureParameters 获取 DSS 字典并将其用作参数来扩展 eu.europa.ec.markt.dss.signature.pdf.PDFTimestampService 类的 timsetamp(..) 函数 .

【讨论】:

  • 我看到您在那些用于 PDF 字典和数组的 sd-dss 包装类中拥有证书、CRL 和 OCSP 响应。正如您自己所说,您没有直接使用 PDFbox。 另一方面,您的问题听起来像是您正在寻找直接使用 PDFBox 的解决方案(或者您至少使用这样的解决方案。因此,我假设您也以未包装的形式拥有这些证书、CRL 和 OCSP 响应。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-17
  • 1970-01-01
相关资源
最近更新 更多