เปลี่ยนซ็อกเก็ตธรรมดาให้เป็นซ็อกเก็ต SSL


115

ฉันเขียนโปรแกรม C อย่างง่ายซึ่งใช้ซ็อกเก็ต ('ไคลเอนต์' และ 'เซิร์ฟเวอร์') (การใช้งาน UNIX / Linux)

ฝั่งเซิร์ฟเวอร์สร้างซ็อกเก็ต:

sockfd = socket(AF_INET, SOCK_STREAM, 0);

จากนั้นผูกเข้ากับ sockaddr:

bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));

และฟัง (และยอมรับและอ่าน):

listen(sockfd,5);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
read(newsockfd,buffer,255);

ไคลเอนต์สร้างซ็อกเก็ตจากนั้นเขียนลงในซ็อกเก็ต

ตอนนี้ฉันต้องการแปลงการเชื่อมต่อแบบธรรมดานี้เป็นการเชื่อมต่อ SSL ด้วยวิธีที่เรียบง่ายงดงามที่สุดเรียบร้อยที่สุดและรวดเร็วที่สุด

ฉันพยายามเพิ่มOpenSSLในโปรเจ็กต์ของฉันแล้ว แต่ฉันไม่พบวิธีง่ายๆในการนำสิ่งที่ฉันต้องการไปใช้


หากคุณกำลังมองหา "การเชื่อมต่อที่ปลอดภัย" โดยเฉพาะอย่างยิ่ง SSL คุณสามารถดูบางอย่างเช่นproxychains.sourceforge.netซึ่งอยู่นอกแอปพลิเคชันของคุณและตั้งค่าให้ส่งการรับส่งข้อมูลผ่านการเชื่อมต่อ SSH สำหรับ SSL ในแอปพลิเคชัน OpenSSL นั้นค่อนข้างง่ายหากคุณเข้าใจว่า SSL / TLS ควรจะทำงานอย่างไร หากคุณต้องการทางเลือกอื่นให้ลองใช้ yaSSL หรือ gnuTLS
Borealid

3
กำหนด 'วิธีง่ายๆ' OpenSSl เป็นมาตรฐานสำหรับโปรแกรมเมอร์ C หากคุณมีปัญหาคุณควรถามเกี่ยวกับเรื่องนี้
Marquis of Lorne

ตรวจสอบเรื่องนี้อย่างใดอย่างหนึ่งรู้เบื้องต้นเกี่ยวกับการเขียนโปรแกรม OpenSSL (Par เสื้อ I) ส่วนที่ II นั้นล้ำหน้าเกินไปและยากสำหรับฉัน แต่ part2 ก็น่าดู
Rick

นอกจากนี้ตรวจสอบการเขียนโปรแกรมที่ปลอดภัยด้วย OpenSSL ของ API แต่ฉันเพิ่งได้ยินความคิดเห็นเกี่ยวกับความไม่ดีของ Openssl และทางเลือกอื่น ๆ ที่ควรลอง
Rick

อีกตัวเลือกหนึ่งคือการใช้เครื่องมือที่ห่อหุ้มภายนอก SSL เช่นstunnelการstunnel4แพคเกจอยู่ใน distros Debian-based และมันง่ายที่จะใช้ มีข้อ จำกัด บางประการเมื่อเทียบกับการเพิ่มการรองรับ SSL ที่เหมาะสมในเซิร์ฟเวอร์ของคุณ แต่อาจเป็นวิธีที่ดีสำหรับการแก้ปัญหาอย่างรวดเร็ว ฉันชอบ Stunnel เพราะดูเหมือนว่าจะเข้ากับแนวทางเครื่องมือซอฟต์แวร์ UNIX
Sam Watkins

คำตอบ:


150

มีหลายขั้นตอนเมื่อใช้ OpenSSL คุณต้องมีใบรับรอง SSL ที่สร้างขึ้นซึ่งสามารถมีใบรับรองที่มีคีย์ส่วนตัวโปรดระบุตำแหน่งที่แน่นอนของใบรับรอง (ตัวอย่างนี้มีอยู่ในรูท) มีบทเรียนดีๆมากมายอยู่ที่นั่น

บางรายการรวมถึง:

#include <openssl/applink.c>
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

คุณจะต้องเริ่มต้น OpenSSL:

void InitializeSSL()
{
    SSL_load_error_strings();
    SSL_library_init();
    OpenSSL_add_all_algorithms();
}

void DestroySSL()
{
    ERR_free_strings();
    EVP_cleanup();
}

void ShutdownSSL()
{
    SSL_shutdown(cSSL);
    SSL_free(cSSL);
}

ตอนนี้สำหรับฟังก์ชันจำนวนมาก คุณอาจต้องการเพิ่ม while loop ในการเชื่อมต่อ

int sockfd, newsockfd;
SSL_CTX *sslctx;
SSL *cSSL;

InitializeSSL();
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd< 0)
{
    //Log and Error
    return;
}
struct sockaddr_in saiServerAddress;
bzero((char *) &saiServerAddress, sizeof(saiServerAddress));
saiServerAddress.sin_family = AF_INET;
saiServerAddress.sin_addr.s_addr = serv_addr;
saiServerAddress.sin_port = htons(aPortNumber);

bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));

listen(sockfd,5);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);

sslctx = SSL_CTX_new( SSLv23_server_method());
SSL_CTX_set_options(sslctx, SSL_OP_SINGLE_DH_USE);
int use_cert = SSL_CTX_use_certificate_file(sslctx, "/serverCertificate.pem" , SSL_FILETYPE_PEM);

int use_prv = SSL_CTX_use_PrivateKey_file(sslctx, "/serverCertificate.pem", SSL_FILETYPE_PEM);

cSSL = SSL_new(sslctx);
SSL_set_fd(cSSL, newsockfd );
//Here is the SSL Accept portion.  Now all reads and writes must use SSL
ssl_err = SSL_accept(cSSL);
if(ssl_err <= 0)
{
    //Error occurred, log and close down ssl
    ShutdownSSL();
}

จากนั้นคุณสามารถอ่านหรือเขียนโดยใช้:

SSL_read(cSSL, (char *)charBuffer, nBytesToRead);
SSL_write(cSSL, "Hi :3\n", 6);

ปรับปรุงSSL_CTX_newควรจะเรียกว่ามีวิธี TLS SSLv23_server_method()ที่เหมาะกับความต้องการของคุณในการสั่งซื้อเพื่อสนับสนุนรุ่นใหม่ของการรักษาความปลอดภัยแทน ดู: คำอธิบาย OpenSSL SSL_CTX_new

TLS_method (), TLS_server_method (), TLS_client_method () นี่คือเมธอด SSL / TLS ที่ยืดหยุ่นสำหรับเวอร์ชันสำหรับวัตถุประสงค์ทั่วไป เวอร์ชันโปรโตคอลจริงที่ใช้จะถูกต่อรองกับเวอร์ชันสูงสุดที่ไคลเอ็นต์และเซิร์ฟเวอร์รองรับ โปรโตคอลที่รองรับ ได้แก่ SSLv3, TLSv1, TLSv1.1, TLSv1.2 และ TLSv1.3


9
ไม่ "เรียบง่าย" อย่างที่คิด แต่สุดท้าย (ขอบคุณพระเจ้า!) ฉันเห็นรหัสบางอย่าง นี่คือการข้ามแพลตฟอร์มหรือสำหรับระบบที่เหมือนยูนิกซ์ / ยูนิกซ์?
juliomalegria

3
ฉันใช้รหัสที่คล้ายกันในหลายแพลตฟอร์ม: arm, linux และ windows
CaptainBli

2
สุดท้ายifคือผิดแม้ว่า มันควรจะเป็นif (ssl_err <= 0) {เพียงข้อผิดพลาด SSL_accept()ผลตอบแทน1จากความสำเร็จ0"ความล้มเหลวที่ควบคุมได้" และ-1"ความล้มเหลวที่ร้ายแรง" ดูหน้าคน
Jite

2
นอกจากนี้การเข้ารหัส DH จะไม่ทำงานหากSSL_CTX_set_tmp_dh[_callback]()ไม่มีการเรียก เพิ่งค้นพบวิธีที่ยากที่การเข้ารหัส aNULL จะใช้ไม่ได้หากไม่มีมันทำให้เกิดการแจ้งเตือนหมายเลข 40
Roman Dmitrienko

5
@DevNull SSLv23_server_method()ระบุว่าเซิร์ฟเวอร์เข้าใจ SSLv2 และ v3 และเลิกใช้แล้ว เพื่อรองรับ TLS 1.1 และ 1.2 ให้แทนที่วิธีการนั้นด้วย TLS_server_method(). ที่มา
ezPaint

17

OpenSSL ค่อนข้างยาก เป็นเรื่องง่ายที่จะทิ้งความปลอดภัยทั้งหมดของคุณโดยไม่ตั้งใจโดยการเจรจาไม่ถูกต้อง (เฮ้ฉันเคยถูกกัดโดยข้อผิดพลาดที่ curl ไม่อ่านการแจ้งเตือน OpenSSL อย่างถูกต้องและไม่สามารถพูดคุยกับบางไซต์ได้)

หากคุณต้องการความรวดเร็วและเรียบง่ายจริงๆให้วางสตั๊ดไว้หน้าโปรแกรมของคุณเรียกว่าวันละ การมี SSL ในกระบวนการอื่นจะไม่ทำให้คุณช้าลง: http://vincent.bernat.im/en/blog/2011-ssl-benchmark.html


11
นี่เป็นคำตอบที่ใช้ได้จริง แต่ไม่ได้ตอบคำถามจริงๆ
สวิส

4
STUD ถูกทิ้งในปี 2559 Readme แนะนำ: github.com/varnish/hitch
Charles

7

สำหรับคนอื่น ๆ เช่นฉัน:

ครั้งหนึ่งเคยมีตัวอย่างในซอร์ส SSL ในไดเร็กทอรีdemos/ssl/พร้อมโค้ดตัวอย่างใน C ++ ตอนนี้มีให้ใช้งานผ่านทางประวัติเท่านั้น: https://github.com/openssl/openssl/tree/691064c47fd6a7d11189df00a0d1b94d8051cbe0/demos/ssl

คุณอาจจะต้องหาเวอร์ชันที่ใช้งานได้ฉันโพสต์คำตอบนี้ไว้ที่ 6 พฤศจิกายน 2558 และฉันต้องแก้ไขแหล่งที่มา - ไม่มากนัก

ใบรับรอง: .pem ในdemos/certs/apps/: https://github.com/openssl/openssl/tree/master/demos/certs/apps


-1

ที่นี่ตัวอย่างเธรดเซิร์ฟเวอร์ซ็อกเก็ต ssl ของฉัน (การเชื่อมต่อหลายรายการ) https://github.com/breakermind/CppLinux/blob/master/QtSslServerThreads/breakermindsslserver.cpp

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <unistd.h>
#include <iostream>

#include <breakermindsslserver.h>

using namespace std;

int main(int argc, char *argv[])
{
    BreakermindSslServer boom;
    boom.Start(123,"/home/user/c++/qt/BreakermindServer/certificate.crt", "/home/user/c++/qt/BreakermindServer/private.key");
    return 0;
}

โปรดรวมวิธีแก้ปัญหาไว้ในโพสต์ SO ของคุณโดยตรง
Maciej Jureczko
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.