谈Delphi中SSL协议的应用(好多相关文章)
摘要:本文主要介绍如何在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服务端编程实例
服务端没有什么好讲的,跟客户端不同的地方,在于初始化时需要加载证书,然后Accept后需要再用IdSslAccept关联.关于如何用Delphi生成证书文件,详见下一讲.下面直接贴代码:
var
Form1: TForm1;
g_Wsa: TWSAData;
g_ServerSocketMain: TSocket = INVALID_SOCKET;
g_ctxServer: PSSL_CTX = nil; //SSL上下文
g_methServer: PSSL_METHOD = nil;
g_DebugCritSec: TRTLCriticalSection;
g_hAcceptThread: THandle = 0;
g_Start: BOOL = False;
implementation
{$R *.dfm}
function AcceptThread(lp: Pointer): DWORD; stdcall;
var
nAddrLen: integer;
sdClient: TSocket;
sAddrs: TSockAddr;
nErrorCode: integer;
sslServer: PSSL;
pClient_Cert: PX509;
pStr: Pchar;
nRet: integer;
buf: array[0..4095] of Char;
strBody, strSend: string;
begin
Result := 0;
while g_Start do
begin
nAddrLen := sizeof(TSockAddr);
sdClient := accept(g_ServerSocketMain, @sAddrs, @nAddrLen);
if (sdClient = INVALID_SOCKET) then
begin
nErrorCode := WSAGetLastError;
EnterCriticalSection(g_DebugCritSec);
if g_Start then Form1.Memo1.Lines.Add(Format('发生错误.错误代码:%d', [nErrorCode]));
LeaveCriticalSection(g_DebugCritSec);
end
else
begin
EnterCriticalSection(g_DebugCritSec);
Form1.Memo1.Lines.Add(Format('新连接.客户端IP:%s', [inet_ntoa(sAddrs.sin_addr)]));
LeaveCriticalSection(g_DebugCritSec);
sslServer := IdSslNew(g_ctxServer); //申请SSL会话的环境,参数就是前面我们申请的 SSL通讯方式,返回当前的SSL 连接环境的指针.
if sslServer = nil then
begin
closesocket(sdClient);
Continue;
end;
IdSslSetFd(sslServer, sdClient); //绑定读写套接字
nRet := IdSslAccept(sslServer);
&nb"7p0�B if (nRet = -1) then<br "2="" ="" begin<br=""> closesocket(sdClient);
IdSslShutdown(sslServer);
IdSslFree(sslServer);
Continue;
end;
EnterCriticalSection(g_DebugCritSec);
Form1.Memo1.Lines.Add(Format('SSL连接使用的算法为:%s', [IdSSLCipherGetName(IdSSLGetCurrentCipher(sslServer))]));
LeaveCriticalSection(g_DebugCritSec);
pClient_Cert := IdSslGetPeerCertificate(sslServer);
if (pClient_Cert <> nil) then
begin
EnterCriticalSection(g_DebugCritSec);
Form1.Memo1.Lines.Add('客户端证书:');
LeaveCriticalSection(g_DebugCritSec);
pStr := IdSslX509NameOneline(IdSslX509GetSubjectName(pClient_Cert), nil, 0);
if pStr = nil then Exit;
EnterCriticalSection(g_DebugCritSec);
Form1.Memo1.Lines.Add(Format('主题: %s', [pStr]));
LeaveCriticalSection(g_DebugCritSec);
IdSslFree(pStr);
pStr := IdSslX509NameOneline(IdSslX509GetIssuerName(pClient_Cert), nil, 0);
if pStr = nil then Exit;
IdSslFree(pStr);
EnterCriticalSection(g_DebugCritSec);
Form1.Memo1.Lines.Add(Format('发行者: %s', [pStr]));
LeaveCriticalSection(g_DebugCritSec);
IdSslFree(pStr);
IdSslFree(pClient_Cert);
end
else
begin
EnterCriticalSection(g_DebugCritSec);
Form1.Memo1.Lines.Add('客户端没有证书.');
LeaveCriticalSection(g_DebugCritSec);
end;
FillChar(buf, sizeof(buf), #0);
nRet := IdSslRead(sslServer, buf, sizeof(buf));
if nRet <= 0 then
begin
closesocket(sdClient);
sdClient := INVALID_SOCKET;
IdSslShutdown(sslServer);
IdSslFree(sslServer);
sslServer := nil;
end
else
begin
EnterCriticalSection(g_DebugCritSec);
Form1.Memo1.Lines.Add('客户端发送请求:'#$D#$A + StrPas(buf));
LeaveCriticalSection(g_DebugCritSec);
end;
strBody := 'Your IP is:' + inet_ntoa(sAddrs.sin_addr);
strSend := 'HTTP/1.1 200 OK'#$D#$A +
'Content-Length: ' + IntToStr(Length(strBody)) + #$D#$A +
'Content-Type: text/html'#$D#$A#$D#$A + strBody;
IdSslWrite(sslServer, @strSend[1], Length(strSend));
closesocket(sdClient);
IdSslShutdown(sslServer);
IdSslFree(sslServer);
end;
end;
g_hAcceptThread := 0;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
sAddr: TSockAddr;
dwThreadID: DWORD;
begin
Button1.Enabled := False;
//Create a socket
g_ServerSocketMain := Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if g_ServerSocketMain = INVALID_SOCKET then
begin
MessageBox(0, '创建Socket失败!', '错误', MB_ICONEXCLAMATION + MB_TOPMOST);
Application.Terminate;
Exit;
end;
//Bind Port
FillChar(sAddr, Sizeof(TSockAddr), #0);
sAddr.sin_family := AF_INET;
sAddr.sin_port := htons(StrToIntDef(Trim(Edit1.Text), 443));
sAddr.sin_addr.S_addr := INADDR_ANY;
if Bind(g_ServerSocketMain, sAddr, Sizeof(TSockAddr)) = SOCKET_ERROR then
begin
MessageBox(0, '绑定端口失败!', '错误', MB_ICONEXCLAMATION + MB_TOPMOST);
Application.Terminate;
Exit;
end;
//listen
if listen(g_ServerSocketMain, SOMAXCONN) = SOCKET_ERROR then
begin
MessageBox(0, '监听端口失败!', '错误', MB_ICONEXCLAMATION + MB_TOPMOST);
Application.Terminate;
Exit;
end;
g_Start := TRUE;
g_hAcceptThread := CreateThread(nil, 0, @AcceptThread, nil, 0, dwThreadId);
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
g_Start := FALSE;
if g_ServerSocketMain <> INVALID_SOCKET then
begin
closesocket(g_ServerSocketMain);
g_ServerSocketMain := INVALID_SOCKET;
end;
if (g_hAcceptThread <> 0) then
begin
WaitForSingleObject(g_hAcceptThread, INFINITE);
CloseHandle(g_hAcceptThread);
g_hAcceptThread := 0;
end;
end;
initialization
if WSAStartup($101, g_Wsa) <> 0 then //初始化Wsock32.dll,2.2版本可以使用MakeWord(2,2),
begin
MessageBox(0, '初始化Winsock动态库失败!', '错误', MB_ICONEXCLAMATION + MB_TOPMOST);
Halt;
end;
if not Load then //装载ssl库失败
begin
MessageBox(0, '装载ssl动态库失败!', '错误', MB_ICONEXCLAMATION + MB_TOPMOST);
Halt;
end;
IdSslLoadErrorStrings;
IdSslAddSslAlgorithms; //load所有的SSL算法.
//========================== 初始化SSL Server ================================
g_methServer := IdSslMethodV23; //建立SSL所用的method.
if g_methServer = nil then
begin
MessageBox(0, '建立SSL Server所用的method失败!', '错误', MB_ICONEXCLAMATION + MB_TOPMOST);
Halt;
end;
g_ctxServer := IdSslCtxNew(g_methServer); //初始化上下文情景.
if g_ctxServer = nil then //创建SSL_CTX失败
begin
MessageBox(0, '创建服务端SSL_CTX失败!', '错误', MB_ICONEXCLAMATION + MB_TOPMOST);
Halt;
end;
if IdSslCtxUseCertificateFile(g_ctxServer, Pchar(ExtractFilePath(GetModuleName(HInstance)) + 'UserCert.pem'), OPENSSL_SSL_FILETYPE_PEM) <= 0 then //加载证书失败
begin
MessageBox(0, '加载证书失败!', '错误', MB_ICONEXCLAMATION + MB_TOPMOST);
Halt;
end;
if IdSslCtxUsePrivateKeyFile(g_ctxServer, Pchar(ExtractFilePath(GetModuleName(HInstance)) + 'UserKey.pem'), OPENSSL_SSL_FILETYPE_PEM) <= 0 then //加载私钥失败
begin
MessageBox(0, '加载私钥失败!', '错误', MB_ICONEXCLAMATION + MB_TOPMOST);
Halt;
end;
if not IdSslCtxCheckPrivateKeyFile(g_ctxServer) = 0 then //密钥证书不匹配
begin
MessageBox(0, '密钥和证书不匹配!', '错误', MB_ICONEXCLAMATION + MB_TOPMOST);
Halt;
end;
InitializeCriticalSection(g_DebugCritSec);
finalization
if g_ctxServer <> nil then IdSslCtxFree(g_ctxServer);
DeleteCriticalSection(g_DebugCritSec);
WSACleanup; //结束对WSocket32.dll调用
end.
完整代码下载地址:http://www.138soft.com/download/ssldemo_server.rar
实际上,稍微用心的人结合第一讲的客户端例子,已经可以写出中间人欺骗程序了.
完整代码下载地址:http://www.138soft.com/download/ssldemo_client.rar
http://www.cnblogs.com/qiubole/archive/2007/12/24/1012077.html
谈Delphi中SSL协议的应用(好多相关文章)的更多相关文章
- 老陈---谈Delphi中SSL协议的应用[转]
摘要:本文主要介绍如何在Delphi中使用SSL协议.一共分为七个部分:(1)SSL协议是什么?(2)Delphi中如何使用SSL协议?(3)SSL客户端编程实例.(4)SSL服务端编程实例.(5)S ...
- Delphi从内存流中判断图片格式(好多相关文章)
废话不多说了,利用内存流来判断文件的格式,其实判断文件的前几个字节就可以简单的判断这个文件是什么类型的文件,例如jpg文件 是 FFD8 (从低位到高位就要反过来 D8FF 下面都是一样)BMP文件 ...
- 开发微信小程序中SSL协议的申请、证书绑定、TLS 版本处理等
在上篇随笔<基于微信小程序的系统开发准备工作>介绍了开发微信小程序的一些前期的架构设计.技术路线 .工具准备等方面内容,本篇随笔继续这个步骤,逐步介绍我们实际开发过程中对SSL协议的申请及 ...
- SSH协议、HTTPS中SSL协议的完整交互过程
1.(SSH)公私钥认证原理 服务器建立公钥:每一次启动sshd服务时,该服务会主动去找/etc/ssh/ssh_host*的文件 客户端通过ssh工具进行连接,如Xshell,SecureCRT 服 ...
- 浅谈python中文件和文件夹的相关操作
文件操作 文件的打开与关闭 打开文件 使用open(文件名,访问方式)函数,可以打开一个已存在的文件,或者创建一个新的文件. 示例如下: f = open('test.txt') # 访问方式可以省略 ...
- Direct UI 思想阐述(好多相关文章)
在界面开发中,目前DirectUI是个热门的技术名称,因为众多的知名公司都是用DirectUI方式作出了很炫丽的界面.而对于大多数熟悉Win32控件,熟悉MFC开发的开发人员来说,我们应该做何选择? ...
- 深入研究 Win32 结构化异常处理(好多相关文章)
摘要 就像人们常说的那样,Win32 结构化异常处理(SEH)是一个操作系统提供的服务.你能找到的所有关于 SEH 的文档讲的都是针对某个特定编译器的.建立在操作系统层之上的封装库.我将从 SEH 的 ...
- OpenCV图片类cv::Mat和QImage之间进行转换(好多相关文章)
在使用Qt和OpenCV混合编程时,我们有时需要在两种图片类cv::Mat和QImage之间进行转换,下面的代码参考了网上这个帖子: //##### cv::Mat ---> QImage ## ...
- 浅谈HTTPS和SSL/TLS协议的背景和基础
相关背景知识要说清楚HTTPS协议的实现原理,至少要需要如下几个背景知识.大致了解几个基础术语(HTTPS.SSL.TLS)的含义大致了解HTTP和TCP的关系(尤其是"短连接"和 ...
随机推荐
- CF 148A Insomnia cure
题目链接:传送门 题目大意:就是给四个数,和一个d,问1-d中有多少个数字不是那四个数的倍数; 这道题的d数据很小直接暴力可以过: 暴力代码:时间复杂度O(1): #include<stdio. ...
- 使用mysql关键字做类字段名报的错,花了我一个钟,坑啊
com.modelsystem.po.ProjectPlan@701faaedHibernate: insert into ld.project_plan (addTime, describe, ex ...
- 利用Jmeter做接口测试
本文作者:大道测试团队-孙云 1.在安装jmeter之前先配置好JDK,再配置jmeter环境变量. 2.启动jmeter 启动jmeter: 双击Jmeter解压路径(apache-jmeter-3 ...
- oracle中的记录类型
单词RECORD有“记录”的意思,因此RECORD也称为“记录类型”,使用该类型的变量可以存储由多个列值组成的一行数据. 在声明记录类型变量之前,首先需要定义记录类型,然后才可以声明记录类型的变量. ...
- SQL SERVER 强制排序规则查询
有时会需要在2个DB之间的数据做比较, 但因为一些原因, 数据库的默认排序规则是不一样的, 例如 SELECT A.Col1, B.Col1, A.* FROM DB1.dbo.A LEFT JOIN ...
- MVC与WebForm的一些区别
MVC与WebForm的一些区别 它们都是ASP.NET WEB开发的两种方式 .但是他们也是有一些不同.做个小结. 1.MVC是没有服务器端控件这么一说的,也就是没有viewstate,也就不会产生 ...
- C# 客户端判断服务器连接已断开
问题描述: 在C# Socket编程中,服务器端已经断开连接(发送数据方),客户端接收服务器端发送数据,在客户端使用client.Recieve()中,服务器端断开连接,客户端任然显示已 ...
- 【UVA】【10828】随机程序
数学期望/高斯消元/马尔可夫过程 刘汝佳老师白书上的例题- -b 本体不满足拓扑关系,但马尔可夫过程是可以高斯消元解的…… 用「高斯·约当消元」更方便! //UVA 10828 #include< ...
- Redis 安装与配置
启动 Redis WINDOW 服务: https://github.com/ServiceStack/ServiceStack.Redis install-package ServiceStack. ...
- Deep Learning and Shallow Learning
Deep Learning and Shallow Learning 由于 Deep Learning 现在如火如荼的势头,在各种领域逐渐占据 state-of-the-art 的地位,上个学期在一门 ...