微软出品的安全编录(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等专有格式会无效! 如:
{ * 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.
此功能已集成于“数字签名工具”中。