程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> Delphi 編寫 數字簽名驗證 並獲取簽名信息

Delphi 編寫 數字簽名驗證 並獲取簽名信息

編輯:Delphi

一個客戶想通過編程實現驗證程序自身的數字簽名來確保程序的完整性,防范病毒感染以及防止一些無聊人士的修改(通過十六進制編輯器替換一些版權、網址、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).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.

 

摘自 一人游走

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved