delphi实现数字签名

http://blog.csdn.net/avan_lau/article/details/7367480

[delphi]view plaincopyprint?

  1. 上周,另一部门需要支援解决数字签名问题。但因为之前也没做过,现学现卖。此方面可参考的中文资料较少,特作分享,方便查阅。
    上周,另一部门需要支援解决数字签名问题。但因为之前也没做过,现学现卖。此方面可参考的中文资料较少,特作分享,方便查阅。

有关数字签名的概念、原理,这里就不做介绍了,请自行google或百度。

利用证书对文件进行签名,从证书来源看,可分为两种:1、软证书:就是将*.pfx文件导入到系统中,这意味着,只要登录到PC中的用户,均可以使用该证书;2、硬证书:通常将证书存放到uKey中(smart card),这样的好处是,只有拥有usb key的人才有权限使用该证书。

USB Key通常支持CryptToAPI——除非特殊安全需要,只公布使用自己的接口,不支持微软接口。由于使用CryptToAPI,使用起来较繁琐,微软提供了CAPICOM组件,方便开发。

不论是硬证书或软证书,只要支持CryptToAPI接口,那么CAPICOM均可使用。为此本次内容以CAPICOM,作为数字签名功能的基础。

动手之前,首先要熟悉数字签名的过程。通过分析,主要是两部分:数字签名(身份标识及防篡改)和数字信封;其实按业务流程,签名之前还有签章的过程(也就是通常的盖章);过程大致如下:

发送方

1、验证证书是否准备好?(若是硬证书,usbkey是否已插入;判断证书是否有效);

2、对文件进行签名;

3、对文件进行数字信封(公钥加密);

4、可选:填入CSP(加密服务提供商,通常是在USB Key当中)信息

接收方:

1、获取文件,读取CSP信息;

2、依据CSP信息,获取相关证书并验证;

3、利用证书进行数字解封;

4、签名验证,确认身份及文件的完整性(是否被篡改);

依据以上分析,程序可这样设计,由于USB Key可能支持CAPICOM,也可能不支持,所以,后续可能会有相应由多种方法去执行签名。可提取接口,来解除这样的依赖。

接口定义如下:

[delphi]view plaincopyprint?

  1. IDigitalIntf = interface(IUNKNOWN)
  2. ['{78657307-FD4A-452F-91FF-956379A7F654}']
  3. //验证设备
  4. function VerifyUserAvailable: Boolean;
  5. //签名与数字信封加密
  6. function Pack(const sInPath: string; const sOutPath: string; bOverride: Boolean): Boolean;
  7. //数字信封解密与签名验证
  8. function Unpack(const sInPath: string; const sOutPath: string;
  9. bCreateDirectory: Boolean): Boolean;
  10. //获取数字指纹
  11. function GetThumbPrint: string;
  12. //获取证书信息
  13. function GetCertficateInfo(var ACertInfo: TStampInfo): Boolean;
  14. end;
  IDigitalIntf = interface(IUNKNOWN)
  ['{78657307-FD4A-452F-91FF-956379A7F654}']
    //验证设备
    function VerifyUserAvailable: Boolean;
    //签名与数字信封加密
    function Pack(const sInPath: string; const sOutPath: string; bOverride: Boolean): Boolean;    
    //数字信封解密与签名验证
    function Unpack(const sInPath: string; const sOutPath: string;
                        bCreateDirectory: Boolean): Boolean;
    //获取数字指纹                    
    function GetThumbPrint: string;
    //获取证书信息 
    function GetCertficateInfo(var ACertInfo: TStampInfo): Boolean;    
  end;

CAPICOM实现类,构造如下:

[delphi]view plaincopyprint?

  1. TDigital_CAPICOM = class(TInterfacedObject, IDigitalIntf)
  2. private
  3. FProviderName, FStoreName: string;
  4. function GetStoreByName(AStoreName: string): TStore;
  5. protected
  6. FStoreList: TStringList;
  7. ICert: ICertificate;
  8. ICert2: ICertificate2;
  9. FPublicKey: string;//公钥
  10. FPKLength: Integer;//算法长度
  11. FAlgType: string; // 算法类型
  12. {----------------------方法定义-----------------------}
  13. //证书库操作
  14. function OpenStore(AStoreName: string): TStore;
  15. procedure CloseStore;
  16. //获取证书接口
  17. procedure GetCertificate;
  18. //执行文件签名
  19. function SignedFile(const AFileName: string;
  20. EncodeType: CAPICOM_ENCODING_TYPE): Boolean;
  21. //验证文件签名
  22. function VerifySign(const AFileName: string): Boolean;
  23. //附加签名信息
  24. function AppendSignedContent(const AFileName, ASignedContent: string): Boolean;
  25. //分解签名信息
  26. function ExtractSignedContent(const AFileName: string): string;
  27. {-----------------------------------------------------}
  28. {---------------------属性定义------------------------}
  29. //CSP提供商
  30. property ProviderName : string read FProviderName;
  31. //证书存放位置
  32. property StoreName : string read FStoreName;
  33. {-----------------------------------------------------}
  34. public
  35. function VerifyUserAvailable: Boolean;
  36. function Pack(const sInPath: string; const sOutPath: string; bOverride: Boolean): Boolean;
  37. function Unpack(const sInPath: string; const sOutPath: string;
  38. bCreateDirectory: Boolean): Boolean;
  39. function GetThumbPrint: string;
  40. function GetCertficateInfo(var ACertInfo: TStampInfo): Boolean;
  41. constructor Create(const StoreName, ProviderName: string); virtual;
  42. destructor Destroy; override;
  43. end;
  TDigital_CAPICOM = class(TInterfacedObject, IDigitalIntf)
  private
    FProviderName, FStoreName: string;

    function GetStoreByName(AStoreName: string): TStore;    
  protected
    FStoreList: TStringList;
    ICert: ICertificate;
    ICert2: ICertificate2;
    FPublicKey: string;//公钥
    FPKLength: Integer;//算法长度
    FAlgType: string; // 算法类型
    {----------------------方法定义-----------------------}
    //证书库操作
    function OpenStore(AStoreName: string): TStore;
    procedure CloseStore;
    //获取证书接口
    procedure GetCertificate;
    //执行文件签名
    function SignedFile(const AFileName: string;
      EncodeType: CAPICOM_ENCODING_TYPE): Boolean;
    //验证文件签名
    function VerifySign(const AFileName: string): Boolean;
    //附加签名信息
    function AppendSignedContent(const AFileName, ASignedContent: string): Boolean;
    //分解签名信息
    function ExtractSignedContent(const AFileName: string): string;
    {-----------------------------------------------------}
    {---------------------属性定义------------------------}
    //CSP提供商
    property ProviderName : string read FProviderName;
    //证书存放位置
    property StoreName : string read FStoreName;
    {-----------------------------------------------------}
  public
    function VerifyUserAvailable: Boolean;
    function Pack(const sInPath: string; const sOutPath: string; bOverride: Boolean): Boolean;
    function Unpack(const sInPath: string; const sOutPath: string;
                        bCreateDirectory: Boolean): Boolean;
    function GetThumbPrint: string;
    function GetCertficateInfo(var ACertInfo: TStampInfo): Boolean;

    constructor Create(const StoreName, ProviderName: string); virtual;
    destructor Destroy; override;
  end; 

其实现代码去除了关键信息:

[delphi]view plaincopyprint?

    [delphi]view plaincopyprint?

    1. function TDigital_CAPICOM.AppendSignedContent(const AFileName,
    2. ASignedContent: string): Boolean;
    3. var
    4. msSrc, ms1: TMemoryStream;
    5. iLen: Integer;
    6. sSignedData, sLength: string;
    7. BDA: TByteDynArray;
    8. begin
    9. if not FileExists(AFileName) then
    10. raise Exception.Create('文件"' + AFileName + '"不存在');
    11. //拼接签名信息
    12. sLength := IntToStr(Length(ASignedContent));
    13. sLength := FillChars(sLength, HashString_Length);
    14. sSignedData := HYMSignature + sLength + ASignedContent;
    15. BDA:= String2Byte(sSignedData);
    16. iLen := Length(sSignedData);
    17. msSrc := TMemoryStream.Create;
    18. ms1 := TMemoryStream.Create;
    19. try
    20. msSrc.LoadFromFile(AFileName);
    21. ms1.Write(BDA[0], iLen); //写入文件头信息
    22. ms1.Write(msSrc.Memory^, msSrc.Size); //把文件内容附加上
    23. ms1.SaveToFile(AFileName);
    24. finally
    25. ms1.Free;
    26. msSrc.Free;
    27. end;
    28. Result := True;
    29. end;
    30. procedure TDigital_CAPICOM.CloseStore;
    31. var
    32. vStore: TStore;
    33. iCnt: Integer;
    34. begin
    35. try
    36. for iCnt := 0 to FStoreList.Count - 1 do
    37. begin
    38. vStore := TStore(FStoreList.Objects[iCnt]);
    39. vStore.Disconnect;
    40. end;
    41. except
    42. raise Exception.Create('关闭密钥库失败!');
    43. end;
    44. end;
    45. constructor TDigital_CAPICOM.Create(const StoreName, ProviderName: string);
    46. begin
    47. CoInitialize(nil);
    48. FProviderName:= ProviderName;
    49. FStoreName := StoreName;
    50. FStoreList:= TStringlist.create;
    51. GetCertificate;
    52. end;
    53. destructor TDigital_CAPICOM.Destroy;
    54. begin
    55. FStoreList.Free;
    56. ICert := nil;
    57. ICert2:= nil;
    58. CoUninitialize;
    59. inherited;
    60. end;
    61. function TDigital_CAPICOM.ExtractSignedContent(
    62. const AFileName: string): string;
    63. var
    64. fs: TFileStream;
    65. iHeadLen, iContentLen, iPos: Integer;
    66. sContentLength: string;
    67. ms: TMemoryStream;
    68. BDA_Head, BDA_Cont: TByteDynArray;
    69. begin
    70. Result := '';
    71. if not FileExists(AFileName) then
    72. raise Exception.Create('文件"' + AFileName + '"不存在');
    73. iHeadLen := Length(HYMSignature) + HashString_Length;
    74. SetLength(BDA_Head, iHeadLen);
    75. ms:= TMemoryStream.Create;
    76. ms.LoadFromFile(AFileName);
    77. fs := TFileStream.Create(AFileName, fmCreate);
    78. try
    79. ms.Position:= 0;
    80. ms.Read(BDA_Head[0], iHeadLen);
    81. sContentLength := Byte2String(BDA_Head); //含有长度信息
    82. iPos := Pos(HYMSignature, sContentLength);
    83. if iPos > 0 then
    84. begin
    85. //取得长度
    86. iContentLen := StrToInt(Copy(sContentLength, Length(HYMSignature) + 1, MaxInt));
    87. SetLength(BDA_Cont, iContentLen);
    88. ms.Read(BDA_Cont[0], iContentLen);
    89. Result := Byte2String(BDA_Cont);
    90. //该位置之后的内容为真正需要的
    91. fs.CopyFrom(ms, ms.Size - ms.Position); //读取文件内容去除文件头部分
    92. fs.Position := 0;
    93. end
    94. finally
    95. ms.Free;
    96. fs.Free;
    97. end;
    98. end;
    99. function TDigital_CAPICOM.GetCertficateInfo(
    100. var ACertInfo: TStampInfo): Boolean;
    101. var
    102. iCnt: Integer;
    103. begin
    104. Result := True;
    105. if ICert <> nil then
    106. begin
    107. ACertInfo.PKAlg := FAlgType;
    108. ACertInfo.PKLength := FPKLength;
    109. for iCnt := 0 to Length(FPublicKey) - 1 do
    110. begin
    111. ACertInfo.PKContent[iCnt] := FPublicKey[iCnt + 1];
    112. end;
    113. ACertInfo.EndDate:= ICert.ValidToDate;
    114. ACertInfo.DispachTime:= ICert.ValidFromDate;
    115. end
    116. else
    117. result:= False;
    118. end;
    119. procedure TDigital_CAPICOM.GetCertificate;
    120. var
    121. vStore: TStore;
    122. iCnt: Integer;
    123. IBaseIntf: IInterface;
    124. ICert2Dsp: ICertificate2Disp;
    125. begin
    126. if ICert2 = nil then
    127. begin
    128. vStore := OpenStore(FStoreName);
    129. for iCnt := 1 to vStore.Certificates.Count do
    130. begin
    131. IBaseIntf := vStore.Certificates.Item[iCnt];
    132. try
    133. if IBaseIntf.QueryInterface(ICertificate2Disp, ICert2Dsp) = 0
    134. then
    135. begin
    136. //确认硬件是否连接
    137. if ICert2Dsp.HasPrivateKey then
    138. begin
    139. //确认是否为指定CSP提供商
    140. if ((FProviderName = CSPProvider_ePass) and
    141. ((ICert2Dsp.PrivateKey.ProviderName = CSPProvider_ePass_1K) or
    142. (ICert2Dsp.PrivateKey.ProviderName = CSPProvider_ePass_3K)))
    143. or (ICert2Dsp.PrivateKey.ProviderName = FProviderName)
    144. then
    145. begin
    146. IBaseIntf.QueryInterface(IID_ICertificate2, ICert2);
    147. IBaseIntf.QueryInterface(IID_ICertificate, ICert);
    148. FPublicKey:= ICert2Dsp.publickey.EncodedKey.Format(True);
    149. FPKLength:= ICert2Dsp.publickey.Length;
    150. FAlgType:= ICert2Dsp.publickey.Algorithm.FriendlyName;
    151. end;
    152. end;
    153. end;
    154. except
    155. //某些不支持CAPICOM的,会出现异常
    156. ICert2 := nil;
    157. end;
    158. end;
    159. end;
    160. end;
    161. function TDigital_CAPICOM.GetStoreByName(AStoreName: string): TStore;
    162. var
    163. i: integer;
    164. begin
    165. i := FStoreList.IndexOf(AStoreName);
    166. if i >= 0 then
    167. result := FStoreList.Objects[i] as Tstore
    168. else
    169. result := nil;
    170. end;
    171. function TDigital_CAPICOM.GetThumbPrint: string;
    172. begin
    173. Result := '';
    174. if ICert <> nil then
    175. Result := ICert.Thumbprint;
    176. end;
    177. function TDigital_CAPICOM.OpenStore(AStoreName: string): TStore;
    178. var
    179. vStore: TStore;
    180. begin
    181. vStore := self.GetStoreByName(AStoreName);
    182. if vStore = nil then
    183. try
    184. vStore := TStore.Create(nil);
    185. //默认为从CurrenUser读取, 后续可能会是CAPICOM_SMART_CARD_USER_STORE 智能卡
    186. vStore.Open(CAPICOM_CURRENT_USER_STORE, AStoreName,
    187. CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED or CAPICOM_STORE_OPEN_INCLUDE_ARCHIVED
    188. or CAPICOM_STORE_OPEN_EXISTING_ONLY);
    189. self.FStoreList.AddObject(AStoreName, vStore);
    190. except
    191. on E:exception do
    192. raise exception.Create('无法打开密钥库!'+E.Message);
    193. end;
    194. Result := vStore;
    195. end;
    196. function TDigital_CAPICOM.Pack(const sInPath, sOutPath: string;
    197. bOverride: Boolean): Boolean;
    198. var
    199. EnvelopedData: IEnvelopedData;
    200. BUFFER: WideString;
    201. FileStm: TFileStream;
    202. iP, oP: string;
    203. begin
    204. ip:= StringReplace(sInPath, '\\', '\', [rfReplaceAll]);
    205. op:= StringReplace(sOutPath, '\\', '\', [rfReplaceAll]);
    206. Result := True;
    207. EnvelopedData := CoEnvelopedData.Create;
    208. //指定采用的CSP算法类型
    209. EnvelopedData.Algorithm.Name := Algorithm;
    210. //指定加密长度
    211. EnvelopedData.Algorithm.KeyLength := EnLength;
    212. try
    213. //获取证书接口
    214. GetCertificate;
    215. //目前sInPath是一个文件夹,先压缩,再解密
    216. Files2ZipArchive(ip, op, RZipPassWd);
    217. //执行签名
    218. SignedFile(op, CAPICOM_ENCODE_BASE64);
    219. //获取要加密的内容
    220. FileStm := TFileStream.Create(sOutPath, fmOpenRead);
    221. try
    222. Pointer(Buffer):= SysAllocStringByteLen (nil, FileStm.Size);
    223. FileStm.ReadBuffer(Pointer(Buffer)^, FileStm.Size);
    224. EnvelopedData.Content:= Buffer;
    225. finally
    226. FileStm.Free;
    227. end;
    228. //基于64位编码加密
    229. EnvelopedData.Recipients.Add(ICert2);
    230. Buffer:= EnvelopedData.Encrypt(CAPICOM_ENCODE_BASE64);
    231. //输出加密内容
    232. FileStm := TFileStream.Create(sOutPath, fmCreate);
    233. try
    234. FileStm.WriteBuffer(Pointer(Buffer)^, SysStringByteLen(PWideChar(Buffer)));
    235. finally
    236. FileStm.Free;
    237. end;
    238. except
    239. Result := False;
    240. end;
    241. end;
    242. function TDigital_CAPICOM.SignedFile(const AFileName: string;
    243. EncodeType: CAPICOM_ENCODING_TYPE): Boolean;
    244. var
    245. Signer: ISigner2;
    246. SignedData: ISignedData;
    247. HashString: string;
    248. SignedContent: WideString;
    249. begin
    250. Result := True;
    251. try
    252. GetCertificate;
    253. //获取文件哈希值
    254. HashString:= GetFileHash(AFileName);
    255. //构建 签名者
    256. Signer := CoSigner.Create;
    257. Signer.Certificate := ICert2;
    258. //构建 数据签名对象
    259. SignedData := CoSignedData.Create;
    260. //执行签名
    261. SignedData.Content:= HashString;
    262. SignedContent := SignedData.Sign(Signer, False, EncodeType);
    263. //附加签名信息
    264. AppendSignedContent(AFileName, SignedContent);
    265. except
    266. Result := False;
    267. end;
    268. end;
    269. function TDigital_CAPICOM.Unpack(const sInPath, sOutPath: string;
    270. bCreateDirectory: Boolean): Boolean;
    271. var
    272. EnvelopedData: IEnvelopedData;
    273. BUFFER: WideString;
    274. FileStm: TFileStream;
    275. vDecryptFileName: string;
    276. begin
    277. Result := True;
    278. EnvelopedData := CoEnvelopedData.Create;
    279. //指定采用的CSP算法类型
    280. EnvelopedData.Algorithm.Name := Algorithm;
    281. //指定加密长度
    282. EnvelopedData.Algorithm.KeyLength := EnLength;
    283. try
    284. //获取数字证书接口
    285. GetCertificate;
    286. //关联证书以解密
    287. EnvelopedData.Recipients.Add(ICert2);
    288. //获取加密内容
    289. FileStm := TFileStream.Create(sInPath, fmOpenRead );
    290. try
    291. Pointer(Buffer):= SysAllocStringByteLen (nil, FileStm.Size);
    292. FileStm.ReadBuffer(Pointer(Buffer)^, FileStm.Size);
    293. finally
    294. FileStm.Free;
    295. end;
    296. //解密
    297. EnvelopedData.Decrypt(Buffer);
    298. Buffer:= EnvelopedData.Content;
    299. //输出解密内容
    300. vDecryptFileName:= sOutPath + ExtractFileName(sInPath);
    301. FileStm := TFileStream.Create(vDecryptFileName, fmCreate);
    302. try
    303. FileStm.WriteBuffer(Pointer(Buffer)^, SysStringByteLen(PWideChar(Buffer)));
    304. finally
    305. FileStm.Free;
    306. end;
    307. //验证签名
    308. VerifySign(vDecryptFileName);
    309. //因为有压缩,再解压 ZipArchive2Files
    310. ZipArchive2Files(vDecryptFileName, sOutPath, RZipPassWd);
    311. DeleteFile(PAnsiChar(vDecryptFileName));
    312. except
    313. Result := False;
    314. end;
    315. end;
    316. function TDigital_CAPICOM.VerifySign(const AFileName: string): Boolean;
    317. var
    318. SignedData: ISignedData;
    319. HashString: WideString;
    320. ASignedContent: string;
    321. begin
    322. Result := True;
    323. try
    324. GetCertificate;
    325. //先获取签名信息,因为会做信息分离,还原出加上签名前的数据
    326. ASignedContent:= ExtractSignedContent(AFileName);
    327. //获取文件哈希值
    328. HashString:= GetFileHash(AFileName);
    329. //构建 数据签名对象
    330. SignedData := CoSignedData.Create;
    331. SignedData.Content := HashString;
    332. //执行检查
    333. SignedData.Verify(ASignedContent, False, CAPICOM_VERIFY_SIGNATURE_ONLY);
    334. except
    335. Result := False;
    336. Raise Exception.Create('数字签名校验失败!');
    337. end;
    338. end;
    339. function TDigital_CAPICOM.VerifyUserAvailable: Boolean;
    340. begin
    341. Result := False;
    342. if (ICert2 <> nil) and ICert2.HasPrivateKey then
    343. Result:= True;
    344. end;
    function TDigital_CAPICOM.AppendSignedContent(const AFileName,
      ASignedContent: string): Boolean;
    var
      msSrc, ms1: TMemoryStream;
      iLen: Integer;
      sSignedData, sLength: string;
      BDA: TByteDynArray;
    begin
      if not FileExists(AFileName) then
        raise Exception.Create('文件"' + AFileName + '"不存在');
      //拼接签名信息  
      sLength := IntToStr(Length(ASignedContent));
      sLength := FillChars(sLength, HashString_Length);
      sSignedData := HYMSignature + sLength + ASignedContent;
      BDA:= String2Byte(sSignedData);
      iLen := Length(sSignedData);
      
      msSrc := TMemoryStream.Create;
      ms1 := TMemoryStream.Create;
      try
        msSrc.LoadFromFile(AFileName);
        ms1.Write(BDA[0], iLen); //写入文件头信息
        ms1.Write(msSrc.Memory^, msSrc.Size); //把文件内容附加上
        ms1.SaveToFile(AFileName);
      finally
        ms1.Free;
        msSrc.Free;
      end;
      Result := True;
    end;
    
    procedure TDigital_CAPICOM.CloseStore;
    var
      vStore: TStore;
      iCnt: Integer; 
    begin
      try
        for iCnt := 0 to FStoreList.Count - 1 do
        begin
          vStore := TStore(FStoreList.Objects[iCnt]);
          vStore.Disconnect;
        end;
      except
        raise Exception.Create('关闭密钥库失败!');
      end;
    end;
    
    constructor TDigital_CAPICOM.Create(const StoreName, ProviderName: string);
    begin
      CoInitialize(nil);
      FProviderName:= ProviderName;
      FStoreName := StoreName;
      FStoreList:= TStringlist.create;
      GetCertificate;
    end;
    
    destructor TDigital_CAPICOM.Destroy;
    begin
      FStoreList.Free;
      ICert := nil;
      ICert2:= nil;
      CoUninitialize;
      inherited;
    end;
    
    function TDigital_CAPICOM.ExtractSignedContent(
      const AFileName: string): string;
    var
      fs: TFileStream;
      iHeadLen, iContentLen, iPos: Integer;
      sContentLength: string;
      ms: TMemoryStream;
      BDA_Head, BDA_Cont: TByteDynArray;
    begin
      Result := '';
      if not FileExists(AFileName) then
        raise Exception.Create('文件"' + AFileName + '"不存在');  
      iHeadLen := Length(HYMSignature) + HashString_Length;
      SetLength(BDA_Head, iHeadLen);
      ms:= TMemoryStream.Create;
      ms.LoadFromFile(AFileName);
      fs := TFileStream.Create(AFileName, fmCreate);
      try
        ms.Position:= 0;
        ms.Read(BDA_Head[0], iHeadLen);
        sContentLength := Byte2String(BDA_Head); //含有长度信息
        iPos := Pos(HYMSignature, sContentLength);
        if iPos > 0 then
        begin
          //取得长度
          iContentLen := StrToInt(Copy(sContentLength, Length(HYMSignature) + 1, MaxInt));
          SetLength(BDA_Cont, iContentLen);
          ms.Read(BDA_Cont[0], iContentLen);
          Result := Byte2String(BDA_Cont);
          //该位置之后的内容为真正需要的
          fs.CopyFrom(ms, ms.Size - ms.Position); //读取文件内容去除文件头部分
          fs.Position := 0;
        end
      finally
        ms.Free;
        fs.Free;
      end;
    end;
    
    function TDigital_CAPICOM.GetCertficateInfo(
      var ACertInfo: TStampInfo): Boolean;
    var
      iCnt: Integer;
    begin
      Result := True;
      if ICert <> nil then
      begin
        ACertInfo.PKAlg := FAlgType;
        ACertInfo.PKLength := FPKLength;
        for iCnt := 0 to Length(FPublicKey) - 1 do
        begin
          ACertInfo.PKContent[iCnt] := FPublicKey[iCnt + 1];
        end;
        ACertInfo.EndDate:= ICert.ValidToDate;
        ACertInfo.DispachTime:= ICert.ValidFromDate;
      end
      else
        result:= False;
    end;
    
    procedure TDigital_CAPICOM.GetCertificate;
    var
      vStore: TStore;
      iCnt: Integer;
      IBaseIntf: IInterface;
      ICert2Dsp: ICertificate2Disp;
    begin
      if ICert2 = nil then
      begin
        vStore := OpenStore(FStoreName);
        for iCnt := 1 to vStore.Certificates.Count do
        begin
          IBaseIntf := vStore.Certificates.Item[iCnt];
          try
            if IBaseIntf.QueryInterface(ICertificate2Disp, ICert2Dsp) = 0
            then
            begin
              //确认硬件是否连接
              if ICert2Dsp.HasPrivateKey then
              begin
                //确认是否为指定CSP提供商
                if ((FProviderName = CSPProvider_ePass) and
                    ((ICert2Dsp.PrivateKey.ProviderName = CSPProvider_ePass_1K) or
                     (ICert2Dsp.PrivateKey.ProviderName = CSPProvider_ePass_3K)))
                   or (ICert2Dsp.PrivateKey.ProviderName = FProviderName)
                then
                begin
                  IBaseIntf.QueryInterface(IID_ICertificate2, ICert2);
                  IBaseIntf.QueryInterface(IID_ICertificate, ICert);
                  FPublicKey:= ICert2Dsp.publickey.EncodedKey.Format(True);
                  FPKLength:= ICert2Dsp.publickey.Length;
                  FAlgType:= ICert2Dsp.publickey.Algorithm.FriendlyName;
                end;
              end;
            end;
          except
            //某些不支持CAPICOM的,会出现异常
            ICert2 := nil;
          end;
        end;
      end;
    end;
    
    function TDigital_CAPICOM.GetStoreByName(AStoreName: string): TStore;
    var
      i: integer;
    begin
      i := FStoreList.IndexOf(AStoreName);
      if i >= 0 then
        result := FStoreList.Objects[i] as Tstore
      else
        result := nil;
    end;
    
    function TDigital_CAPICOM.GetThumbPrint: string;
    begin
      Result := ''; 
      if ICert <> nil then
        Result := ICert.Thumbprint;
    end;
    
    function TDigital_CAPICOM.OpenStore(AStoreName: string): TStore;
    var
      vStore: TStore;
    begin
      vStore := self.GetStoreByName(AStoreName);
      if vStore = nil then 
      try
        vStore := TStore.Create(nil);
        //默认为从CurrenUser读取, 后续可能会是CAPICOM_SMART_CARD_USER_STORE 智能卡
        vStore.Open(CAPICOM_CURRENT_USER_STORE, AStoreName,
           CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED or CAPICOM_STORE_OPEN_INCLUDE_ARCHIVED
           or CAPICOM_STORE_OPEN_EXISTING_ONLY);
        self.FStoreList.AddObject(AStoreName, vStore);
      except
       on E:exception do
        raise exception.Create('无法打开密钥库!'+E.Message);
      end;
      Result := vStore;
    end;
    
    function TDigital_CAPICOM.Pack(const sInPath, sOutPath: string;
      bOverride: Boolean): Boolean;
    var
      EnvelopedData: IEnvelopedData;
      BUFFER: WideString;
      FileStm: TFileStream;
      iP, oP: string;
    begin
      ip:= StringReplace(sInPath, '\\', '\', [rfReplaceAll]);
      op:= StringReplace(sOutPath, '\\', '\', [rfReplaceAll]);
      Result := True;
      EnvelopedData := CoEnvelopedData.Create;
      //指定采用的CSP算法类型
      EnvelopedData.Algorithm.Name := Algorithm;
      //指定加密长度
      EnvelopedData.Algorithm.KeyLength := EnLength;
      try
        //获取证书接口
        GetCertificate;
        //目前sInPath是一个文件夹,先压缩,再解密
        Files2ZipArchive(ip, op, RZipPassWd);
        //执行签名
        SignedFile(op, CAPICOM_ENCODE_BASE64);
        //获取要加密的内容
        FileStm := TFileStream.Create(sOutPath, fmOpenRead);
        try
          Pointer(Buffer):= SysAllocStringByteLen (nil, FileStm.Size);
          FileStm.ReadBuffer(Pointer(Buffer)^, FileStm.Size);
          EnvelopedData.Content:= Buffer;
        finally
          FileStm.Free;
        end;
        //基于64位编码加密
        EnvelopedData.Recipients.Add(ICert2);
        Buffer:= EnvelopedData.Encrypt(CAPICOM_ENCODE_BASE64);
        //输出加密内容
        FileStm := TFileStream.Create(sOutPath, fmCreate);
        try
          FileStm.WriteBuffer(Pointer(Buffer)^, SysStringByteLen(PWideChar(Buffer)));
        finally
          FileStm.Free;
        end;
      except
        Result := False;
      end;
    end;
    
    function TDigital_CAPICOM.SignedFile(const AFileName: string;
      EncodeType: CAPICOM_ENCODING_TYPE): Boolean;
    var
      Signer: ISigner2;
      SignedData: ISignedData;
      HashString: string;
      SignedContent: WideString;
    begin
      Result := True;
      try
        GetCertificate;
        //获取文件哈希值
        HashString:= GetFileHash(AFileName);
        //构建 签名者
        Signer := CoSigner.Create;
        Signer.Certificate := ICert2;
        //构建 数据签名对象
        SignedData := CoSignedData.Create;
        //执行签名
        SignedData.Content:= HashString;
        SignedContent := SignedData.Sign(Signer, False, EncodeType);
        //附加签名信息
        AppendSignedContent(AFileName, SignedContent);
      except
        Result := False;
      end;
    end;
    
    function TDigital_CAPICOM.Unpack(const sInPath, sOutPath: string;
      bCreateDirectory: Boolean): Boolean;
    var
      EnvelopedData: IEnvelopedData;
      BUFFER: WideString;
      FileStm: TFileStream;
      vDecryptFileName: string;
    begin
      Result := True;
      EnvelopedData := CoEnvelopedData.Create;
      //指定采用的CSP算法类型
      EnvelopedData.Algorithm.Name := Algorithm;
      //指定加密长度
      EnvelopedData.Algorithm.KeyLength := EnLength;
      try
        //获取数字证书接口
        GetCertificate;
        //关联证书以解密
        EnvelopedData.Recipients.Add(ICert2);
        //获取加密内容
        FileStm := TFileStream.Create(sInPath, fmOpenRead );
        try
          Pointer(Buffer):= SysAllocStringByteLen (nil, FileStm.Size);
          FileStm.ReadBuffer(Pointer(Buffer)^, FileStm.Size);
        finally
          FileStm.Free;
        end;
        //解密
        EnvelopedData.Decrypt(Buffer);
        Buffer:= EnvelopedData.Content;
        //输出解密内容
        vDecryptFileName:= sOutPath + ExtractFileName(sInPath);
        FileStm := TFileStream.Create(vDecryptFileName, fmCreate);
        try
          FileStm.WriteBuffer(Pointer(Buffer)^, SysStringByteLen(PWideChar(Buffer)));
        finally
          FileStm.Free;
        end;
        //验证签名
        VerifySign(vDecryptFileName);
        //因为有压缩,再解压   ZipArchive2Files
        ZipArchive2Files(vDecryptFileName, sOutPath, RZipPassWd);
        DeleteFile(PAnsiChar(vDecryptFileName));
      except
        Result := False;
      end;
    end;
    
    function TDigital_CAPICOM.VerifySign(const AFileName: string): Boolean;
    var
      SignedData: ISignedData;
      HashString: WideString;
      ASignedContent: string;
    begin
      Result := True;
      try
        GetCertificate;
        //先获取签名信息,因为会做信息分离,还原出加上签名前的数据
        ASignedContent:= ExtractSignedContent(AFileName);
        //获取文件哈希值
        HashString:= GetFileHash(AFileName);
        //构建 数据签名对象
        SignedData := CoSignedData.Create;
        SignedData.Content := HashString;
        //执行检查
        SignedData.Verify(ASignedContent, False, CAPICOM_VERIFY_SIGNATURE_ONLY);
      except
        Result := False;
        Raise Exception.Create('数字签名校验失败!');
      end;
    end;
    
    function TDigital_CAPICOM.VerifyUserAvailable: Boolean;
    begin
      Result := False;
      if (ICert2 <> nil) and ICert2.HasPrivateKey then
        Result:= True;
    end;

    另外,还需要一个管理类,目的是解除依赖,这里就不说明了。

    功能的实现,通过google,不论你了解或不了解,都可以得到较多信息,帮助实现。更多的还是在于怎么去设计?怎么让后续的开发人员更容易维护?

    这里面有个与证书接口相关的问题,比如在GetCertificate,里面有判断PrivateKey,必须使用Disp接口,直接用ICertificate,会出现地址错误。具体原因,还待查证。有谁知道的,还请你指点指点。谢谢!