SSL从理论到实践(五)——SSL通信的实现(Java、Android、IOS、C)

没有读过前几篇文章的同学建议先阅读:
SSL从理论到实践(一)——密码学的相关概念
SSL从理论到实践(二)——SSL
SSL从理论到实践(三)——证书文件
SSL从理论到实践(四)——Keytool
  要使用SSL,首先要生成证书。由于JDK自带的keytool工具默认不能生成BKS类型的密钥库,而Android客户端只支持BKS类型的密钥库,所以要先扩展keytool使其能够生成BKS密钥库。

1. BouncyCastle配置

1.1下载bcprov-ext-jdk15on-158.jar(截止2017/11/19日最新)

下载地址:http://www.bouncycastle.org/latest_releases.html

1.2将bcprov-ext-jdk15on-158.jar复制到 jdk_home\jre\lib\ext下
1.3在jdk_home\jre\lib\security\目录中找到 java.security 在其中增加一行(xx表示数字)
security.provider.xx=org.bouncycastle.jce.provider.BouncyCastleProvider

配置后如下图:
SSL从理论到实践(五)——SSL通信的实现(Java、Android、IOS、C)_第1张图片

2.使用keytool工具生成密钥库、导出证书、导入信任库

2.1生成服务器端的密钥库kserver.keystore,采用默认的JKS类型
keytool -genkeypair -alias server -keystore kserver.keystore -validity 36500 -keyalg ec
(SSLSocket签名算法默认为DSA,Android6.0(API 23)以后KeyStore发生更改,不再支持DSA,但仍支持ECDSA。所以需要指定签名算法:-keyalg ec)
2.2从服务器端密钥库kserver.keystore中导出服务器证书
keytool -exportcert -alias server -file server.cer -keystore kserver.keystore
2.3将服务器端导出的证书导入到客户端信任密钥库tclient.bks中,客户端信任密钥库(tclient.bks)会自动生成,并且此时要特别指明信任密钥库是BKS类型
keytool -importcert -alias server -file server.cer -keystore tclient.bks -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider
2.4生成客户端密钥库kclient.bks
keytool -genkeypair -alias client -keystore kclient.bks -storetype BKS -validity 36500 -provider org.bouncycastle.jce.provider.BouncyCastleProvider -keyalg ec
2.5导出客户端证书
keytool -exportcert -alias client -file client.cer -keystore kclient.bks -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider
2.6将客户端导出的证书导入到服务器端信任密钥库tserver.keystore中,服务器端信任密钥库(tserver.keystore)会自动生成
keytool -importcert -v -alias client -file client.cer -keystore tserver.keystore
2.7将客户端密钥库kclient.bks转成IOS支持的pkcs12格式,会生成kclient.p12文件
keytool -importkeystore -srckeystore kclient.bks -srcstoretype bks -destkeystore kclient.p12 -deststoretype pkcs12

  至此,我们已经生成了Java、Android端和IOS需要的密钥库和证书文件,接下来我们只需要将使用到的文件复制到项目中就可以了。

3.SSLSocket服务器端代码 (Java实现)

public class SocketServer {
    private static final String SERVER_KEY_PASSWORD = "123456";
    private static final String TRUST_KEY_PASSWORD = "123456";
    private static final String SERVER_KEYSTORE_PATH = "src/kserver.keystore";
    private static final String TRUST_KEYSTORE_PATH = "src/tserver.keystore";
    private static final int PORT = 2345;//端口号

    public static void main(String[] args) {
        startServer();
    }

    private static void startServer() {
        ServerSocket serverSocket = null;
        try {
            serverSocket = createServerSocket();
            serverSocket.bind(new InetSocketAddress(PORT));

            while (true) {
                Socket socket = serverSocket.accept();

                OutputStream outputStream = socket.getOutputStream();
                outputStream.write("Hello, I am Server!".getBytes());
                outputStream.flush();

                InputStream inputStream = socket.getInputStream();
                byte[] buffer = new byte[1024];
                int len = inputStream.read(buffer);
                System.out.println("receive:" + new String(buffer,0,len));
                try {
                    if (outputStream != null) {
                        outputStream.close();
                    }
                    if (inputStream != null) {
                        inputStream.close();
                    }
                    if (socket != null) {
                        socket.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (serverSocket != null) {
                    serverSocket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private static ServerSocket createServerSocket() throws Exception {
        SSLContext sslContext = SSLContext.getInstance("TLS");// 使用协议 TLS

        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
        KeyStore keyStore = KeyStore.getInstance("JKS");
        keyStore.load(new FileInputStream(SERVER_KEYSTORE_PATH),SERVER_KEY_PASSWORD.toCharArray());
        keyManagerFactory.init(keyStore, SERVER_KEY_PASSWORD.toCharArray());

        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
        KeyStore trustkeyStore = KeyStore.getInstance("JKS");
        trustkeyStore.load(new FileInputStream(TRUST_KEYSTORE_PATH),TRUST_KEY_PASSWORD.toCharArray());
        trustManagerFactory.init(trustkeyStore);

        sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);

        SSLServerSocket sslServerSocket = (SSLServerSocket) sslContext.getServerSocketFactory().createServerSocket();
        if (sslServerSocket != null) {
            sslServerSocket.setNeedClientAuth(true);// 需要双向认证
        }

        return sslServerSocket;
    }

}

4.SSLSocket客户端代码(Android实现,记得添加权限< uses-permission android:name = “android.permission.INTERNET” />)

public class MainActivity extends Activity {
    private static final String CLIENT_KEY_PASSWORD = "123456";
    private static final String TRUST_KEY_PASSWORD = "123456";
    private static final String CLIENT_KEYSTORE_PATH = "kclient.bks";
    private static final String TRUST_KEYSTORE_PATH = "tclient.bks";
    private static final String IP = "192.168.99.52";//ip和端口号根据服务器不同自行修改
    private static final int PORT = 2345;//ip和端口号根据服务器不同自行修改

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread() {
            public void run() {
                startSocket();
            };
        }.start();
    }

    public void startSocket(){
        byte[] buffer = new byte[1024];
        Socket socket = null;
        InputStream reader = null;
        OutputStream writer = null;
        try {
            socket = createSocket();
            socket.setReuseAddress(true);
            socket.setSoLinger(true, 0);            //socket关闭时,立即释放资源 
            socket.setTcpNoDelay(true);                //数据不作缓冲,立即发送 
            socket.setTrafficClass(0x04 | 0x10);    //高可靠性和最小延迟传输 
            socket.setKeepAlive(true);
            socket.setSoTimeout(10000);
            socket.connect(new InetSocketAddress(IP, PORT), 3000);

            reader = socket.getInputStream();
            int len = reader.read(buffer);
            Log.i("read", "receive:" + new String(buffer,0,len));

            writer = socket.getOutputStream();
            writer.write("Hello, I am Client!".getBytes());
            writer.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (writer != null) {
                    writer.close();
                }
                if (reader != null) {
                    reader.close();
                }
                if (socket != null) {
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private Socket createSocket() throws Exception {
        //取得TLS协议的SSLContext实例
        SSLContext sslContext = SSLContext.getInstance("TLS");

        KeyStore clientKeyStore = KeyStore.getInstance("BKS");
        clientKeyStore.load(getResources().getAssets().open(CLIENT_KEYSTORE_PATH), CLIENT_KEY_PASSWORD.toCharArray());
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("X509");
        keyManagerFactory.init(clientKeyStore, CLIENT_KEY_PASSWORD.toCharArray());

        //取得BKS类型的密钥库实例,这里特别注意:手机只支持BKS密钥库,不支持Java默认的JKS密钥库
        KeyStore trustKeyStore = KeyStore.getInstance("BKS");
        trustKeyStore.load(getResources().getAssets().open(TRUST_KEYSTORE_PATH), TRUST_KEY_PASSWORD.toCharArray());
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");
        trustManagerFactory.init(trustKeyStore);

        //初始化SSLContext实例
        sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);

        //以下两步获得SSLSocket实例
        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
        return sslSocketFactory.createSocket();
    }
}

5.SSLSocket客户端代码(IOS实现——由热心的IOS同事生浩提供)

#import "GCDAsyncSocket.h"
@interface ViewController ()
@property (nonatomic,strong)GCDAsyncSocket *socket;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    _socket = [[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
    [_socket connectToHost:@"192.168.99.52" onPort:2345 error:nil];

}

- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    NSString *str = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
    NSLog(@"接收到数据:%@",str);
}
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
{
    NSLog(@"发送 hello 成功");
}
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
{
    if (err) {
        NSLog(@"%@",err.description);
    }
}
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port
{
    NSLog(@"进入SSL验证");
    [self starSSL:sock];
}
- (void)socket:(GCDAsyncSocket *)sock didReceiveTrust:(SecTrustRef)trust completionHandler:(void (^)(BOOL))completionHandler
{
    //    // SSL 证书
    NSLog(@"进入 didReceiveTrust");
    NSString *certFilePath1 = [[NSBundle mainBundle] pathForResource:@"server" ofType:@"cer"];
    NSData *certData1 = [NSData dataWithContentsOfFile:certFilePath1];

    OSStatus status = -1;
    SecTrustResultType result = kSecTrustResultDeny;

    if(certData1)
    {
        SecCertificateRef   cert1;

        cert1 = SecCertificateCreateWithData(NULL, (__bridge_retained CFDataRef) certData1);

        // 设置证书用于验证
        SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)[NSArray arrayWithObjects:(__bridge id)cert1,nil]);

        // 验证服务器证书和本地证书是否匹配
        status = SecTrustEvaluate(trust, &result);
    }
    else
    {
        NSLog(@"local certificates could not be loaded");

        completionHandler(NO);
    }

    if ((status == noErr && (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified)))
    {
        //成功通过验证,证书可信
        completionHandler(YES);
    }
    else
    {
        CFArrayRef arrayRefTrust = SecTrustCopyProperties(trust);
        NSLog(@"error in connection occured\n%@", arrayRefTrust);

        completionHandler(NO);
    }
}
- (void)socketDidSecure:(GCDAsyncSocket *)sock
{
    NSLog(@"握手成功");
    NSString *dataStr = @"hello server";
    NSData *data = [dataStr dataUsingEncoding:NSUTF8StringEncoding];
    [_socket readDataWithTimeout:-1 tag:0];

    [_socket writeData:data withTimeout:-1 tag:0];
}

- (void)starSSL:(GCDAsyncSocket*)sock {


    /**********单向验证***************/
    // 配置 SSL/TLS 设置信息
    //    NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithCapacity:3];
    //    //允许自签名证书手动验证
    //    [settings setObject:@YES forKey:GCDAsyncSocketManuallyEvaluateTrust];

    // 如果不是自签名证书,而是那种权威证书颁发机构注册申请的证书
    // 那么这个settings字典可不传。
    //    [sock startTLS:settings]; // 开始SSL握手


    /**********双向验证***************/

    NSMutableDictionary *sslSettings = [[NSMutableDictionary alloc] init];

    // SSL 证书
    NSData *pkcs12data = [[NSData alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"kclient" ofType:@"p12"]];

    CFDataRef inPKCS12Data = (CFDataRef)CFBridgingRetain(pkcs12data);

    // c语言字符串
    CFStringRef password = CFSTR("123456");

    const void *keys[] = { kSecImportExportPassphrase };

    const void *values[] = { password };

    CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);

    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);

    OSStatus securityError = SecPKCS12Import(inPKCS12Data, options, &items);
    CFRelease(options);
    CFRelease(password);

    if(securityError == errSecSuccess)
        NSLog(@"Success opening p12 certificate.");

    CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
    SecIdentityRef myIdent = (SecIdentityRef)CFDictionaryGetValue(identityDict,
                                                                  kSecImportItemIdentity);

    SecIdentityRef  certArray[1] = { myIdent };
    CFArrayRef myCerts = CFArrayCreate(NULL, (void *)certArray, 1, NULL);

    [sslSettings setObject:(id)CFBridgingRelease(myCerts) forKey:(NSString *)kCFStreamSSLCertificates];
    [sslSettings setObject:[[NSArray alloc] initWithObjects:(__bridge id)(myIdent), nil] forKey:GCDAsyncSocketSSLCertificates];
    //    SSLProtocol
    [sslSettings setObject:@YES forKey:GCDAsyncSocketManuallyEvaluateTrust];
    [sslSettings setObject:@0 forKey:GCDAsyncSocketSSLProtocolVersionMax];

    // 此方法是GCDScoket 设置ssl验证的唯一方法,需要穿字典
    [sock startTLS:sslSettings];
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


@end

6.使用keytool和openssl生成C需要的密钥文件和证书文件

  C语言需要导入的私钥和证书是pem格式的,而我们之前生成的文件都是der格式的,所以我们需要先将der格式转换成pem格式,然后从pem格式的文件中拷出私钥和证书。

6.1将服务器密钥库kserver.keystore转成pkcs12格式,生成kserver.p12文件
keytool -importkeystore -srckeystore kserver.keystore -srcstorepass 123456 -destkeystore kserver.p12 -deststoretype pkcs12 -deststorepass 123456
(123456是密钥库的密码,请对照自己的密码进行修改)
6.2利用openssl将kserver.p12文件的格式由der转成pem,会生成kserver.pem文件
openssl pkcs12 -in kserver.p12 -out kserver.pem -passin pass:123456 -passout pass:123456
6.3新建文件server.key和server.crt,将kserver.pem文件中的私钥信息复制到server.key中,将kserver.pem文件中的证书信息复制到server.crt中。其中私钥和证书信息如下:

SSL从理论到实践(五)——SSL通信的实现(Java、Android、IOS、C)_第2张图片

6.4利用openssl将kclient.p12文件(之前生成的IOS密钥库)的格式由der转成pem,会生成kclient.pem文件
openssl pkcs12 -in kclient.p12 -out kclient.pem -passin pass:123456 -passout pass:123456
6.5新建文件client.key和client.crt,将kclient.pem文件中的私钥信息复制到client.key中,将kclient.pem文件中的证书信息复制到client.crt中(过程同6.3)

至此,C语言需要的四个文件(server.key、server.crt、client.key和client.crt)我们也已经生成完成。

7.SSLSocket头文件“ssl_common.h”

#ifndef _SSL_COMMON_H_
#define _SSL_COMMON_H_

#define FAIL            -1
#define MAXBUF            1024
#define PRIKEY_PASSWD    "123456"                        //prikey password
#define ALGO_TYPE        "ALL"                            //algorithm type
#define SERVER_CERT        "certs/server.crt"                    //vbox cert file
#define SERVER_KEYF        "certs/server.key"                    //vbox key file
#define CLIENT_CERT        "certs/client.crt"                    //client cert file
#define CLIENT_KEYF        "certs/client.key"                    //client key file
#endif

8.SSLSocket服务端代码(C实现)

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "ssl_common.h"

/*---------------------------------------------------------------------*/
/*--- open_listener - create server socket                          ---*/
/*---------------------------------------------------------------------*/
int open_listener(int port)
{
    int sd;
    struct sockaddr_in addr;

    sd = socket(AF_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;
}

/*---------------------------------------------------------------------*/
/*--- init_server_ctx - initialize SSL server  and create contexts  ---*/
/*---------------------------------------------------------------------*/
SSL_CTX* init_server_ctx(void)
{   
    SSL_METHOD *method;
    SSL_CTX *ctx;

    SSL_library_init();                 /* init algorithms library */
    OpenSSL_add_all_algorithms();       /* load & register all cryptos, etc. */
    SSL_load_error_strings();           /* load all error messages */
    OpenSSL_add_all_ciphers();
    method = SSLv23_server_method();  /* create new server-method instance */
    //method = TLSv1_server_method();
    ctx = SSL_CTX_new(method);          /* screate new context from method */
    if ( ctx == NULL )
    {
        ERR_print_errors_fp(stderr);
        abort();
    }
    return ctx;
}

/*---------------------------------------------------------------------*/
/*--- verify_callback - SSL_CTX_set_verify callback function.       ---*/
/*---------------------------------------------------------------------*/
int verify_callback(int ok, X509_STORE_CTX *store)
{
    char data[256];
    if (ok)
    {
        fprintf(stderr, "verify_callback\n{\n");
        X509 *cert = X509_STORE_CTX_get_current_cert(store);
        int  depth = X509_STORE_CTX_get_error_depth(store);
        int  err = X509_STORE_CTX_get_error(store);

        fprintf(stderr, "certificate at depth: %i\n", depth);
        memset(data, 0, sizeof(data));
        X509_NAME_oneline(X509_get_issuer_name(cert), data, 256);
        fprintf(stderr, "issuer = %s\n", data);
        X509_NAME_oneline(X509_get_subject_name(cert), data, 256);
        fprintf(stderr, "subject = %s\n", data);
        fprintf(stderr, "error status:  %i:%s\n}\n", err, X509_verify_cert_error_string(err));
    }
    return ok;
}

/*---------------------------------------------------------------------*/
/*--- load_certificates - load from files.                          ---*/
/*---------------------------------------------------------------------*/
void load_certificates(SSL_CTX* ctx, char* CaFile, char* CertFile, char* KeyFile)
{
    #if 1
    /* set maximum depth for the certificate chain */
    //SSL_CTX_set_verify_depth(ctx, 1);
    /* set voluntary certification mode*/
    //SSL_VERIFY_NONE SSL_VERIFY_PEER
    //SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
    /* set mandatory certification mode*/
    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback);
    /* load CA certificate file */
    if (SSL_CTX_load_verify_locations(ctx, CaFile, NULL) <=0)
    {
        ERR_print_errors_fp(stderr);
        abort();
    }
    #endif
    /* 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 server private key password */ 
    SSL_CTX_set_default_passwd_cb_userdata(ctx, (void*)PRIKEY_PASSWD);
    /* 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();
    }
    /* set SSL cipher type */
    SSL_CTX_set_cipher_list(ctx, ALGO_TYPE);
}

/*---------------------------------------------------------------------*/
/*--- show_certs_info - print out certificates.                     ---*/
/*---------------------------------------------------------------------*/
void show_certs_info(SSL* ssl)
{
    X509 *cert;
    char *line;

    /* Get connect use algorithm type */
    //printf("SSL connection using %s\n", SSL_get_cipher(ssl));
    /* Get certificates (if available) */
    cert = SSL_get_peer_certificate(ssl);
    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");
}

/*---------------------------------------------------------------------*/
/*--- server_handler - SSL servlet                                  ---*/
/*---------------------------------------------------------------------*/
void server_handler(SSL* ssl)   /* Serve the connection -- threadable */
{
    char buf[1024];
    char reply[1280];
    int sd, bytes;

    if (FAIL == SSL_accept(ssl))                    /* do SSL-protocol accept */
    {
         printf("Accept Fail\n");
        ERR_print_errors_fp(stderr);   
    }
    else
    {
        show_certs_info(ssl);                       /* get any certificates */
        bytes = SSL_read(ssl, buf, sizeof(buf)-1);  /* get request */
        if (FAIL == bytes)
        {
            printf("SSL_read Fail\n");
            ERR_print_errors_fp(stderr);
        }
        buf[bytes] = '\0';
        printf("Client msg: \"%s\"\n", buf);

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

        snprintf(reply, sizeof(reply), "Hello,I am server!", buf);
        bytes = SSL_write(ssl, reply, strlen(reply));   /* send reply */
        if (FAIL == bytes)
        {
            printf("SSL_write Fail\n");
            ERR_print_errors_fp(stderr);
        }
    }
    sd = SSL_get_fd(ssl);               /* get socket connection */
    //SSL_shutdown(ssl);                /* shutdown SSL link */
    SSL_free(ssl);                      /* release SSL state */
    close(sd);                          /* close connection */
}

/*---------------------------------------------------------------------*/
/*--- main - create SSL socket server.                              ---*/
/*---------------------------------------------------------------------*/
int main(int count, char *strings[])
{
    SSL_CTX *ctx;
    int server;
    char *portnum;

    if ( count != 2 )
    {
        printf("Usage: %s \n", strings[0]);
        exit(0);
    }
    portnum = strings[1];
    ctx = init_server_ctx();                                        /* initialize SSL */
    load_certificates(ctx, CLIENT_CERT, SERVER_CERT, SERVER_KEYF);        /* load certs */
    server = open_listener(atoi(portnum));                          /* create server socket */
    while (1)
    {   struct sockaddr_in addr;
        int len = sizeof(addr);
        SSL *ssl;
        /* accept connection as usual */
        int client = accept(server, (struct sockaddr*)&addr, &len);
        printf("Connection from %d, port %d\n", addr.sin_addr.s_addr, addr.sin_port);



        ssl = SSL_new(ctx);             /* get new SSL state with context */
        SSL_set_fd(ssl, client);        /* set connection socket to SSL state */
        server_handler(ssl);            /* service connection */
    }
    close(server);                      /* close server socket */
    SSL_CTX_free(ctx);                  /* release context */

    return 0;
}

9.SSLSocket客户端代码(C实现)

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "ssl_common.h"

/*---------------------------------------------------------------------*/
/*--- open_connection - create socket and connect to server.        ---*/
/*---------------------------------------------------------------------*/
int open_connection(const char *hostname, int port)
{
    int sd;
    struct hostent *host;
    struct sockaddr_in addr;

    if ( (host = gethostbyname(hostname)) == NULL )
    {
        perror(hostname);
        abort();
    }
    sd = socket(AF_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;
}

/*---------------------------------------------------------------------*/
/*--- init_client_ctx - initialize the SSL engine.                  ---*/
/*---------------------------------------------------------------------*/
SSL_CTX* init_client_ctx(void)
{
    SSL_METHOD *method;
    SSL_CTX *ctx;

    SSL_library_init();                 /* init algorithms library */
    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 */
    //method = TLSv1_client_method();
    ctx = SSL_CTX_new(method);          /* create new context from method */
    if ( ctx == NULL )
    {
        ERR_print_errors_fp(stderr);
        abort();
    }
    return ctx;
}

/*---------------------------------------------------------------------*/
/*--- verify_callback - SSL_CTX_set_verify callback function.       ---*/
/*---------------------------------------------------------------------*/
int verify_callback(int ok, X509_STORE_CTX *store)
{
    char data[256];
    if (ok)
    {
        fprintf(stderr, "verify_callback\n{\n");
        X509 *cert = X509_STORE_CTX_get_current_cert(store);
        int  depth = X509_STORE_CTX_get_error_depth(store);
        int  err = X509_STORE_CTX_get_error(store);

        fprintf(stderr, "certificate at depth: %i\n", depth);
        memset(data, 0, sizeof(data));
        X509_NAME_oneline(X509_get_issuer_name(cert), data, 256);
        fprintf(stderr, "issuer = %s\n", data);
        X509_NAME_oneline(X509_get_subject_name(cert), data, 256);
        fprintf(stderr, "subject = %s\n", data);
        fprintf(stderr, "error status:  %i:%s\n}\n", err, X509_verify_cert_error_string(err));
    }
    return ok;
}

/*---------------------------------------------------------------------*/
/*--- load_certificates - load from files.                          ---*/
/*---------------------------------------------------------------------*/
void load_certificates(SSL_CTX* ctx, char* CaFile, char* CertFile, char* KeyFile)
{
    #if 1
    /* set maximum depth for the certificate chain */
    //SSL_CTX_set_verify_depth(ctx, 1);
    /* set verify mode*/
    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback);
    /* load CA certificate file */
    if (SSL_CTX_load_verify_locations(ctx, CaFile, NULL) <=0)
    {
        ERR_print_errors_fp(stderr);
        abort();
    }
    #endif
    /* 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 server private key password */ 
    SSL_CTX_set_default_passwd_cb_userdata(ctx, (void*)PRIKEY_PASSWD);
    /* 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();
    }
    /* set SSL cipher type */
    SSL_CTX_set_cipher_list(ctx, ALGO_TYPE);
}

/*---------------------------------------------------------------------*/
/*--- show_certs_info - print out the certificates.                 ---*/
/*---------------------------------------------------------------------*/
void show_certs_info(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");
}

/*---------------------------------------------------------------------*/
/*--- main - create SSL context and connect                         ---*/
/*---------------------------------------------------------------------*/
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  \n", strings[0]);
        exit(0);
    }
    hostname = strings[1];
    portnum = strings[2];

    ctx = init_client_ctx();                                        /* initialize SSL */
    load_certificates(ctx, SERVER_CERT, CLIENT_CERT, CLIENT_KEYF);  /* load certs */
    server = open_connection(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 */
    {
        ERR_print_errors_fp(stderr);
    }
    else
    {
        char *msg = "Hi! I am Client!";

        printf("Connected with %s encryption\n", SSL_get_cipher(ssl));
        show_certs_info(ssl);                       /* get any certs */
        SSL_write(ssl, msg, strlen(msg));           /* encrypt & send message */
        memset(buf, 0, sizeof(buf));
        bytes = SSL_read(ssl, buf, sizeof(buf)-1);  /* get reply & decrypt */
        buf[bytes] = '\0';
        printf("Server msg: \"%s\"\n", buf);
        SSL_shutdown(ssl);                          /* shutdown SSL link */
        SSL_free(ssl);                              /* release connection state */
    }
    close(server);                                  /* close socket */
    SSL_CTX_free(ctx);                              /* release context */

    return 0;
}

参考

1.SSL协议详解
2.让SSL/TLS协议流行起来:深度解读SSL/TLS实现
3.使用wireshark观察SSL/TLS握手过程–双向认证/单向认证
4.Android 6.0 SSL通信
5.SSL/TLS的原理以及互联网究竟是如何工作的(1-5)
6.JDK 中的证书生成和管理工具 keytool
7.那些证书相关的玩意儿(SSL,X.509,PEM,DER,CRT,CER,KEY,CSR,P12等)

你可能感兴趣的:(SSL,SSL从理论到实践)