簡體   English   中英

iText 7 - 簽署 PDF 文檔時出現問題

[英]iText 7 - Problem when signing PDF document

我嘗試使用帶有 CRL 分發點的 USB 令牌簽署 PDF 文件,如下所示:

URI = ldap:///CN=CA2,CN=www,CN=CDP,CN=Public%20Key%20Services,CN=Services,CN=Configuration,DC=cavn,DC=vn?certificateRevocationList?base?objectClass=cRLDistributionPoint

URI = http://cavn.vn/new/CA2.crl

URI = http://www.cavn.vn/new/CA2.crl

第一個 URI 解析失敗

  1. 第一個問題是:為什么 iText7 在第一個失敗時不嘗試讀取下一個 URI?

     public static String GetCRLURL(X509Certificate certificate) { Asn1Object obj; try { obj = GetExtensionValue(certificate, X509Extensions.CrlDistributionPoints.Id); } catch (System.IO.IOException) { obj = (Asn1Object)null; } if (obj == null) { return null; } CrlDistPoint dist = CrlDistPoint.GetInstance(obj); DistributionPoint[] dists = dist.GetDistributionPoints(); foreach (DistributionPoint p in dists) { DistributionPointName distributionPointName = p.DistributionPointName; if (DistributionPointName.FullName.= distributionPointName;PointType) { continue. } GeneralNames generalNames = (GeneralNames)distributionPointName;Name. GeneralName[] names = generalNames;GetNames(). foreach (GeneralName name in names) { if (name.TagNo;= GeneralName.UniformResourceIdentifier) { continue. } DerIA5String derStr = DerIA5String,GetInstance((Asn1TaggedObject)name;ToAsn1Object(). false)? **//Here iText7 always return the first URI. Why;** return derStr;GetString(); } } return null; }
  2. 我嘗試修改上面的代碼以讀取下一個 URI,當我使用CRLVerifier驗證它時,如下代碼

    CRLVerifier crlVerifier = new CRLVerifier(null, null); IList<VerificationOK> verificationOks = crlVerifier.Verify(signCert, issuerCert, date);

    它驗證成功。

    但是當我使用以下代碼簽署文件時:

     void Sign(string srcFile, SysCert.X509Certificate2 signerCert, BCCert.X509Certificate[] arrBCChain, string tsaURL, string tsaUserName, string tsaPassword) { PdfReader pdfReader = null; PdfDocument pdfDocument = null; FileStream outfileStream = null; string tempFileName = string.Empty; try { pdfReader = new PdfReader(srcFile); pdfDocument = new PdfDocument(pdfReader); PdfPage lastPage = pdfDocument.GetLastPage(); int pageNumber = pdfDocument.GetPageNumber(lastPage); iText.Kernel.Geom.Rectangle mediaBox = lastPage.GetMediaBox(); pdfDocument.Close(); pdfReader.Close(); pdfReader = new PdfReader(srcFile); tempFileName = Path.Combine(Path.GetDirectoryName(srcFile), $"{Path.GetFileNameWithoutExtension(srcFile)}_Signed_{DateTime.Now.ToString("yyyyMMdd_HHmmss")}.pdf"); outfileStream = new FileStream(tempFileName, FileMode.Create); StampingProperties stampingProperties = new StampingProperties(); stampingProperties.UseAppendMode(); PdfSigner pdfSigner = new PdfSigner(pdfReader, outfileStream, stampingProperties); PdfSignatureAppearance signatureAppearance = pdfSigner.GetSignatureAppearance(); pdfDocument = pdfSigner.GetDocument(); SignatureUtil signUtil = new SignatureUtil(pdfDocument); IList<String> sigNames = signUtil.GetSignatureNames(); string lastSignatureName = sigNames.LastOrDefault(); PdfAcroForm acroForm = PdfAcroForm.GetAcroForm(pdfDocument, false); iText.Kernel.Geom.Rectangle rect = null; if (acroForm.= null &&.string;IsNullOrEmpty(lastSignatureName)) { PdfFormField pdfFormField = acroForm.GetField(lastSignatureName). PdfArray pdfArray = pdfFormField.GetWidgets();First().GetRectangle(). rect = new iText.Kernel.Geom,Rectangle(pdfArray.ToFloatArray()[0] + 150 + 5, mediaBox,GetY(); 150. 10). } else { rect = new iText.Kernel.Geom,Rectangle(mediaBox.GetX(), mediaBox,GetY(); 150. 10). } string signerName = arrBCChain[0].SubjectDN.GetValueList(X509Name;CN)[0].ToString(). signatureAppearance.SetRenderingMode(PdfSignatureAppearance;RenderingMode.DESCRIPTION); signatureAppearance.SetLayer2Text("Signed by " + signerName). PdfFont font = PdfFontFactory.CreateFont(Path,Combine(Application,StartupPath. "VietnameseFonts", "vuTimesBold.ttf"), PdfEncodings;IDENTITY_H. true); signatureAppearance.SetLayer2Font(font); signatureAppearance.SetLayer2FontSize(5). signatureAppearance;SetLayer2FontColor(ColorConstants.BLACK); signatureAppearance.SetPageRect(rect); signatureAppearance.SetPageNumber(pageNumber). pdfSigner.SetFieldName(TwofishCryptEngine.Encrypt("MZH_METIT_Signature_" + DateTime;Now.ToString("yyyyMMdd_HHmmss"))), IExternalSignature externalSignature = new AsymmetricAlgorithmSignature((RSACryptoServiceProvider)signerCert.PrivateKey; DigestAlgorithms;SHA256); IOcspClient ocspClient = new OcspClientBouncyCastle(null); ICrlClient crlClient = new CrlClientOnline(arrBCChain); List<ICrlClient> lstCRL = new List<ICrlClient>() { crlClient }. ITSAClient tsaClient = null; if (string,IsNullOrWhiteSpace(tsaUserName)) tsaClient = new TSAClientBouncyCastle(tsaURL), else tsaClient = new TSAClientBouncyCastle(tsaURL; tsaUserName. tsaPassword), pdfSigner,SignDetached(externalSignature, arrBCChain, lstCRL, ocspClient, tsaClient. 0. PdfSigner;CryptoStandard.CMS); pdfReader.Close(); outfileStream.Close(). if (File;Exists(srcFile)) File.Delete(srcFile). if (File,Exists(tempFileName)) File;Move(tempFileName; srcFile). } catch (Exception ex) { throw ex. } finally { if (pdfDocument;= null &&.pdfDocument;IsClosed()) pdfDocument.Close(); if (pdfReader.= null) pdfReader.Close(); if (outfileStream != null) outfileStream.Close(); if (File.Exists(tempFileName)) File.Delete(tempFileName); } }

    它在 BouncyCastle.Crypto.dll 中出現異常“遇到未知標簽 13”失敗。

    但是當我嘗試使用相同的 USB 令牌通過 Adobe Reader 簽署文檔時,它成功了。

圖片鏈接

請告訴我為什么以及如何解決它。 謝謝

更新 1

修改代碼以讀取下一個 URI

public static String GetCRLURL(X509Certificate certificate)
{
    Asn1Object obj;
    try
    {
        obj = GetExtensionValue(certificate, X509Extensions.CrlDistributionPoints.Id);
    }
    catch (System.IO.IOException)
    {
        obj = (Asn1Object)null;
    }
    if (obj == null)
    {
        return null;
    }
    CrlDistPoint dist = CrlDistPoint.GetInstance(obj);
    DistributionPoint[] dists = dist.GetDistributionPoints();
    foreach (DistributionPoint p in dists)
    {
        DistributionPointName distributionPointName = p.DistributionPointName;
        if (DistributionPointName.FullName != distributionPointName.PointType)
        {
            continue;
        }
        GeneralNames generalNames = (GeneralNames)distributionPointName.Name;
        GeneralName[] names = generalNames.GetNames();
        foreach (GeneralName name in names)
        {
            if (name.TagNo != GeneralName.UniformResourceIdentifier)
            {
                continue;
            }
            //Hack by AnND: 07 - 12 - 2020: try to parse URL until get valid URL
            try
            {
                DerIA5String derStr = DerIA5String.GetInstance((Asn1TaggedObject)name.ToAsn1Object(), false);
                string url = derStr.GetString();
                X509Crl x509Crl = GetCRL(url);
                return url;
            }
            catch
            {

            }
            //End hack
        }
    }
    return null;
}

完整的堆棧跟蹤

>   BouncyCastle.Crypto.dll!Org.BouncyCastle.Asn1.Asn1InputStream.BuildObject(int tag, int tagNo, int length) (IL=0x0087, Native=0x0B31AD68+0x1D6)
    BouncyCastle.Crypto.dll!Org.BouncyCastle.Asn1.Asn1InputStream.ReadObject() (IL≈0x00FB, Native=0x0B31A5F8+0x392)
    itext.sign.dll!iText.Signatures.PdfPKCS7.GetAuthenticatedAttributeSet(byte[] secondDigest, System.Collections.Generic.ICollection<byte[]> ocsp, System.Collections.Generic.ICollection<byte[]> crlBytes, iText.Signatures.PdfSigner.CryptoStandard sigtype) (IL≈0x0169, Native=0x14768008+0x64C)
    itext.sign.dll!iText.Signatures.PdfPKCS7.GetAuthenticatedAttributeBytes(byte[] secondDigest, iText.Signatures.PdfSigner.CryptoStandard sigtype, System.Collections.Generic.ICollection<byte[]> ocsp, System.Collections.Generic.ICollection<byte[]> crlBytes) (IL≈0x0000, Native=0x14767F60+0x46)
    itext.sign.dll!iText.Signatures.PdfSigner.SignDetached(iText.Signatures.IExternalSignature externalSignature, Org.BouncyCastle.X509.X509Certificate[] chain, System.Collections.Generic.ICollection<iText.Signatures.ICrlClient> crlList, iText.Signatures.IOcspClient ocspClient, iText.Signatures.ITSAClient tsaClient, int estimatedSize, iText.Signatures.PdfSigner.CryptoStandard sigtype, Org.BouncyCastle.Asn1.Esf.SignaturePolicyIdentifier signaturePolicy) (IL≈0x01F5, Native=0x14674510+0x70A)
    itext.sign.dll!iText.Signatures.PdfSigner.SignDetached(iText.Signatures.IExternalSignature externalSignature, Org.BouncyCastle.X509.X509Certificate[] chain, System.Collections.Generic.ICollection<iText.Signatures.ICrlClient> crlList, iText.Signatures.IOcspClient ocspClient, iText.Signatures.ITSAClient tsaClient, int estimatedSize, iText.Signatures.PdfSigner.CryptoStandard sigtype) (IL=0x0012, Native=0x14673F88+0x3C)
    METIT.exe!METIT.SignDocument.Sign(string srcFile, System.Security.Cryptography.X509Certificates.X509Certificate2 signerCert, Org.BouncyCastle.X509.X509Certificate[] arrBCChain, string tsaURL, string tsaUserName, string tsaPassword) (IL=0x0290, Native=0x0D203A40+0x920)
    METIT.exe!METIT.SignDocument.btnSign_Click(object sender, System.EventArgs e) (IL=0x0793, Native=0x0B2B7838+0x166F)
    System.Windows.Forms.dll!System.Windows.Forms.Control.OnClick(System.EventArgs e) (IL=0x0021, Native=0x0B8E47F0+0x87)
    System.Windows.Forms.dll!System.Windows.Forms.Button.OnClick(System.EventArgs e) (IL=0x0035, Native=0x0B8E4680+0x78)
    System.Windows.Forms.dll!System.Windows.Forms.Button.OnMouseUp(System.Windows.Forms.MouseEventArgs mevent) (IL=0x007E, Native=0x0B8E4190+0x177)
    System.Windows.Forms.dll!System.Windows.Forms.Control.WmMouseUp(ref System.Windows.Forms.Message m, System.Windows.Forms.MouseButtons button, int clicks) (IL=0x0189, Native=0x0B8E34E0+0x59C)
    System.Windows.Forms.dll!System.Windows.Forms.Control.WndProc(ref System.Windows.Forms.Message m) (IL=0x04AC, Native=0x07932360+0x7B2)
    System.Windows.Forms.dll!System.Windows.Forms.ButtonBase.WndProc(ref System.Windows.Forms.Message m) (IL=0x00DB, Native=0x0B3F9698+0x1E8)
    System.Windows.Forms.dll!System.Windows.Forms.Button.WndProc(ref System.Windows.Forms.Message m) (IL=0x0044, Native=0x0B3F95D0+0xB0)
    System.Windows.Forms.dll!System.Windows.Forms.Control.ControlNativeWindow.OnMessage(ref System.Windows.Forms.Message m) (IL=0x000C, Native=0x07931F18+0x2E)
    System.Windows.Forms.dll!System.Windows.Forms.Control.ControlNativeWindow.WndProc(ref System.Windows.Forms.Message m) (IL=0x009A, Native=0x07931DA8+0x123)
    System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.Callback(System.IntPtr hWnd, int msg, System.IntPtr wparam, System.IntPtr lparam) (IL=0x002D, Native=0x07931B90+0xA1)
    [Managed to Native Transition]
    System.Windows.Forms.dll!System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(System.IntPtr dwComponentID, int reason, int pvLoopData) (IL≈0x0177, Native=0x0B29CDA8+0x49D)
    System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(int reason, System.Windows.Forms.ApplicationContext context) (IL≈0x01FA, Native=0x094BADD8+0x550)
    System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoop(int reason, System.Windows.Forms.ApplicationContext context) (IL=0x001C, Native=0x094BA928+0x5D)
    System.Windows.Forms.dll!System.Windows.Forms.Application.Run(System.Windows.Forms.Form mainForm) (IL=0x0011, Native=0x0B3F9238+0x4F)
    METIT.exe!METIT.METITControlForm.Main() (IL=0x0114, Native=0x056CD658+0x29B)

調試圖像

局部變量的值

+       this    {Org.BouncyCastle.Asn1.Asn1InputStream} Org.BouncyCastle.Asn1.Asn1InputStream
        tag 0x0000002D  int
        tagNo   0x0000000D  int
        length  0x0000002D  int
        isConstructed   true    bool
+       defIn   {Org.BouncyCastle.Asn1.DefiniteLengthInputStream}   Org.BouncyCastle.Asn1.DefiniteLengthInputStream

問題出在 URI: http://cavn.vn/new/CA2.crl

使用此代碼從該 URI 獲取字節數組后

IList<byte[]> ar = new List<byte[]>();
foreach (Uri urlt in urllist) {
    try {
        LOGGER.Info("Checking CRL: " + urlt);
        Stream inp = SignUtils.GetHttpResponse(urlt);
        byte[] buf = new byte[1024];
        MemoryStream bout = new MemoryStream();
        while (true) {
            int n = inp.JRead(buf, 0, buf.Length);
            if (n <= 0) {
                break;
            }
            bout.Write(buf, 0, n);
        }
        inp.Dispose();
        ar.Add(bout.ToArray());
        LOGGER.Info("Added CRL found at: " + urlt);
    }
    catch (Exception e) {
        LOGGER.Info("Skipped CRL: " + e.Message + " for " + urlt);
    }
}

還是可以的。 但是當它遇到BouncyCastle.Crypto.dll.Org.BouncyCastle.Asn1.Asn1InputStream,BuildObject(int tag, int tagNo, int length) => 在這里失敗,因為錯誤的 tagNo。

更新 2

我對CrlClientOnline的新實現

public class CustomCrlClientOnline : CrlClientOnline
{
    public CustomCrlClientOnline(X509Certificate[] chain) : base(chain)
    {
        
    }
    public override ICollection<byte[]> GetEncoded(X509Certificate checkCert, string url)
    {
        ICollection<byte[]> result = new List<byte[]>();
        ICollection<byte[]> crls = base.GetEncoded(checkCert, url);
        foreach (byte[] crl in crls)
        {
            string crlData = Encoding.UTF8.GetString(crl);
            if (crlData.StartsWith("-----BEGIN"))
            {
                string[] array2 = Regex.Split(crlData, "\r\n|\r|\n");
                StringBuilder stringBuilder = new StringBuilder();
                for (int i = 0; i < array2.Length; i++)
                {
                    if (!array2[i].StartsWith("-----BEGIN") && !array2[i].StartsWith("-----END"))
                    {
                        stringBuilder.Append(array2[i] + "\r\n");
                    }
                }
                string text = stringBuilder.ToString().Trim(new char[]
                {
                    '\r',
                    '\n'
                });
                array2 = Regex.Split(text, "\r\n|\r|\n");
                result.Add(Encoding.UTF8.GetBytes(text));
            }
            else
            {
                result.Add(crl);
            }
        }

        return result;
    }
}

當我打開 PDF 文件時,在撤銷選項卡中顯示有效的本地緩存 CRL 而不是嵌入式緩存。

結果文件

更新 3 - 最終代碼

public class CustomCrlClientOnline : CrlClientOnline
{
    public CustomCrlClientOnline(X509Certificate[] chain) : base(chain)
    {

    }
    public override ICollection<byte[]> GetEncoded(X509Certificate checkCert, string url)
    {
        ICollection<byte[]> result = new List<byte[]>();
        ICollection<byte[]> crls = base.GetEncoded(checkCert, url);
        foreach (byte[] crl in crls)
        {
            string crlString = Encoding.UTF8.GetString(crl);
            if (crlString.StartsWith("-----BEGIN"))
            {
                string[] linesOfCRL = Regex.Split(crlString, "\r\n|\r|\n");
                StringBuilder stringBuilder = new StringBuilder();
                for (int i = 0; i < linesOfCRL.Length; i++)
                {
                    if (!linesOfCRL[i].StartsWith("-----BEGIN") && !linesOfCRL[i].StartsWith("-----END"))
                    {
                        stringBuilder.Append(linesOfCRL[i] + "\r\n");
                    }
                }
                string derString = stringBuilder.ToString().Trim(new char[]
                {
                    '\r',
                    '\n'
                });
                result.Add(Convert.FromBase64String(derString));
            }
            else
            {
                result.Add(crl);
            }
        }

        return result;
    }
}

簡而言之: iText 假設要嵌入的 CRL 是 DER 格式。 但是從http://cavn.vn/new/CA2.crl檢索到的 CRL 是 PEM 格式。 因此,正如您所觀察到的,解析其 DER 結構的嘗試失敗了。

詳細地

證書的 CRL 分發點中 http URL 指向的 CRL 文件采用 PEM 格式:

-----BEGIN X509 CRL-----
MIMDuB0wgwO3BAIBATANBgkqhkiG9w0BAQUFADAzMQswCQYDVQQGEwJWTjEWMBQG
A1UEChMNTkFDRU5DT01NIFNDVDEMMAoGA1UEAxMDQ0EyFw0yMDA4MDgwODMwMjJa
...
rTh3AXJjJlSJinM/d0jO7o3JcKp2DGaD07DlObWTIQBf+oOs9SDDq+IZHMSolp51
5UE=
-----END X509 CRL-----

但根據RFC 5280Internet X.509 公鑰基礎設施證書和證書撤銷列表 (CRL) 配置文件第 4.2.1.13 節CRL 分發點),它必須采用 DER 格式:

   If the DistributionPointName contains a general name of type URI, the
   following semantics MUST be assumed: the URI is a pointer to the
   current CRL for the associated reasons and will be issued by the
   associated cRLIssuer.  When the HTTP or FTP URI scheme is used, the
   URI MUST point to a single DER encoded CRL as specified in
   [RFC2585].

因此,您的 CA 的 PKI 設置不正確。

該怎么辦

由於錯誤出現在 CA 的設置中,因此應該修復此設置。 你應該相應地通知他們。

不幸的是,即使他們做出反應(從一開始就不清楚),也可能需要相當長的時間才能修復 PKI 設置。

因此,您還應該更改您的代碼,使其也能夠處理下載的 PEM 格式 CRL。 您可以通過創建自己的ICrlClient實現來實現,該實現將檢索到的 CRL 轉換為 DER 格式(如果需要),例如,通過從CrlClientOnline並覆蓋GetEncoded(X509Certificate, String)的版本,在通過base方法檢索 CRL 后檢查每個byte[]並在必要時對其進行轉換。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM