为了账号安全,请及时绑定邮箱和手机立即绑定

使用 Google KMS 和 Entrust 证书签署 PDF 文档

使用 Google KMS 和 Entrust 证书签署 PDF 文档

慕桂英3389331 2023-07-28 10:44:32
我正在尝试使用 CA (Entrust) 的证书在 pdf 文档中进行有效签名,该证书是使用 Google KMS 的私钥生成的(私钥永远不会从 KMS 中流出)。证书链的构成为:[entrustCert,intermediate,rootCert]按照我用来实现此目的的代码部分:String DEST = "/tmp/test_file.pdf";OutputStream outputFile = new FileOutputStream(DEST);CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");X509Certificate[] chain = new X509Certificate[3];chain[0] = (X509Certificate) certificateFactory.generateCertificate(entrustCert);chain[1] = (X509Certificate) certificateFactory.generateCertificate(intermediateCert);chain[2] = (X509Certificate) certificateFactory.generateCertificate(rootCert);int estimatedSize = 8192;PdfReader reader = new PdfReader(contract);ByteArrayOutputStream outputStream = new ByteArrayOutputStream();PdfStamper stamper = PdfStamper.createSignature(reader, outputStream, '\0');PdfSignatureAppearance appearance = stamper.getSignatureAppearance();appearance.setReason(“reason”);appearance.setLocation("Amsterdam");appearance.setVisibleSignature(new Rectangle(36, 748, 144, 780), 1, "sig");appearance.setCertificate(chain[0]);PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);dic.setReason(appearance.getReason());dic.setLocation(appearance.getLocation());dic.setContact(appearance.getContact());dic.setDate(new PdfDate(appearance.getSignDate()));appearance.setCryptoDictionary(dic);HashMap<PdfName, Integer> exc = new HashMap<>();exc.put(PdfName.CONTENTS, (estimatedSize * 2 + 2));appearance.preClose(exc);String hashAlgorithm = DigestAlgorithms.SHA256;BouncyCastleDigest bcd = new BouncyCastleDigest();PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, null, bcd, false);InputStream data = appearance.getRangeStream();byte[] hash = DigestAlgorithms.digest(data, MessageDigest.getInstance("SHA-256"));byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, MakeSignature.CryptoStandard.CMS);目前我遇到以下问题:每个签名均无效:自应用签名以来,文档已被更改或损坏。Google 的签名验证总是错误的。
查看完整描述

1 回答

?
米琪卡哇伊

TA贡献1998条经验 获得超6个赞

损坏的消息摘要值

文件签名失败.pdf分析

在所包含的签名容器的 ASN.1 转储中,有一个问题立即引人注目:该messageDigest属性包含应有的签名属性的副本,即具有正确的messageDigest属性:

    <30 5C>

4172   92: . . . . . . SEQUENCE {

    <06 09>

4174    9: . . . . . . . OBJECT IDENTIFIER messageDigest (1 2 840 113549 1 9 4)

         : . . . . . . . . (PKCS #9)

    <31 4F>

4185   79: . . . . . . . SET {

    <04 4D>

4187   77: . . . . . . . . OCTET STRING, encapsulates {

    <31 4B>

4189   75: . . . . . . . . . SET {

    <30 18>

4191   24: . . . . . . . . . . SEQUENCE {

    <06 09>

4193    9: . . . . . . . . . . . OBJECT IDENTIFIER

         : . . . . . . . . . . . . contentType (1 2 840 113549 1 9 3)

         : . . . . . . . . . . . . (PKCS #9)

    <31 0B>

4204   11: . . . . . . . . . . . SET {

    <06 09>

4206    9: . . . . . . . . . . . . OBJECT IDENTIFIER data (1 2 840 113549 1 7 1)

         : . . . . . . . . . . . . . (PKCS #7)

         : . . . . . . . . . . . . }

         : . . . . . . . . . . . }

    <30 2F>

4217   47: . . . . . . . . . . SEQUENCE {

    <06 09>

4219    9: . . . . . . . . . . . OBJECT IDENTIFIER

         : . . . . . . . . . . . . messageDigest (1 2 840 113549 1 9 4)

         : . . . . . . . . . . . . (PKCS #9)

    <31 22>

4230   34: . . . . . . . . . . . SET {

    <04 20>

4232   32: . . . . . . . . . . . . OCTET STRING    

         : . . . . . . . . . . . . . 40 76 BC 3F 05 25 E4 C3    @v.?.%..

         : . . . . . . . . . . . . . 27 AD 78 FA 73 31 4C 1B    '.x.s1L.

         : . . . . . . . . . . . . . 82 97 3D AA 4E 81 72 D6    ..=.N.r.

         : . . . . . . . . . . . . . 23 3C DD 59 D2 82 81 55                            

         : . . . . . . . . . . . . }

         : . . . . . . . . . . . }

         : . . . . . . . . . . }

         : . . . . . . . . . }

         : . . . . . . . . }

         : . . . . . . . }

事实上,在您的代码中,原因变得很清楚:


byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, MakeSignature.CryptoStandard.CMS);

[...]

byte[] encodedSig = sgn.getEncodedPKCS7(sh, tsaClient, null, null, MakeSignature.CryptoStandard.CMS);

这两个调用必须具有相同的参数(除了在第二个位置添加的参数tsaClient),因为在第一个调用中检索到的经过身份验证的属性(也称为签名属性)是实际签名的字节,因此必须使用相同的输入创建最终的签名容器有效的。


(如果你的变量名更清晰,这个问题可能会更早被发现。)


因此,要修复签名属性,请替换


byte[] encodedSig = sgn.getEncodedPKCS7(sh, tsaClient, null, null, MakeSignature.CryptoStandard.CMS);

经过


byte[] encodedSig = sgn.getEncodedPKCS7(hash, tsaClient, null, null, MakeSignature.CryptoStandard.CMS);

RSA 填充

在努力解决上述问题后,出现了一个新问题:“内部加密库错误。错误代码:0x2726”


test_file.pdf分析


使用签名者证书的 RSA 公钥解密签名字节会导致


2D9B224E0894E73B1D3EDEE43E5C34A152057B008518538F3D6DA9C5AC73B54AEF33EB165ED0815F2E7851C86308AAFEC3FC0CD5CA77D7A745C056CB37783B7B51484D9B6C1F6D7E42C2B1C49127CD7D1C3A371D943A5C6F5DDA47C758493D2D3CA7D165B35A1BE4FA590911E801D7026822A9B9D202AE9A671DF4F36D42AAD712D43506EC3607E5AC7CCE23389BE288DD32C9C45B92CAA7225897EFD9F8ECFE2A40007FD6AC8B625239E6E529B7521E2EB652659A8F8B3F7262D46E8A0207A3004FEF48C87FC8A52B632268FDD0888A00AE6A3B303A138B18F28A66108467BFF743A859ECD193ADB52268B1FC531690B99D35D5E68BF804B59E24FCB180FABC

这显然看起来不像 PKCS1v1.5 填充的哈希值。因此,要么所谓的签名者证书是错误的,我们看到本质上是垃圾,要么签名根本不使用 PKCS1v1.5 填充,而是使用 PSS 填充。尾随BC是后者的指示符,但垃圾也可能以 结尾BC。


但与此同时,OP 已确认:


Google KMS 中生成的私钥是 2048 位 RSA 密钥 PSS Padding - SHA256 Digest


这确实解释了签名的问题:iText 5.x 不支持RSASSA-PSS。创建 RSA 签名时,它会自动采用 PKCS1v1.5 填充;特别是在 CMS 签名容器中,它生成表示签名算法是 RSASSA-PKCS1-v1_5。因此,任何验证者都将无法验证签名。


显而易见的选择是


通过拉皮条或替换 iTextPdfPKCS7类来添加 RSASSA-PSS 支持

或切换到 RSASSA-PKCS1-v1_5 密钥。


查看完整回答
反对 回复 2023-07-28
  • 1 回答
  • 0 关注
  • 149 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信