[摘要]本文介紹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 . |