【问题标题】:How to add time stamp with pdfbox and TimeStampToken如何使用 pdfbox 和 TimeStampToken 添加时间戳
【发布时间】:2017-01-19 06:49:45
【问题描述】:

我想从第三方时间服务器向文件添加时间戳。 当 pdf 在 acrobat 或其他 pdf 查看器中打开时,我想查看有关签名卡或任何其他中时间戳的信息。此外,我想以图形方式将时间戳可视化为带有时间戳的 pdf 中的图像或文本。

我从时间服务器获取令牌:

import org.bouncycastle.tsp.TimeStampResponse;
import org.bouncycastle.tsp.TimeStampToken;

...

public TimeStampToken getTimeStampToken(){

  ...

  return response.getTimeStampToken();
}

现在如何使用 pdf 框为 pdf 添加时间戳?

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;

...

public static void addTimeStamp(final File pdfFile, final File signedPdfFile, TimeStampToken token) {

    try (
        FileInputStream fis1 = new FileInputStream(pdfFile);
        FileOutputStream fos = new FileOutputStream(signedPdfFile);
        FileInputStream fis = new FileInputStream(signedPdfFile);
        PDDocument doc = PDDocument.load(pdfFile)) {
        int readCount;
        final byte[] buffer = new byte[8 * 1024];
        while ((readCount = fis1.read(buffer)) != -1) {
            fos.write(buffer, 0, readCount);
        }

        final PDSignature signature = new PDSignature();
        signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
        signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
        signature.setName("NAME");
        signature.setLocation("LOCATION");
        signature.setReason("REASON");
        signature.setSignDate(Calendar.getInstance());

        doc.addSignature(signature);
        doc.saveIncremental(fos);
    } catch (final Exception e) {
        e.printStackTrace();
    }
}

【问题讨论】:

  • 签名示例代码确实已经有了这个,除了“以图形方式作为 pdf 中的图像或文本”。你看到了吗?

标签: timestamp pdf-generation pdfbox trusted-timestamp


【解决方案1】:

由于您只提到时间戳,而不是带有签名时间戳的签名,我假设您的意思是根据 PAdES LTV 或 PDF-2 的文档时间戳。

PDFBox 签名示例在实现时考虑了常规数字签名,而不是裸数字时间戳。但是你可以很容易地适应它们。 CreateVisibleSignature 和其他示例创建签名字节以嵌入在父类 CreateSignatureBase 中实现的 SignatureInterface 方法 sign 中:

/**
 * SignatureInterface implementation.
 *
 * This method will be called from inside of the pdfbox and create the PKCS #7 signature.
 * The given InputStream contains the bytes that are given by the byte range.
 *
 * This method is for internal use only.
 *
 * Use your favorite cryptographic library to implement PKCS #7 signature creation.
 */
@Override
public byte[] sign(InputStream content) throws IOException
{
    //TODO this method should be private
    try
    {
        List<Certificate> certList = new ArrayList<Certificate>();
        certList.add(certificate);
        Store certs = new JcaCertStore(certList);
        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
        org.bouncycastle.asn1.x509.Certificate cert = org.bouncycastle.asn1.x509.Certificate.getInstance(ASN1Primitive.fromByteArray(certificate.getEncoded()));
        ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA256WithRSA").build(privateKey);
        gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()).build(sha1Signer, new X509CertificateHolder(cert)));
        gen.addCertificates(certs);
        CMSProcessableInputStream msg = new CMSProcessableInputStream(content);
        CMSSignedData signedData = gen.generate(msg, false);
        if (tsaClient != null)
        {
            signedData = signTimeStamps(signedData);
        }
        return signedData.getEncoded();
    }
    catch (GeneralSecurityException e)
    {
        throw new IOException(e);
    }
    catch (CMSException e)
    {
        throw new IOException(e);
    }
    catch (TSPException e)
    {
        throw new IOException(e);
    }
    catch (OperatorCreationException e)
    {
        throw new IOException(e);
    }
}

(CreateSignatureBase)

(顺便说一句,我非常不同意 TODO 的评论。)

另外,CreateVisibleSignature 设置signPDF 中签名的子过滤器:

/**
 * Sign pdf file and create new file that ends with "_signed.pdf".
 *
 * @param inputFile The source pdf document file.
 * @param signedFile The file to be signed.
 * @param tsaClient optional TSA client
 * @param signatureFieldName optional name of an existing (unsigned) signature field
 * @throws IOException
 */
public void signPDF(File inputFile, File signedFile, TSAClient tsaClient, String signatureFieldName) throws IOException
{
    [...]

    PDSignature signature;

    // sign a PDF with an existing empty signature, as created by the CreateEmptySignatureForm example. 
    signature = findExistingSignature(doc, signatureFieldName);

    if (signature == null)
    {
        // create signature dictionary
        signature = new PDSignature();
    }

    // default filter
    signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);

    // subfilter for basic and PAdES Part 2 signatures
    signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);

    [...]
}

要创建 PAdES 文档时间戳,您可以简单地创建 CreateVisibleSignature 示例的副本,覆盖 SignatureInterface 方法 sign 使用返回给定 @987654333 中数据的时间戳令牌的实现@,换行

    signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);

signPDF

    signature.setSubFilter(new COSName("ETSI.RFC3161"));

并添加一行

    signature.setType(new COSName("DocTimeStamp"));

将类型设置为时间戳。

【讨论】:

    【解决方案2】:

    我想在没有密钥库证书和密钥的情况下为 pdf 添加时间戳,它看起来像这样:

    public static void signWithTimeStampToken() throws InvalidPasswordException, NoSuchAlgorithmException, IOException, TSPException {
        final File inFile = new File("test.pdf");
        final File outFile = new File("test_signed.pdf");
        final MessageDigest digest = MessageDigest.getInstance("SHA-1");
        final TSAClient tsaClient = new TSAClient(new URL("your service"), null, null, digest);
        PdfTimeStampUtils.signPdf(inFile, outFile, tsaClient);
    }
    
    private static void signPdf(final File inFile, final File outFile, final TSAClient tsaClient) throws InvalidPasswordException, IOException, NoSuchAlgorithmException,
            TSPException {
        final PDSignature signature = new PDSignature();
        signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
        signature.setSubFilter(COSName.getPDFName("ETSI.RFC3161"));
        signature.setSignDate(Calendar.getInstance());
        final PDDocument pdf = PDDocument.load(inFile);
    
        final TimestampSignatureImpl sig = new TimestampSignatureImpl(tsaClient);
        pdf.addSignature(signature, sig);
        pdf.saveIncremental(new FileOutputStream(outFile));
        pdf.close();
    }
    

    我正在使用 pdfbox 示例中的 tsaclient TSAClient

    当我在 acrobat 中打开签名的 pdf 时,我可以在签名中看到:

    【讨论】:

    • 在你的问题中你说你想要以图形方式可视化时间戳;通常这意味着内容内的可视化,而不是签名面板上的条目。是我误解了你,还是你干脆放弃了这个要求?
    • 我的意思是能够添加一些东西(自定义图像或文本)来区分签名和未签名,而无需查看附件中的签名。但这只是额外的(我很好奇),我的回答根本不包含它。我放置它是因为我知道如何添加时间戳。
    猜你喜欢
    • 2019-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    • 2018-07-06
    • 2014-06-11
    相关资源
    最近更新 更多