微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

base64 编码的签名属性 DER 结构中的消息摘要 简而言之详细

如何解决base64 编码的签名属性 DER 结构中的消息摘要 简而言之详细

我有以下 ASN1 ASN.1 dump

SET (4 elem)
  SEQUENCE (2 elem)
    OBJECT IDENTIFIER 1.2.840.113549.1.9.3 contentType (PKCS #9)
    SET (1 elem)
      OBJECT IDENTIFIER 1.2.840.113549.1.7.1 data (PKCS #7)
  SEQUENCE (2 elem)
    OBJECT IDENTIFIER 1.2.840.113549.1.9.5 signingTime (PKCS #9)
    SET (1 elem)
      UTCTime 2021-05-26 19:03:42 UTC
  SEQUENCE (2 elem)
    OBJECT IDENTIFIER 1.2.840.113549.1.9.52 cmsAlgorithmProtection (RFC 6211)
    SET (1 elem)
      SEQUENCE (2 elem)
        SEQUENCE (2 elem)
          OBJECT IDENTIFIER 2.16.840.1.101.3.4.2.1 sha-256 (NIST Algorithm)
          NULL
        [1] (2 elem)
          OBJECT IDENTIFIER 1.2.840.113549.1.1.11 sha256WithRSAEncryption (PKCS #1)
          NULL
  SEQUENCE (2 elem)
    OBJECT IDENTIFIER 1.2.840.113549.1.9.4 messageDigest (PKCS #9)
    SET (1 elem)
      OCTET STRING (32 byte) E2BB4AD28C95B99E9EDEF70662AFE825AF477680F4867B59833AA05313D8F4C0

并且我知道 OCTET STRING 是我要签名的 messageDigest(hash sha-256)。在这种情况下,这是一个使用 PDFBox 的 PDF 文档,我用来签名的代码如下

public byte[] signPKCS7(InputStream content) throws IOException,SignedBytesException {
        try {
            if (SigUtils.checkCertificateUsage((X509Certificate) certificateChain[0])) {
                CMSSignedDataGenerator signGenerator = new CMSSignedDataGenerator();
                X509Certificate userCert = (X509Certificate) this.certificateChain[0];
                ContentSigner mySigner = new CustomSigner(invoke,String.valueOf(userCert.getSerialNumber()),sad);
                signGenerator.addSignerInfoGenerator(
                        new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build())
                                .build(mySigner,userCert));
                signGenerator.addCertificates(new JcaCertStore(Arrays.asList(certificateChain)));
                CMSProcessableInputStream msg = new CMSProcessableInputStream(content);
                CMSSignedData signedData = signGenerator.generate(msg,false);
                return signedData.getEncoded();
            } else {
                throw new Exception("Unable to sign pdf. Certificate usage not appropiate for request");
            }
        } catch (GeneralSecurityException | CMSException | OperatorCreationException e) {
            logger.error(e.getMessage());
            throw new RuntimeException("unable to sign pdf!",e);
        }
    }

我还计算了我要签名的文档的sha-256,结果如下

0622971147486E1900037EFF229D921D14F5B51AAC7171729B2B66F81CDF6585

所以我的问题是,来自 ANS1 的消息摘要是否与我计算的相同?如果是这样,我如何达到该结果,因为当我使用以下代码浏览 ASN1 结构时,我无法获得相同的结果

private byte[] getMessageDigest(byte[] signatures) throws IOException {
        ASN1InputStream input = new ASN1InputStream(signatures);
        byte[] bytesToSign = null;
        ASN1Primitive p;
        while ((p = input.readobject()) != null) {
            if (p instanceof ASN1Set) {
                ASN1Set set = ASN1Set.getInstance(p);
                ASN1Sequence asn1 = ASN1Sequence.getInstance(set.getobjectAt(3));
                ASN1Set setocter = ASN1Set.getInstance(asn1.getobjectAt(1));
                ASN1OctetString octstr = ASN1OctetString.getInstance(setocter.getobjectAt(0));
                bytesToSign = octstr.getoctets();
            }
        }
        return bytesToSign;
    }

并使用以下代码将字节转换为十六进制

private  String bytesToHex(byte[] bytes) {
        char[] hexChars = new char[bytes.length * 2];
        for (int j = 0; j < bytes.length; j++) {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = HEX_ARRAY[v >>> 4];
            hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
        }
        return new String(hexChars);
    }

我得到以下结果

E2BB4AD28C95B99E9EDEF70662AFE825AF477680F4867B59833AA05313D8F4C0

这是 ASN1 转储的 OCTET STRING,但不是文档的哈希值。而且八位字节字符串总是在变化,所以我可以假设它实际上不是一个常规的消息摘要。那么它到底是什么,我是否能够获得我要发送签名的内容的 sha-256

解决方法

简而言之

文档哈希不是根据您要签名的原始 PDF 计算的。该 PDF 首先准备通过应用某些更改进行签名,然后根据该准备好的 PDF 计算哈希值,除了其中的占位符间隙,准备稍后容纳签名容器。

详细

要创建集成的 PDF 签名,必须对 PDF 应用某些更改:

  • 待集成签名的持有人是 PDF 中的 AcroForm 表单字段。如果 PDF 不包含空的、未使用的签名字段(或不应使用现有字段),则必须向 PDF 添加新的签名字段。
  • 签名表单字段可能有一个可视化、一个小部件注释,它代表文档本身某些页面上的签名。如果需要这样的可视化,则必须在 PDF 中添加匹配的注释。
  • 必须将描述签名模式的信息和其他详细信息添加到 PDF 中。因此,必须将所选签名字段的值设置为带有这些签名详细信息的 PDF 中的新字典对象;这里有两个特殊条目,ByteRangeContents。两者都设置为适合初学者的空白值。
  • 一个标记被添加到 PDF 根 AcroForm 对象,表明 PDF 已签名。

通过这些添加,PDF 被存储。此后,Contents 值在文件中的位置被固定,ByteRange 值的空白值被修补为一个由四个整数组成的数组,即起始偏移量和大小Contents 值之前的文件段以及之后文件段的起始偏移量和大小。

然后对文件的这些段的字节进行散列,并生成对该文档散列进行签名的 CMS 签名容器,然后将其注入到 Contents 值中。


在您的情况下,您在待签名属性中找到的哈希值,

E2BB4AD28C95B99E9EDEF70662AFE825AF477680F4867B59833AA05313D8F4C0

是准备好的文件的这两个部分的散列,它几乎总是与原始 PDF 的散列不同,就像你的情况一样

0622971147486E1900037EFF229D921D14F5B51AAC7171729B2B66F81CDF6585

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。