使用iText7结合外部信任服务给PDF加合格电子签章报错求助
问题排查与解决方案
针对你使用iText7结合外部签名服务添加合格电子签章时遇到的Unknown PDF exception,可以从以下几个核心方向排查修正:
1. 先捕获完整异常信息
Unknown PDF exception是泛化错误,必须获取详细堆栈和内部异常才能定位问题。修改签名代码,添加完整异常捕获:
try { pdfsigner.SignDetached(serverSignature, certificateWrappers, null, null, null, 0, PdfSigner.CryptoStandard.CMS); } catch (Exception ex) { Console.WriteLine($"错误详情:{ex.ToString()}"); // 写入日志或输出InnerException if (ex.InnerException != null) Console.WriteLine($"内部错误:{ex.InnerException.ToString()}"); throw; }
2. 验证IExternalSignature实现的正确性
这是最容易出错的环节,重点检查:
(1)算法名称匹配
确保GetDigestAlgorithm()和GetSignatureAlgorithm()返回iText7/BouncyCastle认可的标准名称,比如ECDSA-SHA256组合:
public string GetDigestAlgorithm() { return "SHA-256"; // 必须与签名服务使用的哈希算法一致 } public string GetSignatureAlgorithm() { return "ECDSA"; // 对应ECDSA签名算法 }
(2)签名格式转换
外部签名服务返回的ECDSA签名通常是ASN.1 DER编码格式,但iText7需要的是原始r+s拼接的字节数组(共64字节,r和s各32字节)。必须添加解析转换:
// 假设服务返回的是Base64编码的DER签名 byte[] derSignature = Convert.FromBase64String(responseData); Asn1Sequence seq = Asn1Sequence.GetInstance(derSignature); DerInteger r = DerInteger.GetInstance(seq[0]); DerInteger s = DerInteger.GetInstance(seq[1]); // 处理r和s的字节长度,确保是32字节 byte[] rBytes = r.Value.ToByteArrayUnsigned(); rBytes = rBytes.Length < 32 ? rBytes.PadLeft(32, (byte)0) : rBytes.Skip(rBytes.Length - 32).ToArray(); byte[] sBytes = s.Value.ToByteArrayUnsigned(); sBytes = sBytes.Length < 32 ? sBytes.PadLeft(32, (byte)0) : sBytes.Skip(sBytes.Length - 32).ToArray(); // 拼接r和s得到原始签名 byte[] rawSignature = new byte[64]; Buffer.BlockCopy(rBytes, 0, rawSignature, 0, 32); Buffer.BlockCopy(sBytes, 0, rawSignature, 32, 32); return rawSignature;
(3)待签数据传递
iText7的Sign(byte[] message)方法传入的是文档内容的哈希值,不是原始数据。确保你传给签名服务的是这个哈希值的Base64编码,而非其他内容。
3. 检查证书链的正确性
- 证书链顺序必须是:用户签名证书 → 中间CA证书 → 根CA证书,顺序错误会导致签名验证失败。
- 验证
X509CertificateBC的初始化逻辑,确保chain数组中的每个证书都是有效的X509证书对象。
4. 核对签名服务请求格式
确认传给aTrust服务的请求参数符合要求:
- 必须指定正确的哈希算法(如SHA256)和签名算法(如SHA256withECDSA)。
- 请求体的JSON格式、字段名称需严格匹配服务文档要求。
修正后的ServerSignature示例
public class ServerSignature : IExternalSignature { private readonly string _certAuth; private readonly string _privateKey; public ServerSignature(string certAuth, string privateKey) { _certAuth = certAuth; _privateKey = privateKey; } public string GetDigestAlgorithm() => "SHA-256"; public string GetSignatureAlgorithm() => "ECDSA"; public byte[] Sign(byte[] message) { // 将iText传入的哈希值转Base64 string hashBase64 = Convert.ToBase64String(message); // 构造符合服务要求的请求内容 string requestJson = $"{{\"hash\":\"{hashBase64}\",\"algorithm\":\"SHA256withECDSA\",\"cert\":\"{_certAuth}\"}}"; if (!PostRequest("你的签名服务地址", requestJson, out string responseData)) { throw new Exception("签名服务调用失败:" + responseData); } // 解析DER格式签名为原始r+s字节数组 try { byte[] derSignature = Convert.FromBase64String(responseData); Asn1Sequence seq = Asn1Sequence.GetInstance(derSignature); DerInteger r = DerInteger.GetInstance(seq[0]); DerInteger s = DerInteger.GetInstance(seq[1]); byte[] rBytes = ProcessSignaturePart(r.Value.ToByteArrayUnsigned()); byte[] sBytes = ProcessSignaturePart(s.Value.ToByteArrayUnsigned()); byte[] rawSignature = new byte[64]; Buffer.BlockCopy(rBytes, 0, rawSignature, 0, 32); Buffer.BlockCopy(sBytes, 0, rawSignature, 32, 32); return rawSignature; } catch (Exception ex) { throw new Exception("签名解析失败:" + ex.Message, ex); } } private byte[] ProcessSignaturePart(byte[] part) { if (part.Length == 32) return part; if (part.Length < 32) return part.PadLeft(32, (byte)0); return part.Skip(part.Length - 32).ToArray(); } // 你的PostRequest方法 public Boolean PostRequest(string postUrl, string content, out string responseData) { // 保留原有实现,注意添加详细异常日志 responseData = null; try { Uri url = new Uri(postUrl); HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url); webRequest.Method = "POST"; webRequest.ContentType = "application/json"; byte[] rawContent = System.Text.Encoding.UTF8.GetBytes(content); webRequest.ContentLength = rawContent.Length; using (var stream = webRequest.GetRequestStream()) { stream.Write(rawContent, 0, rawContent.Length); } using (HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse()) using (var reader = new StreamReader(webResponse.GetResponseStream(), System.Text.Encoding.UTF8)) { responseData = reader.ReadToEnd(); } return true; } catch (WebException wex) { if (wex.Status == WebExceptionStatus.ProtocolError && wex.Response != null) { using (HttpWebResponse webResponse = (HttpWebResponse)wex.Response) using (var reader = new StreamReader(webResponse.GetResponseStream(), System.Text.Encoding.UTF8)) { responseData = reader.ReadToEnd(); } } return false; } catch (Exception ex) { responseData = ex.Message; return false; } } }
内容的提问来源于stack exchange,提问作者user4609913
相关产品推荐
相关产品推荐

