【发布时间】:2021-03-21 17:38:47
【问题描述】:
我尝试通过 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 解析失败
-
第一个问题是:为什么 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; } -
我尝试修改上面的代码以读取下一个 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”失败。
但是当我尝试通过 Adobe Reader 使用相同的 USB 令牌对文档进行签名时,它成功了。
请告诉我为什么以及如何解决它。谢谢
更新 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
我对@987654337@ 的新实现
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;
}
}
【问题讨论】:
-
“第一个问题是:为什么 iText7 在第一个失败时不尝试读取下一个 URI?” - 很难猜出为什么。测试所有提供的 URL 显然是有意义的。
-
“在 BouncyCastle.Crypto.dll 中出现异常“遇到未知标签 13”失败。” - 由于我们没有您的 U 盘,我们几乎无法重现问题。因此,请确保您提供所有可以提供的信息。特别是完整的堆栈跟踪和 BC 试图在那里解析的确切数据。
-
我已经为您的调试更新了更多信息。请帮我找出原因。非常感谢。
-
好的,根据您原始问题中的信息,我应该已经看到了问题,但是额外的信息让我眼前一亮。请参阅下面的答案。