http://blog.csdn.net/avan_lau/article/details/7367480
[delphi] view plaincopyprint?
- 上周,另一部门需要支援解决数字签名问题。但因为之前也没做过,现学现卖。此方面可参考的中文资料较少,特作分享,方便查阅。
上周,另一部门需要支援解决数字签名问题。但因为之前也没做过,现学现卖。此方面可参考的中文资料较少,特作分享,方便查阅。
有关数字签名的概念、原理,这里就不做介绍了,请自行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?
- 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,会出现地址错误。具体原因,还待查证。有谁知道的,还请你指点指点。谢谢!