本文介绍了base64 编码的签名属性 DER 结构中的消息摘要的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下ASN1 ASN.1转储

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 文档,我用来签名的代码如下

and I understand that the OCTET STRING is the messageDigest(hash sha-256) of what I am trying to sign. Which in this case is a PDF document using PDFBOX the code I'm using to sign is the following

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,结果如下

I have also calculated the sha-256 of the document I am trying to sign and the result is the following

0622971147486E1900037EFF229D921D14F5B51AAC7171729B2B66F81CDF6585

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

So my question is, is the message digest from the ANS1 the same as the one I calculated? And if so how do I reach that result as when I'm going through the ASN1 structure with the following code I have not been able to get the same result

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;
    }

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

and the using the following code to convert the bytes to hex

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

Which is the OCTET STRING of the ASN1 dump but its not the hash of the document. And that Octet String is always changing so I can assume its actually not a regular message digest. So what exactly is it and am I able to get the sha-256 of the content I'm sending to sign

推荐答案

In Short

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

In Short

The document hash is not calculated from the original PDF you want to sign. That PDF first is prepared for signing by applying certain changes, and then the hash is calculated from this prepared PDF except a placeholder gap in it prepared to later house the signature container.

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

To create an integrated PDF signature, certain changes have to be applied to the PDF:

  • 待集成签名的持有人是 PDF 中的 AcroForm 表单字段.如果 PDF 不包含空的、未使用的签名字段(或不应使用现有字段),则必须向 PDF 添加新的签名字段.
  • 签名表单字段可能有一个可视化、一个小部件注释,它代表文档本身某些页面上的签名.如果需要这样的可视化,则必须在 PDF 中添加匹配的注释.
  • 必须将描述签名模式的信息和其他详细信息添加到 PDF 中.因此,必须将所选签名字段的值设置为带有这些签名详细信息的 PDF 中的新字典对象;这里有两个特殊条目,ByteRangeContents.两者都设置为适合初学者的空白值.
  • 一个标记被添加到 PDF 根 AcroForm 对象,表明 PDF 已签名.
  • The holder of the to-be-integrated signature is an AcroForm form field in the PDF. If the PDF does not contain an empty, unused signature field (or no existing field shall be used), a new signature field has to be added to the PDF.
  • A signature form field may have a visualization, a widget annotation, which represents the signature on some page of the document itself. If such a visualization is desired, a matching annotation has to be added to the PDF.
  • Information describing the mode and other details of signing have to be added to the PDF. Thus, the value of the chosen signature field has to be set to a new dictionary object in the PDF with these signature details; there are two special entries here, the ByteRange and the Contents. Both are set to blank values of appropriate size for starters.
  • A marker is added to the PDF root AcroForm object indicating that the PDF is signed.

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

With these additions the PDF is stored. Thereafter the position of the Contents value in the file is fixed and the blank value of the ByteRange value is patched to an array of four integers, the start offset and size of the file segment before the Contents value and the start offset and size of the file segment thereafter.

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

Then the bytes of these segments of the file are hashed and a CMS signature container signing this document hash is generated which in turn is injected into the Contents value.

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

In your case the hash you find in the to-be-signed attributes,

E2BB4AD28C95B99E9EDEF70662AFE825AF477680F4867B59833AA05313D8F4C0

是准备好的文件的这两段的哈希值,它几乎总是与原始 PDF 的哈希值不同,就像你的情况一样

is the hash over those two segments of the prepared file which almost always will differ from the hash over the original PDF, like in your case where that is

0622971147486E1900037EFF229D921D14F5B51AAC7171729B2B66F81CDF6585

这篇关于base64 编码的签名属性 DER 结构中的消息摘要的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-27 21:27