我正在尝试创建一个C#
可以对PDF文档进行数字签名的程序。为了将签名元素包含到PDF中,我正在使用iText7
。现在,如果我在没有调试器的情况下运行该程序,System.NullReferenceException
将会抛出一个错误,并且程序将失败。但是如果我使用调试器运行程序,也会发生异常,并且代码继续并正确签名PDF ...
我不确定这是一个问题iText7
还是在创建签名字段时犯了一个错误或忘记了一些重要的事情。
任何想法如何解决这个问题?
例外
System.NullReferenceException:未在对象实例上设置对象引用。适用于C中的iText.Signatures.PdfSignatureAppearance.GetAppearance():\ Development \ Others \ itext7 \ itext \ itext.sign \ itext \ signatures \ PdfSignatureAppearance.cs:第584行。 )在C:\ Development \ Others中的C:\ Development \ Others \ itext7 \ itext \ itext.sign \ itext \ signatures \ PdfSigner.cs:第808行。 \ itext7 \ itext \ itext.sign \ itext \签名\ PdfSigner.cs:582行,位于C:\ Development \ Signature \ SignService.Engine \ Core \ PdfEngine中的SignService.Engine.Core.PdfEngine.AddSignature(SignTask任务)处。 cs:第122行。
我的密码
/// <summary>
/// Add SignatureField to Pdf
/// and digitally sign it
/// </summary>
public SignatureResult AddSignature(SignTask task)
{
_logger.Info("Start Signing PDF");
var prop = task.SignatureProperties;
try
{
var reader = new PdfReader(new MemoryStream(prop.Document));
var stream = new ByteArrayOutputStream();
var signer = new PdfSigner(reader, stream, new StampingProperties().UseAppendMode());
// set appearance
var appearance = signer.GetSignatureAppearance();
appearance.SetReason(prop.SignReason)
.SetLocation(prop.SignLocation)
.SetContact(prop.SignContact);
// set rendering mode
switch (_settings.Pdf.RenderingMode)
{
case SignatureRenderingMode.Description:
appearance.SetRenderingMode(PdfSignatureAppearance.RenderingMode.DESCRIPTION);
break;
case SignatureRenderingMode.Graphic:
appearance.SetRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC);
break;
case SignatureRenderingMode.GraphicAndDescription:
appearance.SetRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC_AND_DESCRIPTION);
break;
case SignatureRenderingMode.NameAndDescription:
appearance.SetRenderingMode(PdfSignatureAppearance.RenderingMode.NAME_AND_DESCRIPTION);
break;
}
// set image
if (!string.IsNullOrEmpty(_settings.Pdf.SignatureGraphicPath))
{
if (File.Exists(_settings.Pdf.SignatureGraphicPath))
{
var imageData = ImageDataFactory.Create(_settings.Pdf.SignatureGraphicPath);
appearance.SetSignatureGraphic(imageData);
}
else if (_settings.Pdf.RenderingMode == SignatureRenderingMode.GraphicAndDescription || _settings.Pdf.RenderingMode == SignatureRenderingMode.GraphicAndDescription)
{
throw new Exception("Failed to create Signature Field.\nIf rendering mode is graphic or graphic and description, a signature image must be provided");
}
}
// set visibility
if (prop.Visible)
{
var rect = new Rectangle(prop.X, prop.Y, prop.Width, prop.Height);
appearance.SetPageRect(rect);
appearance.SetPageNumber(1); // todo create setting for this
}
signer.SetFieldName(_settings.Pdf.SignatureFieldName);
// sign field
var signatureContainer = new ExternalSignatureContainer(task, _settings.Ais);
// **Exeption thrown here**
signer.SignExternalContainer(signatureContainer, GetEstimatedSize(task.TimestampOnly));
var tempResult = stream.ToArray();
// set revocation info if active
if (tempResult.Length > 0 && _settings.Ais.AddRevocationInfo)
{
tempResult = AddRevocationInfo(signatureContainer.Crl, signatureContainer.Ocsp, tempResult);
}
reader.Close();
stream.Close();
_logger.Info("Finished signing");
// return sign result
return tempResult.Length > 0
? new SignatureResult
{
Message = "",
Status = RequestStatus.Success,
Document = stream.ToArray(),
Id = prop.Id
}
: new SignatureResult
{
Message = "Failed to sign the Document",
Status = RequestStatus.Failed,
Document = null,
Id = prop.Id
};
}
catch (AisServiceException aisServiceException)
{
_logger.Error($"While requesting Signature an error occured: {aisServiceException.Message}", aisServiceException);
throw;
}
catch (Exception exception)
{
_logger.Error($"While creating signed pdf an error occured: {exception.Message}", exception);
throw new PdfException($"While creating Signed Pdf an error Occured: {exception.Message}");
}
}
设定:
{
"Pdf": {
"Visible": true,
"Position": {
"X": 50,
"Y": 50,
"Height": 100,
"Width": 200
},
"SignatureFieldName": "SignatureField",
"SignatureGraphicPath": "",
"RenderingMode": 1
}
}
任务:
"SignatureProperties": {
"Id": "1",
"Document": [DocumentAsByteArray],
"Visible": true,
"SignReason": "Test",
"SignLocation": "Test",
"SignContact": "Test",
"Height": 100,
"Width": 200,
"X": 50,
"Y": 50
}
在您的代码中,您不会使用设置签名者证书PdfSignatureAppearance.SetCertificate
。虽然在SignExternalContainer
像您这样的用例的情况下,iText确实不需要证书来进行实际的签名过程,但在使用名称和/或可见签名的情况下,确实需要它来检索有关签名者的信息以用作名称以及描述中的信息。描述(即除纯模式之外的任何渲染模式GRAPHICS
)。
您的设置"RenderingMode": 1
和堆栈跟踪PdfSignatureAppearance.cs:Zeile 584
指示您处于NAME_AND_DESCRIPTION
用例中。因此,iText尝试构建描述,访问描述signCertificate
并由于该成员为而失败null
。
要解决此问题,请同时使用设置签名者证书PdfSignatureAppearance.SetCertificate
。
在和的情况下DESCRIPTION
,GRAPHIC_AND_DESCRIPTION
您也可以使用将说明设置为预制值PdfSignatureAppearance.SetLayer2Text
。
如果是NAME_AND_DESCRIPTION
,iText将无法注入预先计算的名称。
一个更通用的替代方法是全部由您自己创建签名外观,只需用于GetLayer2
检索即可PdfFormXObject
在其上绘制您喜欢的任何可视化效果。在这种情况下,iText不会尝试从证书中检索任何签名者信息。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句