欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

用openssl跟Gmail的smtp对话(一)

发布时间:2025/6/17 编程问答 51 豆豆
生活随笔 收集整理的这篇文章主要介绍了 用openssl跟Gmail的smtp对话(一) 小编觉得挺不错的,现在分享给大家,帮大家做个参考.
一、目的 Gmail的webmail虽然常常报系统遇到错误,用起来不太爽,但是smtp/pop3还是蛮厚道的。 服务稳定,被GFW拦截的概率也小一些,可能是采用SSL加密通讯的缘故吧。所以研究一下gmail的smtp连接还是很有意义的。我们先用手头上的工具进行实际操作,对ssl的连接有个感性的认识,然后用C写一个测试程序,最终的目标是用C++写一个COM可以供ASP调用,当然所谓的C++当然是指基于M$的ATL了,纯C++的COM是学院派的人干的活,作为工程人员应用才是首要目的。为什么要写成COM的,因为纵观网络编程的世界就asp没有实现(或者实现了没有开源)gmail的发信功能,不管jmail还是codsys都没有,可是是没有必要,或者是我孤陋寡闻,其他的php、asp.net、java都很容易实现此功能。在asp遍地的过度,写出来估计还是有一定价值的。 二、要求
  • SSL的大致内容,搞清楚C/S间如何握手的就行了
  • OpenSSL,开源的成功案例,没有OpenSSL估计我们只能望机兴叹了,搞明白SSL的算法好像不是一两个月能明白的,所有掌握Openssl是必须的,不用太深入,但是起码能知道如何建立ssl的上下文和封装socket的发送和接收。
  • C/C++基础,上过理工科的人都应该知道。俺打算用C来写HelloGmail程序,被逼无奈啊,谁让OpenSSL是用C写的呢!
  • COM基础,至少要知道COM是如何实现二进制级代码共享的,如何用ATL做COM的。俺得毕业论文是搞fortran和java的混合编程,所以对混合编程情有独钟。当时还不知道有COM这回事,走了很多的弯路:java调c++写的dll,c++的dll调fotran的dll,相当的丑陋 ^^
  • 三、开始 1.简单的认识 我们先在cmd窗口连连gmail看看,把过程弄明白先。由于采用了ssl,所以用普通的telnet是无法直接连接到gmail的smtp的,我们需要openssl构建一个ssl层,由openssl来负责繁琐的ssl协议 我们先要编译OpenSSL,下面这篇博文已经写得很好,俺就不啰嗦了 http://blog.tom.com/blog/read.php?bloggerid=329665&blogid=41867 我在E:/openssl-0.9.8e下

    E:/openssl-0.9.8e/out32dll>openssl s_client -connect smtp.gmail.com:465
    Loading 'screen' into random state - done
    CONNECTED(00000790)
    depth=0 /C=US/ST=California/L=Mountain View/O=Google Inc/CN=smtp.gmail.com
    verify error:num=20:unable to get local issuer certificate
    verify return:1
    depth=0 /C=US/ST=California/L=Mountain View/O=Google Inc/CN=smtp.gmail.com
    verify error:num=27:certificate not trusted
    verify return:1
    depth=0 /C=US/ST=California/L=Mountain View/O=Google Inc/CN=smtp.gmail.com
    verify error:num=21:unable to verify the first certificate
    verify return:1
    ---
    Certificate chain
     0 s:/C=US/ST=California/L=Mountain View/O=Google Inc/CN=smtp.gmail.com
       i:/C=ZA/ST=Western Cape/L=Cape Town/O=Thawte Consulting cc/OU=Certification S
    ervices Division/CN=Thawte Premium Server CA/emailAddress=premium-server@thawte.
    com
    ---
    Server certificate
    -----BEGIN CERTIFICATE-----
    MIIDYzCCAsygAwIBAgIQYZrZzKZNh1fKVFuUlZ6rKzANBgkqhkiG9w0BAQUFADCB
    zjELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJ
    Q2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UE
    CxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhh
    d3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNl
    cnZlckB0aGF3dGUuY29tMB4XDTA3MDczMDE2NTgwN1oXDTA4MDcyOTE2NTgwN1ow
    aDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1v
    dW50YWluIFZpZXcxEzARBgNVBAoTCkdvb2dsZSBJbmMxFzAVBgNVBAMTDnNtdHAu
    Z21haWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQF2mUMNM+qw/i
    wMVSP2D0pgKb0M3RyWHBTQkno3W4y5TeH8LALnqv9/+Th4wZ5PrZ7YPQjmCxdtz6
    Lm5Yx19nDXNw97or6SXvAoZSF+bwh76UFqxpImAGJzvj8Ro7rNkMidJa+KgGaIng
    sIcWuqsj0rrK1AXoUHKmO4N5t0c6XwIDAQABo4GmMIGjMB0GA1UdJQQWMBQGCCsG
    AQUFBwMBBggrBgEFBQcDAjBABgNVHR8EOTA3MDWgM6Axhi9odHRwOi8vY3JsLnRo
    YXd0ZS5jb20vVGhhd3RlUHJlbWl1bVNlcnZlckNBLmNybDAyBggrBgEFBQcBAQQm
    MCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnRoYXd0ZS5jb20wDAYDVR0TAQH/
    BAIwADANBgkqhkiG9w0BAQUFAAOBgQCUoTmFzdJX+2Pz9FhI+H88lFIeBcFnxpPO
    CHO7zs/J3ZI6ZmkuQm4az89tRqvKvRFrQm2CRlzntqWjSdcsIYlKKGZ32iclpNKw
    1aW/Q3IIyyZTTUo9DJezyCrFBV7JxFXOQgYd45+YxPVUNnkw1lTd4RqweuB5p7r4
    nObS2EE7cA==
    -----END CERTIFICATE-----
    subject=/C=US/ST=California/L=Mountain View/O=Google Inc/CN=smtp.gmail.com
    issuer=/C=ZA/ST=Western Cape/L=Cape Town/O=Thawte Consulting cc/OU=Certification
     Services Division/CN=Thawte Premium Server CA/emailAddress=premium-server@thawt
    e.com
    ---
    No client certificate CA names sent
    ---
    SSL handshake has read 1025 bytes and written 314 bytes
    ---
    New, TLSv1/SSLv3, Cipher is DES-CBC3-SHA
    Server public key is 1024 bit
    Compression: NONE
    Expansion: NONE
    SSL-Session:
        Protocol : TLSv1
        Cipher : DES-CBC3-SHA
        Session-ID: AA41A4829FAB5945984CCE49EF1A135703C437F1F169F1BB9D2E8417D6B50B38

        Session-ID-ctx:
        Master-Key: BEE0D063AF73B0645845F359D8E8A488EC33A38497D381C46ECE6F0E7C8DBFB0
    2EA5A1EE3FFD583DD111EC9567EE6D8F
        Key-Arg : None
        Start Time: 1188884689
        Timeout : 300 (sec)
        Verify return code: 21 (unable to verify the first certificate)
    ---
    220 mx.google.com ESMTP c5sm1770312qbc
    EHLO localhost
    250-mx.google.com at your service, [218.80.208.72]
    250-SIZE 28311552
    250-8BITMIME
    250-AUTH LOGIN PLAIN
    250 ENHANCEDSTATUSCODES
    AUTH LOGIN
    334 VXNlcm5hbWU6
    eHVxaW55b25nQGdtYWlsLmNvbQ==
    334 UGFzc3dvcmQ6
    xxxxxxxxx
    235 2.7.0 Accepted

    上面的连接其实是有问题,因为我们没有装载我们的证书,所以到RCPT命令的时候,gmail就把我们给踢了,我们在C写的HelloGmail中将会装载我们的自己的证书。

    2.用C写发信程序

    #include "stdafx.h"

    /******************************************************************************************
    *SSL/TLS客户端程序WIN32版(以demos/cli.cpp为基础)
    *需要用到动态连接库libeay32.dll,ssleay.dll,
    *同时在setting中加入ws2_32.lib libeay32.lib ssleay32.lib,
    *以上库文件在编译openssl后可在out32dll目录下找到,
    *所需证书文件请参照文章自行生成*/

    ******************************************************************************************/
    #include <stdio.h>
    #include <stdlib.h>
    #include <memory.h>
    #include <errno.h>
    #include <sys/types.h>

    #include <winsock2.h>

    #include "openssl/rsa.h"
    #include "openssl/crypto.h"
    #include "openssl/x509.h"
    #include "openssl/pem.h"
    #include "openssl/ssl.h"
    #include "openssl/err.h"
    #include "openssl/rand.h"

    #include <openssl/sha.h>
    #include <openssl/hmac.h>
    #include <openssl/evp.h>
    #include <openssl/bio.h>
    #include <openssl/buffer.h>

    /*所有需要的参数信息都在此处以#define的形式提供*/
    #define CERTF "client.pem" /*客户端的证书(需经CA签名)*/
    #define KEYF "client.key" /*客户端的私钥(建议加密存储)*/
    #define CACERT "ca.pem" /*CA 的证书*/
    #define PORT 465 /*服务端的端口*/
    #define SERVER_ADDR "209.85.147.111" /*服务段的IP地址*/
    #define GMAILUSERNAME "xuqinyong@gmail.com"/*Gmail 帐号*/
    #define GMAILPASSWORD "xxxx"/*Gmail 帐号密码*/


    #define CHK_NULL(x) if ((x)==NULL) exit (-1)
    #define CHK_ERR(err,s) if ((err)==-1) { perror(s); exit(-2); }
    #define CHK_SSL(err) if ((err)==-1) { ERR_print_errors_fp(stderr); exit(-3); }


    /*仍数据*/
    int put_line(SSL* ssl,char* cmd)
    {
        int err;
        printf("C: %s",cmd);
        err = SSL_write (ssl, cmd, strlen(cmd));
        CHK_SSL(err);
        return 1;
    }

    /*读数据*/
    int get_line(SSL* ssl)
    {
        char buf [4096];
        int err;
        err = SSL_read (ssl, buf, sizeof(buf) - 1);
        CHK_SSL(err);
        buf[err] = '/0';
        printf ("S: :%s", buf);
        free(buf);
        return 1;
    }


    char *base64_encode(char *input)
    {
        BIO *bmem, *b64;
        BUF_MEM *bptr;

        b64 = BIO_new(BIO_f_base64());
        bmem = BIO_new(BIO_s_mem());
        b64 = BIO_push(b64, bmem);
        BIO_write(b64, input, strlen(input));
        BIO_flush(b64);
        BIO_get_mem_ptr(b64, &bptr);

        char *buff = (char *)malloc(bptr->length);
        memcpy(buff, bptr->data, bptr->length-1);
        buff[bptr->length-1] = 0;

        BIO_free_all(b64);

        return buff;
    }


    /*捏造信头*/
    char* makeHeader()
    {
        char buf[128];
        char tmp[1024];
        char *rnt,*next;
        int n,length;

        n = length = 0;

        next = tmp;

        n = sprintf(buf,"Date: %s/r/n","date");
        memcpy(tmp,buf,n);
        next+= n;
        length += n;

        n = sprintf(buf,"Return-Path: %s/r/n",GMAILUSERNAME);
        memcpy(next,buf,n);
        next+= n;
        length += n;

        n = sprintf(buf,"To: %s/r/n",GMAILUSERNAME);
        memcpy(next,buf,n);
        next+= n;
        length += n;
        
        n = sprintf(buf, "From: =?UTF-8?B?%s?= <%s>/r/n",base64_encode(GMAILUSERNAME),GMAILUSERNAME);
        memcpy(next,buf,n);
        next+= n;
        length += n;
        
        n = sprintf(buf, "Subject: =?UTF-8?B?%s?=/r/n",base64_encode("Test mail via C"));
        memcpy(next,buf,n);
        next+= n;
        length += n;

        n = sprintf(buf, "MIME-Version: 1.0/r/n");
        memcpy(next,buf,n);
        next+= n;
        length += n;

        n = sprintf(buf, "Content-Transfer-Encoding: base64/r/n");
        memcpy(next,buf,n);
        next+= n;
        length += n;

        n = sprintf(buf, "Content-Type: text/html; charset=/"UTF-8/"/r/n/r/n");
        memcpy(next,buf,n);
        next+= n;
        length += n;

        tmp[length] = 0;

        rnt = (char *)malloc(length);
        strcpy(rnt, tmp);
        
        return rnt;
    }

    char* makeBody(char* instr)
    {
        char* tmp;
        char* rnt;
        tmp = base64_encode(instr);

        rnt = (char *)malloc(strlen(tmp));
        strcpy(rnt, tmp);
        rnt = strcat(rnt,"/r/n");

        return rnt;
    }

    int main ()
    {
        int err;
        int sd;
        struct sockaddr_in sa;
        SSL_CTX* ctx;
        SSL* ssl;
        X509* server_cert;
        char* str;
        SSL_METHOD *meth;
        int seed_int[100]; /*存放随机序列*/

        WSADATA wsaData;

        if(WSAStartup(MAKEWORD(2,2),&wsaData) != 0){
            printf("WSAStartup()fail:%d/n",GetLastError());
            return -1;
        }

        
    //OpenSSL_add_ssl_algorithms(); /*初始化*/

        SSL_library_init();
        SSL_load_error_strings(); /*为打印调试信息作准备*/

        meth=SSLv23_method();
        ctx = SSL_CTX_new (meth);
        CHK_NULL(ctx);

        SSL_CTX_set_default_passwd_cb_userdata(ctx, "password");

        if(!(SSL_CTX_use_certificate_chain_file(ctx, CERTF))){
              ERR_print_errors_fp(stderr);
            exit(-2);
        }
        
        if(!(SSL_CTX_use_PrivateKey_file(ctx, CERTF,SSL_FILETYPE_PEM))){
            ERR_print_errors_fp(stderr);
            exit(-3);
        }

        /* Load the CAs we trust*/
        if(!(SSL_CTX_load_verify_locations(ctx, CACERT,0))){
            printf("Load CA failed!/n");
            exit(-4);
        }


        /*构建随机数生成机制,WIN32平台必需*/
        srand( (unsigned)time( NULL ) );
        for( int i = 0; i < 100;i++ )
        seed_int[i] = rand();
        RAND_seed(seed_int, sizeof(seed_int));

        /*以下是正常的TCP socket建立过程 .............................. */
        printf("Begin tcp socket.../n");

        sd = socket (AF_INET, SOCK_STREAM, 0); CHK_ERR(sd, "socket");

        memset (&sa, '/0', sizeof(sa));
        sa.sin_family = AF_INET;
        sa.sin_addr.s_addr = inet_addr (SERVER_ADDR); /* Server IP */
        sa.sin_port = htons (PORT); /* Server Port number */

        err = connect(sd, (struct sockaddr*) &sa,
        sizeof(sa));
        CHK_ERR(err, "connect");

        /* TCP 链接已建立.开始 SSL 握手过程.......................... */
        printf("Begin SSL negotiation /n");

        ssl = SSL_new (ctx);
        CHK_NULL(ssl);

        SSL_set_fd (ssl, sd);

        err = SSL_connect (ssl);
        CHK_SSL(err);

        /*打印所有加密算法的信息(可选)*/
        printf ("SSL connection using %s/n", SSL_get_cipher (ssl));

        /*得到服务端的证书并打印些信息(可选) */
        server_cert = SSL_get_peer_certificate (ssl);
        CHK_NULL(server_cert);
        printf ("Server certificate:/n");

        str = X509_NAME_oneline (X509_get_subject_name (server_cert),0,0);
        CHK_NULL(str);
        printf ("/t subject: %s/n", str);
        free (str);

        str = X509_NAME_oneline (X509_get_issuer_name (server_cert),0,0);
        CHK_NULL(str);
        printf ("/t issuer: %s/n", str);
        free (str);

        X509_free (server_cert); /*如不再需要,需将证书释放 */

        /* 数据交换开始,用SSL_write,SSL_read代替write,read */
        printf("Begin SSL data exchange/n");
        
        get_line(ssl);
        put_line(ssl,"EHLO localhost/r/n");
        get_line(ssl);
        put_line(ssl,"AUTH LOGIN/r/n");
        get_line(ssl);
        str = strcat(base64_encode(GMAILUSERNAME),"/r/n");
        put_line(ssl,str);
        free(str);
        get_line(ssl);
        str = strcat(base64_encode(GMAILPASSWORD),"/r/n");
        put_line(ssl,str);
        free(str);
        get_line(ssl);
        put_line(ssl,"MAIL FROM:<xuqinyong@gmail.com>/r/n");
        get_line(ssl);
        put_line(ssl,"RCPT TO:<xuqinyong@gmail.com>/r/n");
        get_line(ssl);
        put_line(ssl,"DATA/r/n");
        get_line(ssl);
        
        str = makeHeader();
        put_line(ssl,str);
        free(str);

        str = makeBody("body");
        put_line(ssl,str);
        free(str);

        put_line(ssl,"/r/n./r/n");
        get_line(ssl);
        put_line(ssl,"QUIT/r/n");
        get_line(ssl);


        /* 收尾工作 */
        SSL_shutdown (ssl); /* send SSL/TLS close_notify */
        shutdown (sd,2);
        SSL_free (ssl);
        SSL_CTX_free (ctx);

        return 0;
    }

    参考资料 Beginner's Tutorial: COM/ATL Simple Project

    转载于:https://www.cnblogs.com/mtcnn/archive/2009/09/07/9410143.html

    总结

    以上是生活随笔为你收集整理的用openssl跟Gmail的smtp对话(一)的全部内容,希望文章能够帮你解决所遇到的问题。

    如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。