Multiple Signatures
When a discloser and a vendor both sign an agreement, each signature must be recognizable and verifiable on its own. Signature fields make that possible: every named field gets its own certificate, its own timestamp and its own entry in the signature panel of a PDF reader. With TX Text Control, all of these signatures are applied in one save, at the end of the process, after every party has put their signature into the document.
The API of signature fields, their images and their signer data are described in Signature Fields and Signature Appearance. The examples below use an agreement with three named signature fields, sign_discloser, sign_vendor and init_vendor, inserted as Signature Fields in Document Editor describes.
How Several Signatures Are Written
- One
DigitalSignatureper field. SaveSettings.SignatureFields takes an array of DigitalSignature objects. Each one names a field and has its own certificate and its own time server URL, so fields can be signed with different certificates, and only some of them with a trusted timestamp. - One save writes them all. The certificates are applied while the PDF is created. Signatures are created only when TX Text Control saves a document as PDF; a PDF that it loads is converted into an editable document, as PDF Import and Its Limitations explains. So a document collects the signature images of its signers in an editable format and becomes a signed PDF in the last step.
- Each signature is listed on its own. PDF readers show every signature with the name of its field and its own validation status.
- Signature fields, not a whole-document signature.
SaveSettings.DigitalSignaturesigns the document once, with one certificate. For several signatures, use signature fields.
Sign Several Fields in One Save
The following handler signs the three fields of the agreement in the editor with two certificates: the discloser's field with a trusted timestamp, and the vendor's signature and initials with the date and time of the computer. Form1.cs needs using System.Security.Cryptography.X509Certificates; and using TXTextControl;.
private void signAgreementButton_Click(object sender, EventArgs e)
{
// The discloser's and the vendor's certificates
X509Certificate2 discloserCertificate = X509CertificateLoader.LoadPkcs12FromFile(
"certificate.pfx", "123", X509KeyStorageFlags.Exportable);
X509Certificate2 vendorCertificate = X509CertificateLoader.LoadPkcs12FromFile(
"vendor.pfx", "123", X509KeyStorageFlags.Exportable);
string[] names = { "sign_discloser", "sign_vendor", "init_vendor" };
foreach (string name in names)
{
SignatureField field = textControl1.SignatureFields.GetItem(name);
if (field == null)
{
MessageBox.Show(this, $"The document has no signature field named {name}.");
return;
}
// The image shown in the field (filter index 4 is PNG)
field.Image = new SignatureImage($"{name}.png", 4);
}
SaveSettings saveSettings = new SaveSettings()
{
SignatureFields = new DigitalSignature[]
{
// With a trusted timestamp
new DigitalSignature(discloserCertificate, "http://timestamp.digicert.com", "sign_discloser"),
// With the date and time of this computer
new DigitalSignature(vendorCertificate, "", "sign_vendor"),
new DigitalSignature(vendorCertificate, "", "init_vendor")
}
};
textControl1.Save("nda-signed.pdf", StreamType.AdobePDFA, saveSettings);
}
The code works as follows:
- vendor.pfx is a second test certificate, created like certificate.pfx in Create a Self-Signed Certificate for Testing. Every certificate needs its private key on the computer that signs.
- SignatureFieldCollection.GetItem makes sure the document contains each field before it's signed, and SignatureField.Image sets its image, here from PNG files named after the fields.
- The third parameter of each
DigitalSignaturenames its field, and the second is the URL of the time server or an empty string. - One
Savecall writes the PDF/A document with all three signatures.
X509CertificateLoader requires .NET 9 or higher; Load the Certificate From a PFX File shows the alternative for .NET 8 and the .NET Framework.
Collect Signatures From Several People
In many processes, the parties sign one after the other, each on their own computer with the same Windows Forms application, and the organization certifies the document at the end:
Prepare the document with one named field per signer, including the signer data, and save it in the TX Text Control format, for example as nda.tx.
Each signer opens the document, adds the image of their signature to their field, as Add a Signature Image shows, and saves the document again in the TX Text Control format:
private void saveForNextSignerButton_Click(object sender, EventArgs e) { // Keep the signatures of this signer in the TX Text Control format textControl1.Save("nda.tx", StreamType.InternalUnicodeFormat); }The last step certifies the document: it checks that every field has an image and signs all fields in one save.
The following handler implements the last step. It signs every named field with the organization's certificate and a trusted timestamp, and refuses to create the PDF while a field is still empty:
private void certifyButton_Click(object sender, EventArgs e)
{
X509Certificate2 certificate = X509CertificateLoader.LoadPkcs12FromFile(
"certificate.pfx", "123", X509KeyStorageFlags.Exportable);
List<string> missing = new List<string>();
List<DigitalSignature> signatures = new List<DigitalSignature>();
foreach (SignatureField field in textControl1.SignatureFields)
{
if (string.IsNullOrEmpty(field.Name))
{
continue;
}
// A field without an image hasn't been signed by its signer yet
if (field.Image == null)
{
missing.Add(field.Name);
continue;
}
signatures.Add(new DigitalSignature(certificate, "http://timestamp.digicert.com", field.Name));
}
if (missing.Count > 0)
{
MessageBox.Show(this, "Not signed yet: " + string.Join(", ", missing));
return;
}
SaveSettings saveSettings = new SaveSettings()
{
SignatureFields = signatures.ToArray()
};
textControl1.Save("nda-signed.pdf", StreamType.AdobePDFA, saveSettings);
}
The code works as follows:
- Image is
nullfor a field that has no signature image yet, so the loop collects the names of the fields that are still missing. - Every other named field gets a
DigitalSignaturewith the same certificate. When each signer has a certificate of their own on the certifying computer, create eachDigitalSignaturewith that signer's certificate instead, as in the previous section. - The PDF/A document is created only when all fields are signed. Until then, the document stays in the TX Text Control format and can still be changed.
The TX Text Control format and DOCX keep the signature fields with their names, images and signer data between the steps. Check who may open and change the file between two signers: before the certificates are applied, the images in the fields prove nothing.
Check Documents With Several Signatures
Open the signed PDF in Adobe Acrobat Reader: the Signatures panel lists each signature with the name of its field, and clicking a signed field shows its validation status. The blog article Understanding the Differences: Signing a PDF Document vs. Signing Signature Fields in a PDF in C# compares a document signature and field signatures in the panel.
Next Steps
- Signature Fields and Signature Appearance: images and signer data of the fields.
- Certificates: load a certificate per signer.
- Timestamp Configuration: add trusted timestamps to the signatures.