关于 c发送邮件,采用ssl模式以及ssl用法简介

1、 c 发送邮件, 即是 模拟 SMTP的 整个流程, 与远程服务器不停地交互,即可完成。  注意每个指令后面都有\r\n, 结束符为\r\n.\r\n

smtp密码 和用户名都采用base64 编码。


2、 采用ssl模式,要求远程服务器,必须支持, 观察 foxmail可以发现, 默认的ssl端口 465。 (发送邮箱的域 必须与 服务器的域 一致)

普通邮件 smtp 端口号为: 25, pop 110  , imap  143

       smtp.126.com       126邮箱

smtp.163.com       163邮箱

smtp.qq.com          qq邮箱   


3、SSL  简介

SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS与SSL在传输层对网络连接进行加密。

  安全证书既包含了用于加密数据的密钥,又包含了用于证实身份的数字签名。安全证书采用公钥加密技术。公钥加密是指使用一对非对称的密钥进行加密或解密。每一对密钥由公钥和私钥组成。公钥被广泛发布。私钥是隐秘的,不公开。用公钥加密的数据只能够被私钥解密。反过来,使用私钥加密的数据只能被公钥解密。这个非对称的特性使得公钥加密很有用。在安全证书中包含着一对非对称的密钥。只有安全证书的所有者才知道私钥。当通信方A将自己的安全证书发送给通信方B时,实际上发给通信方B的是公开密钥,接着通信方B可以向通信方A发送用公钥加密的数据,只有通信方A才能使用私钥对数据进行解密,从而获得通信方B发送的原始数据。安全证书中的数字签名部分是通信方A的电子身份证。数字签名告诉通信方B该信息确实由通信方A发出,不是伪造的,也没有被篡改。

  客户与服务器通信时,首先要进行SSL握手,SSL握手主要完成以下任务:

  1)协商使用的加密套件。加密套件中包括一组加密参数,这些参数指定了加密算法和密钥的长度等信息。

  2)验证对方的身份,此操作是可选的。

  3)确定使用的加密算法。

  4)SSL握手过程采用非对称加密方法传递数据,由此来建立一个安全的SSL会话。SSL握手完成后,通信双方将采用对称加密方法传递实际的应用数据。

   以下是SSL握手的具体流程:

(1)客户将自己的SSL版本号、加密参数、与SSL会话有关的数据及其他一些必要信息发送到服务器。

(2)服务器将自己的SSL版本号、加密参数、与SSL会话有关的数据及其他一些必要信息发送给客户,同时发给客户的还有服务器的证书。如果服务器需要验证客户身份,服务器还会发出要求客户提供安全证书的请求。

(3)客户端验证服务器证书,如果验证失败,就提示不能建立SSL连接。如果成功,那么继续下一步骤。

(4)客户端为本次SSL会话生成预备主密码(pre-master secret),并将其用服务器公钥加密后发送给服务器。

(5)如果服务器要求验证客户身份,客户端还要对另外一些数据签名后,将其与客户端证书一起发送给服务器。

(6)如果服务器要求验证客户身份,则检查签署客户证书的CA(Certificate Authority,证书机构)是否可信。如果不在信任列表中,结束本次会话。如果检查通过,服务器用自己的私钥解密收到的预备主密码(pre-master secret),并用它通过某些算法生成本次会话的主密码(master secret)。

(7)客户端与服务器端均使用此主密码(master secret)生成此次会话的会话密钥(对称密钥)。在双方SSL握手结束后传递任何消息均使用此会话密钥。这样做的主要原因是对称加密比非对称加密的运算量要低一个数量级以上,能够显著提高双方会话时的运算速度。

(8)客户端通知服务器此后发送的消息都使用这个会话密钥进行加密,并通知服务器客户端已经完成本次SSL握手。

(9)服务器通知客户端此后发送的消息都使用这个会话密钥进行加密,并通知客户端服务器已经完成本次SSL握手。

(10)本次握手过程结束,SSL会话已经建立。在接下来的会话过程中,双方使用同一个会话密钥分别对发送和接收的信息进行加密和解密。


4、 SSL编程

OpenSSL是一个开放源代码的SSL协议的产品实现,它采用C语言作为开发语言,具备了跨系统的性能。调用OpenSSL的函数就可以实现一个SSL加密的安全数据传输通道,从而保护客户端和服务器之间数据的安全。

头文件:

#include <openssl/ssl.h>
#include <openssl/err.h>

基于OpenSSL的程序都要遵循以下几个步骤:

(1 ) OpenSSL初始化

在使用OpenSSL之前,必须进行相应的协议初始化工作,这可以通过下面的函数实现:

int SSL_library_int(void);

(2 ) 选择会话协议

在利用OpenSSL开始SSL会话之前,需要为客户端和服务器制定本次会话采用的协议,目前能够使用的协议包括TLSv1.0、SSLv2、SSLv3、SSLv2/v3。

需要注意的是,客户端和服务器必须使用相互兼容的协议,否则SSL会话将无法正常进行。

(3 ) 创建会话环境

在OpenSSL中创建的SSL会话环境称为CTX,使用不同的协议会话,其环境也不一样的。

申请SSL会话环境的OpenSSL函数是:

SSL_CTX *SSL_CTX_new(SSL_METHOD * method);

当SSL会话环境申请成功后,还要根据实际的需要设置CTX的属性,通常的设置是指定SSL握手阶段证书的验证方式和加载自己的证书。

制定证书验证方式的函数是:

int SSL_CTX_set_verify(SSL_CTX *ctx,int mode,int(*verify_callback),int(X509_STORE_CTX *));

为SSL会话环境加载CA证书的函数是:

SSL_CTX_load_verify_location(SSL_CTX *ctx,const char *Cafile,const char *Capath);

为SSL会话加载用户证书的函数是:

SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file,int type);

为SSL会话加载用户私钥的函数是:

SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx,const char* file,int type);

在将证书和私钥加载到SSL会话环境之后,就可以调用下面的函数来验证私钥和证书是否相符:

int SSL_CTX_check_private_key(SSL_CTX *ctx);

(4) 建立SSL套接字

SSL套接字是建立在普通的TCP套接字基础之上,在建立SSL套接字时可以使用下面的一些函数:

SSL *SSl_new(SSL_CTX *ctx);

//申请一个SSL套接字

int SSL_set_fd(SSL *ssl,int fd);)

//绑定读写套接字

int SSL_set_rfd(SSL *ssl,int fd);

//绑定只读套接字

int SSL_set_wfd(SSL *ssl,int fd);

//绑定只写套接字

(5) 完成SSL握手

在成功创建SSL套接字后,客户端应使用函数SSL_connect( )替代传统的函数connect( )来完成握手过程:

int SSL_connect(SSL *ssl);

而对服务器来讲,则应使用函数SSL_ accept ( )替代传统的函数accept ( )来完成握手过程:

int SSL_accept(SSL *ssl);

握手过程完成之后,通常需要询问通信双方的证书信息,以便进行相应的验证,这可以借助于下面的函数来实现:

X509 *SSL_get_peer_certificate(SSL *ssl);

该函数可以从SSL套接字中提取对方的证书信息,这些信息已经被SSL验证过了。

X509_NAME *X509_get_subject_name(X509 *a);

该函数得到证书所用者的名字。

(6) 进行数据传输

当SSL握手完成之后,就可以进行安全的数据传输了,在数据传输阶段,需要使用SSL_read( )和SSL_write( )来替代传统的read( )和write( )函数,来完成对套接字的读写操作:

int SSL_read(SSL *ssl,void *buf,int num);

int SSL_write(SSL *ssl,const void *buf,int num);

(7 ) 结束SSL通信

当客户端和服务器之间的数据通信完成之后,调用下面的函数来释放已经申请的SSL资源:

int SSL_shutdown(SSL *ssl);

//关闭SSL套接字

void SSl_free(SSL *ssl);

 //释放SSL套接字

void SSL_CTX_free(SSL_CTX *ctx); 

//释放SSL会话环境


4.1  在使用OpenSSL前,必须先对OpenSSL 进行初始化,以下的三个函数任选其一:
SSL_library_init(void);
OpenSSL_add_ssl_algorithms();
SSLeay_add_ssl_algorithms();
事实上 后面的两个函数只是第一个函数的宏。
如果要使用OpenSSL的出错信息,使用SSL_load_error_strings (void)进行错误信息的初始化。以后
可以使用void ERR_print_errors_fp(FILE *fp) 打印SSL的错误信息。
一次SSL连接会话一般要先申请一个SSL 环境,基本的过程是:
1. SSL_METHOD* meth TLSv1_client_method(); 创建本次会话连接所使用的协议,如果是客户端可
以使用 
SSL_METHOD* TLSv1_client_method(void); TLSv1.0 协议
SSL_METHOD* SSLv2_client_method(void); SSLv2 协议
SSL_METHOD* SSLv3_client_method(void); SSLv3 协议
SSL_METHOD* SSLv23_client_method(void); SSLv2/v3 协议
服务器同样需要创建本次会话所使用的协议:
SSL_METHOD *TLSv1_server_method(void); 
SSL_METHOD *SSLv2_server_method(void); 
SSL_METHOD *SSLv3_server_method(void); 
SSL_METHOD *SSLv23_server_method(void); 
需要注意的是客户端和服务器需要使用相同的协议。
2.申请SSL会话的环境 CTX,使用不同的协议进行会话,其环境也是不同的。申请SSL会话环境的OpenSSL函数是  SSL_CTX* SSL_CTX_new (SSL_METHOD*); 参数就是前面我们申请的 SSL通讯方式。返回当前的SSL 连接环境的指针。然后根据自己的需要设置CTX的属性,典型的是设置SSL 握手阶段证书的验证方式和加载自己的证书。
void SSL_CTX_set_verify (SSL_CTX* int int* (int, X509_STORE_CTX*) )
设置证书验证的方式。第一个参数是当前的CTX 指针,第二个是验证方式,如果是要验证对方的话,就使用
 SSL_VERIFY_PEER。不需要的话,使用SSL_VERIFY_NONE.一般情况下,客户端需要验证对方,而
 服务器不需要。第三个参数是处理验证的回调函数,如果没有特殊的需要,使用空指针就可以了。
void SSL_CTX_load_verify_locations(SSL_CTX*, const char* const char*); 加载证书;
第一个参数同上,参数二是证书文件的名称,参数三是证书文件的路径;
int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type);
加载本地的证书;type 指明证书文件的结构类型;失败返回-1
int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type);
加载自己的私钥;type 参数指明私钥文件的结构类型;失败返回-1
加载了证书和文件之后,就可以验证私钥和证书是否相符:
BOOl SSL_CTX_check_private_key (SSL_CTX*);
3.既然SSL 使用TCP 协议,当然需要把SSL attach 到已经连接的套接字上了:
SSL* SSL_new (SSL_CTX*); 申请一个SSL 套节字;
int SSL_set_rfd (SSL*); 绑定只读套接字
int SSL_set_wfd (SSL*); 绑定只写套接字
int SSL_set_fd SSL*); 绑定读写套接字
绑定成功返回 1, 失败返回0;
4. 接下来就是SSL 握手的动作了
int SSL_connect (SSL*); 失败返回 -1
5. 握手成功之后,就可以进行通讯了,使用SSL_read 和SS_write 读写SSL 套接字代替传统的
read 、write
int SSL_read (SSL *ssl, char *buf, int num );
int SSL_write (SSL *ssl, char *buf, int num);
如果是服务器,则使用 SSL_accept 代替传统的 accept 调用
int SSL_accept(SSL *ssl);
6. 通讯结束,需要释放前面申请的 SSL资源
int SSL_shutdown(SSL *ssl); 关闭SSL套接字;
void SSL_free (ssl); 释放SSL套接字;
void SSL_CTX_free (ctx); 释放SSL环境;


5、linux SSL  代码范例 来自 : 169博客

客户端 代码:

#include <stdio.h>

#include <errno.h>

#include <unistd.h>

#include <malloc.h>

#include <string.h>

#include <sys/socket.h>

#include <resolv.h>

#include <netdb.h>

#include <openssl/ssl.h>

#include <openssl/err.h>

#define FAIL    -1

int OpenConnection(const char *hostname, int port)

{   int sd;

struct hostent *host;

struct sockaddr_in addr;

if ( (host = gethostbyname(hostname)) == NULL )

{

    printf('Eroor: %s\n',hostname);

    perror(hostname);

    abort();

}

sd = socket(PF_INET, SOCK_STREAM, 0);

bzero(&addr, sizeof(addr));

addr.sin_family = AF_INET;

addr.sin_port = htons(port);

addr.sin_addr.s_addr = *(long*)(host->h_addr);

if ( connect(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 )

{

    close(sd);

    perror(hostname);

    abort();

}

return sd;

}

SSL_CTX* InitCTX(void)

{   SSL_METHOD *method;

SSL_CTX *ctx;

OpenSSL_add_all_algorithms();  /* Load cryptos, et.al. */

SSL_load_error_strings();   /* Bring in and register error messages */

method = SSLv2_client_method();  /* Create new client-method instance */

ctx = SSL_CTX_new(method);   /* Create new context */

if ( ctx == NULL )

{

    ERR_print_errors_fp(stderr);

    printf('Eroor: %s\n',stderr);

    abort();

}

return ctx;

}

void ShowCerts(SSL* ssl)

{   X509 *cert;

    char *line;

    cert = SSL_get_peer_certificate(ssl); /* get the server's certificate */

    if ( cert != NULL )

    {

    printf("Server certificates:\n");

    line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);

    printf("Subject: %s\n", line);

    free(line);       /* free the malloc'ed string */

    line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);

    printf("Issuer: %s\n", line);

    free(line);       /* free the malloc'ed string */

    X509_free(cert);     /* free the malloc'ed certificate copy */

}

else

    printf("No certificates.\n");

}

int main(int count, char *strings[])

{   SSL_CTX *ctx;

int server;

SSL *ssl;

char buf[1024];

int bytes;

char *hostname, *portnum;

if ( count != 3 )

{

    printf("usage: %s <hostname> <portnum>\n", strings[0]);

    exit(0);

}

SSL_library_init();

hostname=strings[1];

portnum=strings[2];

ctx = InitCTX();

server = OpenConnection(hostname, atoi(portnum));

ssl = SSL_new(ctx);      /* create new SSL connection state */

SSL_set_fd(ssl, server);    /* attach the socket descriptor */

if ( SSL_connect(ssl) == FAIL )   /* perform the connection */

{

    printf('Eroor: %s\n',stderr);

    ERR_print_errors_fp(stderr);

}

else

{   char *msg = "HelloWorld";

    printf("Connected with %s encryption\n", SSL_get_cipher(ssl));

    ShowCerts(ssl);        /* get any certs */

    SSL_write(ssl, msg, strlen(msg));   /* encrypt & send message */

    bytes = SSL_read(ssl, buf, sizeof(buf)); /* get reply & decrypt */

    buf[bytes] = 0;

    printf("Received: \"%s\"\n", buf);

    SSL_free(ssl);        /* release connection state */

}

close(server);         /* close socket */

SSL_CTX_free(ctx);        /* release context */

return 0;

}


服务端:

#include <errno.h>

#include <unistd.h>

#include <malloc.h>

#include <string.h>

#include <arpa/inet.h>

#include <sys/socket.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <resolv.h>

#include "openssl/ssl.h"

#include "openssl/err.h"

#define FAIL    -1

using namespace std;

int OpenListener(int port)

{   int sd;

struct sockaddr_in addr;

sd = socket(PF_INET, SOCK_STREAM, 0);

bzero(&addr, sizeof(addr));

addr.sin_family = AF_INET;

addr.sin_port = htons(port);

addr.sin_addr.s_addr = INADDR_ANY;

if ( bind(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 )

{

    perror("can't bind port");

    abort();

}

if ( listen(sd, 10) != 0 )

{

    perror("Can't configure listening port");

    abort();

}

return sd;

}

SSL_CTX* InitServerCTX(void)

{

SSL_CTX *ctx = NULL;

    #if OPENSSL_VERSION_NUMBER >= 0x10000000L

           const SSL_METHOD *method;

    #else

            SSL_METHOD *method;

    #endif

    SSL_library_init();

    OpenSSL_add_all_algorithms();  /* load & register all cryptos, etc. */

    SSL_load_error_strings();   /* load all error messages */

    method = SSLv23_client_method(); /* create new server-method instance */

    ctx = SSL_CTX_new(method);   /* create new context from method */

    if ( ctx == NULL )

    {

        ERR_print_errors_fp(stderr);

        abort();

    }

    return ctx;

}

void LoadCertificates(SSL_CTX* ctx, char* CertFile, char* KeyFile)

{

//New lines

    if (SSL_CTX_load_verify_locations(ctx, CertFile, KeyFile) != 1)

        ERR_print_errors_fp(stderr);

    if (SSL_CTX_set_default_verify_paths(ctx) != 1)

        ERR_print_errors_fp(stderr);

    //End new lines

/* set the local certificate from CertFile */

if ( SSL_CTX_use_certificate_file(ctx, CertFile, SSL_FILETYPE_PEM) <= 0 )

{

    ERR_print_errors_fp(stderr);

    abort();

}

/* set the private key from KeyFile (may be the same as CertFile) */

if ( SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0 )

{

    ERR_print_errors_fp(stderr);

    abort();

}

/* verify private key */

if ( !SSL_CTX_check_private_key(ctx) )

{

    fprintf(stderr, "Private key does not match the public certificate\n");

    abort();

}

printf("LoadCertificates Compleate Successfully.....\n");

}

void ShowCerts(SSL* ssl)

{   X509 *cert;

char *line;

cert = SSL_get_peer_certificate(ssl); /* Get certificates (if available) */

if ( cert != NULL )

{

    printf("Server certificates:\n");

    line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);

    printf("Subject: %s\n", line);

    free(line);

    line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);

    printf("Issuer: %s\n", line);

    free(line);

    X509_free(cert);

}

else

    printf("No certificates.\n");

}

void Servlet(SSL* ssl) /* Serve the connection -- threadable */

{   char buf[1024];

char reply[1024];

int sd, bytes;

const char* HTMLecho="<html><body><pre>%s</pre></body></html>\n\n";

if ( SSL_accept(ssl) == FAIL )     /* do SSL-protocol accept */

    ERR_print_errors_fp(stderr);

else

{

    ShowCerts(ssl);        /* get any certificates */

    bytes = SSL_read(ssl, buf, sizeof(buf)); /* get request */

    if ( bytes > 0 )

    {

        buf[bytes] = 0;

        printf("Client msg: \"%s\"\n", buf);

        sprintf(reply, HTMLecho, buf);   /* construct reply */

        SSL_write(ssl, reply, strlen(reply)); /* send reply */

    }

    else

        ERR_print_errors_fp(stderr);

}

sd = SSL_get_fd(ssl);       /* get socket connection */

SSL_free(ssl);         /* release SSL state */

close(sd);          /* close connection */

}

int main(int count, char *strings[])

{   SSL_CTX *ctx;

int server;

char *portnum;

if ( count != 2 )

{

    printf("Usage: %s <portnum>\n", strings[0]);

    exit(0);

}

else

{

    printf("Usage: %s <portnum>\n", strings[1]);

}

SSL_library_init();

portnum = strings[1];

ctx = InitServerCTX();        /* initialize SSL */

LoadCertificates(ctx, "/home/stud/kawsar/mycert.pem", "/home/stud/kawsar/mycert.pem");  /* load certs */

server = OpenListener(atoi(portnum));    /* create server socket */

while (1)

{   struct sockaddr_in addr;

    socklen_t len = sizeof(addr);

    SSL *ssl;

    int client = accept(server, (struct sockaddr*)&addr, &len);  /* accept connection   as usual */

    printf("Connection: %s:%d\n",inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));

    ssl = SSL_new(ctx);              /* get new SSL state with context */

    SSL_set_fd(ssl, client);      /* set connection socket to SSL state */

    Servlet(ssl);         /* service connection */

}

close(server);          /* close server socket */

SSL_CTX_free(ctx);         /* release context */

}

 具体 还可以参考:  http://blog.csdn.net/jinhill/article/details/3615626

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tiny丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值