मैं ओपनएसएसएल लाइब्रेरी का उपयोग कर एक साधारण एसएसएल क्लाइंट लिख रहा हूं। मैं कनेक्शन पूर्ण होने के बाद सर्वर द्वारा प्रस्तुत प्रमाणपत्र श्रृंखला को प्रिंट करने में सक्षम होना चाहता हूं। जब कनेक्शन सफलतापूर्वक पूरा हो जाता है, तो यह कोई समस्या नहीं है। हालांकि, अगर किसी कारण से कनेक्शन विफल हो जाता है, तो मैं सर्वर द्वारा प्रस्तुत असफल प्रमाणपत्र प्राप्त करने में असमर्थ हूं। यहां एक SSCCE है जो इसे प्रदर्शित करता है।

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>

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

#define CIPHER_LIST "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"
// #define HOST "google.com" // works
#define HOST "expired.badssl.com" // does not print presented certificate


void print_certificates(SSL *ssl){
    STACK_OF(X509) * sk = SSL_get_peer_cert_chain(ssl);
    X509* cert = NULL;
    char sbuf[1024];
    char ibuf[1024];

    if(sk == NULL){
        printf("Cert chain is null!\n");
    }

    for (int i = 0; i < sk_X509_num(sk); i++) {
        cert = sk_X509_value(sk, i);
        fprintf(stdout, "Subject: %s\n", X509_NAME_oneline(X509_get_subject_name(cert), sbuf, 1024));
        fprintf(stdout, "Issuer: %s\n", X509_NAME_oneline(X509_get_issuer_name(cert), ibuf, 1024));
        PEM_write_X509(stdout, cert);
    }
}

void verify_cert(SSL *ssl, char* host){
    print_certificates(ssl);
    X509* cert = SSL_get_peer_certificate(ssl);
    if(cert) { X509_free(cert); }

    int res = SSL_get_verify_result(ssl);

    if(!(X509_V_OK == res)){
        printf("ERROR (NOT VERIFIED - %s): %s\n", X509_verify_cert_error_string(res), host);
        return;
    }

    printf("SUCCESS: %s\n", host);
    fflush(stdout);

}

int main(int argc, char **argv){
    SSL * ssl = NULL;
    SSL_CTX *ctx = NULL;
    BIO *bio = NULL;
    int res;

    SSL_library_init();
    SSL_load_error_strings();

    const SSL_METHOD* method = SSLv23_method();
    if(method == NULL)
        goto End;

    ctx = SSL_CTX_new(method);
    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
    if (ctx == NULL)
        goto End;
    SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 |
        SSL_OP_NO_SSLv3);

    res = SSL_CTX_set_default_verify_paths(ctx);
    if (res != 1)
        goto End;

    bio = BIO_new_ssl_connect(ctx);
    if (bio == NULL)
        return 0;

    BIO_set_conn_hostname(bio, HOST);
    BIO_set_conn_port(bio, "443");
    BIO_set_nbio(bio, 1);

    BIO_get_ssl(bio, &ssl);
    if(ssl == NULL)
        goto End;

    SSL_set_cipher_list(ssl, CIPHER_LIST);
    res = SSL_set_tlsext_host_name(ssl, HOST);

    int still_connecting = 1;
    while(still_connecting){
        int res = SSL_connect(ssl);
        if (res <= 0){
            unsigned long error = SSL_get_error(ssl, res);
            if ( (error != SSL_ERROR_WANT_CONNECT) &&
                 (error != SSL_ERROR_WANT_READ) && (error != SSL_ERROR_WANT_WRITE) )
            {
                printf("Connection encountered fatal error\n");
                ERR_print_errors_fp(stdout);
                still_connecting = 0;
            }
        }
        else{
            printf("Connection completed succesfully\n");
            still_connecting = 0;
        }
    }

    verify_cert(ssl, HOST);

End:
return 0;
}

(इसे संकलित करने का सबसे तेज़ तरीका gcc sscce.c -lcrypto -lssl -o sscce है)।

जब भी SSL_connect(ssl) 1 लौटाता है (जब भी कनेक्शन सफल होता है) , print_certificates(ssl) उम्मीद के मुताबिक काम करता है। हालांकि, अगर SSL_connect(ssl) 1 के अलावा कुछ भी लौटाता है (कनेक्शन विफल हुआ), print_certificates(ssl) कुछ भी प्रिंट नहीं करता है, क्योंकि SSL_get_peer_cert_chain(ssl) शून्य लौटाता है। हालांकि यह उन सर्वरों के लिए एक तार्किक व्यवहार है जो प्रमाणपत्र प्रस्तुत नहीं करते हैं, ऐसे सर्वर पर जो अमान्य प्रमाणपत्र प्रस्तुत करते हैं, प्रमाणपत्र तक पहुंच नहीं होने से सर्वर कॉन्फ़िगरेशन समस्याओं को डीबग करना मुश्किल हो जाता है।

दिलचस्प बात यह है कि, SSL_get_verify_result(ssl) कनेक्शन विफल होने पर सही त्रुटि कोड लौटाता है, इस तथ्य के बावजूद कि मुझे स्वयं प्रमाणपत्र श्रृंखला नहीं मिल सकती है। * मैंने ओपनएसएसएल कोडबेस को थोड़ा सा देखा है और यह पता लगाने की कोशिश की है कि यह क्यों है है, और जबकि मैं अभी भी यह समझने के लिए संघर्ष कर रहा हूं कि सब कुछ एक साथ कैसे फिट बैठता है, ऐसा लगता है कि ssl_verify_cert_chain फ़ंक्शन कि त्रुटि जांच करने के बाद प्रमाणपत्रों को मुक्त कर देता है। मैं अनुमान लगा रहा हूं कि उपरोक्त उदाहरण कोड में, SSL_connect, एक बार जब इसकी पूरी प्रमाणपत्र श्रृंखला हो जाती है, तो कुछ अंतर्निहित सत्यापन कोड चलाता है जो प्रमाण पत्र को print_certificates तक पहुंचने से पहले मुक्त कर देता है। यह मुझे भ्रमित करता है, क्योंकि मुझे समझ में नहीं आता कि सत्यापन विफल होने पर प्रमाणपत्रों को मुक्त क्यों किया जाएगा, लेकिन जब यह सफल नहीं होता है। शायद कोई व्यक्ति जो ओपनएसएसएल के आंतरिक व्यवहार के बारे में अधिक जानता है, इस पर कुछ प्रकाश डाल सकता है।

मैंने नोट किया है कि openssl उपयोगिता द्वारा प्रदान किया गया स्टॉक s_client, जब showcerts विकल्प (openssl s_client -showcerts -connect expired.badssl.com:443) के साथ चलाया जाता है, इस व्यवहार को प्रदर्शित नहीं करता है। चाहे कनेक्शन सफल हो या विफल, प्रमाण पत्र मुद्रित होते हैं। मेरे SSCCE में print_certificates फ़ंक्शन s_client सर्टिफिकेट प्रिंटिंग कोड, लेकिन s_client SSL_connect का उपयोग नहीं करता है, इसलिए यह आश्चर्यजनक नहीं है कि यह अलग व्यवहार प्रदर्शित करता है। मैं ध्यान देता हूं कि s_client एक कस्टम प्रमाणपत्र सेट करता है कॉलबैक सत्यापित करें ( यहां परिभाषित), लेकिन मैं अनिच्छुक हूं डिफ़ॉल्ट ** के अलावा कुछ भी उपयोग करने के लिए फ़ंक्शन को सत्यापित करें।

tl;dr SSL_get_peer_cert_chain(ssl) यदि सर्वर अमान्य प्रमाणपत्र श्रृंखला प्रस्तुत करता है, तो यह शून्य हो जाता है। असफल प्रमाणपत्र श्रृंखला को प्रिंट करने के लिए मैं इसे कैसे प्राप्त कर सकता हूं?

संपादित करें: मैंने पुष्टि की है कि यह समस्या तब भी होती है जब मैं बीआईओ स्थिति को अवरुद्ध करने के लिए सेट करता हूं, और साथ ही, (इसके लायक क्या है), जब उपरोक्त कोड लिब्रेएसएसएल का उपयोग करके संकलित किया जाता है।

2 संपादित करें: मैंने पाया कि एक ऐसा फ़ंक्शन बनाना जो सिर्फ 1 लौटाता है और उसे SSL_CTX_set_verify को कॉलबैक फ़ंक्शन (NULL के बजाय) के रूप में पास करता है, SSL_get_peer_cert_chain(ssl) को प्रमाणपत्र श्रृंखला को अपेक्षित रूप से वापस करने का कारण बनता है, अमान्य प्रमाणपत्रों के बावजूद जंजीर। हालांकि, मैं इसे समस्या का समाधान कहने के लिए अनिच्छुक हूं, क्योंकि मुझे स्पष्ट रूप से ओपनएसएसएल के अंतर्निर्मित कार्यों में से एक को ओवरराइड करना होगा।

* - इस पर स्पष्ट प्रतिक्रिया यह है कि चूंकि ओपनएसएसएल मुझे बताता है कि कनेक्शन क्यों विफल हुआ, इसलिए मुझे अपने मुद्दों को डीबग करने के लिए कच्चे प्रमाण तक पहुंचने की आवश्यकता नहीं है। किसी भी अन्य स्थिति में, यह सच होगा, लेकिन चूंकि मैं इस क्लाइंट का उपयोग एक शोध परियोजना के हिस्से के रूप में कर रहा हूं जिसमें इंटरनेट पर अमान्य प्रमाणपत्र उपयोग शामिल है, इसलिए मुझे असफल प्रमाणपत्रों को फ़ाइल में सहेजने में सक्षम होना चाहिए।

** - जहाँ तक मैं बता सकता हूँ, NULL को SSL_CTX_set_verify के सत्यापन_कॉलबैक पैरामीटर के रूप में पास करना ओपनएसएसएल को प्रमाणपत्र सत्यापन के लिए एक बिल्टिन डिफ़ॉल्ट फ़ंक्शन का उपयोग करने के लिए कहता है। दस्तावेज इस पर बहुत स्पष्ट नहीं है।

3
ecapstone 1 अगस्त 2016, 21:25

2 जवाब

सबसे बढ़िया उत्तर

TLDR: कॉलबैक का उपयोग करें।

कनेक्शन (हैंडशेक) सफल होने पर ही प्रमाणपत्र श्रृंखला सहित सभी सत्र पैरामीटर उपलब्ध हैं; ऐसा इसलिए है क्योंकि यदि हैंडशेक सफल नहीं हुआ तो विश्वास करने का कोई तरीका नहीं है कि कोई भी परिणाम मान्य है। सिद्धांत रूप में प्राप्त कर्ट एक विशेष मामला हो सकता है, लेकिन एक विशेष मामला अधिक जटिल होगा और जैसा कि आपने देखा होगा कि ओपनएसएसएल एपीआई पहले से ही जटिल है, लोग नियमित रूप से इसका इस्तेमाल करते हैं।

जैसा कि आपने देखा s_client एक सत्यापित कॉलबैक सेट करता है जो किसी भी प्रमाणपत्र श्रृंखला की स्वीकृति को बाध्य करता है, यहां तक ​​कि एक अमान्य भी; यह हैंडशेक सफल होने का कारण बनता है और प्रमाण श्रृंखला सहित पैरामीटर उपलब्ध होने का कारण बनता है। s_client एक परीक्षण उपकरण के रूप में अभिप्रेत है जहां यह मायने नहीं रखता कि डेटा वास्तव में सुरक्षित है या नहीं।

यदि आप केवल सत्यापित सर्वर से कनेक्ट करना चाहते हैं, तो डिफ़ॉल्ट सत्यापन तर्क का उपयोग करें। यदि आप असत्यापित सर्वर से कनेक्ट करना चाहते हैं और डेटा के इंटरसेप्ट किए जाने और/या छेड़छाड़ किए जाने के जोखिम (संभवतः आपके मामले में न्यूनतम) को संभालना चाहते हैं, तो कॉलबैक का उपयोग करें। यह एक कॉलबैक है का कारण अनुप्रयोगों को नियंत्रित करने की अनुमति देना है।

तथ्य s_client पहले डेटा ट्रांसफर से पहले हाथ मिलाने के लिए SSL_set_connect_state का उपयोग करता है, बजाय स्पष्ट रूप से SSL_connect को कॉल करने के, अप्रासंगिक है और इससे कोई फर्क नहीं पड़ता।

जोड़ा गया: आप कॉलबैक का उपयोग करने के बाद त्रुटि का पता लगा सकते हैं -- या इसके बिना भी!

सबसे पहले स्पष्ट होने के लिए, जिस कॉलबैक के बारे में हम यहां बात कर रहे हैं ('सत्यापित' कॉलबैक) का उपयोग के अलावा बिल्टिन चेन सत्यापन तर्क किया जाता है। एक भिन्न कॉलबैक है, एक बहुत ही समान नाम के साथ, 'सर्टिफिकेट वेरिफाई' कॉलबैक, जिसे आप नहीं चाहते हैं। मैन पेज का हवाला देते हुए

वास्तविक सत्यापन प्रक्रिया या तो अंतर्निहित सत्यापन प्रक्रिया का उपयोग करके या SSL_CTX_set_cert_verify_callback के साथ सेट किए गए सत्यापन फ़ंक्शन प्रदान किए गए किसी अन्य एप्लिकेशन का उपयोग करके की जाती है। निम्नलिखित विवरण अंतर्निहित प्रक्रिया के मामले में लागू होते हैं। एक एप्लिकेशन प्रदान की गई प्रक्रिया में भी गहराई से जानकारी सत्यापित करने और Verify_callback() फ़ंक्शन तक पहुंच होती है, लेकिन जिस तरह से इस जानकारी का उपयोग किया जाता है वह भिन्न हो सकता है।

(आसानी से हाइपरलिंक किए गए) SSL_[CTX_]set_cert_verify_callback मैन पेज कहते हैं

[डिफ़ॉल्ट रूप से] अंतर्निहित सत्यापन फ़ंक्शन का उपयोग किया जाता है। यदि सत्यापन कॉलबैक कॉलबैक [यह स्पष्ट रूप से एक टाइपो है] SSL_CTX_set_cert_verify_callback() के माध्यम से निर्दिष्ट किया गया है, तो इसके बजाय आपूर्ति किए गए कॉलबैक फ़ंक्शन को कॉल किया जाता है। [...]

प्रमाणपत्र उद्देश्य सेटिंग्स आदि सहित एक पूर्ण सत्यापन प्रक्रिया प्रदान करना एक जटिल कार्य है। अंतर्निहित प्रक्रिया काफी शक्तिशाली है और ज्यादातर मामलों में यह Verify_callback फ़ंक्शन का उपयोग करके अपने व्यवहार को संशोधित करने के लिए पर्याप्त होना चाहिए।

जहां 'verify_callback function' का मतलब set_verify एक नहीं set_verify_callback से है। वास्तव में यह सटीक नहीं है; बिल्टिन लॉजिक का भाग हमेशा उपयोग किया जाता है और इसके केवल भाग को सर्टिफिकेट वेरीफाई कॉलबैक द्वारा प्रतिस्थापित किया जाता है। लेकिन आप अभी भी ऐसा नहीं करना चाहते हैं, केवल कॉलबैक सत्यापित करें।

SSL_[CTX_]set_verify[_depth] पेज बिल्टिन लॉजिक का वर्णन करना जारी रखता है:

SSL_CTX_set_verify_depth() और SSL_set_verify_depth() सत्यापन प्रक्रिया के दौरान एक श्रृंखला में गहराई प्रमाणपत्रों का उपयोग करने की सीमा निर्धारित करते हैं। [...]

प्रमाणपत्र श्रृंखला को सबसे गहरे नेस्टिंग स्तर (रूट CA प्रमाणपत्र) से शुरू करके चेक किया जाता है और सहकर्मी के प्रमाणपत्र तक ऊपर की ओर काम किया जाता है। प्रत्येक स्तर पर हस्ताक्षर और जारीकर्ता विशेषताओं की जाँच की जाती है। जब भी कोई सत्यापन त्रुटि मिलती है, तो त्रुटि संख्या x509_ctx में संग्रहीत होती है और Verify_callback को preverify_ok=0 के साथ कॉल किया जाता है। [...] यदि किसी प्रमाणपत्र के लिए कोई त्रुटि नहीं मिलती है, तो अगले स्तर पर आगे बढ़ने से पहले Verify_callback को preverify_ok=1 के साथ कॉल किया जाता है।

Verify_callback का वापसी मूल्य आगे की सत्यापन प्रक्रिया की रणनीति को नियंत्रित करता है। यदि सत्यापन_कॉलबैक 0 लौटाता है, तो सत्यापन प्रक्रिया तुरंत "सत्यापन विफल" स्थिति के साथ रोक दी जाती है। [,,,] अगर Verify_callback 1 लौटाता है, तो सत्यापन प्रक्रिया जारी रहती है। यदि सत्यापन_कॉलबैक हमेशा 1 लौटाता है, तो सत्यापन विफलताओं के संबंध में टीएलएस/एसएसएल हैंडशेक समाप्त नहीं किया जाएगा और कनेक्शन स्थापित हो जाएगा। हालांकि कॉलिंग प्रक्रिया SSL_get_verify_result का उपयोग करके या Verify_callback द्वारा प्रबंधित अपने स्वयं के त्रुटि संग्रहण को बनाए रखते हुए अंतिम सत्यापन त्रुटि के त्रुटि कोड को पुनः प्राप्त कर सकती है।

यदि कोई सत्यापन_कॉलबैक निर्दिष्ट नहीं है, तो डिफ़ॉल्ट कॉलबैक का उपयोग किया जाएगा। इसका वापसी मूल्य preverify_ok के समान है, ताकि किसी भी सत्यापन विफलता के कारण एक चेतावनी संदेश के साथ TLS/SSL हैंडशेक समाप्त हो जाएगा, यदि SSL_VERIFY_PEER सेट है।

इसलिए कॉलबैक में विकल्प होता है, जब बिलिन लॉजिक में त्रुटि पाई जाती है (0 के साथ कॉल, रिटर्न 1) या बल विफलता, भले ही बिल्टिन लॉजिक प्रमाणित श्रृंखला को ठीक मानता हो (कॉल 1 के साथ), वापसी 0) लेकिन अगर यह बिल्टिन तर्क नियंत्रण नहीं करता है। s_client द्वारा उपयोग किया जाने वाला विशेष कॉलबैक (और s_server द्वारा जब क्लाइंट प्रमाणीकरण का उपयोग किया जाता है, लेकिन यह अपेक्षाकृत दुर्लभ है) प्रिंट स्थिति के साथ प्रत्येक प्रमाणपत्र का विषय नाम (लेबल ' वेरिफाई रिटर्न') बिल्टिन लॉजिक से, लेकिन हमेशा रिटर्न 1 जिससे किसी भी त्रुटि की परवाह किए बिना (शेष सत्यापन और) कनेक्शन जारी रखने के लिए मजबूर होना पड़ता है।

दूसरे पैराग्राफ में सूचना "जब भी कोई सत्यापन त्रुटि पाई जाती है, तो त्रुटि संख्या x509_ctx में संग्रहीत की जाती है और [फिर] Verify_callback को preverify_ok=0 के साथ कॉल किया जाता है" और तीसरे पैराग्राफ में "कॉलिंग प्रक्रिया अंतिम के त्रुटि कोड को पुनः प्राप्त कर सकती है SSL_get_verify_result का उपयोग करके सत्यापन त्रुटि" - यह सच है भले ही कॉलबैक ठीक = 1 के लिए मजबूर हो।

लेकिन इसके लिए संदर्भों की जाँच करने पर मुझे एक और भी बेहतर समाधान मिला, मैं उस पृष्ठ पर ही चूक गया था: यदि आप बस डिफ़ॉल्ट (या सेट) मोड SSL_VERIFY_NONE ( जोर और स्पष्टीकरण जोड़ा गया):

क्लाइंट मोड: यदि एक अनाम सिफर (डिफ़ॉल्ट रूप से अक्षम) का उपयोग नहीं कर रहा है, तो सर्वर एक प्रमाण पत्र भेजेगा जिसकी जांच की जाएगी [बिलिन लॉजिक द्वारा, भले ही कोई भी आपको यह सोचने पर मजबूर न करे कि यह चेक नहीं किया गया है। ]. प्रमाणपत्र सत्यापन प्रक्रिया के परिणाम को SSL_get_verify_result फ़ंक्शन का उपयोग करके TLS/SSL हैंडशेक [पूर्ण] के बाद जांचा जा सकता है। सत्यापन परिणाम की परवाह किए बिना हैंडशेक जारी रहेगा।

यह प्रभावी रूप से कॉलबैक जैसा ही है जो ठीक = 1 को मजबूर करता है और जो आप चाहते हैं वह करता है।

2
Community 20 जून 2020, 12:12

STACK_OF(X509)* को दिए गए पॉइंटर के साथ स्वयं सत्यापन कॉलबैक का उपयोग करने के लिए SSL_CTX_set_cert_verify_callback(ctx, own_cert_verify_callback, &cert) का उपयोग करना संभव है। कॉलबैक तो इस तरह दिखता है:

static int own_cert_verify_callback(X509_STORE_CTX * ctx, void * arg) {
   int result = 0;
   STACK_OF(X509) ** untrusted_chain = (STACK_OF(X509) **) (arg);

   result = X509_verify_cert(ctx);
   if (result != 1) {
      *untrusted_chain = X509_chain_up_ref(X509_STORE_CTX_get0_untrusted(ctx));
   }

   return result;
}

तो प्रमाणपत्र सत्यापन के लिए डिफ़ॉल्ट फ़ंक्शन का उपयोग किया जाता है और जब प्रमाणपत्र अमान्य होता है, तो उथली प्रतिलिपि को आर्ग पर रखा जाता है ताकि बाद में इसका उपयोग किया जा सके।

0
user12121748 26 सितंबर 2019, 00:46