參考:深入探索 OpenSSL:概念、原理、開發步驟、使用方法、使用場景及代碼示例
地址:https://oneisall.blog.csdn.net/article/details/131489812?spm=1001.2014.3001.5502
目錄
- 1. OpenSSL 概念
- 2. OpenSSL 原理
- 3. OpenSSL 開發步驟
- 4. OpenSSL 使用方法
- 5. OpenSSL 應用場景
- pdf教程代碼
當涉及到網絡通信和數據傳輸的安全性時,OpenSSL 是一個非常重要的工具和庫。它是一個開源軟件包,提供了廣泛的加密和安全功能。在本博客中,我們將詳細介紹 OpenSSL 的概念、原理、開發步驟、使用方法、使用場景,并結合代碼示例進行說明。
1. OpenSSL 概念
OpenSSL 是一個用于實現安全通信的軟件包,它由一組密碼學函數庫組成。它的主要目標是通過使用公開的密碼學算法來保護數據的機密性、完整性和身份驗證。它支持對稱加密、非對稱加密、數字簽名、證書管理等功能。
2. OpenSSL 原理
OpenSSL 采用了多種密碼學算法和協議來實現其安全功能。它支持各種對稱密碼算法,如 AES、DES 和 Blowfish,用于加密和解密數據。此外,它還支持非對稱密碼算法,如 RSA 和 DSA,用于生成公鑰和私鑰,并進行數字簽名和驗證。OpenSSL 還支持 SSL/TLS 協議,用于在網絡通信中建立安全連接。
3. OpenSSL 開發步驟
使用 OpenSSL 進行開發可以分為以下幾個步驟:
步驟 1: 安裝 OpenSSL
首先,您需要從 OpenSSL 官方網站上下載(免費提供)并安裝 OpenSSL 軟件包。根據您的操作系統和環境,選擇相應的版本進行安裝。
步驟 2: 包含頭文件
在代碼中包含 OpenSSL 的頭文件,以便能夠使用其中的函數和數據結構。例如,在 C 語言中,可以使用以下語句進行包含:
#include <openssl/ssl.h>
步驟 3: 初始化 OpenSSL
在使用 OpenSSL 之前,需要進行初始化設置。通過調用 SSL_library_init() 函數來初始化 SSL 庫。
SSL_library_init();
步驟 4: 創建 SSL 上下文
要建立一個安全連接,需要創建一個 SSL 上下文對象,該對象將存儲有關加密和身份驗證等設置。您可以使用以下代碼片段創建一個 SSL 上下文:
SSL_CTX *ctx = SSL_CTX_new(SSLv23_client_method());
步驟 5: 加載證書和私鑰
如果您要在服務器端使用 OpenSSL,您需要加載服務器的數字證書和私鑰。可以使用以下代碼片段進行加載:
SSL_CTX_use_certificate_file(ctx, "server.crt", SSL_FILETYPE_PEM);
SSL_CTX_use_PrivateKey_file(ctx, "server.key", SSL_FILETYPE_PEM);
步驟 6: 建立安全連接
使用 SSL 上下文對象,可以建立與另一端的安全連接。對于服務器端,可以使用以下代碼片段:
SSL *ssl = SSL_new(ctx);
SSL_set_fd(ssl, sockfd);
SSL_accept(ssl);
對于客戶端,可以使用以下代碼片段:
SSL *ssl = SSL_new(ctx);
SSL_set_fd(ssl, sockfd);
SSL_connect(ssl);
步驟 7: 數據傳輸和處理
一旦安全連接建立成功,就可以在連接上進行數據傳輸和處理。您可以使用 SSL_read() 和 SSL_write() 函數來讀取和寫入數據。
char buf[1024];
int len = SSL_read(ssl, buf, sizeof(buf));
步驟 8: 關閉連接
最后,在完成數據傳輸后,應該關閉 SSL 連接并釋放資源。可以使用以下代碼片段:
SSL_shutdown(ssl);
SSL_free(ssl);
SSL_CTX_free(ctx);
4. OpenSSL 使用方法
使用 OpenSSL,您可以執行許多加密和安全操作。以下是一些常見的 OpenSSL 使用方法的示例:
-
對稱加密:使用 OpenSSL 命令行工具,您可以加密和解密文件。例如,可以使用以下命令使用 AES 加密一個文件: openssl enc -aes-256-cbc -salt -in <input_file> -out <output_file>
-
非對稱加密:您可以使用 OpenSSL 生成 RSA 密鑰對,并使用公鑰加密數據,私鑰解密數據。例如,可以使用以下命令生成 RSA 密鑰對:openssl genpkey -algorithm RSA -out private.pem ,然后使用以下命令加密數據:openssl rsautl -encrypt -pubin -inkey public.pem -in <input_file> -out <output_file>
-
數字簽名:您可以使用 OpenSSL 生成數字簽名,并驗證簽名的有效性。例如,可以使用以下命令生成簽名:openssl dgst -sign private.pem -out signature <data_file> ,然后使用以下命令驗證簽名:openssl dgst -verify public.pem -signature signature <data_file>
5. OpenSSL 應用場景
OpenSSL 在許多領域都有廣泛的應用,以下是一些常見的應用場景:
-
網絡安全:OpenSSL 被廣泛用于實現 SSL/TLS 協議,以確保在互聯網上的數據傳輸的安全性。它可以用于創建安全的網站、虛擬專用網絡(VPN)以及安全電子郵件通信。
-
證書管理:OpenSSL 可以生成和管理數字證書,用于身份驗證和安全通信。它可以用于簽署和驗證數字證書,并用于創建自簽名證書和受信任的證書頒發機構(CA)。
-
加密工具:OpenSSL 提供了各種加密和解密算法,可以用于保護敏感數據。它可以用于加密文件、密碼生成器以及實現端到端的加密通信。
總結:OpenSSL 是一個功能強大的開源軟件包,提供了廣泛的加密和安全功能。它的概念、原理、開發步驟、使用方法和應用場景都非常豐富。通過使用 OpenSSL,您可以保護您的數據和通信,確保其機密性、完整性和身份驗證。無論是在網絡通信還是數據安全方面,OpenSSL 都是一個重要的工具和庫。
#include <stdio.h>
#include <stdlib.h>
#include <openssl/ssl.h>int main() {// 初始化 OpenSSLSSL_library_init();// 創建 SSL 上下文SSL_CTX *ctx = SSL_CTX_new(SSLv23_client_method());if (ctx == NULL) {printf("Failed to create SSL context.\n");return 1;}// 加載服務器證書if (SSL_CTX_use_certificate_file(ctx, "server.crt", SSL_FILETYPE_PEM) != 1) {printf("Failed to load server certificate.\n");SSL_CTX_free(ctx);return 1;}// 加載服務器私鑰if (SSL_CTX_use_PrivateKey_file(ctx, "server.key", SSL_FILETYPE_PEM) != 1) {printf("Failed to load server private key.\n");SSL_CTX_free(ctx);return 1;}// 創建 SSL 對象SSL *ssl = SSL_new(ctx);if (ssl == NULL) {printf("Failed to create SSL object.\n");SSL_CTX_free(ctx);return 1;}// 設置連接套接字int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0) {printf("Failed to create socket.\n");SSL_free(ssl);SSL_CTX_free(ctx);return 1;}// 連接服務器struct sockaddr_in server_addr;server_addr.sin_family = AF_INET;server_addr.sin_port = htons(443);server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {printf("Failed to connect to server.\n");SSL_free(ssl);SSL_CTX_free(ctx);return 1;}// 將 SSL 對象綁定到連接套接字SSL_set_fd(ssl, sockfd);// 建立 SSL 連接if (SSL_connect(ssl) <= 0) {printf("Failed to establish SSL connection.\n");SSL_free(ssl);SSL_CTX_free(ctx);return 1;}// 發送數據char message[] = "Hello, server!";SSL_write(ssl, message, strlen(message));// 接收數據char buffer[1024];SSL_read(ssl, buffer, sizeof(buffer));printf("Received: %s\n", buffer);// 關閉 SSL 連接SSL_shutdown(ssl);SSL_free(ssl);SSL_CTX_free(ctx);return 0;
}
上面的示例演示了如何使用 OpenSSL 在客戶端建立一個安全連接。首先,我們需要初始化 OpenSSL 和創建 SSL 上下文對象。然后,加載服務器的證書和私鑰。接下來,我們創建一個 SSL 對象并將其綁定到連接套接字。通過調用SSL_connect() 建立與服務器的安全連接。然后,我們可以使用 SSL_write() 發送數據,使用 SSL_read() 接收數據。最后,我們關閉 SSL 連接并釋放資源。
注:這只是一個簡單的示例,實際的使用場景可能會更復雜。具體的開發步驟和代碼實現可能因應用場景而異。在實際開發過程中,建議參考 OpenSSL 的官方文檔和相關資料以獲取更詳細的信息和指導。
pdf教程代碼
/*************服務端代碼**************/
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <errno.h>
#ifndef _WIN32
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#else
#include <winsock2.h>
#include <windows.h>
#endif
#include "pthread.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>
#define CERTF "certs/sslservercert.pem"
#define KEYF "certs/sslserverkey.pem"
#define CAFILE "certs/cacert.pem"
pthread_mutex_t mlock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t *lock_cs;
static long *lock_count;
#define CHK_NULL(x) \if ((x) == NULL) \{ \printf("null\n"); \}
#define CHK_ERR(err, s) \if ((err) == -1) \{ \printf(" -1 \n"); \}
#define CHK_SSL(err) \if ((err) == -1) \{ \printf(" -1 \n"); \}#define CAFILE "certs/cacert.pem"int verify_callback_server(int ok, X509_STORE_CTX *ctx)
{printf("verify_callback_server \n");return ok;
}int SSL_CTX_use_PrivateKey_file_pass(SSL_CTX *ctx, char *filename, char *pass)
{EVP_PKEY *pkey = NULL;BIO *key = NULL;key = BIO_new(BIO_s_file());BIO_read_filename(key, filename);pkey = PEM_read_bio_PrivateKey(key, NULL, NULL, pass);if (pkey == NULL){printf("PEM_read_bio_PrivateKey err");return -1;}if (SSL_CTX_use_PrivateKey(ctx, pkey) <= 0){printf("SSL_CTX_use_PrivateKey err\n");return -1;}BIO_free(key);return 1;
}
static int s_server_verify = SSL_VERIFY_NONE;void *thread_main(void *arg)
{SOCKET s, AcceptSocket;WORD wVersionRequested;WSADATA wsaData;struct sockaddr_in service;int err;size_t client_len;SSL_CTX *ctx;SSL *ssl;X509 *client_cert;char *str;char buf[1024];SSL_METHOD *meth;ssl = (SSL *)arg;s = SSL_get_fd(ssl); // SSL_get_fderr = SSL_accept(ssl); // SSL_acceptif (err < 0){printf("ssl accerr\n");return;}printf("SSL connection using %s\n", SSL_get_cipher(ssl));client_cert = SSL_get_peer_certificate(ssl);if (client_cert != NULL){printf("Client certificate:\n");str = X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0);CHK_NULL(str);printf("\t subject: %s\n", str);OPENSSL_free(str);str = X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0);CHK_NULL(str);printf("\t issuer: %s\n", str);OPENSSL_free(str);X509_free(client_cert);}else{printf("Client does not have certificate.\n");}memset(buf, 0, 1024);err = SSL_read(ssl, buf, sizeof(buf) - 1); // SSL_readif (err < 0){printf("ssl read err\n");closesocket(s);return;}printf("get : %s\n", buf);
#if 0
buf[err] = '\0'; // SSL_write
err = SSL_write (ssl, "I hear you.", strlen("I hear you.")); CHK_SSL(err);
#endifSSL_free(ssl);closesocket(s);
}pthread_t pthreads_thread_id(void)
{pthread_t ret;ret = pthread_self();return (ret);
}void pthreads_locking_callback(int mode, int type, char *file,int line)
{if (mode & CRYPTO_LOCK){pthread_mutex_lock(&(lock_cs[type]));lock_count[type]++;}else{pthread_mutex_unlock(&(lock_cs[type]));}
}/*
SSL_CTX:數據結構主要用于SSL 握手前的環境準備,設置CA 文件和目錄、設置SSL 握手中的證書文件和私鑰、設置協議版本以及其他一些SSL 握手時的選項;
SSL:數據結構主要用于SSL 握手以及傳送應用數據;
SSL_SESSION:中保存了主密鑰、session id、讀寫加解密鑰、讀寫MAC 密鑰等信息;
SSL_CTX 中緩存了所有SSL_SESSION 信息,SSL 中包含SSL_CTX。
*/
int main()
{int err;int i;SOCKET s, AcceptSocket;WORD wVersionRequested;WSADATA wsaData;struct sockaddr_in service;pthread_t pid;size_t client_len;SSL_CTX *ctx; SSL *ssl;X509 *client_cert;char *str;char buf[1024];SSL_METHOD *meth;SSL_load_error_strings();SSLeay_add_ssl_algorithms();meth = SSLv3_server_method();ctx = SSL_CTX_new(meth);if (!ctx){ERR_print_errors_fp(stderr);exit(2);}if ((!SSL_CTX_load_verify_locations(ctx, CAFILE, NULL)) ||(!SSL_CTX_set_default_verify_paths(ctx))){printf("err\n");exit(1);}if (SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0){ERR_print_errors_fp(stderr);exit(3);}if (SSL_CTX_use_PrivateKey_file_pass(ctx, KEYF, "123456") <= 0){ERR_print_errors_fp(stderr);exit(4);}if (!SSL_CTX_check_private_key(ctx)){fprintf(stderr, "Private key does not match the certificate public key\n");exit(5);}s_server_verify = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT |SSL_VERIFY_CLIENT_ONCE;SSL_CTX_set_verify(ctx, s_server_verify, verify_callback_server);SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(CAFILE));wVersionRequested = MAKEWORD(2, 2);err = WSAStartup(wVersionRequested, &wsaData);if (err != 0){printf("err\n");return -1;}s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // socket,第一步if (s < 0){return -1;}service.sin_family = AF_INET;service.sin_addr.s_addr = inet_addr("127.0.0.1");service.sin_port = htons(1111);if (bind(s, (SOCKADDR *)&service, sizeof(service)) == SOCKET_ERROR) // bind{printf("bind() failed.\n");closesocket(s);return -1;}if (listen(s, 1) == SOCKET_ERROR) // listen{printf("Error listening on socket.\n");}printf("recv .....\n");lock_cs = OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));lock_count = OPENSSL_malloc(CRYPTO_num_locks() * sizeof(long));for (i = 0; i < CRYPTO_num_locks(); i++){lock_count[i] = 0;pthread_mutex_init(&(lock_cs[i]), NULL);}// 多線程需要設置的兩個回調函數CRYPTO_set_id_callback((unsigned long (*)())pthreads_thread_id);CRYPTO_set_locking_callback((void (*)())pthreads_locking_callback);while (1){struct timeval tv;fd_set fdset;tv.tv_sec = 1;tv.tv_usec = 0;FD_ZERO(&fdset);FD_SET(s, &fdset);select(s + 1, &fdset, NULL, NULL, (struct timeval *)&tv);if (FD_ISSET(s, &fdset)){AcceptSocket = accept(s, NULL, NULL); // acceptssl = SSL_new(ctx); // SSL_newCHK_NULL(ssl);err = SSL_set_fd(ssl, AcceptSocket); // SSL_set_fdif (err > 0){err = pthread_create(&pid, NULL, &thread_main, (void *)ssl);pthread_detach(pid);}elsecontinue;}}SSL_CTX_free(ctx);return 0;
}
/**************客戶端代碼**************/
#include <stdio.h>
#include <memory.h>
#include <errno.h>
#ifndef _WIN32
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#else
#include <windows.h>
#endif
#include "pthread.h"
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#define MAX_T 1000
#define CLIENTCERT "certs/sslclientcert.pem"
#define CLIENTKEY "certs/sslclientkey.pem"
#define CAFILE "certs/cacert.pem"
static pthread_mutex_t *lock_cs;
static long *lock_count;pthread_t pthreads_thread_id(void)
{pthread_t ret;ret = pthread_self();return (ret);
}
void pthreads_locking_callback(int mode, int type, char *file,int line)
{if (mode & CRYPTO_LOCK){pthread_mutex_lock(&(lock_cs[type]));lock_count[type]++;}else{pthread_mutex_unlock(&(lock_cs[type]));}
}
int verify_callback(int ok, X509_STORE_CTX *ctx)
{printf("verify_callback\n");return ok;
}
int SSL_CTX_use_PrivateKey_file_pass(SSL_CTX *ctx, char *filename, char *pass)
{EVP_PKEY *pkey = NULL;BIO *key = NULL;key = BIO_new(BIO_s_file());BIO_read_filename(key, filename);pkey = PEM_read_bio_PrivateKey(key, NULL, NULL, pass);if (pkey == NULL){printf("PEM_read_bio_PrivateKey err");return -1;}if (SSL_CTX_use_PrivateKey(ctx, pkey) <= 0){printf("SSL_CTX_use_PrivateKey err\n");return -1;}BIO_free(key);return 1;
}void *thread_main(void *arg)
{int err, buflen, read;int sd;SSL_CTX *ctx = (SSL_CTX *)arg;struct sockaddr_in dest_sin;SOCKET sock;PHOSTENT phe;WORD wVersionRequested;WSADATA wsaData;SSL *ssl;X509 *server_cert;char *str;char buf[1024];SSL_METHOD *meth;FILE *fp;wVersionRequested = MAKEWORD(2, 2);err = WSAStartup(wVersionRequested, &wsaData);if (err != 0){printf("WSAStartup err\n");return -1;}sock = socket(AF_INET, SOCK_STREAM, 0); // socket,第一步dest_sin.sin_family = AF_INET;dest_sin.sin_addr.s_addr = inet_addr("127.0.0.1");dest_sin.sin_port = htons(1111);
again:err = connect(sock, (PSOCKADDR)&dest_sin, sizeof(dest_sin)); // connectif (err < 0){Sleep(1);goto again;}ssl = SSL_new(ctx); // SSL_newif (ssl == NULL){printf("ss new err\n");return;}SSL_set_fd(ssl, sock); //SSL_connecterr = SSL_connect(ssl);if (err < 0){printf("SSL_connect err\n");return;}printf("SSL connection using %s\n", SSL_get_cipher(ssl));server_cert = SSL_get_peer_certificate(ssl);printf("Server certificate:\n");str = X509_NAME_oneline(X509_get_subject_name(server_cert), 0, 0);printf("\t subject: %s\n", str);OPENSSL_free(str);str = X509_NAME_oneline(X509_get_issuer_name(server_cert), 0, 0);printf("\t issuer: %s\n", str);OPENSSL_free(str);X509_free(server_cert);err = SSL_write(ssl, "Hello World!", strlen("Hello World!")); // SSL_writeif (err < 0){printf("ssl write err\n");return;}
#if 0
memset(buf,0,ONE_BUF_SIZE);
err = SSL_read (ssl, buf, sizeof(buf) - 1); // SSL_read
if(err<0)
{
printf("ssl read err\n");
return ;
}
buf[err] = '\0';
printf ("Got %d chars:'%s'\n", err, buf);
#endifSSL_shutdown(ssl); /* send SSL/TLS close_notify */SSL_free(ssl);closesocket(sock);
}int main()
{int err, buflen, read;int sd;struct sockaddr_in dest_sin;SOCKET sock;PHOSTENT phe;WORD wVersionRequested;WSADATA wsaData;SSL_CTX *ctx;SSL *ssl;X509 *server_cert;char *str;char buf[1024];SSL_METHOD *meth;int i;pthread_t pid[MAX_T];SSLeay_add_ssl_algorithms();meth = SSLv3_client_method();SSL_load_error_strings();ctx = SSL_CTX_new(meth);if (ctx == NULL){printf("ssl ctx new eer\n");return -1;}if (SSL_CTX_use_certificate_file(ctx, CLIENTCERT, SSL_FILETYPE_PEM) <= 0){ERR_print_errors_fp(stderr);exit(3);}if (SSL_CTX_use_PrivateKey_file_pass(ctx, CLIENTKEY, "123456") <= 0){ERR_print_errors_fp(stderr);exit(4);}lock_cs = OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));lock_count = OPENSSL_malloc(CRYPTO_num_locks() * sizeof(long));for (i = 0; i < CRYPTO_num_locks(); i++){lock_count[i] = 0;pthread_mutex_init(&(lock_cs[i]), NULL);}// 多線程需要設置的兩個回調函數CRYPTO_set_id_callback((unsigned long (*)())pthreads_thread_id);CRYPTO_set_locking_callback((void (*)())pthreads_locking_callback);for (i = 0; i < MAX_T; i++){err = pthread_create(&(pid[i]), NULL, &thread_main, (void *)ctx);if (err != 0){printf("pthread_create err\n");continue;}}for (i = 0; i < MAX_T; i++){pthread_join(pid[i], NULL);}SSL_CTX_free(ctx);printf("test ok\n");return 0;
}