iText7 (.net) SignExternalSignatureContainer NullReferenceException
iText7 (.net) SignExternalSignatureContainer NullReferenceException
我正在尝试创建一个可以对 PDF 文档进行数字签名的 C#
程序。为了将签名元素包含到 PDF 中,我使用 iText7
。
现在,如果我 运行 没有调试器的程序将被抛出 System.NullReferenceException
并且程序失败。但是如果我 运行 使用调试器的程序也会发生异常并且代码继续并正确签署 PDF...
我不确定这是 iText7
的问题还是我在创建签名字段时犯了错误或忘记了一些重要的事情。
有什么解决办法吗?
异常
System.NullReferenceException: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt.
bei iText.Signatures.PdfSignatureAppearance.GetAppearance() in C:\Development\Others\itext7\itext\itext.sign\itext\signatures\PdfSignatureAppearance.cs:Zeile 584.
bei iText.Signatures.PdfSigner.PreClose(IDictionary`2 exclusionSizes) in C:\Development\Others\itext7\itext\itext.sign\itext\signatures\PdfSigner.cs:Zeile 808.
bei iText.Signatures.PdfSigner.SignExternalContainer(IExternalSignatureContainer externalSignatureContainer, Int32 estimatedSize) in C:\Development\Others\itext7\itext\itext.sign\itext\signatures\PdfSigner.cs:Zeile 582.
bei SignService.Engine.Core.PdfEngine.AddSignature(SignTask task) in C:\Development\Signature\SignService.Engine\Core\PdfEngine.cs:Zeile 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 确实不需要用于实际签名过程的证书,但它确实需要它来检索有关签名者的信息以用作名称,并在可见签名的情况下用于描述带有名称 and/or 描述(即任何渲染模式,但纯 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 不会尝试从证书中检索任何签名者信息。
我正在尝试创建一个可以对 PDF 文档进行数字签名的 C#
程序。为了将签名元素包含到 PDF 中,我使用 iText7
。
现在,如果我 运行 没有调试器的程序将被抛出 System.NullReferenceException
并且程序失败。但是如果我 运行 使用调试器的程序也会发生异常并且代码继续并正确签署 PDF...
我不确定这是 iText7
的问题还是我在创建签名字段时犯了错误或忘记了一些重要的事情。
有什么解决办法吗?
异常
System.NullReferenceException: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt. bei iText.Signatures.PdfSignatureAppearance.GetAppearance() in C:\Development\Others\itext7\itext\itext.sign\itext\signatures\PdfSignatureAppearance.cs:Zeile 584. bei iText.Signatures.PdfSigner.PreClose(IDictionary`2 exclusionSizes) in C:\Development\Others\itext7\itext\itext.sign\itext\signatures\PdfSigner.cs:Zeile 808. bei iText.Signatures.PdfSigner.SignExternalContainer(IExternalSignatureContainer externalSignatureContainer, Int32 estimatedSize) in C:\Development\Others\itext7\itext\itext.sign\itext\signatures\PdfSigner.cs:Zeile 582. bei SignService.Engine.Core.PdfEngine.AddSignature(SignTask task) in C:\Development\Signature\SignService.Engine\Core\PdfEngine.cs:Zeile 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 确实不需要用于实际签名过程的证书,但它确实需要它来检索有关签名者的信息以用作名称,并在可见签名的情况下用于描述带有名称 and/or 描述(即任何渲染模式,但纯 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 不会尝试从证书中检索任何签名者信息。