首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用DSS (CMS容器)确保LTV验证

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

Stack Overflow用户
提问于 2021-04-07 20:25:27
回答 1查看 401关注 0票数 0

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

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

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

结果五:

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

*

代码语言:javascript
复制
    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;
        }

    }
}

并且能够对计算出来的散列进行签名:

*

代码语言:javascript
复制
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;
    }

}
}
EN

回答 1

Stack Overflow用户

发布于 2022-02-01 09:59:05

在对的评论中,开始了一次讨论,您在讨论中指出了这个问题并请求帮助。在那次讨论中,很明显,你还不知道你在努力实现什么。因此,让我们澄清一点。

LTV验证

你说你想在你的签名中添加LTV验证。让我们先来看看那是什么意思。

LTVLong Term V的缩写。它所代表的目标是确保签字仍能在若干年内得到核实。

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

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

到现在为止还好。

Adobe早期(在PDF成为ISO标准之前)定义了一种实现LTV的机制:他们指定了一个特定的签名属性,在签名之前应该收集验证所需的数据,他们建议将时间戳应用到嵌入式签名容器中。

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

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

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

attribute.

  • The
  • B级别只包含一个基本签名容器,特别是包含ESS证书ID的ESS证书ID
  • T级别基于B级别,但另外还需要一个签名后时间戳。此时间戳可以作为签名时间戳应用于原始签名容器,也可以作为document.
  • The LT级别的额外增量更新中的文档时间戳,该时间戳基于T级别,要求在新的增量更新中添加丢失的中间证书和所需的撤销信息。
  • LTA级别基于LTE 230级别,并需要在另一个增量更新中添加另一个文档时间戳。H 231F 232

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

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

使用eSig决策支持系统的扩展

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

代码语言:javascript
复制
PAdESService service = new PAdESService(certificateVerifier);
service.setTspSource(tspSource);
PAdESSignatureParameters extensionParameters = new PAdESSignatureParameters();
extensionParameters.setSignatureLevel(extensionLevel);

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

哪里

  • certificateVerifier是为在线资源初始化的CommonCertificateVerifier
  • tspSource是为您选择的时间戳服务初始化的OnlineTSPSource,and
  • extensionLevel是所需的级别,例如SignatureLevel.PAdES_BASELINE_LT.

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

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66993643

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档