windows 从 Delphi 以编程方式检查数字签名

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5993877/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 16:50:52  来源:igfitidea点击:

Checking digital signature programmatically from Delphi

windowsdelphiwinapidigital-signaturedigital-certificate

提问by kes

I need a function in Delphi to verify the digital signature of an external EXE or DLL. In my particular application, I am going to occasionally invoke other processes, but for security purposes I want to make sure these executables were created by our organization before running them.

我需要 Delphi 中的一个函数来验证外部 EXE 或 DLL 的数字签名。在我的特定应用程序中,我偶尔会调用其他进程,但出于安全目的,我想确保这些可执行文件在运行之前是由我们的组织创建的。

I have seen Microsoft's example in C, however, I do not want to waste the time translating this to Delphi if somebody else already has.

我已经在 C 中看到了Microsoft 的示例,但是,如果其他人已经拥有,我不想浪费时间将其翻译成 Delphi。

I would prefer a snippet or code example over a third-party library. Thanks.

与第三方库相比,我更喜欢片段或代码示例。谢谢。

回答by Zo? Peterson

Here you go:

干得好:

// IsCodeSigned, which verifies that the exe hasn't been modified, uses
// WinVerifyTrust, so it's NT only.  IsCompanySigningCertificate works on Win9x, 
// but it only checks that the signing certificate hasn't been replaced, which
// keeps someone from re-signing a modified executable.

// Imagehlp.dll
const
  CERT_SECTION_TYPE_ANY = $FF;      // Any Certificate type

function ImageEnumerateCertificates(FileHandle: THandle; TypeFilter: WORD;
  out CertificateCount: DWORD; Indicies: PDWORD; IndexCount: Integer): BOOL; stdcall; external 'Imagehlp.dll';
function ImageGetCertificateHeader(FileHandle: THandle; CertificateIndex: Integer;
  var CertificateHeader: TWinCertificate): BOOL; stdcall; external 'Imagehlp.dll';
function ImageGetCertificateData(FileHandle: THandle; CertificateIndex: Integer;
  Certificate: PWinCertificate; var RequiredLength: DWORD): BOOL; stdcall; external 'Imagehlp.dll';

// Crypt32.dll
const
  CERT_NAME_SIMPLE_DISPLAY_TYPE = 4;
  PKCS_7_ASN_ENCODING = 
const
  WTD_UI_ALL    = 1;
  WTD_UI_NONE   = 2;
  WTD_UI_NOBAD  = 3;
  WTD_UI_NOGOOD = 4;

  WTD_REVOKE_NONE       = 
procedure CodeSignVerify(const FileName: string; AllowUserPrompt: Boolean = False);
var
  SignedCode: ISignedCode;
begin
  SignedCode := CoSignedCode.Create;
  SignedCode.FileName := FileName;
  SignedCode.Verify(AllowUserPrompt);
end;
000000; WTD_REVOKE_WHOLECHAIN = ##代码##000001; WTD_CHOICE_FILE = 1; WTD_CHOICE_CATALOG = 2; WTD_CHOICE_BLOB = 3; WTD_CHOICE_SIGNER = 4; WTD_CHOICE_CERT = 5; WTD_STATEACTION_IGNORE = ##代码##000000; WTD_STATEACTION_VERIFY = ##代码##000001; WTD_STATEACTION_CLOSE = ##代码##000002; WTD_STATEACTION_AUTO_CACHE = ##代码##000003; WTD_STATEACTION_AUTO_CACHE_FLUSH = ##代码##000004; type PWinTrustFileInfo = ^TWinTrustFileInfo; TWinTrustFileInfo = record cbStruct: DWORD; pcwszFilePath: PWideChar; hFile: THandle; pgKnownSubject: PGUID; end; PWinTrustData = ^TWinTrustData; TWinTrustData = record cbStruct: DWORD; pPolicyCallbackData: Pointer; pSIPClientData: Pointer; dwUIChoice: DWORD; fdwRevocationChecks: DWORD; dwUnionChoice: DWORD; pUnionData: Pointer; dwStateAction: DWORD; hWVTStateData: THandle; pwszURLReference: PWideChar; dwProvFlags: DWORD; dwUIContext: DWORD; end; function VerifySignature(const FileName: WideString): Longint; var FileInfo: TWinTrustFileInfo; TrustData: TWinTrustData; begin FillChar(FileInfo, SizeOf(FileInfo), 0); FileInfo.cbStruct := SizeOf(FileInfo); FileInfo.pcwszFilePath := PWideChar(FileName); FillChar(TrustData, SizeOf(TrustData), 0); TrustData.cbStruct := SizeOf(TrustData); TrustData.dwUIChoice := WTD_UI_NONE; TrustData.fdwRevocationChecks := WTD_REVOKE_NONE; TrustData.dwUnionChoice := WTD_CHOICE_FILE; TrustData.pUnionData := @FileInfo; TrustData.dwStateAction := WTD_STATEACTION_IGNORE; TrustData.dwProvFlags := WTD_SAFER_FLAG; TrustData.dwUIContext := WTD_UICONTEXT_EXECUTE; Result := WinVerifyTrust(0, WINTRUST_ACTION_GENERIC_VERIFY_V2, @TrustData); end;
010000; X509_ASN_ENCODING = ##代码##000001; type PCCERT_CONTEXT = type Pointer; HCRYPTPROV_LEGACY = type Pointer; PFN_CRYPT_GET_SIGNER_CERTIFICATE = type Pointer; CRYPT_VERIFY_MESSAGE_PARA = record cbSize: DWORD; dwMsgAndCertEncodingType: DWORD; hCryptProv: HCRYPTPROV_LEGACY; pfnGetSignerCertificate: PFN_CRYPT_GET_SIGNER_CERTIFICATE; pvGetArg: Pointer; end; function CryptVerifyMessageSignature(const pVerifyPara: CRYPT_VERIFY_MESSAGE_PARA; dwSignerIndex: DWORD; pbSignedBlob: PByte; cbSignedBlob: DWORD; pbDecoded: PBYTE; pcbDecoded: PDWORD; ppSignerCert: PCCERT_CONTEXT): BOOL; stdcall; external 'Crypt32.dll'; function CertGetNameStringA(pCertContext: PCCERT_CONTEXT; dwType: DWORD; dwFlags: DWORD; pvTypePara: Pointer; pszNameString: PAnsiChar; cchNameString: DWORD): DWORD; stdcall; external 'Crypt32.dll'; function CertFreeCertificateContext(pCertContext: PCCERT_CONTEXT): BOOL; stdcall; external 'Crypt32.dll'; function CertCreateCertificateContext(dwCertEncodingType: DWORD; pbCertEncoded: PBYTE; cbCertEncoded: DWORD): PCCERT_CONTEXT; stdcall; external 'Crypt32.dll'; // WinTrust.dll const WINTRUST_ACTION_GENERIC_VERIFY_V2: TGUID = '{00AAC56B-CD44-11d0-8CC2-00C04FC295EE}'; WTD_CHOICE_FILE = 1; WTD_REVOKE_NONE = 0; WTD_UI_NONE = 2; type PWinTrustFileInfo = ^TWinTrustFileInfo; TWinTrustFileInfo = record cbStruct: DWORD; // = sizeof(WINTRUST_FILE_INFO) pcwszFilePath: PWideChar; // required, file name to be verified hFile: THandle; // optional, open handle to pcwszFilePath pgKnownSubject: PGUID; // optional: fill if the subject type is known end; PWinTrustData = ^TWinTrustData; TWinTrustData = record cbStruct: DWORD; pPolicyCallbackData: Pointer; pSIPClientData: Pointer; dwUIChoice: DWORD; fdwRevocationChecks: DWORD; dwUnionChoice: DWORD; pFile: PWinTrustFileInfo; dwStateAction: DWORD; hWVTStateData: THandle; pwszURLReference: PWideChar; dwProvFlags: DWORD; dwUIContext: DWORD; end; function WinVerifyTrust(hwnd: HWND; const ActionID: TGUID; ActionData: Pointer): Longint; stdcall; external wintrust; {-----------------------------------------------} function IsCodeSigned(const Filename: string): Boolean; var file_info: TWinTrustFileInfo; trust_data: TWinTrustData; begin // Verify that the exe is signed and the checksum matches FillChar(file_info, SizeOf(file_info), 0); file_info.cbStruct := sizeof(file_info); file_info.pcwszFilePath := PWideChar(WideString(Filename)); FillChar(trust_data, SizeOf(trust_data), 0); trust_data.cbStruct := sizeof(trust_data); trust_data.dwUIChoice := WTD_UI_NONE; trust_data.fdwRevocationChecks := WTD_REVOKE_NONE; trust_data.dwUnionChoice := WTD_CHOICE_FILE; trust_data.pFile := @file_info; Result := WinVerifyTrust(INVALID_HANDLE_VALUE, WINTRUST_ACTION_GENERIC_VERIFY_V2, @trust_data) = ERROR_SUCCESS end; {-----------------------------------------------} function IsCompanySigningCertificate(const Filename, CompanyName :string): Boolean; var hExe: HMODULE; Cert: PWinCertificate; CertContext: PCCERT_CONTEXT; CertCount: DWORD; CertName: AnsiString; CertNameLen: DWORD; VerifyParams: CRYPT_VERIFY_MESSAGE_PARA; begin // Returns TRUE if the SubjectName on the certificate used to sign the exe is // "Company Name". Should prevent a cracker from modifying the file and // re-signing it with their own certificate. // // Microsoft has an example that does this using CryptQueryObject and // CertFindCertificateInStore instead of CryptVerifyMessageSignature, but // CryptQueryObject is NT-only. Using CertCreateCertificateContext doesn't work // either, though I don't know why. Result := False; // Verify that the exe was signed by our private key hExe := CreateFile(PChar(Filename), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_RANDOM_ACCESS, 0); if hExe = INVALID_HANDLE_VALUE then Exit; try // There should only be one certificate associated with the exe if (not ImageEnumerateCertificates(hExe, CERT_SECTION_TYPE_ANY, CertCount, nil, 0)) or (CertCount <> 1) then Exit; // Read the certificate header so we can get the size needed for the full cert GetMem(Cert, SizeOf(TWinCertificate) + 3); // ImageGetCertificateHeader writes an DWORD at bCertificate for some reason try Cert.dwLength := 0; Cert.wRevision := WIN_CERT_REVISION_1_0; if not ImageGetCertificateHeader(hExe, 0, Cert^) then Exit; // Read the full certificate ReallocMem(Cert, SizeOf(TWinCertificate) + Cert.dwLength); if not ImageGetCertificateData(hExe, 0, Cert, Cert.dwLength) then Exit; // Get the certificate context. CryptVerifyMessageSignature has the // side effect of creating a context for the signing certificate. FillChar(VerifyParams, SizeOf(VerifyParams), 0); VerifyParams.cbSize := SizeOf(VerifyParams); VerifyParams.dwMsgAndCertEncodingType := X509_ASN_ENCODING or PKCS_7_ASN_ENCODING; if not CryptVerifyMessageSignature(VerifyParams, 0, @Cert.bCertificate, Cert.dwLength, nil, nil, @CertContext) then Exit; try // Extract and compare the certificate's subject names. Don't // compare the entire certificate or the public key as those will // change when the certificate is renewed. CertNameLen := CertGetNameStringA(CertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, nil, nil, 0); SetLength(CertName, CertNameLen - 1); CertGetNameStringA(CertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, nil, PAnsiChar(CertName), CertNameLen); if CertName <> CompanyName then Exit; finally CertFreeCertificateContext(CertContext) end; finally FreeMem(Cert); end; finally CloseHandle(hExe); end; Result := True; end;

回答by Ondrej Kelle

##代码##

There are more details in the documentation.

文档中有更多详细信息。

Alternatively, you can use CAPICOM. Import the CAPICOM type library from capicom.dll and then use the generated CAPICOM_TLB unit:

或者,您可以使用CAPICOM。从 capicom.dll 导入 CAPICOM 类型库,然后使用生成的 CAPICOM_TLB 单元:

##代码##