//
// This code is part of Document Solutions for PDF demos.
// Copyright (c) MESCIUS inc. All rights reserved.
//
using System;
using System.IO;
using System.Drawing;
using System.Text;
using System.Linq;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using GrapeCity.Documents.Pdf;
using GrapeCity.Documents.Pdf.Security;
using GrapeCity.Documents.Pdf.AcroForms;
using GrapeCity.Documents.Text;
namespace DsPdfWeb.Demos
{
// This sample shows how to sign an existing PDF file containing
// an empty signature field with a signature that complies with
// the various PAdES (PDF Advanced Electronic Signatures) levels.
// Note that when run online, this sample simply returns an unsigned
// PDF with an empty signature field. To actually sign the PDF
// you will need to provide a valid .pfx file (see "JohnDoe.pfx").
// The sample contains 5 methods that can be used to sign a PDF
// with the various PAdES levels, the last 3 methods can be
// used in a chain to incrementally increase the signature strength.
// To check the signature compliance, open the signed PDF in Acrobat
// Reader and inspect the signature properties.
//
// IMPORTANT! The code in this sample WILL NOT work correctly
// unless DsPdf is licensed with a valid license key.
// Email us.sales@mescius.com to obtain a trial key.
public class PadesLevels
{
// Certificate issued by CA Cert Signing Authority, used for verification:
static X509Certificate2 s_caCertRoot = new X509Certificate2(Path.Combine("Resources", "Misc", "CACertRoot.crt"));
// TODO: replace with a real certificate:
static X509Certificate2 s_cert = new X509Certificate2(Path.Combine("Resources", "Misc", "JohnDoe.pfx"), "secret");
static PadesLevels()
{
// TODO: GcPdfDocument MUST BE LICENSED for the signatures to remain valid,
// as otherwise the license nag text added when saving the PDF invalidates
// incremental updates. Email us.sales@mescius.com to obtain a trial key.
// GcPdfDocument.SetLicenseKey("my key");
}
public void CreatePDF(Stream stream, int paramsIdx = 0)
{
CreatePDF(stream, GetSampleParamsList()[paramsIdx]);
}
// While this sample includes code that does the actual signing and time stamping,
// that code is NOT executed by the online demo. So the online sample driver
// simply returns the PDF that includes info about the selected PAdES level,
// and can be signed or time stamped using a valid certificate.
// You can copy the corresponding code and use it in your applications.
public void CreatePDF(Stream stream, string[] sampleParams)
{
// TODO: change to true to actually run the signing/time stamping code:
bool doSigning = false;
// The unsigned PDF with a signature field to sign:
var fn = Path.Combine("Resources", "PDFs", sampleParams[3]);
if (doSigning)
{
// Running this code will produce 5 PDFs with increasing PAdES levels:
// PAdES B-B:
Do_B_B(fn);
// PAdES B-T:
Do_B_T(fn);
// Note that the next 3 steps use PDFs produced by the previous step,
// incrementally adding verification info:
// PAdES B-LT:
Do_B_LT("B-T.pdf");
// PAdES B-LTA:
Do_B_LTA("B-LT.pdf");
// LTV Enabled:
Do_B_LTA_LTV("B-LTA.pdf");
}
// Copy the source PDF to output stream
// to provide the demo result:
using var fs = File.OpenRead(fn);
fs.CopyTo(stream);
}
// Signs a PDF with a PAdES B-B Level signature:
void Do_B_B(string fn)
{
using FileStream fs = File.OpenRead(fn);
var doc = new GcPdfDocument();
doc.Load(fs);
var signField = doc.AcroForm.Fields.FirstOrDefault(f_ => f_ is SignatureField);
if (signField == null)
throw new Exception("Could not find a signature field.");
var sp = new SignatureProperties()
{
SignatureField = signField,
SignatureBuilder = new Pkcs7SignatureBuilder(s_cert)
{
Format = Pkcs7SignatureBuilder.SignatureFormat.ETSI_CAdES_detached,
},
};
// Sign and save the PDF to a file:
doc.Sign(sp, "B-B.pdf");
}
// Signs a PDF with a PAdES B-T Level signature:
void Do_B_T(string fn)
{
using FileStream fs = File.OpenRead(fn);
var doc = new GcPdfDocument();
doc.Load(fs);
var signField = doc.AcroForm.Fields.FirstOrDefault(f_ => f_ is SignatureField);
if (signField == null)
throw new Exception("Could not find a signature field.");
var sp = new SignatureProperties()
{
SignatureField = signField,
SignatureBuilder = new Pkcs7SignatureBuilder(s_cert)
{
Format = Pkcs7SignatureBuilder.SignatureFormat.ETSI_CAdES_detached,
},
TimeStamp = new TimeStamp(@"http://ts.ssl.com"),
};
// Sign and save the PDF to a file:
doc.Sign(sp, "B-T.pdf");
}
// Adds LTV information to a B-T level signature (e.g. as created by the do_B_T method above),
// which makes the signature compliant with PAdES B-LT:
void Do_B_LT(string fn)
{
using FileStream fs = File.OpenRead(fn);
var doc = new GcPdfDocument();
doc.Load(fs);
var signField = doc.AcroForm.Fields.FirstOrDefault(f_ => f_ is SignatureField);
if (signField == null)
throw new Exception("Could not find a signature field.");
// Get the signature and add verification information for it:
var sig = (Signature)signField.Value;
var vp = new DocumentSecurityStore.VerificationParams();
vp.Certificates = new X509Certificate2[] { s_caCertRoot };
if (!doc.SecurityStore.AddVerification(sig, vp))
throw new Exception($"Could not add verification for {sig.Name}.");
// Save the PDF to a file using incremental update so that the signature remains valid:
doc.Save("B-LT.pdf", SaveMode.IncrementalUpdate);
}
// Adds time stamp to a signed PDF (e.g. as created by the do_B_LT method above),
// which makes the document compliant with B-LTA level:
void Do_B_LTA(string fn)
{
using FileStream fs = File.OpenRead(fn);
var doc = new GcPdfDocument();
doc.Load(fs);
var ts = new TimeStampProperties()
{
TimeStamp = new TimeStamp(@"http://ts.ssl.com"),
};
// Save the PDF to a file adding a time stamp to it:
doc.TimeStamp(ts, "B-LTA.pdf");
}
// Adds verification information for a PDF time-stamp (e.g. as created by the do_B_LTA method above)
// which makes the signature LTV enabled:
void Do_B_LTA_LTV(string fn)
{
using FileStream fs = File.OpenRead(fn);
var doc = new GcPdfDocument();
doc.Load(fs);
var signField = doc.AcroForm.Fields.FirstOrDefault(f_ => f_ is SignatureField);
if (signField == null)
throw new Exception("Could not find a signature field.");
// Get the signature and add verification information for it:
var sig = (Signature)signField.Value;
if (!doc.SecurityStore.AddVerification(sig))
throw new Exception($"Could not add verification for {sig.Name}.");
// Save the PDF to a file using incremental update so that the signature remains valid:
doc.Save("B-LTA_LTV.pdf", SaveMode.IncrementalUpdate);
}
public static List<string[]> GetSampleParamsList()
{
// Strings are name, description, info, rest are arbitrary strings:
return new List<string[]>()
{
new string[] { "@b-sign/PAdES B-B Level", "How to sign a PDF complying with PAdES B-B level", "",
"PAdES-B-B.pdf" },
new string[] { "@b-sign/PAdES B-T Level", "How to sign a PDF complying with PAdES B-T level", "",
"PAdES-B-T.pdf" },
new string[] { "@b-sign/PAdES B-LT Level", "How to add LTV information to a signature", "",
"PAdES-B-LT.pdf" },
new string[] { "@b-sign/PAdES B-LTA Level", "How to time stamp a signed PDF", "",
"PAdES-B-LTA.pdf" },
new string[] { "@b-sign/LTV Enabled Signature", "How to make a signature LTV enabled", "",
"PAdES-B-LTA-LTV.pdf" },
};
}
}
}