您的位置:首页 > 编程语言 > Delphi

Delphi 编写 数字签名验证 并获取签名信息

2012-06-25 15:56 489 查看
Delphi 编写 数字签名验证 并获取签名信息

2012-03-03 09:56:12 我来说两句
收藏 我要投稿
[字体: ]

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

示例:


WinAPI:

• 安全编录(CAT)

  CryptCATAdminReleaseCatalogContext

  CryptCATCatalogInfoFromContext

  CryptCATAdminEnumCatalogFromHash

  CryptCATAdminCalcHashFromFileHandle

  CryptCATAdminReleaseContext

  CryptCATAdminAcquireContext

• 验证文件的签名(主API)

  WinVerifyTrust

• 获取签名信息

  WTHelperProvDataFromStateData

• 获取证书名字信息

  CertGetNameString

代码:

{

* 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). href="http://www.2cto.com/kf/web/asp/" target=_blank>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.

摘自 一人游走
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: