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,会出现地址错误。具体原因,还待查证。有谁知道的,还请你指点指点。谢谢!

delphi实现数字签名,布布扣,bubuko.com

时间: 2024-10-10 07:35:20

delphi实现数字签名的相关文章

Delphi常用系统函数总结

字符串处理函数 Unit System 函数原型 function Concat(s1 [, s2,..., sn]: string): string; 说明 与 S := S1 + S2 + S3 ...; 相同. 将字符串相加. 函数原型 function Copy(S: string; Index, Count: Integer): string;说明 S : 字符串. Indexd : 从第几位开始拷贝. Count : 总共要拷贝几位. 从母字符串拷贝至另一个字符串. 函数原型 pro

最新的Delphi版本号对照

The CompilerVersion constant identifies the internal version number of the Delphi compiler. It is defined in the System unit and may be referenced either in code just as any other constant: if CompilerVersion = 20 then sCompilerName := 'Delphi 2009';

Delphi XE10 dxLayoutControl 控件应用指南

http://www.cnblogs.com/Bonny.Wong/p/7440288.html DevExpress VCL套件是一套非常强大的界面控件,可惜关于Delphi开发方面的说明太少,有些控件使用起来一头雾水,不知从何下手.本节详细介绍在Delphi Xe10 Seattle中如何利用dxLayoutControl 控件来做界面布局. 1.  首先从工具箱面板中将dxLayoutControl放在Form上,设置2个关键属性如下: 属性 属性值 说明 Align alClient 一

delphi 移动开发博客地址收集

这个是各位博主学习整理的笔记,很值得大家学习. XE2011的博客: http://www.cnblogs.com/xe2011/ 万一的博客:http://www.cnblogs.com/del/ 武稀松的博客:http://www.raysoftware.cn/ delphiteacher的博客:http://blog.csdn.net/DelphiTeacher 我一路走来的博客:http://blog.csdn.net/tingsking18/article/details/477210

Delphi使用android的NDK是通过JNI接口,封装好了,不用自己写本地代码,直接调用

一.Android平台编程方式:      1.基于Android SDK进行开发的第三方应用都必须使用Java语言(Android的SDK基于Java实现)      2.自从ndk r5发布以后,已经允许完全用C/C++ 来开发应用或者游戏,而不再需要编写任何Java 的代码   Android程序运行在Dalvik虚拟机中,NDK允许用户使用类似C / C++之类的原生代码语言执行部分程序. 二.跨平台移动开发   Delphi使用android的NDK是通过JNI接口,封装好了,不用自己

TStringBuilder类 - Delphi

摘自万一老师的博客,略作整理. //TStringBuilder.Create 可以无参数 procedure TForm1.Button1Click(Sender: TObject); var sb: TStringBuilder; begin sb := TStringBuilder.Create; sb.Append('Embarcadero'); sb.Append(' CodeGear'); sb.Append(' Delphi'); sb.Append(' 2009'); ShowM

delphi 属性 参数 新注释

delphi 属性 参数 新注释,在写代码的时候,可以自动看到属性.参数的的备注说明,太方便了. Tmyclass=class /// <summary> /// 姓名 /// </summary> name:string; /// <summary> /// 性别 /// </summary> sex:string; end; var aclass: Tmyclass; begin aclass.name; aclass.sex; 鼠标放上去的时候提示 写

[转]delphi的procedure of object

delphi的procedure of object(一个特殊的指针类型) 理论:     //适用于实现不是某一特定过程或函数          type                TNotifyEvent = procedure(Sender: TObject) of object;       首先:procedure 也是类型,可以理解为过程类型,定义过程的参数结构,而具体的实现可以动态赋值  onclick那样例子:      声明:  onclick= procedure(Sen

JDK自带方法实现RSA数字签名

JDK只支持MD2withRSA, MD5withRSA, SHA1withRSA 其他的如SHA512withRSA需要第三方包支持,如BC(bouncy castle) JDK的密钥长度默认仍是1024 1 package jdbc.pro.lin; 2 3 import java.security.InvalidKeyException; 4 import java.security.NoSuchAlgorithmException; 5 import java.security.Priv