sgcSign 五分钟上手

两个组件即可对文档签名:签名器和密钥提供程序。本页使用 Windows 证书存储区中的证书,以 PAdES 对 PDF 签名,然后展示如何验证您生成的结果。如果您想对 XML 签名,下方链接了一份更详细的 XAdES 演练。

PAdES、XAdES、CAdES、ASiC
十种密钥提供程序,从 PFX 到云端 HSM
Windows,Win32 和 Win64

一个签名器和一个密钥提供程序

签名器了解文档格式。密钥提供程序了解私钥存放在哪里。它们通过一个属性相互关联。

签名器

TsgcPAdESSigner,声明在 sgcSign_PAdES.pas 中,注册在 SGC Sign 组件面板页上。SignPDFFile 接受输入路径和输出路径。

密钥提供程序

Windows 证书存储区使用 TsgcWindowsCertStoreProvider,.pfx 文件使用 TsgcPFXKeyProvider。两者都在同一个组件面板页上。

把它们连在一起的属性

KeyProvider,其类型是接口 IsgcKeyProvider,而不是组件引用。这一区别关系到生命周期,下面的常见问题部分会解释原因。

平台

Win32、Win64、Linux64、Intel 和 Apple Silicon 上的 macOS、iOS 和 Android。在 Windows 上,哈希和签名通过 Windows CNG API 完成,在其他所有平台上则通过库自己的纯 Pascal 密码学实现,无需部署 OpenSSL。Windows 证书存储区提供程序是唯一仍然仅限 Windows 的组件。

要求与版本

sgcSign 没有功能层级,因此这张表讲的是编译器和平台,而不是版本。

项目 值
IDE Delphi 7 到 RAD Studio 13,以及 C++Builder。C++Builder 的做法是把 lib 文件夹放在 System Include 路径上,而不是库路径上。
uses 子句 演示中写的是 sgcSign_Types, sgcSign_Interfaces, sgcSign_Classes, sgcSign_PAdES, sgcSign_KeyProvider_WinCertStore, sgcSign_KeyProvider_PEM, sgcSign_KeyProvider_PFX。请去掉您不使用的提供程序。
版本 没有版本之分。产品自己的 sgcVer.inc 中完全没有 SGC_EDT_* 定义,也没有任何功能按层级限制。一个库,每个组件,每种许可证都包含。商业层级按席位数量划分:单个、团队和站点,另外还有免费的 Community Edition。
平台,已在源代码中核实 Win32、Win64、Linux64、OSX64、OSXARM64、iOS 和 Android。密码学通过一个衔接单元 sgcSign_Crypto.pas 完成,在 Windows 上是 CNG,在其他所有平台上是纯 Pascal;HTTP 在 Windows 上是 WinHTTP,在其他平台上是 Delphi RTL 客户端;运行时包从 Delphi 10.3 起启用 Linux64 和 Intel 上的 macOS,从 10.4 起启用 Android 和 iOS,从 11 起启用 Apple Silicon 上的 macOS。sgcSign_KeyProvider_WinCertStore.pas 是唯一仅限 Windows 的单元。
外部依赖 无。在 Windows 上,库直接调用 Windows CNG 和 WinHTTP API;在其他平台上,它使用自己的纯 Pascal 密码学和 Delphi RTL HTTP 客户端,因此无需随应用程序部署 OpenSSL DLL。
默认值 全新的 TsgcPAdESSigner 已经带有可用的配置文件:构造函数设置了基本的 PAdES 配置文件、基线 B 签名级别和 SHA-256。您无需修改 Profile 就能生成有效的签名。

更喜欢 XML 而不是 PDF?五分钟 XAdES 演练改用 PFX 文件对 XML 文档签名,并涵盖 Delphi 7 的 Unicode 陷阱和 UTC 签名时间。本页则是 PDF 和证书存储区方面的对应内容。

安装并找到组件面板页

请先编译运行时包,再安装设计时包,因为后者引用前者。

1. 解压

将下载文件解压到一个文件夹,下文称之为 {$DIR}。

2. 库路径

依次选择 Tools、Environment Options、Directories。添加 {$DIR}\delphi\source,它适用于每个 RAD Studio 版本。

3. 添加 lib 文件夹

同时添加特定版本的文件夹,例如 RAD Studio 13 使用 {$DIR}\delphi\libD13\$(Platform),一直到 Delphi 7 的 libD7。对于 C++Builder,这些应改放在 System Include 路径上。

4. 构建包

针对您的 IDE 版本打开 Packages\sgcSignD13.groupproj,C++Builder 则打开 sgcSignC13.groupproj。先编译 sgcSign 包,再安装 dclsgcSign 包。

5. 检查组件面板

会出现一个名为 SGC Sign 的页面,其中包含签名器、验证器、时间戳和 OCSP 客户端,以及十种密钥提供程序。在 Windows 上,它还包含 Authenticode 签名器和验证器。

对 PDF 签名,大约二十行代码

选择一个证书,创建签名器,把它指向提供程序,然后调用 SignPDFFile。第一个选项卡使用 Windows 证书存储区,第二个使用 PFX 文件。

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;

请注意这里没有什么。Profile 从未被修改,因为构造函数已经设置了基本的 PAdES 配置文件、基线 B 签名级别和 SHA-256。关于接口临时变量的注释来自随包附带的演示本身,而 finally 块中的释放顺序正是它重要的原因。

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;

与第一个选项卡唯一的区别是您创建哪个提供程序,以及如何让它指向密钥。从 KeyProvider := 开始,之后的一切都完全相同,这对全部十个提供程序都成立,包括 PKCS#11 硬件和云密钥服务。

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;

与签名器相同的接口规则:把类型转换赋给一个具名的局部变量,并在释放组件之前将其清除。当布尔值提供的证据不够时,GetValidationReportXML 会生成 ETSI 验证报告格式的报告。

前两个选项卡来自随包附带的演示 Demos\Delphi\PAdES\frmMain.pas,并把其分支压缩为每个选项卡一条路径。关于把接口保存在具名局部变量中的注释来自演示本身,值得保留。同类的更丰富的演示 Demos\Delphi\PAdES_Providers 用硬件和云提供程序完成同样的事情。

检查签名,而不要只看文件

出现了一个文件,并不等于出现了一个能通过验证的签名。

证书已解析

选择之后,像演示那样读取 Certificate.Subject。这决定了您是用想要的证书签名,还是用最先匹配到的那个证书签名。IsLoaded 以布尔值回答同样的问题。

文件已出现

SignPDFFile 会写入您给它的输出路径。随包附带的演示使用 ChangeFileExt 推导该路径,使已签名的文件落在原文件旁边。

它抛出异常,而不是返回代码

没有可检查的结果,因此请把调用放在 try except 中,并读取异常消息。演示就是这样做的,这也是唯一的失败通道。

签名通过验证

一个文件并不是一个有效的签名。TsgcSignatureVerifier.VerifyPDF 返回 TsgcVerificationStatus,您将其与 vsValid 比较,GetVerificationDetails 会解释失败的原因。在 PDF 阅读器中打开该文件,也能让人看到同样的结果。

第一次通常会出什么问题

六个问题几乎涵盖了所有首次签名的情况。

退出时出现访问冲突

这是演示在自己的注释中提醒过的问题。KeyProvider 接受 IsgcKeyProvider,因此内联的 as 类型转换会让编译器生成的接口引用一直存活到例程返回,而那是在您释放提供程序组件之后。请把接口赋给一个具名的局部变量,并按以下顺序释放:先是签名器,然后把接口设为 nil,最后是提供程序。

Profile 不是字符串

它是一个 TsgcSignProfileConfig 对象。您应设置 Profile.Profile 和 Profile.SignatureLevel,而不是 Profile := 'something'。构造函数已经填入了可用的默认值,因此第一个示例根本不需要修改它。

找不到证书

SelectCertificateBySubject 按主题匹配,SelectCertificateByThumbprint 按指纹匹配。选择之后,像演示那样读取 Certificate.Subject,这样您就能看到实际得到的是哪个证书。EnumerateCertificates 会列出可用的证书。

在 Windows 之外无法编译

无法编译。签名器和每个提供程序都在接口部分的 uses 子句中无条件地放入了 Windows,因此这是编译错误,而不是空单元。sgcSign 是一个 Windows 库。

阅读器中的签名显示为未知

基本签名不携带信任锚点,也没有吊销数据。请通过 TSAClient 添加时间戳,并在文档需要在证书过期后仍可验证时,升级到长期配置文件。

SignPDFFile 抛出异常而不是返回代码

这是设计使然。没有可检查的返回值,因此请把调用放在 try except 中并读取异常消息,随包附带的演示就是这样做的。

第一个签名之后

四个方向,都在同一个库内。

其他文档格式

适用于 XML 的 XAdES 和 XMLDSig,适用于分离式 CMS 的 CAdES,ASiC 容器,以及适用于 ClickOnce、NuGet 和 VSIX 包的专用签名器。在 Windows 上还有 Authenticode 签名器。

全部 sgcSign 组件

密钥存放在哪里

附带十种密钥提供程序:PFX、PEM、Windows 存储区、PKCS#11 硬件、Azure Trusted Signing、AWS KMS、Google Cloud KMS、Certum SimplySign、HashiCorp Vault 和 CSC 远程签名协议。

密钥提供程序

国家配置文件

二十一个国家和行业配置文件,从西班牙的 VeriFactu 到欧盟发票格式,每个都带有该制度所要求的字段和签名级别。

签名配置文件

在别处签名

sgcSign Server 是一个自托管的守护进程,它保管密钥并按请求签名,因此证书永远不会离开您信任的那台机器。

sgcSign Server

参考、演示和文档

演示项目包含在下载包内,位于 Demos\Delphi 下。本页就是基于 PAdES 演示构建的。

五分钟 XAdES 演练 详细版快速入门:全新的 VCL 项目、一个 PFX 文件和一个已签名的 XML 封装。
密钥提供程序 私钥可以存放的全部十个位置,以及各自的需求。
签名配置文件 二十一个国家和行业配置文件,以及各自的要求。
PDF 签名教程 关于 PAdES 的更详细演练,包括可见签名。
sgcSign Server 自托管的签名守护进程,适用于密钥不得外传的场景。
下载试用版 与正式版相同的安装程序,有时间限制,另有免费的 Community Edition。

相关阅读:sgcSign 简介和代码签名服务器。每个产品都有自己的快速入门,列在入门页面上。

sgcSign 快速入门常见问题

TsgcPAdESSigner(声明在 sgcSign_PAdES.pas 中)和一个密钥提供程序。对于 Windows 证书存储区,是来自 sgcSign_KeyProvider_WinCertStore.pas 的 TsgcWindowsCertStoreProvider。对于 .pfx 文件,是来自 sgcSign_KeyProvider_PFX.pas 的 TsgcPFXKeyProvider。两者都位于 SGC Sign 组件面板页上。把提供程序赋给签名器的 KeyProvider 属性,然后调用 SignPDFFile(aInputFile, aOutputFile)。
因为 KeyProvider 的类型是接口 IsgcKeyProvider,而不是组件。内联的 as 类型转换会创建一个由编译器生成的接口临时变量,它会一直存活在栈帧中直到例程返回,而那是在提供程序组件被释放之后,此时再释放它就会访问已经不存在的内存。演示把类型转换赋给一个具名的局部变量,然后按以下顺序释放:签名器、把接口设为 nil、提供程序。请照搬这个顺序。
没有版本之分。产品的 sgcVer.inc 中完全没有 SGC_EDT_* 定义,也没有任何组件或格式按层级限制。每种许可证都包含每个签名器、每个密钥提供程序和每个国家配置文件。商业层级按席位数量划分,即单个、团队和站点,试用版之外还有免费的 Community Edition。
可以,自 2026.10.0 起。该库可以在 Linux64、Intel 和 Apple Silicon 上的 macOS、iOS 和 Android 上构建和运行,当然也包括 Win32 和 Win64。在这些平台上,文档的签名和验证、PKCS#12 文件的读写、PE 文件、目录、MSI、MSP、MSIX 和 APPX 的签名,以及 PKCS#11 令牌都能正常工作,签名服务器在 Linux 上作为 systemd 守护进程运行。有两个提供程序使用平台已有的密钥存储:Apple 钥匙串和 Android KeyStore。有两样东西仍然仅限 Windows:Windows 证书存储区提供程序,以及服务器和命令行工具上特定于 Windows 的格式,尽管库本身可以对它们签名,但在非 Windows 的构建上它们仍然会拒绝这些格式。
第一次签名不需要。构造函数已经设置了基本的 PAdES 配置文件、基线 B 签名级别和 SHA-256。当您确实想要更改时,Profile 是一个 TsgcSignProfileConfig 对象,因此您设置的是 Profile.Profile 和 Profile.SignatureLevel,而不是赋一个字符串。对于长期签名,请切换到 LTV 配置文件和基线 LT 级别,并附加一个 TSAClient。
使用 TsgcSignatureVerifier。VerifyPDF 接受一个流并返回 TsgcVerificationStatus,您将其与 vsValid 比较。GetVerificationDetails 会解释失败的原因,当您需要证据而不是一个布尔值时,GetValidationReportXML 会生成 ETSI 验证报告格式的报告。
不需要。在 Windows 上,sgcSign 通过 CNG 和 BCrypt API 进行哈希和签名,并通过 WinHTTP 访问网络。在 Linux、macOS、iOS 和 Android 上,它使用自己的纯 Pascal 密码学和 Delphi RTL HTTP 客户端。无论哪种方式,都没有需要随附的 OpenSSL DLL,这是部署简单的原因之一。
它们有意涵盖不同的任务。五分钟快速入门构建一个全新的 VCL 项目,使用 PFX 文件,并用 XAdES 对 XML 文档签名,还讲解了 Delphi 7 的 Unicode 陷阱和 UTC 签名时间。本页使用 Windows 存储区中的证书,以 PAdES 对 PDF 签名,这也是随包附带的 PAdES 演示所做的事情。请先阅读本页,需要处理 XML 时再阅读那一页。
超值之选:All-AccesseSeGeCe 全部产品,含高级支持,每年 €1,059 起。
查看 All-Access 价格

准备好签署您的第一份文档了吗?

下载试用版,或者从免费的 Community Edition 开始。