Talk about the application of SSL protocol in Delphi [transfer]

Abstract: This article mainly introduces how to use the SSL protocol in Delphi. It is divided into seven parts: (1) What is the SSL protocol? (2) How to use the SSL protocol in Delphi? (3) SSL client programming examples. (4) SSL server programming examples. (5) SSL certificate programming examples. (6) Man-in-the-middle spoofing examples. (7) Others. The author of this article also has a work written in SSL protocol called SSLPROXY.
1.What is the SSL protocol?
SSL is an encrypted transmission protocol. To quote an online passage: SSL is the English abbreviation of Secure socket Layer, and its Chinese means Secure Socket Layer Protocol, which refers to a secure network communication protocol that uses a combination of public and private key technologies. The SSL protocol is a web application-based security protocol launched by Netscape. The SSL protocol specifies a way to provide data security between application protocols (such as Http, Telenet, NMTP and FTP, etc.) and TCP/IP protocols. A layered mechanism, which provides data encryption, server authentication, message integrity, and optional client authentication for TCP/IP connections. It is mainly used to improve the security of data between applications and encrypt and hide transmitted data To ensure that the data is not changed during transmission, that is, to ensure the integrity of the data.
2. How to use SSL protocol in Delphi?
To use the SSL protocol, there are several methods, one is to implement it yourself according to the SSL protocol document, and the other is to call a third-party development library. Now general developers (whether Delphi or VC) use the open source OpenSSL library The introduction in this article is based on OpenSSL.
The Indy component in Delphi itself supports OpenSSL. For example, for IdTCPClient, you only need to drag an IdSSLIOHandlerSocket control to the window, and then point the IOHandler of IdTCPClient to it. However, the statement of IdSSLOpenSSLHeaders.pas is older and is based on OpenSSL 0.9.4 Yes, and 0.9.4 DLL files are now relatively rare. Now OpenSSL 0.9.7i is generally used. (You can open IdSSLOpenSSLHeaders.pas, search for the version, the old version is like this:
OPENSSL_OPENSSL_VERSION_NUMBER = $00904100;
OPENSSL_OPENSSL_VERSION_TEXT = ‘OpenSSL 0.9.4 09 Aug 1999’; {Do not localize}
The difference between 0.9.4 and 0.9.7i is that some functions of 0.9.4 are located in ssleay32.dll, while 0.9.7i discards this DLL. There are many modified versions of IdSSLOpenSSLHeaders.pas based on 0.9.7i on the Internet. The demo code is mainly based on 0.9.7i.
3. SSL client programming example
Before using OpenSSL, you must first load the SSL library. Call the Load function directly to complete:
if not uIdSSLOpenSSLHeaders.Load then //Load ssl library
begin
Application.MessageBox(‘Failed to load ssl dynamic library!’,’error’, MB_ICONEXCLAMATION + MB_TOPMOST);
Halt;
end;
Then load the wrong character library and encryption algorithm:
IdSslLoadErrorStrings;
IdSslAddSslAlgorithms; //load all SSL algorithms
Then, initialize the protocol version and SSL context used by SSL:
methClient := IdSslMethodClientV23;//There are multiple versions of the SSL protocol, including SSLv2, SSLv23, SSLv3 and TLSv1. We use v23 here.
if methClient = nil then
begin
Application.MessageBox(‘The method used to establish SSL Client failed!’,’Error’, MB_ICONEXCLAMATION + MB_TOPMOST);
Halt;
end;
ctxClient := IdSslCtxNew(methClient); //Initialize the context scenario.
if ctxClient = nil then
begin
Application.MessageBox(‘Failed to create client SSL_CTX!’,’Error’, MB_ICONEXCLAMATION + MB_TOPMOST);
Halt;
end;
After the context scenario is initialized, socket communication can be carried out. First, create a normal socket, connect to the destination address, and then apply for an SSL session environment:
sslClient := IdSslNew(ctxClient);
if sslClient = nil then
begin
closesocket(ClientSocket);
Application.MessageBox(‘Application for SSL session environment failed!’,’error’, MB_ICONEXCLAMATION + MB_TOPMOST);
Halt;
end;
Then bind the socket:
IdSslSetFd(sslClient, ClientSocket);
IdSslConnect(sslClient);
After success, you can call IdSslRead and IdSslWrite to read and write data.
Below we give a complete example for connecting the security focus of the forum homepage. This program uses a Memo control and a button control. Note: This code does not do too much error handling, readers can add it by themselves:
procedure TForm1.Button1Click(Sender: TObject);
const
//Url: string = ‘https://www.delphifan.online/index.php?lang=en’;
Host: string = ‘www.delphifan.online’;
Port: Word = 443;
var
Wsa: TWSAData;
ctxClient: PSSL_CTX; //SSL context
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 //Initialize Wsock32.dll, MakeWord(2,2),
begin
Application.MessageBox(‘Failed to initialize Winsock dynamic library!’,’error’, MB_ICONEXCLAMATION + MB_TOPMOST);
Halt;
end;
if not uIdSSLOpenSSLHeaders.Load then //Load ssl library
begin
Application.MessageBox(‘Failed to load ssl dynamic library!’,’error’, MB_ICONEXCLAMATION + MB_TOPMOST);
Halt;
end;
IdSslLoadErrorStrings;
IdSslAddSslAlgorithms; //load all SSL algorithms
methClient := IdSslMethodClientV23;
if methClient = nil then
begin
Application.MessageBox(‘The method used to establish SSL Client failed!’,’Error’, MB_ICONEXCLAMATION + MB_TOPMOST);
Halt;
end;
ctxClient := IdSslCtxNew(methClient); //Initialize context
if ctxClient = nil then
begin
Application.MessageBox(‘Failed to create client SSL_CTX!’,’Error’, MB_ICONEXCLAMATION + MB_TOPMOST);
Halt;
end;
sRemoteIP := GetIPAddressByHost(Host);
if sRemoteIP =” then
begin
Application.MessageBox(‘Failed to obtain remote IP address!’,’Error’, MB_ICONEXCLAMATION + MB_TOPMOST);
Halt;
end;
ClientSocket := Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if ClientSocket = INVALID_SOCKET then
begin
Application.MessageBox(‘Failed to create socket!’,’Error’, 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(‘Failed to connect to remote server!’,’Error’, MB_ICONEXCLAMATION + MB_TOPMOST);
Halt;
end;
sslClient := IdSslNew(ctxClient); //The environment for applying for an SSL session, the parameter is the SSL communication method we applied for. Returns a pointer to the current SSL connection environment.
if sslClient = nil then
begin
closesocket(ClientSocket);
Application.MessageBox(‘Application for SSL session environment failed!’,’error’, MB_ICONEXCLAMATION + MB_TOPMOST);
Halt;
end;
IdSslSetFd(sslClient, ClientSocket); //Binding read and write sockets
if IdSslConnect(sslClient) = -1 then
begin
IdSslFree(sslClient);
closesocket(ClientSocket);
Application.MessageBox(‘Failed to bind read and write socket!’,’error’, MB_ICONEXCLAMATION + MB_TOPMOST);
Halt;
end;
Memo1.Lines.Add(Format(‘The algorithm used by the SSL connection is: %s’, [IdSSLCipherGetName(IdSSLGetCurrentCipher(sslClient))]));
pServer_Cert := IdSslGetPeerCertificate(sslClient);
if (pServer_Cert <> nil) then
begin
Memo1.Lines.Add(‘Server certificate:’);
pStr := IdSslX509NameOneline(IdSslX509GetSubjectName(pServer_Cert), nil, 0);
if pStr <> nil then Memo1.Lines.Add(Format(‘Subject: %s’, [pStr]));
pStr := IdSslX509NameOneline(IdSslX509GetIssuerName(pServer_Cert), nil, 0);
if pStr <> nil then Memo1.Lines.Add(Format(‘Issuer: %s’, [pStr]));
end
else
begin
Memo1.Lines.Add(‘The client does not have a certificate.’);
end;
Memo1.Lines.Add(”);
strSend := ‘GET //index.php?lang=en HTTP/1.1’#$D#$A +
‘Accept: */*’#$D#$A +
‘Accept-Language: en-en’#$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(‘Send failed!’,’Error’, 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; //End the call to WSocket32.dll
Button1.Enabled := True;
end;
4.SSL server programming examples
The server side has nothing to talk about. The difference from the client side is that the certificate needs to be loaded during initialization, and then IdSslAccept needs to be associated after Accept. For details on how to generate a certificate file with Delphi, see the next lecture. The code is posted below:
var
Form1: TForm1;
g_Wsa: TWSAData;
g_ServerSocketMain: TSocket = INVALID_SOCKET;
g_ctxServer: PSSL_CTX = nil; //SSL context
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(‘An error occurred. Error code: %d’, [nErrorCode]));
LeaveCriticalSection(g_DebugCritSec);
end
else
begin
EnterCriticalSection(g_DebugCritSec);
Form1.Memo1.Lines.Add(Format(‘New connection. Client IP:%s’, [inet_ntoa(sAddrs.sin_addr)]));
LeaveCriticalSection(g_DebugCritSec);
sslServer := IdSslNew(g_ctxServer); //Apply for the SSL session environment, the parameter is the SSL communication method we applied for earlier, and returns the pointer of the current SSL connection environment.
if sslServer = nil then
begin
closesocket(sdClient);
Continue;
end;
IdSslSetFd(sslServer, sdClient); //Binding read and write sockets
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(‘The algorithm used for the SSL connection is: %s’, [IdSSLCipherGetName(IdSSLGetCurrentCipher(sslServer))]));
LeaveCriticalSection(g_DebugCritSec);
pClient_Cert := IdSslGetPeerCertificate(sslServer);
if (pClient_Cert <> nil) then
begin
EnterCriticalSection(g_DebugCritSec);
Form1.Memo1.Lines.Add(‘Client certificate:’);
LeaveCriticalSection(g_DebugCritSec);
pStr := IdSslX509NameOneline(IdSslX509GetSubjectName(pClient_Cert), nil, 0);
if pStr = nil then Exit;
EnterCriticalSection(g_DebugCritSec);
Form1.Memo1.Lines.Add(Format(‘Subject: %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(‘Issuer: %s’, [pStr]));
LeaveCriticalSection(g_DebugCritSec);
IdSslFree(pStr);
IdSslFree(pClient_Cert);
end
else
begin
EnterCriticalSection(g_DebugCritSec);
Form1.Memo1.Lines.Add(‘The client does not have a certificate.’);
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);
orm1.Memo1.Lines.Add(‘Client sends request:’#$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,’Failed to create Socket!’,’Error’, 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,’Failed to bind port!’,’Error’, MB_ICONEXCLAMATION + MB_TOPMOST);
Application.Terminate;
Exit;
end;
//listen
if listen(g_ServerSocketMain, SOMAXCONN) = SOCKET_ERROR then
begin
MessageBox(0,’Listening port failed!’,’Error’, 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 //Initialize Wsock32.dll, version 2.2 can use MakeWord(2,2),
begin
MessageBox(0,’Failed to initialize Winsock dynamic library!’,’Error’, MB_ICONEXCLAMATION + MB_TOPMOST);
Halt;
end;
if not Load then //Failed to load the ssl library
begin
MessageBox(0,’Failed to load the ssl dynamic library!’,’Error’, MB_ICONEXCLAMATION + MB_TOPMOST);
Halt;
end;
IdSslLoadErrorStrings;
IdSslAddSslAlgorithms; //load all SSL algorithms.
//========================= Initialize SSL Server =================== =============
g_methServer := IdSslMethodV23; //The method used to establish SSL.
if g_methServer = nil then
begin
MessageBox(0,’The method used to establish the SSL Server failed!’,’Error’, MB_ICONEXCLAMATION + MB_TOPMOST);
Halt;
end;
g_ctxServer := IdSslCtxNew(g_methServer); //Initialize the context scenario.
if g_ctxServer = nil then //Failed to create SSL_CTX
begin
MessageBox(0,’Failed to create server SSL_CTX!’,’Error’, MB_ICONEXCLAMATION + MB_TOPMOST);
Halt;
end;
if IdSslCtxUseCertificateFile(g_ctxServer, Pchar(ExtractFilePath(GetModuleName(HInstance)) +’UserCert.pem’), OPENSSL_SSL_FILETYPE_PEM) <= 0 then //Failed to load the certificate
begin
MessageBox(0,’Failed to load certificate!’,’Error’, MB_ICONEXCLAMATION + MB_TOPMOST);
Halt;
end;
if IdSslCtxUsePrivateKeyFile(g_ctxServer, Pchar(ExtractFilePath(GetModuleName(HInstance)) +’UserKey.pem’), OPENSSL_SSL_FILETYPE_PEM) <= 0 then //Failed to load the private key
begin
MessageBox(0,’Failed to load the private key!’,’Error’, MB_ICONEXCLAMATION + MB_TOPMOST);
Halt;
end;
if not IdSslCtxCheckPrivateKeyFile(g_ctxServer) = 0 then //Key certificate does not match
begin
MessageBox(0,’Key and certificate do not match!’,’Error’, MB_ICONEXCLAMATION + MB_TOPMOST);
Halt;
end;
InitializeCriticalSection(g_DebugCritSec);
finalization
if g_ctxServer <> nil then IdSslCtxFree(g_ctxServer);
DeleteCriticalSection(g_DebugCritSec);
WSACleanup; //End the call to WSocket32.dll
end.