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: 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实现类,构造如下:

  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;

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


  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实现数字签名的更多相关文章

  1. Delphi发布ActiveX控件 制作CAB包 数字签名相关

    文件: SignTool.rar 大小: 84KB 下载: 下载 最近我正在研究ActiveX技术.我使用Delphi 7创建了一个具有ActiveForm的ActiveX控件应用程序.这个控件产生一 ...

  2. Delphi在win7/vista下写注册表等需要管理员权限的解决方案

    看到论坛好多人问win7下写注册表的问题,我结合自己的理解写了一点东西,首先声明一下,本人初学Delphi,水平有限,大家见笑了,有什么不对之处请老鸟多指点. [背景]win7/Vista提供的UAC ...

  3. delphi开源JWT

    delphi开源JWT 开源GIT地址:https://github.com/paolo-rossi/delphi-jose-jwt JSON Web Token (JWT)是一个开放标准(RFC 7 ...

  4. 学习笔记:7z在delphi的应用

    最近做个发邮件的功能,需要将日志文件通过邮件发送回来用于分析,但是日志文件可能会超级大,测算下来一天可能会有800M的大小.所以压缩是不可避免了,delphi中的默认压缩算法整了半天不太好使,就看了看 ...

  5. 2、摘要函数——MD2/MD4/MD5数字签名

    摘要是用来防止数据被私自改动的方法,其中用到的函数叫做摘要函数.这些函数的输入可以是任意大小的信息,但是输出是大小固定的摘要.摘要有个重要的特性:如果改变了输入信息的任何内容,即使改变一位,输出也将发 ...

  6. delphi连接sql存储过程

    针对返回结果为参数的 一. 先建立自己的存储过程 ALTER PROCEDURE [dbo].[REName] ) AS BEGIN select ROW_NUMBER() over(order by ...

  7. delphi 2010与delphi XE破解版的冲突

    在系统中同时安装了Dephi 2010LITE版与Delphi XE lite后,总是会有一个有问题 是因为两者都是读取C:\ProgramData\Embarcadero目录下的license文件, ...

  8. [Delphi] Delphi版本号对照

    VER300    Delphi Seattle / C++Builder Seattle    23    230    (Delphi:Win32/Win64/OSX/iOS32/iOS64/An ...

  9. Android 数字签名

    一个ApK如果要安装到手机上,必须要一个数字签名,不过你是debug也好,release也好,这个数字签名来源一个叫做证书的东西,在我们debug的时候,开发工具已经帮我们生成了一个叫做debug.k ...

随机推荐

  1. 初步认识mitmproxy(一)

    在windows机器上,经常用的最多的是fiddler工具,很强大,图形化界面,使用方便.简单:在mac上,Charles 类似fiddler工具,同样是易于操作的图形化界面,同样都是通过代理的方式实 ...

  2. C/C++杂记:深入理解数据成员指针、函数成员指针

    1. 数据成员指针 对于普通指针变量来说,其值是它所指向的地址,0表示空指针. 而对于数据成员指针变量来说,其值是数据成员所在地址相对于对象起始地址的偏移值,空指针用-1表示.例: 代码示例: str ...

  3. tomcat报错catalina.sh: line 401: /usr/java/jdk1.7.52/bin/java: No such file or directory

    将生产服务器的Tomcat目录打包过来后解压后,启动Tomcat后,发现如下问题: # ./shutdown.sh  Using CATALINA_BASE:   /usr/local/tomcat  ...

  4. ps自由变换以及再次变换快捷键

    ctrl+t:自由变换ctrl+shift+t:再次变换ctrl+shift+alt+t:复制一次,再次变换.

  5. LeetCode(23):合并K个排序链表

    Hard! 题目描述: 合并 k 个排序链表,返回合并后的排序链表.请分析和描述算法的复杂度. 示例: 输入: [   1->4->5,   1->3->4,   2-> ...

  6. InnoDB master thread工作原理

    我们简单交流下InnoDB master thread学习,有兴趣的朋友可以参考<<MySQL技术内蒙--InnoDB存储引擎第二版>> void master_thread( ...

  7. 使用python爬取整本《盗墓笔记》

    一.前言 <盗墓笔记>是一本经典的盗墓题材小说,故事情节引人入胜.本文将使用python2.7通过小说网站http://www.daomubiji.com/来爬取整本盗墓笔记并保存,在这一 ...

  8. 关于Sublime text3 配置及插件整理

    Package Control组件在线安装 按Ctrl+`调出console(注:避免热键冲突) 粘贴以下代码到命令行并回车: import urllib.request,os; pf = 'Pack ...

  9. Codeforces Round 542 (Div. 2)

    layout: post title: Codeforces Round 542 (Div. 2) author: "luowentaoaa" catalog: true tags ...

  10. ApiPost自动化测试基础之:接口参数依赖的情景处理

    在<ApiPost环境变量之第1课>里,我们介绍了什么是ApiPost环境变量,并如何定义.使用它. 环境变量.接口参数依赖的处理是ApiPost自动化测试的基础.本文主要讲解接口参数依赖 ...