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. 【译】Asp.Net Identity与Owin,到底谁是谁?

    送给正在学习Asp.Net Identity的你 :-) 原文出自 trailmax 的博客AspNet Identity and Owin. Who is who. Recently I have ...

  2. .NET C#错误:所生成项目的处理器框架“MSIL”与引用“wdapi_dotnet1021”的处理器架构“AMD64”不匹配

    .NET C#错误:所生成项目的处理器框架“MSIL”与引用“wdapi_dotnet1021”的处理器架构“AMD64”不匹配. 直接在项目右键属性->生成->x64. 即可解决

  3. 使用TensorFlow给花朵🌺分类

    第一步:准备好需要的库 tensorflow-gpu  1.8.0 opencv-python     3.3.1 numpy skimage os pillow 第二步:准备数据集: 链接:http ...

  4. Demo005 小学四则运算自动生成程序

    目录 小学四则运算自动生成程序 0.传送门 1.题目要求 2.功能实现 2.1 总体设计 2.2 用户欢迎界面 2.3 用户功能界面 2.4 屏幕输出 2.5 文本输出 2.6 获取时间 2.7 用户 ...

  5. django之class Meta

    通过一个内嵌类 "class Meta" 给你的 model 定义元数据, 类似下面这样: class Foo(models.Model): bar = models.CharFi ...

  6. SqlServer中Sql查看存储过程

    ( 一)利用Sql语句查询数据库中的所有表 1.利用sysobjects系统表 select * from sysobjects where xtype='U'  2,利用sys.tables目录视图 ...

  7. ClouderaManager配置报警邮件

  8. 【IObit】五大软件激活码( Advanced Systemcare....)

    IObit Malware Fighter 6Pro 破解: 打开软件安装位置,下载替换dll文件 链接: https://pan.baidu.com/s/1Euz87MCANuCnRqZsMQ_w4 ...

  9. Javascript数组Array的方法总结!

    1.join() 将数组的元素组成一个字符串,以分隔符连接,如果省略则默认逗号为分隔符,该方法只接收一个参数:分隔符.此方法不会改变原数组. let arr = [1,2,3,4] let arr1 ...

  10. css基础之line-height

    什么是line-height(行高)?line-height设置1.5和150%有什么区别?这是一个比较常见的css面试题,带着这个问题往下看.所谓行高是指一段文字中某一行的高度吗?具体来说不是.w3 ...