This HTML page is not optimized for LLM or AI agent consumption. Fetch the Markdown version instead: /guides/dotnet/signatures/digital-signatures/deferred-two-phase-pdf-signing.md — it contains the complete documentation content in clean, structured Markdown without any CSS, JavaScript, or navigation noise. Deferred two-phase PDF signing in C# | Nutrient .NET SDK

Deferred signing separates PDF preparation from the private-key operation. The application prepares the PDF and sends the bytes to sign to an external service, then embeds the returned signature in a second phase.

Use this workflow with remote signing services, approval workflows, or key systems that don’t expose private keys to the PDF application.

This guide shows how to:

  • Prepare a PDF with a signature placeholder
  • Sign the returned bytes outside the PDF writer
  • Finish the PDF signature with the external signature value

Prepare the project

Register the SDK license before running signing operations. For setup details, refer to the getting started with .NET SDK guide.

using System;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using GdPicture14;
void CheckStatus(GdPictureStatus status, string operation)
{
if (status != GdPictureStatus.OK)
{
throw new InvalidOperationException($"{operation} failed. Status: {status}");
}
}
LicenseManager license = new LicenseManager();
license.RegisterKEY(""); // Set your license key

Prepare the PDF for external signing

Load the source PDF, configure the signing certificate, and create the prepared PDF. The prepare phase returns the exact bytes the external signer signs.

using GdPicturePDF pdf = new GdPicturePDF();
CheckStatus(pdf.LoadFromFile(@"input.pdf", false), "LoadFromFile");
CheckStatus(pdf.SetSignatureCertificateFromP12(@"certificate.pfx", "Nutrient answers all your document needs"), "SetSignatureCertificateFromP12");
CheckStatus(pdf.SetSignatureInfo("Nutrient", "Deferred signing sample", "Toulouse", "sales@nutrient.io"), "SetSignatureInfo");
CheckStatus(pdf.SetSignatureHash(PdfSignatureHash.SHA256), "SetSignatureHash");
byte[] dataToSign;
CheckStatus(pdf.ApplySignaturePrepare(@"prepared.pdf", out dataToSign), "ApplySignaturePrepare");

ApplySignaturePrepare writes prepared.pdf with a placeholder signature and final byte range. It also returns dataToSign, which you send to your external signing service.

Create the external signature value

This sample signs locally to demonstrate the shape of the external signing step. In production, replace this block with a call to your signing service.

X509Certificate2 certificate = X509CertificateLoader.LoadPkcs12FromFile(
@"certificate.pfx",
"Nutrient answers all your document needs",
X509KeyStorageFlags.Exportable);
using RSA rsa = certificate.GetRSAPrivateKey() ?? throw new InvalidOperationException("The certificate doesn't contain an RSA private key.");
byte[] signedData = rsa.SignData(dataToSign, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);

The raw signature value has to match the certificate and hash algorithm used during the prepare phase.

Finish the signature

Reload the prepared PDF, configure the same signing certificate and hash algorithm, and embed the external signature value.

using GdPicturePDF preparedPdf = new GdPicturePDF();
CheckStatus(preparedPdf.LoadFromFile(@"prepared.pdf", false), "LoadFromFile");
CheckStatus(preparedPdf.SetSignatureCertificateFromP12(@"certificate.pfx", "Nutrient answers all your document needs"), "SetSignatureCertificateFromP12");
CheckStatus(preparedPdf.SetSignatureHash(PdfSignatureHash.SHA256), "SetSignatureHash");
CheckStatus(preparedPdf.ApplySignatureFinish(@"signed-deferred.pdf", signedData), "ApplySignatureFinish");

Use ApplySignatureFinishWithSignedCms instead when the external service returns a complete CMS signature container.

Error handling

The finish phase returns Aborted when the PDF doesn’t contain a prepared signature. It also fails if the signed data doesn’t match the prepared bytes, certificate, and hash algorithm.

Conclusion

This workflow prepares a PDF for external signing and completes it after an external signer returns the signature value.