[摘要]本文介绍Delphi 编写数字签名验证并获取签名信息,并提供详细的示例代码供参考。

一个客户想通过编程实现验证程序自身的数字签名来确保程序的完整性,防范病毒感染以及防止一些无聊人士的修改(通过十六进制编辑器替换一些版权、网址、LOGO..); 为此我做了一个数字签名验证的小例子,其中也有获取签名者信息的方法,以满足“自验证”的需求。

示例图:

Windows API:

安全编录(CAT)

  • CryptCATAdminReleaseCatalogContext
  • CryptCATCatalogInfoFromContext
  • CryptCATAdminEnumCatalogFromHash
  • CryptCATAdminCalcHashFromFileHandle
  • CryptCATAdminReleaseContext
  • CryptCATAdminAcquireContext

验证文件的签名(主API)

  • WinVerifyTrust

获取签名信息

  • WTHelperProvDataFromStateData

获取证书名字信息

  • CertGetNameString

Delphi 编写数字签名验证并获取签名信息代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
{
  * by: HouSoft
  * site: www.yryz.net
  * created: 2012/02/03
}
unit Unit1;
  
interface
  
uses
  Windows, Sysutils, jwaWinCrypt, WinTrustApi;
  
procedure Test;
  
implementation
  
procedure PrintCertChain(pCertChain: PCERT_SIMPLE_CHAIN);
var
  I: Integer;
  sBuf: string;
begin
  // 开启指针运算
{$POINTERMATH ON}
  //
  // 输出书链元素
  for I := pCertChain^.cElement - 1 downto 0 do
  begin
    SetLength(sBuf, 1024);
    SetLength(sBuf,
      CertGetNameString(
      pCertChain^.rgpElement[I].pCertContext,
      CERT_NAME_SIMPLE_DISPLAY_TYPE, // 简单名字
      0,
      nil,
      PChar(sBuf),
      Length(sBuf)) - 1);
  
    WriteLn(#9, StringOfChar(' ', 2 * (pCertChain^.cElement - I - 1)), sBuf);
  end;
end;
  
procedure OutSignerInfo(hWVTStateData: THANDLE);
var
  provData: PCRYPT_PROVIDER_DATA;
  LSysTime: TSystemTime;
begin
  // 获取签名信息
  // http://msdn.microsoft.com/ZH-CN/library/windows/desktop/aa388429(v=vs.85).aspx
  
  provData := WTHelperProvDataFromStateData(hWVTStateData);
  if (provData <> nil) and (provData^.pasSigners <> nil) then
  begin
    // 采用安全编录(CAT)签名
    if provData^.pPDSip^.psSipCATSubjectInfo <> nil then
    begin
      WriteLn('安全编录: ');
      WriteLn(#9, provData^.pPDSip^.psSipCATSubjectInfo^.pwsFileName);
      WriteLn('');
    end;
  
    /// 注意: provData^.pasSigners 是数组, 但常见的都是一个元素,so...
  
    // 时间戳
    if provData^.pasSigners^.pasCounterSigners <> nil then
    begin
      FileTimeToSystemTime(provData^.pasSigners^.pasCounterSigners^.sftVerifyAsOf, LSysTime);
      WriteLn('时间戳: ');
      WriteLn(#9, FormatDateTime('yyyy-MM-dd hh:mm:ss', SystemTimeToDateTime(LSysTime)));
      WriteLn('');
      WriteLn('时间戳证书链: ');
      PrintCertChain(provData^.pasSigners^.pasCounterSigners^.pChainContext^.rgpChain[0]);
      WriteLn('');
    end;
  
    WriteLn('签名者证书链:');
    PrintCertChain(provData^.pasSigners^.pChainContext^.rgpChain[0]);
    WriteLn('');
  end;
end;
  
function SignVerify(FileName: string): Boolean;
var
  aByteHash: array [0 .. 255] of Byte;
  iByteCount: Integer;
  
  hCatAdminContext: HCatAdmin;
  WTrustData: WINTRUST_DATA;
  WTDCatalogInfo: WINTRUST_CATALOG_INFO;
  WTDFileInfo: WINTRUST_FILE_INFO;
  CatalogInfo: CATALOG_INFO;
  
  hFile: THANDLE;
  hCatalogContext: THANDLE;
  
  swFilename: WideString;
  swMemberTag: WideString;
  
  ilRet: Longint;
  I: Integer;
begin
  Result := False;
  
  if not FileExists(FileName) then
    Exit;
  
  swFilename := FileName;
  
  ZeroMemory(@CatalogInfo, SizeOf(CatalogInfo));
  ZeroMemory(@WTDFileInfo, SizeOf(WTDFileInfo));
  ZeroMemory(@WTDCatalogInfo, SizeOf(WTDCatalogInfo));
  ZeroMemory(@WTrustData, SizeOf(WTrustData));
  
  hCatalogContext := 0;
  hCatAdminContext := 0;
  
  try
    // 先查询安全编目
    if not CryptCATAdminAcquireContext(@hCatAdminContext,
      nil,
      0) then
      Exit;
  
    hFile := CreateFile(PChar(FileName),
      GENERIC_READ,
      FILE_SHARE_READ,
      nil,
      OPEN_EXISTING,
      FILE_ATTRIBUTE_NORMAL,
      0);
  
    if hFile = INVALID_HANDLE_VALUE then
      Exit;
  
    iByteCount := SizeOf(aByteHash);
  
    // 文件哈希函数计算的
    CryptCATAdminCalcHashFromFileHandle(hFile,
      @iByteCount,
      @aByteHash,
      0);
  
    for i := 0 to iByteCount - 1 do
    begin
      swMemberTag := swMemberTag + IntToHex(aByteHash[i], 2);
    end;
  
    CloseHandle(hFile);
  
    // 枚举目录包含一个指定的哈希
    hCatalogContext := CryptCATAdminEnumCatalogFromHash(hCatAdminContext,
      @aByteHash,
      iByteCount,
      0,
      nil);
  
    // 准备验证参数
    // http://msdn.microsoft.com/en-us/library/windows/desktop/aa388205(v=vs.85).aspx
  
    WTrustData.dwUIChoice := WTD_UI_NONE;
    WTrustData.fdwRevocationChecks := WTD_REVOKE_NONE;
    WTrustData.dwStateAction := WTD_STATEACTION_VERIFY; // 获取信息后需要手动 WTD_STATEACTION_CLOSE
    WTrustData.dwProvFlags := WTD_REVOCATION_CHECK_NONE;
  
    if hCatalogContext = 0 then // 未找到包含此文件的安全编目
    begin
      WTDFileInfo.cbStruct := SizeOf(WTDFileInfo);
      WTDFileInfo.pcwszFilePath := PWideChar(swFilename);
  
      WTrustData.cbStruct := SizeOf(WTrustData);
      WTrustData.dwUnionChoice := WTD_CHOICE_FILE;
      WTrustData.union.pFile := @WTDFileInfo;
  
    end
    else
    begin
      CryptCATCatalogInfoFromContext(hCatalogContext, @CatalogInfo, 0);
  
      WTDCatalogInfo.cbStruct := SizeOf(WTDCatalogInfo);
      WTDCatalogInfo.pcwszCatalogFilePath := CatalogInfo.sCatalogFile;
      WTDCatalogInfo.pcwszMemberFilePath := PWideChar(swFilename);
      WTDCatalogInfo.pcwszMemberTag := PWideChar(swMemberTag);
  
      WTrustData.cbStruct := SizeOf(WTrustData);
      WTrustData.dwUnionChoice := WTD_CHOICE_CATALOG;
      WTrustData.union.pCatalog := @WTDCatalogInfo;
  
      // WriteLn(CatalogInfo.sCatalogFile);
    end;
  
    // 验证
    // http://msdn.microsoft.com/en-us/library/windows/desktop/aa388208(v=vs.85).aspx
    ilRet := WinVerifyTrust(INVALID_HANDLE_VALUE,
      @WINTRUST_ACTION_GENERIC_VERIFY_V2,
      @WTrustData);
  
    Result := ilRet = 0;
  
    // 输出签名信息
    OutSignerInfo(WTrustData.hWVTStateData);
  
    // 释放
    WTrustData.dwStateAction := WTD_STATEACTION_CLOSE;
    WinVerifyTrust(INVALID_HANDLE_VALUE,
      @WINTRUST_ACTION_GENERIC_VERIFY_V2,
      @WTrustData);
  finally
    if hCatAdminContext > 0 then
    begin
      if hCatalogContext > 0 then
        CryptCATAdminReleaseCatalogContext(hCatAdminContext,
          hCatalogContext, 0);
  
      CryptCATAdminReleaseContext(hCatAdminContext, 0);
    end;
  end;
end;
  
procedure Test;
begin
  if ParamCount < 1 then
  begin
    WriteLn('请输入要验证的文件名!');
    Exit;
  end;
  
  if SignVerify(ParamStr(1)) then
    WriteLn('签名有效.')
  else
    WriteLn('签名无效.');
end;
  
end.

Delphi 编写数字签名验证并获取签名信息相关推荐

  1. 无法获取签名信息,请上传有效包(110506)

    此篇文章将要介绍安卓App提交应用商店时遇到的两个小问题的相关介绍,具体代码请看下文 陆陆续续做了一个半月左右的「喵呜天气」终于在今天下午成功提交到应用商店(腾讯应用宝).期间遇到两个小问题,记录如下 ...

  2. android获取签名信息

    android程序里面获取签名信息,常用于自校验,防止别人篡改您的APP. 其实就是获取package 信息. import android.app.Activity; import android. ...

  3. jni程序中获取签名信息打印截断问题

    在jni获取的签名信息打印出来与java层获取的不一样,少了一部分,验证jni程序LOG最长只能打印1024字节,因此不是获取错误,只是打印限制. 转载于:https://www.cnblogs.co ...

  4. Android 使用代码获取签名信息

    在使用百度地图,微信分享等等第三方SDK的时候,都需要在对应的网站上注册应用信息,其中之一就是通过包名和签名生成一个MD5或者SHA1编码的字符串. 通常情况下,这些信息可以通过ADT工具或者keyt ...

  5. android+获取电池信息,Delphi XE5 Android应用程序获取电池信息

    将android.os.BatteryManager类移植到Delphi. uses Androidapi.JNI.JavaTypes, Androidapi.JNIBridge; type JBat ...

  6. APK解析签名错误 无法获取签名信息,请上传有效包

    现象 国内上架应用市场,新版本的 Android Studio (白狐之后的版本)出来的包在有的应用市场会出现以上的问题,但是我们自己查看包信息的时候签名都是有的,这是什么情况呢? 原因 新版本的 A ...

  7. Delphi编程 -- 使用CPUID指令获取CPU信息(转自大富翁)

    最近到整理了一份CPU的信息,应该算是比较全面的吧. 几乎现在所有的X86 CPU都内置了CPUID指令以辨别真伪,一些CPU厂商例如AMD,VIA等还内置了更加丰富的扩展CPUID指令,用着更方便了 ...

  8. 应用宝 无法获取签名信息 360 解析证书为空!

    我明明已经签名了. 但是缺给我报错 其他的市场都还好.就360和应用宝不行! 原因 minSdkVersion 25 改为 23 或者以下即可. 因为..android 本身有v1和v2两种签名. A ...

  9. Android开发之微信支付获取签名小工具分享

    老套路,先看图: 大家支付的时候下面的签名一般是如何弄出来的? 1.一个字母一个字母手写?容易出错 2.用QQ截图然后使用QQ的图片文字识别功能?(我一般用这个) 但是都特别麻烦,于是乎我自己把微信官 ...

最新文章

  1. Qt之excel 操作使用说明
  2. php数组排序面试题,PHP按子数组值对数组排序
  3. baseline发布!OPPO安全AI挑战赛,人脸识别对抗攻击赛题详解
  4. 关于prefrenceactivity和preferencefragment的作用
  5. 惠普HP LaserJet 2100 打印机驱动
  6. React实现todos
  7. 论文阅读-2017-Vidal-NEARP
  8. 数据结构C语言——广义表
  9. css3的媒体查询(Media Queries)
  10. 连接超时计算机无法连接失败,爱思助手无法连接或者连接超时解决办法
  11. unity ios 应用名称多语言本地化
  12. 致敬!向中外9名杰出女数学家
  13. android studio用mysql_Android Studio使用JDBC远程连接mysql的注意事项(附示例)
  14. 关于维基百科你不知道的十件事:
  15. Oracle和MySQL的数据类型
  16. C语言求两个数的较大值
  17. 作业:找出“你、我、他”在Unicode表中的位置
  18. 科技点亮课堂,智能黑板解决方案
  19. 利用手机绘制标准曲线并且计算相关系数
  20. [英语语法]词法之动词:虚拟语气

热门文章

  1. 低版本MacOS安装Nginx
  2. MTCNN中的重叠度IOU和非极大值抑制NMS原理及Python实现
  3. C++程序闪退原因定位
  4. 【R语言】时间序列案例:住宅销量预测的乘法季节模型
  5. 如何用迅雷下载OneDrive文件
  6. BLE-2の蓝牙4.0协议栈のLL层 Scaning 和 initiating状态的区别
  7. NVIDIA Jetson之远程控制软件NoMachine安装使用
  8. Python AutoCAD 注释
  9. 将Shapefile数据导入Winbugs的方法
  10. 微信域名防封,细说微信域名防封技术原理