获取PDF内容以供签名,然后在以后签名

问题描述 投票:0回答:1

我关注以下帖子:iTextSharp - How to get PDF content for signing and then sign at a later time作为从PDF文档获取数据的基础,创建带有空白签名的临时文件,然后返回该临时文件的哈希值。哈希将发送到具有令牌的客户端,该令牌在本地进行签名并返回到已签名的Web应用程序。到那时,我将其转换为字节并生成最终的签名文档。

但是,已签名的文档没有有效的签名,显示消息:解码BER时出错。

1)生成临时文件,添加空白签名并返回Base64编码哈希的代码:

Public Shared Function GetBytesToSign(ByVal unsignedPdf As String, ByVal tempPdf As String, ByVal signatureFieldName As String) As String
    Using reader As PdfReader = New PdfReader(unsignedPdf)
        Using os As FileStream = File.OpenWrite(tempPdf)
            Dim stamper As PdfStamper = PdfStamper.CreateSignature(reader, os, vbNullChar)
            Dim appearance As PdfSignatureAppearance = stamper.SignatureAppearance
            appearance.SetVisibleSignature(New Rectangle(36, 748, 144, 780), 1, signatureFieldName)
            Dim external As IExternalSignatureContainer = New ExternalBlankSignatureContainer(PdfName.ADBE_PKCS7_DETACHED, PdfName.ADBE_PKCS7_SHA1)
            MakeSignature.SignExternalContainer(appearance, external, 8192)
            Dim Hash As Byte() = SHA256.Create().ComputeHash(appearance.GetRangeStream())
            Dim Base64 As String = Convert.ToBase64String(Hash)
            Return Base64
        End Using
    End Using
End Function

2)客户获得本地的Hash na suamáquina(证书)和密码哈希assinado

3)我收到签名的哈希并将其转换为字节,并生成最终的签名文件:

Public Shared Sub EmbedSignature(ByVal tempPdf As String, ByVal signedPdf As String, ByVal signatureFieldName As String, ByVal signedBytes As Byte())
    Using reader As PdfReader = New PdfReader(tempPdf)
        Using os As FileStream = File.Create(signedPdf)
            Dim external As IExternalSignatureContainer = New MyExternalSignatureContainer(signedBytes)
            MakeSignature.SignDeferred(reader, signatureFieldName, os, external)
        End Using
    End Using
End Sub

Partial Class MyExternalSignatureContainer
    Implements IExternalSignatureContainer

    Private ReadOnly signedBytes As Byte()

    Public Sub New(ByVal signedBytes As Byte())
        Me.signedBytes = signedBytes
    End Sub

    Public Function Sign(data As Stream) As Byte() Implements IExternalSignatureContainer.Sign
        Return signedBytes
        'Throw New NotImplementedException()
    End Function

    Public Sub ModifySigningDictionary(signDic As PdfDictionary) Implements IExternalSignatureContainer.ModifySigningDictionary
        Throw New NotImplementedException()
    End Sub
End Class

SignedPDF


mkl,感谢您的回复

我正在做几次测试,因为我真的不知道在这种情况下使用什么正确的方法,因为材料非常宽而且也很旧。所以我想我走得更远,因为现在显示了证书数据,但显示了:无效签名。应用签名后,文档被更改或损坏。

新代码:

  Public Function GetBytesToSignNew(ByVal unsignedPdf As String, ByVal tempPdf As String, ByVal signatureFieldName As String, ByVal certificateChain As Org.BouncyCastle.X509.X509Certificate()) As Byte()
    ' we create a reader and a stamper
    Dim reader As PdfReader = New PdfReader(unsignedPdf)
    Dim baos As FileStream = File.OpenWrite(tempPdf)
    Dim chain = certificateChain
    Dim pdfStamper As PdfStamper = PdfStamper.CreateSignature(reader, baos, Microsoft.VisualBasic.ChrW(92), Nothing, True)
    Dim sap As PdfSignatureAppearance = pdfStamper.SignatureAppearance
    sap.Certificate = certificateChain(0)
    sap.SetVisibleSignature(New iTextSharp.text.Rectangle(36, 720, 160, 780), 1, signatureFieldName)
    'sap.SetVisibleSignature(signatureFieldName);
    sap.SignDate = DateTime.Now
    Dim dic As PdfSignature = New PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED)
    dic.Date = New PdfDate(sap.SignDate)
    dic.Name = CertificateInfo.GetSubjectFields(chain(0)).GetField("CN")
    sap.CryptoDictionary = dic
    sap.Certificate = certificateChain(0)
    sap.Acro6Layers = True
    sap.Reason = "test"
    sap.Location = "test"
    Dim external As IExternalSignatureContainer = New ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED)
    MakeSignature.SignExternalContainer(sap, external, 8192)
    Dim signatureContainer As PdfPKCS7 = New PdfPKCS7(Nothing, chain, "SHA256", False)
    Dim hash() As Byte = DigestAlgorithms.Digest(sap.GetRangeStream, "SHA256")

    PDFHash.Text = Convert.ToBase64String(hash)

    Session("signatureContainer") = signatureContainer

    Return hash

End Function

 Private Sub Sign21032020(signatureContainer As PdfPKCS7, ByVal certificateChain As Org.BouncyCastle.X509.X509Certificate(), signedBytes As Byte(), tmpPdf As String, signedPdf As String, signatureFieldName As String) As Byte

    Try
        Using reader As PdfReader = New PdfReader(tmpPdf)
            Using outputStream As FileStream = File.OpenWrite(signedPdf)
                Dim external As IExternalSignatureContainer = New MyExternalSignatureContainer(signedBytes, certificateChain, signatureContainer)
                MakeSignature.SignDeferred(reader, signatureFieldName, outputStream, external)
            End Using
        End Using

        ' Return New Byte()

    Catch ex As Exception
        File.Delete(tmpPdf)
        Console.WriteLine("Error signing file: " + ex.Message)
    End Try

End Sub

生成的Code64哈希到客户端签名:

FB / lxYEGgc4xBSM + JlDx02sw / xtHF8jMT13tS9ZLf3A =

从客户端返回的签名哈希:

DU7koJKqS8 / 7 + O00Te7F / IyZCZrIWlTxaOwAAdJWK2SsZyNAr1fxb4AnoImlQe3xKR680egdG5orJgD4iiU4GdcM0LgIrTO / + + yFwz rAlL6PUsW8ZKi3UkTcnGxGAi3uudGghMv / KnFxknNOVD5JuAvSsL3h6cLjqv8 / knb2vfcpm7r5K4ZiyxM7LtvkJ98OwS7D7sErVp6FJTCmijftq6iveF6v5MjPfrzzx43ETKoU1iGrYNiwvR4dgem9gKYibAoQqgpI + Xb6hvDMv0loFYfYhMDgzmIDgSN171ZIdt8FzjIU3vWt5coyhPYYmI23CAX75dED5zQrXZ + IaPD4CQ ==

TempPDF:

TempPDF

SignedPDF:

SignedPDF

我在客户端使用此插件签名哈希:

web-pki

再次感谢,抱歉。我用Google翻译。

罗伯托·皮雷斯

pdf itext
1个回答
0
投票

您的原始代码

这里的主要问题是,您只是简单地获取了客户端返回的签名字节,并将其嵌入到PDF中,而无需多加费力。

但是,由于您在其子过滤器中的签名声称是adbe.pkcs7.sha1签名,因此您需要嵌入一个PKCS#7 / CMS签名容器,而这不是您的客户端返回的。

您的更新代码

这里切换到在签名子过滤器中声明adbe.pkcs7.detached”。这也需要您嵌入PKCS#7 / CMS签名容器,并且最好使用adbe.pkcs7.sha1,该方法利用SHA1(一种摘要算法,在以前的环境中不够安全) PDF甚至被证明是不安全的。

此外,您现在确实嵌入了PKCS#7 / CMS签名容器。因此,Adobe Reader现在可以解码嵌入的签名(不再[解码BER时出错)。

但是您错误地构建了该签名容器:

  • 您将签名文档范围的哈希值发送给客户进行签名。这是不正确的。除非您创建非常原始的CMS容器,否则客户端必须在容器中对所谓的“签名属性”集合进行签名,并且签名字节的哈希仅仅是这些属性之一。
  • 您将客户端返回的签名既作为签名又作为文档哈希嵌入到PKCS#7容器中。

因此,如果使用iText PdfPKCS7类创建CMS容器,则必须:

  • 使用此PdfPKCS7方法将带符号的属性检索为字节数组:

    virtual public byte[] getAuthenticatedAttributeBytes(byte[] secondDigest, byte[] ocsp, ICollection<byte[]> crlBytes, CryptoStandard sigtype)
    

    ((secondDigest参数必须包含PDF范围流的哈希。)

  • 消化该字节数组,

  • 让您的客户在此哈希上签名,
  • 使用此方法在PdfPKCS7对象中设置客户端的签名字节:

    virtual public void SetExternalDigest(byte[] digest, byte[] RSAdata, String digestEncryptionAlgorithm)
    

    ((digest包含客户端的签名字节,RSAdata参数必须为null,并且digestEncryptionAlgorithm必须为“ RSA”,“ DSA”或“ ECDSA”。

  • ]
  • 使用此PdfPKCS7对象的方法完成CMS签名容器:

    virtual public byte[] GetEncodedPKCS7(byte[] secondDigest, ITSAClient tsaClient, byte[] ocsp, ICollection<byte[]> crlBytes, CryptoStandard sigtype)
    

    (这里secondDigestocspcrlBytessigtype的值必须与getAuthenticatedAttributeBytes调用中的值相同。)

  • 最后使用适当的MakeSignature.SignDeferred实现,使用IExternalSignatureContainer将这些字节嵌入到PDF中。
© www.soinside.com 2019 - 2024. All rights reserved.