Warm tip: This article is reproduced from serverfault.com, please click

c#-iText 7

(c# - iText 7)

发布于 2020-12-10 09:50:37

我尝试通过USB令牌使用CRL分发点对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;
    }
}
Questioner
duyan
Viewed
0
mkl 2020-12-11 17:01:39

简而言之: iText假定要嵌入的CRL为DER格式。但是从中检索的CRLhttp://cavn.vn/new/CA2.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的设置中,因此应固定此设置。你应该相应地通知他们。

不幸的是,即使他们做出 React (开始时尚不清楚),也可能需要花费相当长的时间才能解决PKI设置问题。

因此,你还应该更改代码以也能够处理下载的PEM格式CRL。你可以通过创建自己的ICrlClient实现(如果需要的话)将检索到的CRL转换为DER格式来实现,例如,通过派生CrlClientOnline并覆盖GetEncoded(X509Certificate, String)一个版本,该版本在通过base方法检索CRL之后将对其进行检查byte[]并在必要时进行转换。