---
title: "Deferred two-phase PDF signing in C# | Nutrient .NET SDK"
canonical_url: "https://www.nutrient.io/guides/dotnet/signatures/digital-signatures/deferred-two-phase-pdf-signing/"
md_url: "https://www.nutrient.io/guides/dotnet/signatures/digital-signatures/deferred-two-phase-pdf-signing.md"
last_updated: "2026-09-22T00:00:00.000Z"
description: "Learn how to prepare a PDF for external signing and finish the signature using Nutrient .NET SDK."
---

# Deferred two-phase PDF signing in C#

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](https://www.nutrient.io/sdk/dotnet/getting-started.md) guide.

```csharp

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.

```csharp

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.

```csharp

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.

```csharp

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.
---

## Related pages

- [Add a digital signature to a PDF in C#](/guides/dotnet/signatures/digital-signatures/add-a-digital-signature.md)
- [Add a document timestamp to a PDF in C#](/guides/dotnet/signatures/digital-signatures/add-document-timestamp-to-pdf.md)
- [Add LTV information to PDF signatures in C#](/guides/dotnet/signatures/digital-signatures/add-ltv-information-to-pdf-signatures.md)
- [Sign PDF documents with a certificate in C#](/guides/dotnet/signatures/digital-signatures/certify-a-document.md)
- [Create PAdES B-LT and B-LTA signatures in C#](/guides/dotnet/signatures/digital-signatures/create-pades-b-lt-and-b-lta-signatures.md)
- [Create a self-signed certificate with PADES extensions](/guides/dotnet/signatures/digital-signatures/sign-a-docx-as-pdf-ua.md)
- [Create a self-signed certificate with PADES extensions](/guides/dotnet/signatures/digital-signatures/sign-a-pdf-ua-document.md)
- [Sign a PDF with an ECDSA certificate in C#](/guides/dotnet/signatures/digital-signatures/sign-pdf-with-ecdsa-certificate.md)
- [Sign a PDF with PKCS#11 in C#](/guides/dotnet/signatures/digital-signatures/sign-pdf-with-pkcs11.md)

