前几天,本人写了几个Delphi的Base64转换函数,并写了一篇博客文章《Delphi版的Base64转换函数 》,该文中,本人用Indy中的TIdHashMessageDigest5类,通过MD5的Hash验证Base64编码和解码的正确性。事后,本人想验证文件的Base64编码和解码,并重新写了Base64转换函数,对整个文件流的Base64编、解码无疑也是正确的,见以下代码(其中的Base64Encode和Base64Decode函数是重新写的对流的Base64操作):
var
Source,Dest:TStream;
md5:TIdHashMessageDigest5;
Value:T4x4LongWordRecord;
s1,s2:string;
begin
ifOpenDialog1.Executethen
begin
md5:=TIdHashMessageDigest5.Create;
try
Source:=TFileStream.Create(OpenDialog1.FileName,fmOpenRead);//打开源文件
Value:=md5.HashValue(Source);//取得源文件Hash
s1:=md5.AsHex(Value);
Dest:=TFileStream.Create('Base64EncodeTest.txt',fmCreate);//Base64编码文件
try
Base64Encode(Source,Dest);
finally
Source.Free;
Dest.Free;
end;
Source:=TFileStream.Create('Base64EncodeTest.txt',fmOpenRead);//打开编码文件
Dest:=TFileStream.Create('Base64DecodeTest.txt',fmCreate);//还原源文件内容
try
Base64Decode(Source,Dest);
Dest.Position:=0;
Value:=md5.HashValue(Dest);//取得还原文件Hash
s2:=md5.AsHex(Value);
finally
Source.Free;
Dest.Free;
end;
finally
md5.Free;
end;
//显示源文件和还原文件的Hash十六进制字串,如果相等,证明编码和解码过程正确
ShowMessage(s1+#10+s2);
end;
end;
为了反复验证,又决定对流的部分内容进行验证,在代码Value := md5.HashValue(Source);前加了Source.Position := 100,代码Base64Encode(Source, Dest)改为Base64Encode(Source, Dest, 100);(该函数有2个默认参数,可定义起始位置和长度)
表示从流的100字节开始MD5Hash和Base64编码,可是无论如何2次Hash不相等,而截取的文件内容和长度无疑是正确的,通过反复调试,最后发现问题在TIdHashMessageDigest5类上,请看Indy的TIdHashMessageDigest5类的源码片断:
functionTIdHashMessageDigest4.HashValue(AStream:TStream):T4x4LongWordRecord;
Var
LStartPos:Integer;
LBitSize,
LSize:Int64;
S:String;
S1:String;
LFillSize:Integer;
begin
LStartPos:=AStream.Position;
LSize:=AStream.Size-LStartPos;
FBuffer:=MD4_INIT_VALUES;
whileLSize-AStream.Position>=SizeOf(FCBuffer)do
begin
AStream.Read(FCBuffer[0],SizeOf(FCBuffer));
MDCoder;
end;
//EnsureS1hassufficientsizetoholdacomplete64-bytechunk
SetLength(S1,SizeOf(FCBuffer));
//Readthelastsetofbytes.
LStartPos:=AStream.Read(S1[1],64);
//NowadjustS1toonlyholdthelastsetofbytes.
SetLength(S1,LStartPos);
(后面的代码略)
问题出在while LSize - AStream.Position >= SizeOf(FCBuffer) do上,函数中,LSize := AStream.Size - LStartPos计算的是流的剩余长度,如果LStartPos=0,则LSize为整个流的长度,这时运行是正确的,否则如果LSartPos<>0则是错误的!我测试时的流长度为1565,AStream.Position=100(即LStartPos=100),LSize=1465,正确的读法应该是每次读64字节(Sizeof(FCBuffer)=64),读22次后,余57字节。而按上面循环条件读21次后,AStream.Position=21*64+100=1444,循环结束,余21字节,这就不正确了,而且当运行LStartPos := AStream.Read(S1[1], 64)时,由于流长度为1565,流的当前位置AStream.Position=1444,剩121字节,所以读出的字节还是64,更是错上加错!
我又在Delphi2007中看了Indy9和Indy10的源码,虽代码有所改动,但循环原理一样,由于MD5继承Md4类,所以TIdHashMessageDigest4也是错误的,而且TIdHashMessageDigest2也是一样!我将代码稍稍改了一下:LSize := AStream.Size - LStartPos;改为LSize := AStream.Size; LBitSize := (AStream.Size - AStream.Position) * 8,后面的LBitSize计算句删掉,重新运行,结果就正确了!
另外,即使上面改正正确了,但该类的流操作函数还是有缺陷的,只能定义流的开始位置,不能定义结束位置或者读的长度。Indy10的Md5(md4)函数修改了参数,理应可以定义读的长度,不料看了一下代码,那个参数根本就没有使用。
所以,在此提醒大家,一般使用Indy的Hash类是没有问题的,特殊操作就不行了,这种BUG,开发者们这些年都没发现,真是吃惊!为了方便和正确起见,我只好着手自己写这些类了。
分享到:
相关推荐
### INDY控件使用指南:TIdAntiFreeze与线程技术详解 #### 一、TIdAntiFreeze概述 在开发基于网络的应用程序时,经常会遇到由于长时间等待网络操作而导致用户界面(UI)冻结的问题。为了解决这个问题,Indy...
在Delphi中使用Indy10实现TCP连接是一种常见的网络编程方式,我们可以使用IdTCPServer和IdTCPClient组件来实现服务器端和客户端的编程,并使用Fiber API来提高性能。 Indy10 的优势 Indy10具有以下优势: * 高...
首先,要删除已有的 Indy 9 组件,你需要遵循以下步骤: 1. **备份项目**:在进行任何修改之前,务必先备份你的项目,以防意外情况发生。 2. **找到 Indy 9 的组件库**:在 Delphi 7 的 Component Palette 中,...
不错的Indy 资料。CSDN上有人上传过了。他说的十分。我下了,看了看写的不错,还整理了下。要想学习Indy9的话可以下来看看。
根据delphi2010中的indy组件的TIdHTTP类制作封装了类:THttpModule/THttpsModule,实现了方法get和post。https访问需要的2个动态链接库文件(libeay32.dll、ssleay32.dll)也打在包中了。代码文件已经在delphi2010中...
"Indy09.pdf"和"Indy10.pdf"很可能是Indy 9和Indy 10的用户手册或开发指南,这些文档对于理解和使用Indy组件至关重要。它们会详细介绍如何在Delphi项目中配置和使用Indy,以及如何处理各种网络通信任务。 "Demos...
描述中提到,“安装时直接添加bpl文件,将目录添加引用即可”,这是指安装和使用Indy10的过程。BPL(Binary Package Library)是Delphi中的二进制包文件,包含了编译好的组件和类。开发者需要将Indy10的bpl文件添加...
Indy(Internet Direct)是一个广泛使用的开源网络组件库,主要用于Delphi和C++Builder开发人员。这个名为"indy10.6.0_10.6.2.zip"的压缩包包含了Indy 10的两个版本:10.6.0和10.6.2。Indy提供了一系列的组件,涵盖...
Indy(Internet Direct)是一个广泛使用的开源网络组件库,主要用于Delphi和C++Builder等RAD Studio集成开发环境。Indy10.5.9是该库的一个特定版本,旨在提供全面的互联网协议支持,包括TCP/IP、HTTP、FTP、SMTP、...
基于Delphi的MQTT协议实现(使用INDY无三方控件)使用方法参考: http://blog.tdiot.cc/?p=10&preview=true
Indy 10 是一个广泛使用的网络组件套件,尤其在Delphi开发环境中,它为开发者提供了构建网络应用的强大工具。对于Delphi 7这样的较旧版本,Indy 10 提供了更新的网络协议支持,如HTTP、FTP、SMTP、IMAP等,以及SSL/...
Indy的核心优势在于其模块化的设计,每个网络协议或功能都有独立的组件,这使得开发人员可以灵活地选择需要的功能,而不必引入不必要的依赖。例如,如果你的项目只需要发送电子邮件,你可以只添加处理SMTP和POP3通信...
### indy的客户端使用说明 #### 一、引言 indy 是一款强大的跨平台网络通信组件库,广泛应用于 Delphi 和 Free Pascal 开发环境中。它提供了丰富的功能来处理各种网络协议,如 HTTP、FTP、SMTP 等。本文将详细介绍 ...
Indy10是一款广泛使用的网络通信库,尤其在Delphi编程环境中非常流行。它提供了丰富的组件和函数,用于实现各种网络协议,如TCP/IP、UDP、HTTP、SMTP、IMAP等。Indy10帮助文档是开发者在使用该库时的重要参考资料,...
4. **集成OpenSSL到Delphi项目**: 要在Delphi 2010中使用Indy 10的SSL功能,首先需要将libeay32.dll和ssleay32.dll文件放置在应用程序的运行目录下,或者将其注册到系统路径中,以确保程序运行时可以找到这两个库。...
在Delphi7这个经典的开发环境中,使用Indy库可以实现高效、稳定的网络通信功能,其中包括电子邮件的发送和接收。Indy是一个强大的网络组件库,它提供了丰富的组件来处理各种网络协议,如TCP/IP、SMTP(Simple Mail ...
在“Indy9 Demo”中,BasicClientServer可能是一个包含了客户端和服务器端功能的示例程序,用于演示如何使用Indy9进行TCP通信。这个Demo可能展示了如何创建连接、发送和接收数据的基本步骤。然而,随着Indy的发展,...
这个压缩包文件集合了各种Indy的使用示例,帮助开发者深入理解和应用Indy的强大功能。 Indy是一个跨平台的组件库,支持多种网络协议,包括HTTP、FTP、SMTP、POP3、IMAP4等,广泛应用于网络服务器和客户端的开发。在...
配置Indy 9的SSL功能,首先要在你的项目中引入Indy相关的单元,如IndyCore, IndySystem, IndyProtocols和IndySSL。然后,对于服务器端,你需要实例化TIdServerComponent,并在其上添加TIdTCPServer组件,接着添加...