signer.verify(signerInformationVerifier) 到底验证了什么?

What exactly does signer.verify(signerInformationVerifier) verify?

下面代码中的 signer.verify(signerInformationVerifier) 究竟检查了什么?

将消息的哈希值与签名进行比较是否正确(我理解这也是消息的哈希值,但使用发件人的私钥加密)。在比较之前,它使用证书中的 public 密钥和 "decrypt" 签名。如果签名使用的是有问题的证书,两者应该给出相同的结果。那样的话objective的"verify"就是看文没有change/the签名对应的证书。 "verify" 还可以检查证书在签名时是否未过期...
到目前为止,我了解到像下面编码的 "verify" 不会检查证书是否受信任。

我尝试了 javadoc,google 但找不到答案。

SignerInformationStore signers = cmsSignedData.getSignerInfos();
// variable "it" iterates all signers
Iterator<?> it = signers.getSigners().iterator();
while (it.hasNext){
    SignerInformation signer = (SignerInformation) it.next();
    // get all 
    CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
    InputStream in = new  ByteArrayInputStream(certificateHolder.getEncoded());
    X509Certificate cert2 = (X509Certificate) certFactory.generateCertificate(in);

    SignerInformationVerifier signerInformationVerifier = new JcaSimpleSignerInfoVerifierBuilder().build(cert2);
    if (signer.verify(signerInformationVerifier)){
        System.out.println("PDF signature verification is correct");
    } else { System.out.println ("PDF signature verification failed");}

它 returns 对于我签名的 pdf 文档是正确的。我已经用一个 pdf 进行了测试,该 pdf 具有来自受信任的 CA 1(在密钥库中注册为这样)的受信任签名和一个来自 CA 2 的 pdf,但未在密钥库中注册为受信任。

非常感谢explanation/help。

JcaSimpleSignerInfoVerifierBuilder...build(cert) 生成的 SignerInformationVerifier(主要包装 ContentVerifierProvider)由 SignerInformation.verify 驱动,如下所示:

  • 因为使用了 build(cert) 重载而不是 build(publickey) 重载,if (must-be-) authenticated/signed 属性 signingTime 存在,它检查 signingTime 值是否在证书的有效期内

  • 如果存在 authenticated/signed 属性,它会检查它们,包括检查(接收到的)内容的摘要是否与摘要属性匹配,并检查签名是否验证为证书中公钥下的签名属性(以及消息和 SignerInfo 中指定的算法)

  • 否则(无签名属性)它检查签名是否验证为证书中公钥下(接收到的)内容的签名(以及消息和 SignerInfo 中指定的算法)

如果您改用 JcaSimpleSignerInfoVerifierBuilder...build(cert.getPublicKey()) 重载,唯一的区别是它不会根据证书有效期检查签名时间(如果存在)。

It could check the attributes of the signer against the attributes of the certificate (Subject only? Or also serial number, Issuer?)

它没有。你的代码应该;请参阅 org.bouncycastle.cms.CMSSignedDataParser javadoc 获取最小示例,它使用消息中的 Store 证书来查找与 SignerInfo 中的 'SID' (SignerIdentification) 匹配的证书.这个'SID' 通常是Issuer 和Serial 对,但在CMS 中可以是SubjectKeyIdentifier。标准 PKCS7/CMS 格式不指定签名者的主题名称(或 SubjectAltName,就此而言),因此任何确定并使用它来查找或检查证书的方法都是非标准的。但是,很有可能一旦您通过其他方式找到证书,您可能希望将其主题(或 SAN)用作处理签名内容时的有用信息。

它根本不检查证书是 valid/trusted。该示例将此标记为一个问题。

如果您想采用简单的方法,您可以简单地检查证书是否在您的信任库中或以其他方式直接配置,并且它没有过期,如果 SignerInformation 包含已签名的证书,则可能会跳过后者如上所述 检查的属性 signingTime。如果签名者证书因密钥泄露而被撤销,或者证书被确定为欺诈,这会使您面临风险;您将继续接受小偷或欺诈者的签名。此外,在手动验证每个新的签名证书后,您还依赖于您自己或更新本地信任库或配置的人,如果您被欺骗错误地这样做,您将再次接受伪造的签名。

如果您想真正验证证书,请参阅 Java PKI Programmer's Guide(或某些旧版本中的 CertPathProgGuide)和相关 classes 主要 java.security.cert.CertPathValidator.[=28= 的 javadoc ]

In my understanding the signature is a private key encryption of the hash (message digest) of the pdf document. The reason seems to be that this way signing is much faster. The function signer.verify(signerInformationVerifier) then most likely decrypts the signed hashed message digest using the public key of the certificate and compares it against the hashed message digest (using the hash function defined in the certificate) - it should give the same result.

大部分不是。签名不是加密。对于 one 算法,RSA,加密和签名的核心操作之间存在数学对称性,最初诱使人们将签名描述为 'encrypting with the privatekey',但将其视为很快发现这种方法会导致漏洞,现在使用的实际加密和签名方案有很大不同,不能互换,尽管很多人只是复制他们在网上找到的东西,这些东西是 10 年前某人发现的东西的副本是别人 20 年前发现的东西的副本,不断重复错误。 Java 加密没有帮助,因为它是在 1990 年代指定的,并且包含错​​误特征,即 RSA 的 Cipher.init 接受参数 'encrypt' 与私钥或 'decrypt' publickey 实际上,默默地进行 PKCS1 签名或恢复而不是加密和解密。对于 DSA、ECDSA 和(现在的)EdDSA 等其他算法,签名和加密之间没有任何相似性或任何类型的关系。如果您有兴趣,crypto.SX 和 security.SX 上的许多 Q 都讨论了这一点。

现在大多数签名方案都是混合的,PKCS7/CMS支持的都是混合的:'bulk'数据首先被安全地散列(也称为消化),然后使用(小)hash/digest而不是批量数据计算签名。如果您指的是签名和验证的速度,那会有所不同;对于在现代时代(大约从 1980 年开始)实施的 RSA,签名比验证慢得多;对于 DSA 和 ECDSA,验证比签名稍慢。

对于 PKCS7/CMS,正如上面的描述所暗示的,签名通常不会取自 'content'(您的 PDF 文件)。相反,内容的散列以及一些其他元数据包含在 PKCS7 中称为 authenticatedAttributes 的数据结构中,并在 CMS 中重命名为 signedAttr[ibute]s,that 是涵盖的内容通过公钥签名(包括散列)。但是有一个 backward-compatibility 选项,其中不使用 signedAttributes 并且公钥签名 应用于内容哈希。 RFC 5652 et pred sections 5.4-5.6 中描述了签名者和验证者在这两种情况下的确切操作,尽管您可能需要第 5 节的全部内容来了解​​上下文。还有哈希算法rithm used 未在证书中定义,但在 PKCS7/CMS 消息中定义;然而,公钥算法和大小(以及强度)在使用的证书 if 中定义(这是首选但实际上不是必需的),好的做法是选择要匹配的哈希,相当接近,公钥强度。

对于 RSA,验证者'recover'(不解密)来自签名的散列并将其与接收数据的新计算散列进行比较——内容或签名属性,如上所述。从技术上讲,这是在 JCA 提供程序 class 中完成的,用于由 BouncyCastle classes(间接)调用的 RSA,就像 DSA 和 ECDSA 的完全不同的验证操作在 他们的 提供商 classes,但结果与您所关心的相同。