delphi实现数字签名

  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,也可能不支持,所以,后续可能会有相应由多种方法去执行签名。可提取接口,来解除这样的依赖。

      接口定义如下:

 

  1. IDigitalIntf = interface(IUNKNOWN)  
  2. ['{78657307-FD4A-452F-91FF-956379A7F654}']  
  3.   //验证设备   
  4.   function VerifyUserAvailable: Boolean;  
  5.   //签名与数字信封加密   
  6.   function Pack(const sInPath: stringconst sOutPath: string; bOverride: Boolean): Boolean;      
  7.   //数字信封解密与签名验证   
  8.   function Unpack(const sInPath: stringconst 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实现类,构造如下:

 

 

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

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

 

 

  1.   

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

posted @ 2014-05-18 20:21  Max Woods  阅读(2599)  评论(0编辑  收藏  举报