检查一个随机选择的数字签名是否足以验证多个签名文件?

Is checking one randomly chosen digital signature is enough for verifying multiple signed file?

想象一下,我有一些二进制文件的多个分离签名,它们存储在不同的 .sig 文件中。 当有人修改文件时,所有签名都会失效

对于xml,封装签名被广泛使用。检查它更加困难 - 必须提取最后一个 ds:Signature 元素,检查签名,删除最后一个元素并检查当前最后一个 ds:Signature 元素等等。但是XML修改无论如何都会破坏最后一个签名,为什么我要检查别人?

唯一的解释 - 每个 ds:Signature 或 PKCS7 签名包含 public 密钥证书,其中包含有关签名者的信息、密钥有效期、撤销检查信息。 通常,验证过程不仅检查哈希值,而且提供有关签名者的信息,这是迭代所有签名的唯一原因,对吗?

因此,当我想验证多个签名文件并提供有关签名者的信息时,我是否可以进行小的优化 - 提取所有签名的信息但仅在一个随机选择的签名中执行散列检查?

还有一个问题——如何在一个文件中存储多个PKCS7签名?我正在使用 bouncycastle Java 库。它是否有某种方法来连接签名并从一个文件中提取它们?我不想手动将所有签名存储在一个 .sig 文件中,关心签名分隔符,是否可以在开箱即用的充气城堡中使用?

更新

我的 BouncyCastle 签名代码

    Certificate[] certchain = (Certificate[]) keystore.getCertificateChain(alias);

    final List<Certificate> certlist = new ArrayList<Certificate>();

    for (int i = 0, length = certchain == null ? 0 : certchain.length; i < length; i++) {
        certlist.add(certchain[i]);
    }

    Store certstore = new JcaCertStore(certlist);

    Certificate cert = keystore.getCertificate(alias);

    ContentSigner signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").
            build((PrivateKey) (keystore.getKey(alias, getPassword().toCharArray())));

    CMSSignedDataGenerator generator = new CMSSignedDataGenerator();

    generator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").
            build()).build(signer, (X509Certificate) cert));

    generator.addCertificates(certstore);
    CMSTypedData cmsdata = new CMSProcessableByteArray(content);
    CMSSignedData signeddata = generator.generate(cmsdata, false);

如您所见,generate 方法的第二个参数为 false,这意味着我正在生成不包含数据的非封装签名==.sig 文件。

验证码

CMSSignedData cms = new CMSSignedData(new CMSProcessableByteArray(contents.getBytes()), signature);
            Store store = cms.getCertificates();
            SignerInformationStore signers = cms.getSignerInfos();
            Collection c = signers.getSigners();
            Iterator it = c.iterator();

            while (it.hasNext()) {
                SignerInformation signer = (SignerInformation) it.next();
                Collection certCollection = store.getMatches(signer.getSID());
                Iterator certIt = certCollection.iterator();
                X509CertificateHolder certHolder = (X509CertificateHolder) certIt.next();
                cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certHolder);
                ok = signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert));
                if (!ok) break; // probably should remove it
            }

问题 - 我可以在开箱即用的 BouncyCastle 中做这样的事情(下面的描述)吗?

使分离的签名被包围 - 第一个符号 = hash(doc)-->appendToSigFile,第二个符号 = hash(doc+sigFile)-->appendToSigFile 等等。这样的做法可以模仿XML的包封签名吗?

你实际上问了很多问题。因此,

  1. 当我想验证多个签名文件并提供有关签名者的信息时,我可以进行小的优化 - 提取所有签名的信息但仅在一个随机选择的签名中执行哈希检查吗?

如果您确定(例如出于组织原因)所有这些签名最初都正确地签署了同一文档,则可以考虑此优化。但即便如此,您也不应该随意选择一个签名,而应该更喜欢那些具有更可靠算法的签名。

而且如果你真的那样优化,你绝对不应该声称所有签名仍然有效,但一个选择的是.如果你没有测试某些东西,你不应该声称你做了。

  1. 但是XML修改无论如何都会破坏最后一个签名,为什么我要检查别人?

这在很大程度上取决于您的用例。如果(如上所述)您确定(例如出于组织原因)所有这些签名最初正确地签署了各自的文档修订版,那么检查最外面的签名就足够了。

如果另一方面,文件是从一个人转发给另一个人签名的,并且您检查的操作可能在任何步骤发生,您必须检查这些签名中的每一个,以确保每个人都签署了内容没有不必要的更改。

如果不是每个 XML 签名都至少签署了与各自包含的数据相同的数据,使用至少与那个签名一样严格的规范化(XML 签名可以非常有选择性他们签署的元素以及他们允许的偏差),您肯定必须检查每一个。

  1. 通常,验证过程不仅检查哈希值,还提供有关签名者的信息,这是迭代所有签名的唯一原因,对吗?

显然...如果您只想检查散列值,您只需存储散列值(好吧,除非有政治或营销相关的原因...)。签名是为了证明特定的人在特定情况下出于特定目的看到并接受了某些特定数据。你的问题似乎只想考虑特定数据部分。

  1. 如何在一个文件中存储多个 PKCS7 签名?

PKCS#7 签名容器可以包含相同数据的任意数量的并行签名和额外的反签名。

如果您查看规范 RFC 2315,您会发现:

The signed-data content type shall have ASN.1 type SignedData:

SignedData ::= SEQUENCE {
     version Version,
     digestAlgorithms DigestAlgorithmIdentifiers,
     contentInfo ContentInfo,
     certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL,
     crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
     signerInfos SignerInfos }
DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
SignerInfos ::= SET OF SignerInfo

...

signerInfos is a collection of per-signer information. There may be any number of elements in the collection, including zero.

BouncyCastle 提供了许多实用功能,也是为此目的,但由于您没有展示如何使用 BC,因此很难解释您应该如何更改 BC 使用。

  1. 使分离的签名被包围 - 第一个符号 = hash(doc)-->appendToSigFile,第二个符号 = hash(doc+sigFile)-->appendToSigFile 等等。这样的做法可以模仿XML的包封签名吗?

您可以使用与您的设计等效的副署。

或者... PKCS#7 签名容器允许您嵌入已签名的内容,不需要将其分离。因此,您可以将原始内容嵌入到第一个签名容器中,然后将此签名容器嵌入到第二个签名容器中,依此类推。这将隐含地明确每个签名所签名的内容。

数字签名不只是为了提供完整性检查。如果必要,它们也是证明该人签署了该文件的一种方式。这就像支票上的签名。因此,您必须全部验证。如果失败了,您手上就有伪造品。如果它们都失败了,那么您的文件已损坏或联合伪造。无论哪种情况,它都是无用的,应该被拒绝。