当前位置: 首页 > 知识库问答 >
问题:

如何在C或C中为PDF中的数字签名创建Sig类型对象的内容值?

淳于博文
2023-03-14

我们通过添加所有必需的对象,使用内部库(C)以编程方式创建PDF,以便PDF阅读器能够正确地呈现它们。目前,我们正在增强库以支持PDF格式的数字签名。我们的用户将使用USB令牌或Windows证书签署PDF。在研究带有数字签名的原始PDF文件时,我们能够理解除Sig类型对象内容之外的所有对象。

18 0 obj
<<
/Type /Sig
/Filter /Adobe.PPKLite              --> signature handler for authenticating the fields contents
/SubFilter /adbe.pkcs7.sha1         --> submethod of the handler
/Contents <....>                    --> signature token
/ByteRange [ 0 101241 105931 7981                                                                                                                                                                                       
]                                   --> byte range for digest calc
/M (D:20210505094125+05'30')        --> timestamp
/Reason ()
/Location ()
/ContactInfo ()
>>
endobj

我们参考了https://www.adobe.com/devnet-docs/acrobatetk/tools/DigSigDC/Acrobat_DigitalSignatures_in_PDF.pdf来理解什么都构成签名令牌。我们需要指导如何使用windows API以编程方式为PDF创建签名令牌。目前我们没有考虑第三方lib解决方案。提前感谢。

我们尝试了以下方法:

  1. 更新了我们的内部PDF库,以支持增量更新,以便可以添加数字签名相关对象。除了上面提到的obj#18之外,我们还添加了这样的内容:
16 0 obj                       --> new Acroform obj
<<
/Fields [ 17 0 R ]
/SigFlags 3
>>
endobj
2 0 obj                        --> Updating root to add AcroForm
<<
/Type /Catalog
/Pages 3 0 R
/AcroForm 16 0 R
>>
endobj
17 0 obj                       --> new obj for signature field
<<
/T (SignatureField1)
/Type /Annot
/Subtype /Widget
/FT /Sig
/F 4
/Rect [ 270 159 503 201 ]  --> field position. this will have image of sign
/P 5 0 R
/V 18 0 R
/AP <<
/N 19 0 R
>>
>>
endobj
5 0 obj                    --> updating existing page obj with Annots
<<
/Type /Page
/Parent 3 0 R
/MediaBox [ 0 0 595 841 ]
/Resources 4 0 R
/Contents 6 0 R
/Annots [ 17 0 R ]
>>
endobj
18 0 obj
<<
/Type /Sig
/Filter /Adobe.PPKLite
/SubFilter /adbe.pkcs7.sha1  --> we tried with adbe.pkcs7.detached as well
/Contents <>           --> updated contents using windows APIs
/ByteRange [ 0 100381 102645 7322                                                                                                                                                                                       
]                      --> updated ByteRange with right offsets and lengths
/M (D:20210610125837+05'30') --> sign verified time
/Reason ()
/Location ()
/ContactInfo ()
>>
endobj
19 0 obj                  --> new obj
<<
/Length 7
/BBox [ 0 0 233 42 ]
/Type /XObject
/Subtype /Form
/Resources <<
/XObject <<
/FRM 20 0 R
>>
>>
>>
stream
/FRM Do
endstream
endobj
20 0 obj                 --> new obj for image manipulation
<<
/Length 29
/Type /XObject
/Subtype /Form
/Resources <<
/XObject <<
/Im1 21 0 R
>>
>>
/BBox [ 0 0 233 42 ]
>>
stream
q 233 0 0 42 0 0 cm /Im1 Do Q
endstream
endobj
21 0 obj           --> image obj which contains sign info. Generated by us
<<
/Length 6166
/Type /XObject
/Subtype /Image
/Width 372
/Height 82
/ColorSpace /DeviceRGB
/BitsPerComponent 8
/Filter /DCTDecode
>>
stream
---------------------------------> image stream
endstream
endobj
xref               --> updated xref
0 1
0000000000 65535 f 
2 1
0000099954 00000 n 
5 1
0000100172 00000 n 
16 6
0000099901 00000 n 
0000100020 00000 n 
0000100297 00000 n 
0000102944 00000 n 
0000103096 00000 n 
0000103271 00000 n 
trailer             --> updated trailer
<<
/Root 2 0 R
/Info 1 0 R
/Size 22
/ID [ <982AAACB948CE1AD9FDD976D177BF316> <982AAACB948CE1AD9FDD976D177BF316> ] 
--> ID generated via windows API
/Prev 99491
>>
startxref
109605
%%EOF

bool SignMessageBySubjectName (BytePtr pMessage, ULong pMessageSize, StrPtr pSubjectName, CRYPT_DATA_BLOB * pSignBlob)
{
        HCERTSTORE               store_handle   = NULL;
        PCCERT_CONTEXT           cert_context   = NULL;
        BYTE *                   signed_blob    = NULL;
        ULong                    signed_blob_size;
        ULong                    message_size;
        CRYPT_SIGN_MESSAGE_PARA  signature_params;
        BYTE *                   message;

    pSignBlob->cbData = 0;
    pSignBlob->pbData = NULL;

    message = (BYTE *) pMessage;

    message_size = (pMessageSize + 1) * sizeof(Char); //Size in bytes

        const BYTE * message_array[] = {message};
        DWORD        message_array_size[1];

    message_array_size[0] = message_size;

    store_handle = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL,
                                 CERT_SYSTEM_STORE_CURRENT_USER, L"MY");

    cert_context = CertFindCertificateInStore( store_handle, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0,
                                               CERT_FIND_SUBJECT_STR, pSubjectName, NULL);

    signature_params.cbSize = sizeof(CRYPT_SIGN_MESSAGE_PARA);
    signature_params.dwMsgEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING;
    signature_params.pSigningCert = cert_context;
    signature_params.HashAlgorithm.pszObjId = szOID_RSA_SHA1RSA;
    signature_params.HashAlgorithm.Parameters.cbData = NULL;
    signature_params.cMsgCert = 1;
    signature_params.rgpMsgCert = &cert_context;
    signature_params.cAuthAttr = 0;
    signature_params.dwInnerContentType = 0;
    signature_params.cMsgCrl = 0;
    signature_params.cUnauthAttr = 0;
    signature_params.dwFlags = 0;
    signature_params.pvHashAuxInfo = NULL;
    signature_params.rgAuthAttr = NULL;

    //Get size of signed message
CryptSignMessage(&signature_params, TRUE, 1, message_array, message_array_size,NULL, &signed_blob_size);

signed_blob = (BYTE *) Malloc(signed_blob_size);


CryptSignMessage(&signature_params, TRUE, 1, message_array, message_array_size,  signed_blob, &signed_blob_size);

    pSignBlob->cbData = signed_blob_size;
    pSignBlob->pbData = signed_blob;

    CertFreeCertificateContext(cert_context);
    CertCloseStore(store_handle, CERT_CLOSE_STORE_FORCE_FLAG);

    return true;
}

当使用CryptSignMessage()并将分离的参数设置为TRUE时,我们得到一个大约850长度的符号令牌,我们将其转换为十六进制并添加到内容部分。大约1700个字符。

对于新添加的字段中使用的图像,我们生成了自己的图像,并将其添加为PDF obj。

对于拖车部分中的ID,我们使用Bcrypt中的API生成了相同的ID。lib(BCryptGenRandom()),将其输出转换为十六进制并更新ID部分。

列出我们所做的步骤:

>

  • 我们生成了2个缓冲区。对于所需的所有PDF对象,这两个缓冲区都是相同的,由BCryptGenRandom()和ByteRange数组生成的ID用实际值更新。buffer1的内容数据为0,具有一定长度,充当占位符。buffer2的内容数据为空(/contents

    buffer2将传递到CryptSignMessage()以生成签名令牌。这将转换为十六进制。

    十六进制符号令牌将添加到buffer1的内容部分,根据其长度替换0。

    buffer1将写入PDF文件。

    当我们做了所有这些,并在阅读器中打开PDF时,我们得到了如下错误

    • 签名无效
    • 自应用签名后,文档已损坏或更改

    PDF阅读器错误:

    详细错误:

    但是也有这些错误,读者能够识别用户、证书、哈希算法和使用的签名算法。

    我们认为我们需要以某种方式添加时间戳数据作为符号令牌的一部分,以避免此错误。或者其他我们会错过的东西。

    PFA示例PDF:https://drive.google.com/file/d/1Udog4AmGoq2ls3Tu3Wq5s2xU9LxaI3fH/view?usp=sharing

    请帮助我们解决这个问题。提前感谢。

  • 共有2个答案

    江渊
    2023-03-14

    好的,签名容器已正确嵌入

    但签名容器本身存在问题:

    >

    然后,签名字节的SHA1散列为BB78A402F7A537A34D6892B83881266501A691A8,但您签名的散列为90E28B8A0D8E48691DAFE2BA10A4761FFFDCCD3D。这可能是因为您散列了buffer2和

    buffer2的内容数据为空(/contents

    十六进制字符串分隔符

    此外,您的签名非常弱:

    • 它使用SHA1作为哈希算法。与此同时,SHA1被认为是文档签名的哈希算法太弱了。
    • 它不使用签名属性,无论是ESS签名证书还是算法标识符保护属性。许多验证策略需要这样的特殊属性。
    邴越彬
    2023-03-14

    我们使用了一组不同的API来完成这项工作。在这里粘贴代码:

    bool SignatureHandler::SignMessageTest (BytePtr pMessage, ULong pMessageSize, StrPtr pSubjectName, CRYPT_DATA_BLOB * pSignBlob, LPSTR pOid, DWORD pFlag, DWORD pType)
    {
            HCERTSTORE              store_handle  = NULL;
            PCCERT_CONTEXT          cert_context  = NULL;
            BYTE *                  signed_blob   = NULL;
            ULong                   signed_blob_size = 0;
            CRYPT_SIGN_MESSAGE_PARA signature_params;
            BYTE *                  message;
            BOOL                    rc;
    
            pSignBlob->cbData = 0;
            pSignBlob->pbData = NULL;
    
        store_handle = CertOpenStore (CERT_STORE_PROV_SYSTEM, 0, NULL, CERT_SYSTEM_STORE_CURRENT_USER, L"MY");
    
        cert_context = CertFindCertificateInStore (store_handle, (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING), 0, CERT_FIND_SUBJECT_STR, pSubjectName, NULL);
    
            HCRYPTPROV_OR_NCRYPT_KEY_HANDLE a         = 0;
            DWORD                           ks        = 0;
            BOOL                            bfr       = false;
    
            HCRYPTPROV_OR_NCRYPT_KEY_HANDLE PrivateKeys;
            CERT_BLOB                       CertsIncluded;
            CMSG_SIGNER_ENCODE_INFO         Signers;
    
            HCRYPTMSG                       hMsg;
    
        rc = CryptAcquireCertificatePrivateKey (cert_context, 0, 0, &a, &ks, &bfr);
    
            CMSG_SIGNER_ENCODE_INFO SignerEncodeInfo = {0};
    
        SignerEncodeInfo.cbSize = sizeof (CMSG_SIGNER_ENCODE_INFO);
    
        if (a)
            SignerEncodeInfo.hCryptProv = a;
    
        if (bfr)
            PrivateKeys = a;
    
        CERT_BLOB SignerCertBlob;
        
        SignerCertBlob.cbData = cert_context->cbCertEncoded;
        SignerCertBlob.pbData = cert_context->pbCertEncoded;
        CertsIncluded         = SignerCertBlob;
    
                        SignerEncodeInfo.cbSize    = sizeof (CMSG_SIGNER_ENCODE_INFO);
                        SignerEncodeInfo.pCertInfo = cert_context->pCertInfo;
                        SignerEncodeInfo.dwKeySpec = ks;
                        SignerEncodeInfo.HashAlgorithm.pszObjId          = pOid;
                        SignerEncodeInfo.HashAlgorithm.Parameters.cbData = NULL;
                        SignerEncodeInfo.pvHashAuxInfo = NULL;
            
                        Signers = SignerEncodeInfo;
            
                        CMSG_SIGNED_ENCODE_INFO SignedMsgEncodeInfo = {0};
            
                        SignedMsgEncodeInfo.cbSize        = sizeof (CMSG_SIGNED_ENCODE_INFO);
                        SignedMsgEncodeInfo.cSigners      = 1;
                        SignedMsgEncodeInfo.rgSigners     = &Signers;
                        SignedMsgEncodeInfo.cCertEncoded  = 1;
                        SignedMsgEncodeInfo.rgCertEncoded = &CertsIncluded;
                        SignedMsgEncodeInfo.rgCrlEncoded  = NULL;
            
                        signed_blob_size = 0;
            
                        signed_blob_size = CryptMsgCalculateEncodedLength ((PKCS_7_ASN_ENCODING | X509_ASN_ENCODING), pFlag, pType, &SignedMsgEncodeInfo, 0, pMessageSize);
            
                        if (signed_blob_size) {
            
                            signed_blob_size *= 2;
            
                            hMsg = CryptMsgOpenToEncode (CERTIFICATE_ENCODING_TYPE,
                                                         pFlag,                   
                                                         pType,                   
                                                         &SignedMsgEncodeInfo, 
                                                         0,                    
                                                         NULL);
            
                            if (hMsg) {
            
                                signed_blob = (BYTE *)malloc (signed_blob_size);
            
                                    BOOL CU = CryptMsgUpdate (hMsg, (BYTE *)pMessage, (DWORD)pMessageSize, true);
            
                                if (CU) {
            
                                    if (CryptMsgGetParam (
                                            hMsg,               // Handle to the message
                                            CMSG_CONTENT_PARAM, // Parameter type
                                            0,                  // Index
                                            signed_blob,  // Pointer to the BLOB
                                            &signed_blob_size))    // Size of the BLOB
                                    {
            
                                        signed_blob = (BYTE *)realloc (signed_blob, signed_blob_size);
            
                                        if (hMsg) {
            
                                            CryptMsgClose (hMsg);
                                            hMsg = 0;
                                        }
                                    }
                                }
                                if (hMsg)
                                    CryptMsgClose (hMsg);
                                hMsg = 0;
                            }
                        }
            
        CryptReleaseContext (a, 0);
    
        pSignBlob->cbData = signed_blob_size;
        pSignBlob->pbData = signed_blob;
    
        CertFreeCertificateContext (cert_context);
        CertCloseStore (store_handle, CERT_CLOSE_STORE_FORCE_FLAG);
    
        return true;
    }
    

    我们使用的oid、标志和类型分别是szOID_RSA_SHA1RSA、CMSG_DETACHED_标志和CMSG_SIGNED。关于转换pSignBlob-

     类似资料:
    • 我需要用数字签名对xml文档进行签名,并且需要用该文档生成数字签名xml的.sig文件。我也在使用PKCS7算法。我能够成功地将签名放在xml中。但无法生成.sig文件。我的代码如下: 现在我正在生成这样的. sig文件: 抛出长度错误的错误\r\n。数字签名后的 xml 是: 现在我知道我要么做错了,要么错过了什么。我的问题是 有没有办法生成带有签名 xml 的 .sig 文件? PKCS7 中

    • 例如,类的ViewBag属性,我们可以动态地获取/设置值,并向该对象添加任意数量的附加字段或属性,这很酷。我想在其他类型的应用程序中使用类似的东西,除了MVC应用程序和类之外。当我试图创建动态对象并设置它的属性时,如下所示: 我有,带有message属性或索引器'<>f__anonymoustype0.a'不能赋值--它在第2行中是只读的。我也认为这不是我想要的。也许有什么课可以让我做一些类似的事

    • 我正在用C#开发一个执行数字签名验证的webserver,以确保pdf文件没有被修改。我使用了iText和iTextSharp。 和我的C#验证码: 在VerifySignature(name)行中;抛出NullReferenceException! 有趣的是,如果我使用C#代码执行签名,我就可以在java中验证它,因为我添加了这些指令:BouncyCastleProvider provider=

    • 我喜欢使用enum类来尽可能避免硬编码值,但当涉及到字符串时,它会稍微复杂一点。我有一个设计,我想分享,我觉得是有点笨重,我会喜欢一些想法如何简化它。 欢迎提出任何意见。 编辑:我不使用字符串文字,因为它不会强制开发人员使用定义列表中的值。即使您希望它们使用字符串文字,它们仍然可以硬编码字符串。如果我在做一些疯狂的事情,我希望得到一些建设性的批评,而不是仅仅否决这个问题。

    • 我试图创建一个自定义类型的数组,就像C++中的对象一样。由于C++支持int、float等本机对象,因此创建它们的数组不是问题。 但当我创建一个类并创建一个数组时,它就不起作用了。 下面是我的代码: 没有编译错误,但当我运行程序时,它崩溃了。我不知道这里到底发生了什么。 请帮帮忙。