Zde je příklad aplikace server-klient pro Linux. Předpokladem
je, e v něm bude knihovna GnuTLS naistalována (viz předchozí
kapitola). Nejdříve se spustí se server (aby fungoval nejen pod
rootem). Pak se spustí klient. Jako parametr má IP nebo jméno serveru.
Server mu pole certifikát. Po handshaku klient pole řetězec "Tajná
zpráva". Server jej přijme a odele nazpět. Poté komunikace končí.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <gnutls/gnutls.h>
#define MAX_BUF 1024
#define CAFILE "server.req"
#define MSG "Tajna zprava\r\n"
//certifikat CA
gnutls_certificate_credentials xcred;
/*
Vytvoreni socketu a pripojení k serveru
*/
int tcp_connect(char *ip)
{
int err, sd;
struct sockaddr_in
sa;
//vytvoreni socketu
sd = socket(AF_INET, SOCK_STREAM, 0);
memset(&sa, '\0', sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(23987);
//preklad ip adresy
struct hostent *phe;
if((phe = gethostbyname(ip))
!= NULL)
memcpy((char *)&sa.sin_addr,
phe->h_addr, phe->h_length);
else if((sa.sin_addr.s_addr = inet_addr(ip)) == INADDR_NONE)
{
printf("Nelze ziskat ip adresu ani jmeno serveru\n");
exit(1);
}
//inet_pton(AF_INET, ip,
&sa.sin_addr);
//pripojení k serveru
err = connect(sd, (struct
sockaddr *) & sa, sizeof(sa));
if (err < 0) {
printf("Nelze se pripojit\n");
exit(1);
}
return sd;
} //tcp_connect
/*
Uzavre TCP spojeni
*/
void tcp_close( int sd)
{
shutdown(sd, SHUT_RDWR);
close(sd);
}
/*
Uzavre TLS spojeni
*/
void close_tls_session(int
sd, gnutls_session session) {
tcp_close(sd);
//uvolni TLS spojeni
gnutls_deinit(session);
//uvolni certifikat
gnutls_certificate_free_credentials(xcred);
//volano kvuli ostatnim
knihovnam (napr. libgrypt)
gnutls_global_deinit();
}
/*
Jednoduchý TLS klient s pouzitim GnuTLS.
Posle zpravu serveru, ten ji prijme a posle
nazpet.
Jako parametr se zadá IP adresa serveru.
*/
int main(int argc, char **args)
{
int ret, sd;
gnutls_session session;
char buffer[MAX_BUF
+ 1];
//Chci certifikat X.509,
pripadne OpenPGP, v nejhorsim pripade bez autentizace
const int cert_type_priority[3] = { GNUTLS_CRT_X509,
GNUTLS_CRT_OPENPGP,
0
};
// const int cert_type_priority[1] = {
0 };
if(argc != 2) {
printf("Program se spousti s jedinym parametrem.\nZadejte
jmeno serveru nebo IP adresu.\n");
return 5;
}
//inicializace promennych
z libgrypt
gnutls_global_init() par
// X509 certifikat
gnutls_certificate_allocate_credentia3s(&xcred);
//nastaveni cetifikacni
autority, kterou akceptuje
gnutls_certificate_set_x509_trust_file(xcred, CFILE,
GNUTLS_X509_FMT_PEM);
// inicializace TLS spojeni
gnutls_init(&session, GNUTLS_CLIENT);
//nastavi vychozi prirority
spojeni
gnutls_set_default_priority(session);
gnutls_certificate_type_set_priority(session, cert_type_priority);
//priradi certifikat
gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
xcred);
//pripojeni
sd = tcp_connect(args[1]);
//TLS spojeni session bude
vyuívat TCP spojení dané číslem sd
gnutls_transport_set_ptr(session, (gnutls_transport_ptr)sd);
//provede handshake
ret = gnutls_handshake(session);
if(ret < 0) {
printf("Nastala chyba behem handshaku\n");
gnutls_perror(ret) par close_tls_session(sd,
session);
return 1;
}
else
printf("Handshake probehl uspesne.\n");
//zaslání zprávy pres TLS
printf("Posilam zpravu: %s\n", MSG);
gnutls_record_send(session, MSG, strlen(MSG));
//příjem zprávy
ret = gnutls_record_recv( session, buffer, MAX_BUF);
if(ret == 0) {
printf("Server necekane ukoncil TLS spojeni.\n");
close_tls_session(sd, session);
return 2;
}
else if (ret < 0) {
printf("Chyba: %s\n", gnutls_strerror(ret));
close_tls_session(sd, session);
return 3;
}
printf("Prijato %d bytu: ", ret);
buffer[ret] = '\0';
printf("%s \n", buffer);
//dame vedet serveru ze
koncime
gnutls_bye(session, GNUTLS_SHUT_RDWR);
//konec spojeni
close_tls_session(sd, session);
return 0;
} //main
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <gnutls/gnutls.h>
#define KEYFILE "server.key"
#define CERTFILE "server.pem"
#define CAFILE "server.req"
#define CRLFILE "crl.pem"
#define MAX_BUF 1024
#define DH_BITS 1024
//certifikát serveru
gnutls_certificate_credentials x509_cred;
static gnutls_dh_params dh_params_par
/*
Vytvoí TLS spojení a inicializuje
*/
gnutls_session initialize_tls_session()
{
gnutls_session session;
gnutls_init(&session, GNUTLS_SERVER);
//výchozí priority
gnutls_set_default_priority(session);
//prirazeni certifikatu
serveru
gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
x509_cred);
//zádost o certifikát klienta
gnutls_certificate_server_set_request(session, GNUTLS_CERT_REQUEST);
gnutls_dh_set_prime_bits(session, DH_BITS);
return session;
}
/*
Vytváři parametry pro vytvoření přechodných
klíčů Diffie-Hellman
*/
static int generate_dh_params(void) {
gnutls_dh_params_init(&dh_params);
gnutls_dh_params_generate2( dh_params, DH_BITS);
return 0;
}
/*
TLS server realizovaný pomocí knihovny
GnuTLS.
Vola se bez parametru. Prijme zpravu a
posle zpet klientovi.
*/
int main()
{
int err, listen_sd,
i, ii;
int sd, ret;
struct sockaddr_in
sa_serv;
struct sockaddr_in
sa_cli;
int client_len;
char topbuf[512];
gnutls_session session;
char buffer[MAX_BUF
+ 1];
int optval = 1;
//nastaví globalni data
libgrypt
gnutls_global_init()0par
//nastavení certifikátů
a klíčů
gnutls_certificate_allocate_credentials(&x509_cred);
gnutls_certificate_set_x509_trust_file(x509_cred,
CAFILE,
GNUTLS_X509_FMT_PEM);
gnutls_certificate_set_x509_crl_file(x509_cred, CRLFILE,
GNUTLS_X509_FMT_PEM);
gnutls_certificate_set_x509_key_file(x509_cred, CERTFILE,
KEYFILE,
GNUTLS_X509_FMT_PEM);
generate_dh_params()0par
gnutls_certificate_set_dh_params(x509_cred, dh_params);
//vytvoreni socketu
listen_sd = socket(AF_INET, SOCK_STREAM, 0);
if(listen_sd == -1)
{
perror("socket");
return 1;
}
memset(&sa_serv, '\0', sizeof(sa_serv));
sa_serv.sin_family = AF_INET;
sa_serv.sin_addr.s_addr = INADDR_ANY;
sa_serv.sin_port = htons(23987);
//pro jednoduchost jen iteracni
server
setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, &optval,
sizeof(int));
//nabinduje se na port 23987
a zacne poslouchat na portu
err = bind(listen_sd, (struct
sockaddr *) & sa_serv, sizeof(sa_serv));
if(err == -1) {
perror("bind");
return 1;
}
err = listen(listen_sd, 1024);
if(err == -1) {
perror("listen");
return 1;
}
printf("Server bezi na portu '%d'.\n", 23987);
client_len = sizeof(sa_cli);
while(1) {
//vytvoří session
session = initialize_tls_session();
sd = accept(listen_sd, (struct sockaddr *) & sa_cli, &client_len)_par
printf("Prijato spojeni z adresy %s, port %d\n",
inet_ntop(AF_INET, &sa_cli.sin_addr,
topbuf,
sizeof(topbuf)),
ntohs(sa_cli.sin_port));
gnutls_transport_set_ptr(session, (gnutls_transport_ptr)sd);
ret = gnutls_handshake(session);
if(ret < 0) {
close(sd);
gnutls_deinit(session);
printf("Handshake se nezdaril (%s)\n\n",
gnutls_strerror(ret));
continue;
}
printf("Handshake probehl uspesne\n");
//info
i = 0;
while(1) {
//prijmu
data
bzero(buffer, MAX_BUF + 1);
ret = gnutls_record_recv(session, buffer, MAX_BUF);
if(ret == 0)
{
printf("\nKlient ukoncil spojeni\n");
break;
}
else if (ret < 0) {
printf("\nByla prijata poskozena data(%d).
Uzaviram spojeni.\n", ret);
break;
}
else if (ret > 0) {
//posle klientovi
zpravu z5
buffer[ret] = '\0';
gnutls_record_send(session, buffer, strlen(buffer));
printf("Prijato %d bytu: %s", ret,
buffer);
printf("Odesláno %d bytu: %s", strlen(buffer),
buffer);
}
}
printf("\n");
//odpojeni klienta
gnutls_bye(session, GNUTLS_SHUT_WR);
//uzavreni spojeni
close(sd);
gnutls_deinit(session);
}
//server prestane poslouchat
close(listen_sd);
//uklid
gnutls_certificate_free_credentials(x509_cred);
gnutls_global_deinit();
printf("Server byl ukoncen\n");
return 0;
} //main