MakeCat Delphi实现生成安全编录(用于数字签名)

寻工具无果

微软出品的安全编录(cat)生成工具不太灵活,有些需求无法较好的满足。

  1. 无法对包含中文文件名的文件进行操作

  2. 不能同时对多个目录中的文件进行操作(特别是32bit & 64bit 文件名相同时),必须分别生成后,再进行签名。如 i386、amd64 

自己动手丰衣足食

 

注意:

  1. 目前貌似微软的API对cat文件生成cat有BUG; 强制使用CRYPT_SUBJTYPE_FLAT_IMAGE好像可以... 待验证

  2. 必须先通过 CryptSIPRetrieveSubjectGuid 根据文件类型获取相应的算法接口GUID,再用CryptSIPLoad 载入算法提供者。虽然直接使用“通用”的GUID: CRYPT_SUBJTYPE_FLAT_IMAGE 可以成功生成Cat,但在验证签名时PE、CAB等专有格式会无效! 如:

MakeCat Delphi实现生成安全编录(用于数字签名)

 

 代码

{

  * 2011/12/14 Created by Hou

  * Site: www.yryz.net

}

unit MakeCat_u;



interface



uses

  Windows,

  SysUtils,

  jwaWinCrypt,

  MssipApi,

  MsCatApi;



procedure Test;



implementation



const

  // 根据Win2k泄漏的部分源码 mscdfapi.cpp + OllyICE makecat.exe获得(2011/12/20 by Hou)

  CRYPT_SUBJTYPE_FLAT_IMAGE: TGUID = '{DE351A42-8E59-11D0-8C47-00C04FC295EE}';



function BytesToHex(const Bytes; Len: Integer): string;

var

  I: Integer;

begin

  Result := '';

  for I := 0 to Len - 1 do

    Result := Result + IntToHex(PByteArray(@Bytes)^[I], 2);

end;



// 计算间接数据(HASH)

function CalcIndirectData(hCat: THandle; pgSubjectType: PGUID; FileName: string;

  var dwCertVersion: DWORD): TBytes;

var

  SubjInfo: SIP_SUBJECTINFO;

  DispInfo: SIP_DISPATCH_INFO;

  LDataLen: Integer;

begin

  ZeroMemory(@SubjInfo, SizeOf(SubjInfo));

  SubjInfo.cbSize := SizeOf(SubjInfo);

  SubjInfo.hProv := PCRYPTCATSTORE(hCat)^.hProv;

  SubjInfo.DigestAlgorithm.pszObjId := CertAlgIdToOID(CALG_SHA1); // 摘要算法

  SubjInfo.dwFlags := SPC_INC_PE_RESOURCES_FLAG

    or SPC_INC_PE_IMPORT_ADDR_TABLE_FLAG

    or MSSIP_FLAGS_PROHIBIT_RESIZE_ON_CREATE;

  SubjInfo.dwEncodingType := PCRYPTCATSTORE(hCat)^.dwEncodingType;



  SubjInfo.pgSubjectType := pgSubjectType;

  SubjInfo.pwsFileName := PWChar(WideString(FileName));



  // 载入主体接口套件, 主体类型可以是exe、cab、cat、flat

  // http://msdn.microsoft.com/en-us/library/windows/desktop/ms721625(v=VS.85).aspx#_security_subject_interface_package_gly

  if not CryptSIPLoad(pgSubjectType,

    0,

    @DispInfo) then

    raise Exception.CreateFmt('CryptSIPLoad Fail(%d:%s)',

      [GetLastError, SysErrorMessage(GetLastError)]);



  if not DispInfo.pfCreate(@SubjInfo,

    @LDataLen,

    nil) and (LDataLen < 1) then

    raise Exception.CreateFmt('DispInfo.pfCreate Fail(%d:%s)',

      [GetLastError, SysErrorMessage(GetLastError)]);



  SetLength(Result, LDataLen);



  if not DispInfo.pfCreate(@SubjInfo,

    @LDataLen,

    PSIP_INDIRECT_DATA(Result)) then

    raise Exception.CreateFmt('DispInfo.pfCreate Fail2(%d:%s)',

      [GetLastError, SysErrorMessage(GetLastError)]);



  dwCertVersion := SubjInfo.dwIntVersion;

end;



procedure Test;

var

  hCat: THandle;

  IndirectData: TBytes;

  pMember: PCRYPTCATMEMBER;

  FileName: string;

  SubjectGuid: TGUID;

  dwCertVersion: DWORD;

  pwszFileName: LPWSTR;

  pwszReferenceTag: LPWSTR;

begin

  if ParamCount < 1 then

    WriteLn('Command: App ');



  FileName := ParamStr(ParamCount);



  try

    // 创建CAT

    hCat := CryptCATOpen('test.cat',

      CRYPTCAT_OPEN_CREATENEW,

      0,

      $00000001,

      PKCS_7_ASN_ENCODING or X509_ASN_ENCODING);



    if hCat = INVALID_HANDLE_VALUE then

      raise Exception.CreateFmt('CryptCATOpen Fail(%d:%s)',

        [GetLastError, SysErrorMessage(GetLastError)]);



    // 根据文件类型获取Subject GUID

    // !必须这样,SignTool验证时是根据文件类型采用相应的算法接口

    if not CryptSIPRetrieveSubjectGuid(PWChar(WideString(FileName)),

      0,

      @SubjectGuid) then

      SubjectGuid := CRYPT_SUBJTYPE_FLAT_IMAGE;



    WriteLn('SubjectGuid: ' + GUIDToString(SubjectGuid));



    // 计算间接数据(HASH)

    IndirectData := CalcIndirectData(hCat,

      @SubjectGuid,

      FileName,

      dwCertVersion);



    // WriteLn(PSIP_INDIRECT_DATA(IndirectData)^.Data.pszObjId);



    pwszFileName := PWChar(WideString(ExtractFileName(FileName)));

    pwszReferenceTag := PWChar(WideString(BytesToHex(PSIP_INDIRECT_DATA(IndirectData)^.Digest.pbData^,

      PSIP_INDIRECT_DATA(IndirectData)^.Digest.cbData))); // Hash



    // 添加成员

    // http://msdn.microsoft.com/en-us/library/windows/desktop/bb736352(v=vs.85).aspx

    pMember := CryptCATPutMemberInfo(hCat,

      pwszFileName,

      pwszReferenceTag,

      @CRYPT_SUBJTYPE_FLAT_IMAGE,

      dwCertVersion,

      Length(IndirectData),

      PByte(IndirectData));



    // 成员属性

    // http://msdn.microsoft.com/en-us/library/bb736351(d=lightweight,l=en-us,v=VS.85).aspx

    if Assigned(pMember) then

      CryptCATPutAttrInfo(hCat,

        pMember,

        PWChar('File'),

        CRYPTCAT_ATTR_AUTHENTICATED or CRYPTCAT_ATTR_DATAASCII or CRYPTCAT_ATTR_NAMEASCII,

        (Length(pwszFileName) + 1) * SizeOf(WChar),

        PByte(pwszFileName));



    // 保存(持久化存储,关键!)

    if not CryptCATPersistStore(hCat) then

      raise Exception.CreateFmt('CryptCATPersistStore Fail(%d:%s)',

        [GetLastError, SysErrorMessage(GetLastError)]);



    // 关闭CAT

    CryptCATClose(hCat);

  except

    on E: Exception do

      MessageBox(GetActiveWindow, PChar(E.Message), '异常', 0);

  end;

end;



end.

 

WinAPI:

  1. CryptCATOpen
  2. CryptCATPutMemberInfo
  3. CryptSIPRetrieveSubjectGuid
  4. CryptSIPLoad
  5. CryptCATPutMemberInfo
  6. CryptCATPutAttrInfo
  7. CryptCATPersistStore
  8. CryptCATClose

提示

  此功能已集成于“数字签名工具”中。

你可能感兴趣的:(Delphi)