delphi實現數字簽名
git
上週,另外一部門須要支援解決數字簽名問題。但由於以前也沒作過,現學現賣。此方面可參考的中文資料較少,特做分享,方便查閱。 上週,另外一部門須要支援解決數字簽名問題。但由於以前也沒作過,現學現賣。此方面可參考的中文資料較少,特做分享,方便查閱。 有關數字簽名的概念、原理,這裏就不作介紹了,請自行google或百度。 利用證書對文件進行簽名,從證書來源看,可分爲兩種:一、軟證書:就是將*.pfx文件導入到系統中,這意味着,只要登陸到PC中的用戶,都可以使用該證書;二、硬證書:一般將證書存放到uKey中(smart card),這樣的好處是,只有擁有usb key的人才有權限使用該證書。 USB Key一般支持CryptToAPI——除非特殊安全須要,只公佈使用本身的接口,不支持微軟接口。因爲使用CryptToAPI,使用起來較繁瑣,微軟提供了CAPICOM組件,方便開發。 不管是硬證書或軟證書,只要支持CryptToAPI接口,那麼CAPICOM都可使用。爲此本次內容以CAPICOM,做爲數字簽名功能的基礎。 動手以前,首先要熟悉數字簽名的過程。經過分析,主要是兩部分:數字簽名(身份標識及防篡改)和數字信封;其實按業務流程,簽名以前還有簽章的過程(也就是一般的蓋章);過程大體以下: 發送方 一、驗證證書是否準備好?(如果硬證書,usbkey是否已插入;判斷證書是否有效); 二、對文件進行簽名; 三、對文件進行數字信封(公鑰加密); 四、可選:填入CSP(加密服務提供商,一般是在USB Key當中)信息 接收方: 一、獲取文件,讀取CSP信息; 二、依據CSP信息,獲取相關證書並驗證; 三、利用證書進行數字解封; 四、簽名驗證,確認身份及文件的完整性(是否被篡改); 依據以上分析,程序可這樣設計,因爲USB Key可能支持CAPICOM,也可能不支持,因此,後續可能會有相應由多種方法去執行簽名。可提取接口,來解除這樣的依賴。 接口定義以下: [delphi] view plaincopyprint? 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; 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? 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; 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? 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; 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,會出現地址錯誤。具體緣由,還待查證。有誰知道的,還請你指點指點。謝謝!算法