sgcSign in five minutes

Two components sign a document: a signer and a key provider. This page signs a PDF with PAdES, using a certificate from the Windows certificate store, and then shows how to verify what you produced. If you would rather sign XML, there is a longer XAdES walkthrough linked below.

PAdES, XAdES, CAdES, ASiC
Ten key providers, from PFX to cloud HSM
Windows, Win32 and Win64

A signer and a key provider

The signer knows the document format. The key provider knows where the private key lives. They meet on one property.

The signer

TsgcPAdESSigner, declared in sgcSign_PAdES.pas and registered on the SGC Sign palette page. SignPDFFile takes an input path and an output path.

The key provider

TsgcWindowsCertStoreProvider for the Windows certificate store, or TsgcPFXKeyProvider for a .pfx file. Both are on the same palette page.

The property that joins them

KeyProvider, whose type is the interface IsgcKeyProvider rather than a component reference. That distinction matters for the lifetime, and the pitfall section below explains why.

Platform

Windows only, Win32 and Win64. Every signer and every provider puts Windows in its interface uses clause, and the hashing is done with the Windows CNG API. There is no Linux or macOS build.

Requirements and editions

sgcSign has no feature tiers, so this table is about compilers and platforms rather than editions.

What Value
IDE Delphi 7 through RAD Studio 13, and C++Builder. The C++Builder route puts the lib folder on the System Include path rather than the library path.
Uses clause The demo writes sgcSign_Types, sgcSign_Interfaces, sgcSign_Classes, sgcSign_PAdES, sgcSign_KeyProvider_WinCertStore, sgcSign_KeyProvider_PEM, sgcSign_KeyProvider_PFX. Drop the providers you do not use.
Editions There are none. The product's own sgcVer.inc contains no SGC_EDT_* define at all, and no feature is gated by tier. One library, every component, in every licence. The commercial tiers are by seat count: single, team and site, plus a free Community Edition.
Platform, checked in the source Windows only. sgcSign_KeyProvider_WinCertStore.pas and sgcSign_PAdES.pas both have Windows in the interface uses clause with no conditional around it, the hashing unit uses the CNG BCrypt API, and the packages enable Win32 and Win64 and nothing else. There are no POSIX or Linux guards anywhere in the source.
External dependencies None. The library calls the Windows CNG and WinHTTP APIs directly, so there are no OpenSSL DLLs to deploy with your application.
Defaults A fresh TsgcPAdESSigner already has a usable profile: the constructor sets the basic PAdES profile, the baseline B signature level and SHA-256. You do not have to touch Profile to produce a valid signature.

Prefer XML to PDF? The five-minute XAdES walkthrough signs an XML document with a PFX file instead, and covers the Delphi 7 Unicode pitfall and the UTC signing time. This page is the PDF and certificate store counterpart.

Install and find the palette page

Compile the runtime package before you install the design-time one, because the second references the first.

1. Unzip

Unzip the download to a folder, called {$DIR} below.

2. Library path

Tools, Environment Options, Directories. Add {$DIR}\delphi\source, which applies to every RAD Studio version.

3. Add the lib folder

Add the version-specific folder as well, for example {$DIR}\delphi\libD13\$(Platform) on RAD Studio 13, down to libD7 on Delphi 7. For C++Builder these go on the System Include path instead.

4. Build the packages

Open Packages\sgcSignD13.groupproj for your IDE version, or sgcSignC13.groupproj for C++Builder. Compile the sgcSign package first, then install the dclsgcSign one.

5. Check the palette

A page called SGC Sign appears, holding the signers, the verifier, the timestamp and OCSP clients, and the ten key providers. On Windows it also carries the Authenticode signer and verifier.

Sign a PDF, in about twenty lines

Pick a certificate, create the signer, point it at the provider and call SignPDFFile. The first tab uses the Windows certificate store, the second a PFX file.

frmMain.pas
uses
  SysUtils, Classes,
  // sgcSign
  sgcSign_Types, sgcSign_Interfaces, sgcSign_Classes,
  sgcSign_PAdES, sgcSign_KeyProvider_WinCertStore;

procedure TFormMain.btnSignClick(Sender: TObject);
var
  vSigner: TsgcPAdESSigner;
  vKeyProvider: TsgcWindowsCertStoreProvider;
  vProviderIntf: IsgcKeyProvider;
  vOutputFile: string;
begin
  vKeyProvider := TsgcWindowsCertStoreProvider.Create(nil);
  vSigner := TsgcPAdESSigner.Create(nil);
  try
    vKeyProvider.SelectCertificateBySubject('My Company');
    Log('Certificate found: ' + vKeyProvider.Certificate.Subject);

    // Hold the interface in an explicit local: an inline "as" cast
    // would leave a compiler-generated interface temporary alive in
    // this stack frame until the routine returns, i.e. past
    // vKeyProvider.Free, and releasing it would touch freed memory.
    vProviderIntf := vKeyProvider as IsgcKeyProvider;
    vSigner.KeyProvider := vProviderIntf;
    vSigner.Reason := 'Demo signature';
    vSigner.Location := 'Spain';
    vSigner.SignerName := 'sgcSign Demo';

    vOutputFile := ChangeFileExt(edInputFile.Text, '_signed.pdf');
    vSigner.SignPDFFile(edInputFile.Text, vOutputFile);

    Log('SUCCESS: PDF signed with PAdES profile');
    Log('Output file: ' + vOutputFile);
  finally
    vSigner.Free;
    vProviderIntf := nil;
    vKeyProvider.Free;
  end;
end;

Note what is not here. Profile is never touched, because the constructor already sets a basic PAdES profile, the baseline B signature level and SHA-256. The comment about the interface temporary is the shipped demo's own, and the release order in the finally block is the reason it matters.

frmMain.pas
uses
  SysUtils, Classes,
  // sgcSign
  sgcSign_Types, sgcSign_Interfaces, sgcSign_Classes,
  sgcSign_PAdES, sgcSign_KeyProvider_PFX;

var
  vSigner: TsgcPAdESSigner;
  vKeyProvider: TsgcPFXKeyProvider;
  vProviderIntf: IsgcKeyProvider;
begin
  vKeyProvider := TsgcPFXKeyProvider.Create(nil);
  vSigner := TsgcPAdESSigner.Create(nil);
  try
    vKeyProvider.FileName := 'C:\certs\signer.pfx';
    vKeyProvider.Password := GetPfxPassword;
    vKeyProvider.LoadFromFile;
    Log('Certificate loaded (PFX): ' + vKeyProvider.Certificate.Subject);

    vProviderIntf := vKeyProvider as IsgcKeyProvider;
    vSigner.KeyProvider := vProviderIntf;

    vSigner.SignPDFFile('C:\docs\contract.pdf',
      'C:\docs\contract_signed.pdf');
  finally
    vSigner.Free;
    vProviderIntf := nil;
    vKeyProvider.Free;
  end;
end;

The only difference from the first tab is which provider you create and how you point it at a key. Everything from KeyProvider := onward is identical, and that is true of all ten providers, including PKCS#11 hardware and the cloud key services.

uVerify.pas
uses
  SysUtils, Classes,
  // sgcSign
  sgcSign_Types, sgcSign_Interfaces, sgcSign_Verifier;

var
  oVerifier: TsgcSignatureVerifier;
  vVerifier: IsgcSignatureVerifier;
  oStream: TFileStream;
begin
  oVerifier := TsgcSignatureVerifier.Create(nil);
  oStream := TFileStream.Create('C:\docs\contract_signed.pdf',
    fmOpenRead or fmShareDenyWrite);
  try
    // VerifyPDF is reached through the interface the component implements
    vVerifier := oVerifier as IsgcSignatureVerifier;

    if vVerifier.VerifyPDF(oStream) = vsValid then
      Writeln('valid')
    else
      Writeln(oVerifier.GetVerificationDetails);
  finally
    vVerifier := nil;
    oStream.Free;
    oVerifier.Free;
  end;
end;

The same interface rule as the signer: assign the cast to a named local and clear it before you free the component. GetValidationReportXML produces a report in the ETSI validation report format when a boolean is not enough evidence.

The first two tabs come from the shipped demo Demos\Delphi\PAdES\frmMain.pas, with its branching collapsed to one path per tab. The comment about holding the interface in a named local is the demo's own, and it is worth keeping. A richer sibling demo, Demos\Delphi\PAdES_Providers, does the same with hardware and cloud providers.

Check the signature, do not just look at the file

A file appeared. That is not the same as a signature that validates.

The certificate resolved

Read Certificate.Subject after selecting, as the demo does. It is the difference between signing with the certificate you meant and signing with whichever one matched first. IsLoaded answers the same question as a boolean.

The file appeared

SignPDFFile writes the output path you gave it. The shipped demo derives it with ChangeFileExt so the signed file lands beside the original.

It raises, it does not return a code

There is no result to test, so put the call in a try except and read the exception message. That is what the demo does, and it is the only failure channel.

The signature validates

A file is not a valid signature. TsgcSignatureVerifier.VerifyPDF returns a TsgcVerificationStatus you compare against vsValid, and GetVerificationDetails explains a failure. Opening the file in a PDF reader shows the same thing to a person.

What usually goes wrong the first time

Six problems account for nearly every first signature.

An access violation on the way out

This is the one the demo warns about in its own comment. KeyProvider takes an IsgcKeyProvider, so an inline as cast leaves a compiler-generated interface reference alive until the routine returns, which is after you have freed the provider component. Assign the interface to a named local, and release in the order signer, then interface to nil, then provider.

Profile is not a string

It is a TsgcSignProfileConfig object. You set Profile.Profile and Profile.SignatureLevel, not Profile := 'something'. The constructor already fills in a usable default, so the first sample does not need to touch it at all.

No certificate is found

SelectCertificateBySubject matches on the subject, and SelectCertificateByThumbprint on the thumbprint. Read Certificate.Subject after selecting, as the demo does, so you can see which certificate you actually got. EnumerateCertificates lists what is available.

It will not compile off Windows

It cannot. The signer and every provider put Windows in the interface uses clause with no conditional, so this is a compile error rather than an empty unit. sgcSign is a Windows library.

The signature shows as unknown in the reader

A basic signature carries no trust anchor and no revocation data. Add a timestamp through TSAClient, and move up to a long-term profile when the document has to stay verifiable after the certificate expires.

SignPDFFile raises instead of returning a code

That is the design. There is no return value to test, so put the call in a try except and read the exception message, which is what the shipped demo does.

Beyond the first signature

Four directions, all inside the same library.

Other document formats

XAdES and XMLDSig for XML, CAdES for detached CMS, ASiC containers, and dedicated signers for ClickOnce, NuGet and VSIX packages. On Windows there is an Authenticode signer as well.

All sgcSign components

Where the key lives

Ten key providers ship: PFX, PEM, the Windows store, PKCS#11 hardware, Azure Trusted Signing, AWS KMS, Google Cloud KMS, Certum SimplySign, HashiCorp Vault and the CSC remote signing protocol.

Key providers

Country profiles

Twenty-one country and sector profiles, from Spanish VeriFactu to the EU invoicing formats, each with the fields and the signature level that regime expects.

Signature profiles

Sign somewhere else

sgcSign Server is a self-hosted daemon that holds the keys and signs on request, so the certificate never leaves the machine you trust with it.

sgcSign Server

Reference, demos and documentation

Demo projects ship inside the download, under Demos\Delphi. The PAdES demo is the one this page is built from.

Five-minute XAdES walkthrough The long form quick start: a fresh VCL project, a PFX file and a signed XML envelope.
Key providers All ten places a private key can live, and what each needs.
Signature profiles The twenty-one country and sector profiles, and what each requires.
PDF signing tutorial A longer walkthrough of PAdES, including visible signatures.
sgcSign Server The self-hosted signing daemon, when the key must not travel.
Download the trial The same installer as production, time limited, plus a free Community Edition.

Related reading: the introduction to sgcSign and the code signing server. Every product has its own quick start, listed on the getting started page.

sgcSign quick start questions

TsgcPAdESSigner, declared in sgcSign_PAdES.pas, and a key provider. For the Windows certificate store that is TsgcWindowsCertStoreProvider, from sgcSign_KeyProvider_WinCertStore.pas. For a .pfx file it is TsgcPFXKeyProvider, from sgcSign_KeyProvider_PFX.pas. Both live on the SGC Sign palette page. Assign the provider to the signer's KeyProvider property, then call SignPDFFile(aInputFile, aOutputFile).
Because KeyProvider is typed as the interface IsgcKeyProvider, not as a component. An inline as cast creates a compiler-generated interface temporary that stays alive in the stack frame until the routine returns, which is after the provider component has been freed, and releasing it then touches memory that is gone. The demo assigns the cast to a named local and then releases in the order signer, interface to nil, provider. Copy that order.
There are no editions. The product's sgcVer.inc contains no SGC_EDT_* define at all, and no component or format is gated by tier. Every licence contains every signer, every key provider and every country profile. The commercial tiers are seat counts, single, team and site, and there is a free Community Edition alongside the trial.
No. It is Windows only, Win32 and Win64. The signer and every key provider put Windows in the interface uses clause without a conditional, the hashing goes through the Windows CNG BCrypt API, HTTP goes through WinHTTP, and the shipped packages enable Win32 and Win64 and nothing else. There are no POSIX or Linux guards anywhere in the source, so on another platform it is a compile error rather than a stub.
Not for a first signature. The constructor already sets a basic PAdES profile, the baseline B signature level and SHA-256. When you do want to change it, Profile is a TsgcSignProfileConfig object, so you set Profile.Profile and Profile.SignatureLevel rather than assigning a string. For a long-term signature move to the LTV profile and the baseline LT level, and attach a TSAClient.
Use TsgcSignatureVerifier. VerifyPDF takes a stream and returns a TsgcVerificationStatus, which you compare against vsValid. GetVerificationDetails explains a failure, and GetValidationReportXML produces a report in the ETSI validation report format when you need evidence rather than a boolean.
No. sgcSign uses the Windows CNG and BCrypt APIs for hashing and signing and WinHTTP for network calls, so there are no OpenSSL DLLs to ship. That is a consequence of being a Windows-only library, and it is one of the reasons deployment is simple.
They cover different jobs on purpose. The five-minute quick start builds a fresh VCL project, uses a PFX file and signs an XML document with XAdES, and it goes into the Delphi 7 Unicode pitfall and the UTC signing time. This page signs a PDF with PAdES using a certificate from the Windows store, which is what the shipped PAdES demo does. Read this one first, then that one when you need XML.
Best value: All-AccessEvery eSeGeCe product, Premium Support included, from €1,059/year.
See All-Access pricing

Ready to sign your first document?

Download the trial, or start with the free Community Edition.