首页  编辑  

如何在Delphi中使用SSL协议

Tags: /超级猛料/Network.网络通讯/其它/   Date Created:

摘要:本文主要介绍如何在Delphi中使用SSL协议.一共分为七个部分:(1)SSL协议是什么?(2)Delphi中如何使用SSL协议?(3)SSL客户端编程实例.(4)SSL服务端编程实例.(5)SSL证书编程实例.(6)中间人欺骗实例.(7)其它.本文作者同时有一个用SSL协议编写的作品叫SSLPROXY,感兴趣的读者可以从作者主页 http://www.138soft.org 下载.

一:SSL协议是什么?

 SSL是一种加密传输协议.引用网上一段话:SSL 是Secure socket Layer英文缩写,它的中文意思是安全套接层协议,指使用公钥和私钥技术组合的安全网络通讯协议。SSL协议是网景公司(Netscape)推出的基于 WEB应用的安全协议,SSL协议指定了一种在应用程序协议(如Http、Telenet、NMTP和FTP等)和TCP/IP协议之间提供数据安全性分层的机制,它为TCP/IP连接提供数据加密、服务器认证、消息完整性以及可选的客户机认证,主要用于提高应用程序之间数据的安全性,对传送的数据进行加密和隐藏,确保数据在传送中不被改变,即确保数据的完整性。

二:Delphi中如何使用SSL协议?

 要使用SSL协议,有几种方法,一种是根据SSL协议文档自己实现,另外一种是调用第三方的开发库.现在一般的开发者(无论是Delphi还是VC)都是使用开源的OpenSSL库.本文的介绍都是基于OpenSSL的.

 Delphi中的Indy组件本身就是支持OpenSSL的.例如,对于IdTCPClient,只需要拖一个IdSSLIOHandlerSocket控件到窗口,然后将IdTCPClient的IOHandler指向它即可.不过IdSSLOpenSSLHeaders.pas的声明比较老,是基于OpenSSL 0.9.4的,而0.9.4的DLL文件现在比较少见了.现在一般使用OpenSSL 0.9.7i.(可以打开IdSSLOpenSSLHeaders.pas,搜索版本,老版本是这样的:

 OPENSSL_OPENSSL_VERSION_NUMBER = $00904100;

 OPENSSL_OPENSSL_VERSION_TEXT = 'OpenSSL 0.9.4 09 Aug 1999';  {Do not localize}

 0.9.4和0.9.7i的区别是0.9.4的部分函数位于ssleay32.dll中,而0.9.7i则抛弃了此DLL.网上有很多修改过的基于0.9.7i版本的IdSSLOpenSSLHeaders.pas,本文的演示代码主要是基于0.9.7i.

三:SSL客户端编程实例

 使用OpenSSL之前,得先装载SSL库.直接调用Load函数即可完成:

if not uIdSSLOpenSSLHeaders.Load then //装载ssl库

 begin

   Application.MessageBox('装载ssl动态库失败!', '错误', MB_ICONEXCLAMATION + MB_TOPMOST);

   Halt;

 end;

 然后装载错误字符库和加密算法:

 IdSslLoadErrorStrings;

 IdSslAddSslAlgorithms; //load所有的SSL算法

 

 然后,初始化SSL使用的协议版本和SSL上下文:

 methClient := IdSslMethodClientV23;//SSL协议有多个版本,包括SSLv2,SSLv23,SSLv3和TLSv1我们这里使用v23.

 if methClient = nil then

 begin

   Application.MessageBox('建立SSL Client所用的method失败!', '错误', MB_ICONEXCLAMATION + MB_TOPMOST);

   Halt;

 end;

 ctxClient := IdSslCtxNew(methClient); //初始化上下文情景.

 if ctxClient = nil then

 begin

   Application.MessageBox('创建客户端SSL_CTX失败!', '错误', MB_ICONEXCLAMATION + MB_TOPMOST);

   Halt;

 end;

 上下文情景初始化完毕后,就可以进行socket通信了.首先创建一个正常的socket,连接到目的地址,然后申请一个SSL会话的环境:

 sslClient := IdSslNew(ctxClient);

 if sslClient = nil then

 begin

   closesocket(ClientSocket);

   Application.MessageBox('申请SSL会话的环境失败!', '错误', MB_ICONEXCLAMATION + MB_TOPMOST);

   Halt;

 end;

 然后绑定套接字:

 IdSslSetFd(sslClient, ClientSocket);

 IdSslConnect(sslClient);

 成功后就可以调用IdSslRead和IdSslWrite来读写数据了.

 下面我们给出一个完整的例子,用于连接安全焦点的论坛首页.此程序用到一个Memo控件和一个按钮控件.注意:此代码没有做过多的错误处理,读者可以自行添加:

procedure TForm1.Button1Click(Sender: TObject);

const

 //Url: string = ' https://www.xfocus.net/bbs/index.php?lang=cn ';

 Host: string = ' www.xfocus.net ';

 Port: Word = 443;

var

 Wsa: TWSAData;

 ctxClient: PSSL_CTX; //SSL上下文

 methClient: PSSL_METHOD;

 sRemoteIP: string;

 ClientSocket: TSocket;

 sAddr: TSockAddr;

 sslClient: PSSL;

 pServer_Cert: PX509;

 pStr: Pchar;

 buf: array[0..4095] of Char;

 nRet: integer;

 strSend: string;

begin

 Button1.Enabled := False;

 if WSAStartup($101, Wsa) <> 0 then //初始化Wsock32.dll,MakeWord(2,2),

 begin

   Application.MessageBox('初始化Winsock动态库失败!', '错误', MB_ICONEXCLAMATION + MB_TOPMOST);

   Halt;

 end;

 if not uIdSSLOpenSSLHeaders.Load then //装载ssl库

 begin

   Application.MessageBox('装载ssl动态库失败!', '错误', MB_ICONEXCLAMATION + MB_TOPMOST);

   Halt;

 end;

 IdSslLoadErrorStrings;

 IdSslAddSslAlgorithms; //load所有的SSL算法

 methClient := IdSslMethodClientV23;

 if methClient = nil then

 begin

   Application.MessageBox('建立SSL Client所用的method失败!', '错误', MB_ICONEXCLAMATION + MB_TOPMOST);

   Halt;

 end;

 ctxClient := IdSslCtxNew(methClient); //初始化上下文情景

 if ctxClient = nil then

 begin

   Application.MessageBox('创建客户端SSL_CTX失败!', '错误', MB_ICONEXCLAMATION + MB_TOPMOST);

   Halt;

 end;

 sRemoteIP := GetIPAddressByHost(Host);

 if sRemoteIP = '' then

 begin

   Application.MessageBox('获取远程IP地址失败!', '错误', MB_ICONEXCLAMATION + MB_TOPMOST);

   Halt;

 end;

 ClientSocket := Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

 if ClientSocket = INVALID_SOCKET then

 begin

   Application.MessageBox('创建socket失败!', '错误', MB_ICONEXCLAMATION + MB_TOPMOST);

   Halt;

 end;

 FillChar(sAddr, Sizeof(TSockAddr), #0);

 sAddr.sin_family := AF_INET;

 sAddr.sin_addr.S_addr := inet_addr(Pchar(sRemoteIP));

 sAddr.sin_port := htons(Port);

 if connect(ClientSocket, sAddr, sizeof(TSockAddr)) = SOCKET_ERROR then

 begin

   Application.MessageBox('连接远程服务器失败!', '错误', MB_ICONEXCLAMATION + MB_TOPMOST);

   Halt;

 end;

 sslClient := IdSslNew(ctxClient); //申请SSL会话的环境,参数就是前面我们申请的 SSL通讯方式。返回当前的SSL 连接环境的指针。

 if sslClient = nil then

 begin

   closesocket(ClientSocket);

   Application.MessageBox('申请SSL会话的环境失败!', '错误', MB_ICONEXCLAMATION + MB_TOPMOST);

   Halt;

 end;

 IdSslSetFd(sslClient, ClientSocket); //绑定读写套接字

 if IdSslConnect(sslClient) = -1 then

 begin

   IdSslFree(sslClient);

   closesocket(ClientSocket);

   Application.MessageBox('绑定读写套接字失败!', '错误', MB_ICONEXCLAMATION + MB_TOPMOST);

   Halt;

 end;

 Memo1.Lines.Add(Format('SSL连接使用的算法为:%s', [IdSSLCipherGetName(IdSSLGetCurrentCipher(sslClient))]));

 pServer_Cert := IdSslGetPeerCertificate(sslClient);

 if (pServer_Cert <> nil) then

 begin

   Memo1.Lines.Add('服务端证书:');

   pStr := IdSslX509NameOneline(IdSslX509GetSubjectName(pServer_Cert), nil, 0);

   if pStr <> nil then Memo1.Lines.Add(Format('主题: %s', [pStr]));

   pStr := IdSslX509NameOneline(IdSslX509GetIssuerName(pServer_Cert), nil, 0);

   if pStr <> nil then Memo1.Lines.Add(Format('发行者: %s', [pStr]));

 end

 else

 begin

   Memo1.Lines.Add('客户端没有证书.');

 end;

 Memo1.Lines.Add('');

 strSend := 'GET /bbs/index.php?lang=cn HTTP/1.1'#$D#$A +

   'Accept: */*'#$D#$A +

   'Accept-Language: zh-cn'#$D#$A +

   'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)'#$D#$A +

   'Host: ' + Host + #$D#$A +

   'Connection: Keep-Alive'#$D#$A#$D#$A;

 if IdSslWrite(sslClient, @strSend[1], Length(strSend)) <= 0 then

 begin

   IdSslFree(sslClient);

   closesocket(ClientSocket);

   Application.MessageBox('发送失败!', '错误', MB_ICONEXCLAMATION + MB_TOPMOST);

   Halt;

 end;

 FillChar(buf, sizeof(buf), #0);

 nRet := IdSslRead(sslClient, buf, sizeof(buf));

 while nRet > 0 do

 begin

   Memo1.Lines.Add(StrPas(Buf));

   FillChar(buf, sizeof(buf), #0);

   Application.ProcessMessages;

   nRet := IdSslRead(sslClient, buf, sizeof(buf));

 end;

 IdSslFree(sslClient);

 closesocket(ClientSocket);

 if ctxClient <> nil then IdSslCtxFree(ctxClient);

 WSACleanup; //结束对WSocket32.dll调用

 Button1.Enabled := True;

end;

 

四:SSL服务端编程实例

五:SSL证书编程实例

六:中间人欺骗实例

七:其它

ssldemo_client.rar (526.6KB)