当前位置: 首页 > 知识库问答 >
问题:

确保使用DSS(CMS容器)进行LTV验证

汪文光
2023-03-14

我正在开发一个解决方案,允许在远程服务器上使用p12证书进行签名。

首先,我有一个服务器上计算的文档摘要,然后我把它发送到另一个服务器上签名。

这里是PDF文件,你会发现两个PDF版本。“CURRENT_SIGNATURE.pdf”文件是我使用下面的代码得到的结果。“targetr_SIGNATUREPDF.pdf”是我想要的目标。正如您所看到的,目标文件显示“签名中嵌入的证书撤销列表”另一方面,当前文件表明“文件中包含的证书的撤销清单”此外,目标文件只有一个签名,没有添加修订:https://www.grosfichiers.com/i4fmqCz43is

结果vérification:

我现在的目标是添加LTV验证,因为我知道我正在使用:padescsSignedDataBuilder在服务器部件上签名

********************* 服务器A **********************

    public class ServerA {
    private static PAdESSignatureParameters signatureParameters;
    private static DSSDocument documentToSign;
    public static ExternalCMSPAdESService service;
    private static final String TSA_URL = "http://dss.nowina.lu/pki-factory/tsa/good-tsa";


    public static void main(String[] args) throws Exception {
        documentToSign = new FileDocument(new File("Doc 2.pdf"));

        signatureParameters = new PAdESSignatureParameters();
        signatureParameters.setSignatureLevel(SignatureLevel.PAdES_BASELINE_B);
        signatureParameters.setLocation("Luxembourg");
        signatureParameters.setReason("DSS testing");
        signatureParameters.setContactInfo("Jira");
        signatureParameters.setGenerateTBSWithoutCertificate(true);
        CommonCertificateVerifier commonCertificateVerifier = new CommonCertificateVerifier();

        commonCertificateVerifier.setCrlSource(new OnlineCRLSource());
        commonCertificateVerifier.setOcspSource(new OnlineOCSPSource());
        commonCertificateVerifier.setCheckRevocationForUntrustedChains(true);
        service = new ExternalCMSPAdESService(commonCertificateVerifier);
        byte[] documentDigest = computeDocumentDigest(documentToSign, signatureParameters);

        // Embedded CAdES is generated by a third party
        byte[] cmsSignedData = ServerB.getSignedCMSignedData(documentDigest);

        service.setCmsSignedData(cmsSignedData);
        DSSDocument finalDoc = service.signDocument(documentToSign, signatureParameters, null);


        PAdESService service = new PAdESService(commonCertificateVerifier);
        TimestampDataLoader timestampDataLoader = new TimestampDataLoader();// uses the specific content-type
        OnlineTSPSource tsa1 = new OnlineTSPSource("http://dss.nowina.lu/pki-factory/tsa/ee-good-tsa");
        tsa1.setDataLoader(timestampDataLoader);
        service.setTspSource(tsa1);
        PAdESSignatureParameters extensionParameters = new PAdESSignatureParameters();
        extensionParameters.setSignatureLevel(SignatureLevel.PAdES_BASELINE_LT);

        DSSDocument extendedDocument = service.extendDocument(finalDoc, extensionParameters);


        save(finalDoc);
        save2(extendedDocument);
    }

    private static void save(DSSDocument signedDocument) {
        try (FileOutputStream fos = new FileOutputStream("DSS.pdf")) {
            Utils.copy(signedDocument.openStream(), fos);
        } catch (Exception e) {
            Alert alert = new Alert(Alert.AlertType.ERROR, "Unable to save file : " + e.getMessage(), ButtonType.CLOSE);
            alert.showAndWait();
            return;
        }
    }
    private static void save2(DSSDocument signedDocument) {
        try (FileOutputStream fos = new FileOutputStream("DSS-2.pdf")) {
            Utils.copy(signedDocument.openStream(), fos);
        } catch (Exception e) {
            Alert alert = new Alert(Alert.AlertType.ERROR, "Unable to save file : " + e.getMessage(), ButtonType.CLOSE);
            alert.showAndWait();
            return;
        }
    }

    public static CertificateVerifier getOfflineCertificateVerifier() {
        CertificateVerifier cv = new CommonCertificateVerifier();
        cv.setDataLoader(new IgnoreDataLoader());
        return cv;
    }

    protected static byte[] computeDocumentDigest(final DSSDocument toSignDocument, final PAdESSignatureParameters parameters) {
        IPdfObjFactory pdfObjFactory = new ServiceLoaderPdfObjFactory();
        final PDFSignatureService pdfSignatureService = pdfObjFactory.newPAdESSignatureService();
        return pdfSignatureService.digest(toSignDocument, parameters);
    }

    private static class ExternalCMSPAdESService extends PAdESService {

        private static final long serialVersionUID = -2003453716888412577L;

        private byte[] cmsSignedData;

        public ExternalCMSPAdESService(CertificateVerifier certificateVerifier) {
            super(certificateVerifier);
        }

        @Override
        protected byte[] generateCMSSignedData(final DSSDocument toSignDocument, final PAdESSignatureParameters parameters,
                                               final SignatureValue signatureValue) {
            if (this.cmsSignedData == null) {
                throw new NullPointerException("A CMS signed data must be provided");
            }
            return this.cmsSignedData;
        }

        public void setCmsSignedData(final byte[] cmsSignedData) {
            this.cmsSignedData = cmsSignedData;
        }

    }
}

并且能够对计算出的哈希进行签名:

public class ServerB {

private static PAdESSignatureParameters signatureParameters;
private static DSSDocument documentToSign;
public static ExternalCMSPAdESService service;

/**
 * Computes a CAdES with specific things for PAdES
 */
public static byte[] getSignedCMSignedData(byte[] documentDigest) throws Exception {
    signatureParameters = new PAdESSignatureParameters();
    signatureParameters.setSigningCertificate(getSigningCert());
    signatureParameters.setCertificateChain(getCertificateChain());
    signatureParameters.setSignatureLevel(SignatureLevel.PAdES_BASELINE_B);
    signatureParameters.setLocation("Luxembourg");
    signatureParameters.setReason("DSS testing");
    signatureParameters.setContactInfo("Jira");

    CMSProcessableByteArray content = new CMSProcessableByteArray(documentDigest);

    PadesCMSSignedDataBuilder padesCMSSignedDataBuilder = new PadesCMSSignedDataBuilder(getOfflineCertificateVerifier());
    SignatureAlgorithm signatureAlgorithm = signatureParameters.getSignatureAlgorithm();

    CustomContentSigner customContentSigner = new CustomContentSigner(signatureAlgorithm.getJCEId());
    SignerInfoGeneratorBuilder signerInfoGeneratorBuilder = padesCMSSignedDataBuilder.getSignerInfoGeneratorBuilder(signatureParameters, documentDigest);

    CMSSignedDataGenerator generator = padesCMSSignedDataBuilder.createCMSSignedDataGenerator(signatureParameters, customContentSigner,
            signerInfoGeneratorBuilder, null);

    CMSUtils.generateDetachedCMSSignedData(generator, content);

    SignatureTokenConnection signingToken = new Pkcs12SignatureToken("certificate.p12",
            new KeyStore.PasswordProtection("123456".toCharArray()));
    DSSPrivateKeyEntry privateKey = getKey("certificate.p12","123456");

    SignatureValue signatureValue = signingToken.sign(new ToBeSigned(customContentSigner.getOutputStream().toByteArray()),
            signatureParameters.getDigestAlgorithm(), privateKey);

    customContentSigner = new CustomContentSigner(signatureAlgorithm.getJCEId(), signatureValue.getValue());
    generator = padesCMSSignedDataBuilder.createCMSSignedDataGenerator(signatureParameters, customContentSigner, signerInfoGeneratorBuilder, null);

    CMSSignedData cmsSignedData = CMSUtils.generateDetachedCMSSignedData(generator, content);
    return DSSASN1Utils.getDEREncoded(cmsSignedData);
}

public static CertificateVerifier getOfflineCertificateVerifier() {
    CertificateVerifier cv = new CommonCertificateVerifier();
    cv.setDataLoader(new IgnoreDataLoader());
    return cv;
}

public static List<CertificateToken> getCertificateChain() throws Exception {
    List<CertificateToken> list = new ArrayList<>();
    CertificateToken[] l = getKey("certificate.p12","123456").getCertificateChain();
    for (int i = 0; i < l.length; i++) {
        list.add(l[i]);
    }
    return list;
}

public static CertificateToken getSigningCert() throws Exception {
    return getKey("certificate.p12","123456").getCertificate();
}

public static DSSPrivateKeyEntry getKey(String certificate, String pin) throws Exception {
    try (Pkcs12SignatureToken signatureToken = new Pkcs12SignatureToken("certificate.p12",
            new KeyStore.PasswordProtection("123456".toCharArray()))) {
        List<DSSPrivateKeyEntry> keys = signatureToken.getKeys();
        KSPrivateKeyEntry dssPrivateKeyEntry = (KSPrivateKeyEntry) keys.get(0);
        DSSPrivateKeyEntry entry = signatureToken.getKey(dssPrivateKeyEntry.getAlias(),
                new KeyStore.PasswordProtection("123456".toCharArray()));
        return entry;
    }
}
private static class ExternalCMSPAdESService extends PAdESService {

    private static final long serialVersionUID = -2003453716888412577L;

    private byte[] cmsSignedData;

    public ExternalCMSPAdESService(CertificateVerifier certificateVerifier) {
        super(certificateVerifier);
    }

    @Override
    protected byte[] generateCMSSignedData(final DSSDocument toSignDocument, final PAdESSignatureParameters parameters,
                                           final SignatureValue signatureValue) {
        if (this.cmsSignedData == null) {
            throw new NullPointerException("A CMS signed data must be provided");
        }
        return this.cmsSignedData;
    }

    public void setCmsSignedData(final byte[] cmsSignedData) {
        this.cmsSignedData = cmsSignedData;
    }

}
}

共有1个答案

谢建业
2023-03-14

在对另一个问题的回答的评论中,一场讨论开始了,你指出了这个问题并寻求帮助。在那次讨论中,很明显,你还不知道自己想要实现什么。因此,让我们澄清一下。

你说你想在签名中添加LTV验证。让我们先看看这意味着什么。

LTV是长期验证的缩写。它代表的目标是确保签名在几年后仍然可以被验证。

这个目标试图克服的挑战是,从长远来看,验证者需要的信息将无法在线获得,使用的算法最终将不再被认为是安全的。

这些手段是一次检索所需信息,并以可信的方式将其与签名捆绑在一起,并应用数字时间戳来证明某组数据、签名和额外信息存在并在给定时间捆绑在一起(例如,当使用的签名算法仍然被认为是强大的时)。

到目前为止,一切顺利。

Adobe很早(在PDF成为ISO标准之前)就html" target="_blank">定义了一种实现LTV的机制:他们指定了一个特定的已签名属性,在签名之前应该收集验证所需的数据,并建议对嵌入的签名容器应用时间戳。

然而,从那时起,这种机制变得过于简单和静态。根据使用中的验证模型,签名前收集的信息不够好:要在签名时检查给定证书是否有效,严格来说需要在签名后生成信息。为了应对算法变得脆弱,可能需要一次又一次地对整个文档进行时间戳。

为了解决这个问题,ETSI(一个欧洲规范化组织)指定了向文档中添加验证相关信息的替代方法,以及添加覆盖整个文档(而不仅仅是嵌入的签名容器)的额外时间戳的方法。这些机制不会更改原始签名容器,而是在原始文档的增量更新中添加信息。同时,这些机制已添加到ISO 32000-2中的国际PDF标准中。它们被归入PAdES一词。

ETSI还定义了如何使用这些新机制以互操作方式增强签名的标准方案,即PAdES基线配置文件:

  • B级别只包含一个基本的签名容器,该容器的配置文件特别包括ESS证书ID属性

为了实现长期验证,可以重复添加LT和LTA,以提供前一个时间戳的验证信息,并记录使用的算法在它们仍然强大的时候应用。

Adobe已经建立了自己的“支持LTV”配置文件,该配置文件假定对验证数据的要求不那么严格(不需要时间戳),并且不关心算法变得脆弱。他们基本上收集了他们在文档中找到的所有与验证相关的信息,并按原样使用它们。(更确切地说,这是Adobe Acrobat标准设置的行为。您可以对Acrobat进行不同的配置,以更改要求,例如,确保某些时间戳确实重要。因此,在谈论“启用LTV”签名时,始终确保您与您的讨论伙伴考虑的设置相同……)

如果您想在服务器A上使用eSig DSS扩展PDF签名,只需将finalDoc

PAdESService service = new PAdESService(certificateVerifier);
service.setTspSource(tspSource);
PAdESSignatureParameters extensionParameters = new PAdESSignatureParameters();
extensionParameters.setSignatureLevel(extensionLevel);

DSSDocument extendedDocument = service.extendDocument(finalDoc, extensionParameters);

哪里

  • CertificateQualifier是为在线资源初始化的CommonCertificateQualifier
  • tspSource是为您选择的时间戳服务初始化的onlinespsource,并且
  • extensionLevel是所需的级别,例如SignatureLevel。PAdES_基线_LT

extendedDocument中的结果应包含所需的验证相关信息。

 类似资料:
  • 我们正在尝试启用签名LTV。我正在使用下面的代码添加验证。签名时。isTsp()为false,PDF表示签名未启用LTV,但在另一种情况下(Signature.isTsp()为true),它显示为有效。当我们打开PDF并尝试通过右键单击签名手动添加验证信息时,它将启用LTV,而不会出现任何问题。不知道我们在这里遗漏了什么。任何输入都将非常有用。

  • 5.2 使用Spring的验证器接口进行验证 {#toc_2} Spring具有一个Validator接口可以让你用于验证对象。Validator接口在工作时需要使用一个Errors对象,以便于在验证过程中,验证器可以将验证失败的信息报告给这个Errors对象。 让我们考虑一个小的数据对象: public class Person { private String name; p

  • 我正在尝试验证Json Objects。我使用https://code.google.com/p/rest-assured/wiki/Downloads?tm=2, greeter-schema.json:http://cs606926.vk.me/v606926718/15603/0Kauo1bTDi8.jpg 即使JsonString不等于这个“{\”isSuccess\“:false}”,我

  • 这个问题可能是泛泛的,但我试图理解这里的主要含义。 我正在尝试使用BCEL库进行一些字节码工程,部分工作流程要求我多次读取同一字节码文件(从一开始)。流程如下 在第4步中,我需要重置标记或获取流,就像它是从一开始的一样。我知道以下选择。 1)使用包装流-有机会获得“重置为无效标记”IOException 2)使用ByteArrayInputStream包装它-即使一些在线研究表明它是错误的,它仍然

  • 我正在尝试用DSS签署PDF文档,我的问题是我无法在服务器a中计算文档的哈希值,然后在服务器B中签署它。 知道服务器A包含PDF文档,我们在服务器B中检索用于签名的证书 我的问题是如何在不需要证书的情况下计算服务器a中文档的哈希值。然后在服务器B上发送签名? 更新: ******散列的准备和计算******** ******散列签名******** ********PDF错误:*********