1. Hintergrund 1.1 Probleme In einem aktuellen Produkttestbericht wurde die Verwendung einer PKI-basierten Authentifizierungsmethode empfohlen. Da das Produkt HTTPS implementiert hat, diskutierten wir und gingen davon aus, dass dies die Verwendung einer Zwei-Wege-Authentifizierung zur Abwehr von Man-in-the-Middle-Angriffen bedeutete. Ich bin im Kurs „Information Security Engineering“ auf die Zwei-Wege-Authentifizierung gestoßen, es gibt jedoch zwei Probleme. Erstens war der endgültige Kursdesign-Client ein Browser und der Server Tomcat. Für die Zwei-Wege-Authentifizierung war nur die Konfiguration beider erforderlich, eine tatsächliche Implementierung des Codes war nicht erforderlich. Zweitens: Obwohl der Kurs auch Implementierungscode nahe der Zwei-Wege-Authentifizierung enthält, war er damals in der Java+JCE-Umgebung und muss jetzt die C+++OpenSSL-Umgebung verwenden. Die Gesamtbedeutung ist zwar ähnlich, aber es gibt immer noch viele Unterschiede bei bestimmten Funktionen und Parametern. Was wir jetzt haben, ist: die Idee der Zertifikatsgenerierung + die Idee der Implementierung einer bidirektionalen Authentifizierung. Bei den Lesern wird vorausgesetzt, dass sie über ein grundlegendes Verständnis verschiedener Konzepte wie Zertifikate, SSL/TSL, Socket-Programmierung usw. verfügen, auf die in diesem Artikel nicht näher eingegangen wird. Darauf basierend lautet das in diesem Artikel zu lösende Problem: Wie generiert OpenSSL speziell Zertifikate + wie implementiert OpenSSL eine bidirektionale Authentifizierung. Die Kernpunkte der Zwei-Wege-Authentifizierung sind die folgenden Funktionen (die für Server und Client gleich sind). Den Rest entnehmen Sie bitte den Codekommentaren: SSL_CTX_set_verify----Konfigurieren, um die bidirektionale Authentifizierung zu aktivieren SSL_CTX_load_verify_locations – Vertrauenswürdige Stammzertifikate laden SSL_CTX_use_certificate_file----Laden Sie Ihr eigenes Zertifikat SSL_CTX_use_PrivateKey_file----Laden Sie Ihren eigenen privaten Schlüssel SSL_get_verify_result – – Um eine echte Überprüfung durchzuführen, müssen Sie diese Funktion aufrufen. Andernfalls sind die vorherigen vier Funktionen nur Konfigurationen und führen keine bidirektionale Überprüfung durch. 2. Implementierung des bidirektionalen Authentifizierungsverfahrens 2.1 OpenSSL installieren und API entwickeln apt-get installiere libssl-dev 2.2 Servercode #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> #einschließen <sys/wait.h> #include <unistd.h> #include <arpa/inet.h> #include <openssl/ssl.h> #include <openssl/err.h> #MAXBUF 1024 definieren ungültige ShowCerts(SSL * ssl) { X509 *Zertifikat; char *Zeile; Zertifikat = SSL_get_peer_certificate(ssl); // SSL_get_verify_result() ist der entscheidende Punkt. SSL_CTX_set_verify() konfiguriert nur, ob es aktiviert werden soll, und führt keine Authentifizierung durch. Der Aufruf dieser Funktion überprüft tatsächlich die Zertifikatauthentifizierung. // Wenn die Überprüfung fehlschlägt, löst das Programm eine Ausnahme aus, um die Verbindung zu beenden, wenn (SSL_get_verify_result (ssl) == X509_V_OK) { printf("Zertifikatsprüfung bestanden\n"); } if (cert != NULL) { printf("Informationen zum digitalen Zertifikat:\n"); Zeile = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); printf("Zertifikat: %s\n", Zeile); frei (Zeile); Zeile = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); printf("Aussteller: %s\n", Zeile); frei (Zeile); X509_free(Zertifikat); } anders printf("Keine Zertifikatsinformationen!\n"); } int main(int argc, char **argv) { int sockfd, neue_fd; socklen_t len; Struktur sockaddr_in meine_Adresse, ihre_Adresse; vorzeichenloser int myport, lisnum; char Puffer[MAXBUF + 1]; SSL_CTX *ctx; wenn (argv[1]) meinport = atoi(argv[1]); anders meinPort = 7838; wenn (argv[2]) lisnum = atoi(argv[2]); anders lisnum = 2; /* Initialisierung der SSL-Bibliothek */ SSL_library_init(); /* Alle SSL-Algorithmen laden */ OpenSSL_add_all_algorithms(); /* Alle SSL-Fehlermeldungen laden */ SSL_load_error_strings(); /* Generieren Sie einen SSL_CTX (SSL-Inhaltstext) auf eine Weise, die mit den SSL V2- und V3-Standards kompatibel ist */ ctx = SSL_CTX_new(SSLv23_Servermethode()); /* Sie können auch SSLv2_server_method() oder SSLv3_server_method() verwenden, um V2- oder V3-Standards separat anzugeben*/ wenn (ctx == NULL) { ERR_print_errors_fp(stdout); Ausgang (1); } // Zwei-Wege-Verifizierung // SSL_VERIFY_PEER --- erfordert eine Zertifikatsauthentifizierung und lässt die Anforderung durch, auch wenn kein Zertifikat vorhanden ist. // SSL_VERIFY_FAIL_IF_NO_PEER_CERT --- erfordert, dass der Client ein Zertifikat bereitstellt, lässt die Anforderung aber durch, auch wenn kein Zertifikat vorhanden ist. SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); // Richten Sie das vertrauenswürdige Stammzertifikat ein, wenn (SSL_CTX_load_verify_locations(ctx, "ca.crt",NULL)<=0){ ERR_print_errors_fp(stdout); Ausgang (1); } /* Laden Sie das digitale Zertifikat des Benutzers, das zum Senden an den Client verwendet wird. Das Zertifikat enthält den öffentlichen Schlüssel*/ wenn (SSL_CTX_use_certificate_file(ctx, argv[3], SSL_FILETYPE_PEM) <= 0) { ERR_print_errors_fp(stdout); Ausgang (1); } /* Privaten Benutzerschlüssel laden */ wenn (SSL_CTX_use_PrivateKey_file(ctx, argv[4], SSL_FILETYPE_PEM) <= 0) { ERR_print_errors_fp(stdout); Ausgang (1); } /* Überprüfen Sie, ob der private Schlüssel des Benutzers korrekt ist*/ wenn (!SSL_CTX_check_private_key(ctx)) { ERR_print_errors_fp(stdout); Ausgang (1); } /* Einen Socket-Listener öffnen */ wenn ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) { perror("Buchse"); Ausgang (1); } anders printf("Socket erstellt\n"); bzero(&meine_Adresse, sizeof(meine_Adresse)); meine_Adresse.sin_family = PF_INET; meine_Adresse.sin_port = htons(meinPort); meine_Adresse.sin_addr.s_addr = INADDR_ANY; wenn (binden(sockfd, (Struktur sockaddr *) &meine_Adresse, Größevon(Struktur sockaddr)) == -1) { perror("binden"); Ausgang (1); } anders printf("gebunden\n"); wenn (listen(sockfd, lisnum) == -1) { perror("zuhören"); Ausgang (1); } anders printf("beginne mit dem Abhören\n"); während (1) { *ssl; Länge = Größe von (Struktur sockaddr); /* Warten auf die Verbindung zum Client*/ wenn ((new_fd = akzeptiere(sockfd, (Struktur sockaddr *) &ihre_Adresse, &Länge)) == -1) { perror("akzeptieren"); beenden(Fehlernummer); } anders printf("Server: Verbindung von %s, Port %d, Socket %d\n", inet_ntoa(ihre_Adresse.sin_Adresse), ntohs(ihre_Adresse.sin_Port), neu_fd); /* Ein neues SSL basierend auf ctx generieren */ ssl = SSL_neu(ctx); /* Den Socket des verbundenen Benutzers zu SSL hinzufügen */ SSL_set_fd(ssl, neues_fd); /* SSL-Verbindung herstellen */ wenn (SSL_accept(ssl) == -1) { perror("akzeptieren"); schließen(new_fd); brechen; } Zertifikate anzeigen(ssl); /* Beginnen Sie mit dem Senden und Empfangen von Daten bei jeder neuen Verbindung*/ bzero(buf, MAXBUF + 1); strcpy(buf, "Server->Client"); /* Nachricht an Client senden */ len = SSL_write(ssl, buf, strlen(buf)); wenn (Länge <= 0) { printf("Nachricht '%s' konnte nicht gesendet werden! Fehlercode ist %d, Fehlermeldung ist '%s'\n", buf, errno, Fehler: gehe zum Ende; } anders printf("Nachricht '%s' wurde erfolgreich gesendet, insgesamt wurden %d Bytes gesendet!\n", buf, len); bzero(buf, MAXBUF + 1); /* Nachrichten vom Client empfangen */ len = SSL_read(ssl, buf, MAXBUF); wenn (Länge > 0) printf("Erfolgreicher Nachrichtenempfang: '%s', %d Bytes an Daten\n", buf, len); anders printf("Nachrichtenempfang fehlgeschlagen! Fehlercode ist %d, Fehlermeldung ist '%s'\n", Fehlernummer, Strerror(Fehlernummer)); /* Behandeln Sie das Ende des Sendens und Empfangens von Daten bei jeder neuen Verbindung*/ beenden: /* Schließen Sie die SSL-Verbindung */ SSL_Shutdown(SSL); /* SSL freigeben */ SSL_frei(ssl); /* Socket schließen */ schließen(new_fd); } /* Schließen Sie den Abhörsocket */ schließen(sockfd); /* CTX freigeben */ SSL_CTX_frei(ctx); gebe 0 zurück; } 2.3 Kundencode #include <stdio.h> #include <string.h> #include <errno.h> #include <sys/socket.h> #include <resolv.h> #include <stdlib.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <openssl/ssl.h> #include <openssl/err.h> #MAXBUF 1024 definieren ungültige ShowCerts(SSL * ssl) { X509 *Zertifikat; char *Zeile; Zertifikat = SSL_get_peer_certificate(ssl); // SSL_get_verify_result() ist der entscheidende Punkt. SSL_CTX_set_verify() konfiguriert nur, ob es aktiviert werden soll, und führt keine Authentifizierung durch. Der Aufruf dieser Funktion überprüft tatsächlich die Zertifikatauthentifizierung. // Wenn die Überprüfung fehlschlägt, löst das Programm eine Ausnahme aus, um die Verbindung zu beenden, wenn (SSL_get_verify_result (ssl) == X509_V_OK) { printf("Zertifikatsprüfung bestanden\n"); } wenn (Zertifikat != NULL) { printf("Informationen zum digitalen Zertifikat:\n"); Zeile = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); printf("Zertifikat: %s\n", Zeile); frei (Zeile); Zeile = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); printf("Aussteller: %s\n", Zeile); frei (Zeile); X509_free(Zertifikat); } anders printf("Keine Zertifikatsinformationen!\n"); } int main(int argc, char **argv) { int sockfd, Länge; Struktur sockaddr_in Ziel; Zeichenpuffer [MAXBUF + 1]; SSL_CTX *ctx; *ssl; wenn (argc != 5) { printf("Parameterformatfehler! Die korrekte Verwendung ist wie folgt:\n\t\t%s IP-Adressport\n\tBeispiel:\t%s 127.0.0.1 80\nDieses Programm wird verwendet, um von einem bestimmten" „Der Server unter der IP-Adresse empfängt Nachrichten mit höchstens MAXBUF Bytes an einem bestimmten Port“, argv[0], argv[0]); Ausfahrt (0); } /* Initialisierung der SSL-Bibliothek, siehe Code von ssl-server.c*/ SSL_library_init(); OpenSSL_add_all_algorithms(); SSL_load_error_strings(); ctx = SSL_CTX_neu(SSLv23_Clientmethode()); wenn (ctx == NULL) { ERR_print_errors_fp(stdout); Ausgang (1); } // Zwei-Wege-Verifizierung // SSL_VERIFY_PEER --- erfordert eine Zertifikatsauthentifizierung und lässt die Anforderung durch, auch wenn kein Zertifikat vorhanden ist. // SSL_VERIFY_FAIL_IF_NO_PEER_CERT --- erfordert, dass der Client ein Zertifikat bereitstellt, lässt die Anforderung aber durch, auch wenn kein Zertifikat vorhanden ist. SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); // Richten Sie das vertrauenswürdige Stammzertifikat ein, wenn (SSL_CTX_load_verify_locations(ctx, "ca.crt",NULL)<=0){ ERR_print_errors_fp(stdout); Ausgang (1); } /* Laden Sie das digitale Zertifikat des Benutzers, das zum Senden an den Client verwendet wird. Das Zertifikat enthält den öffentlichen Schlüssel*/ wenn (SSL_CTX_use_certificate_file(ctx, argv[3], SSL_FILETYPE_PEM) <= 0) { ERR_print_errors_fp(stdout); Ausgang (1); } /* Privaten Benutzerschlüssel laden */ wenn (SSL_CTX_use_PrivateKey_file(ctx, argv[4], SSL_FILETYPE_PEM) <= 0) { ERR_print_errors_fp(stdout); Ausgang (1); } /* Überprüfen Sie, ob der private Schlüssel des Benutzers korrekt ist*/ wenn (!SSL_CTX_check_private_key(ctx)) { ERR_print_errors_fp(stdout); Ausgang (1); } /* Einen Socket für die TCP-Kommunikation erstellen */ wenn ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("Buchse"); beenden(Fehlernummer); } printf("Socket erstellt\n"); /* Initialisiere die Adresse und Portinformationen des Servers (der Gegenpartei)*/ bzero(&dest, Größe von(dest)); Ziel.sin_family = AF_INET; dest.sin_port = htons(atoi(argv[2])); wenn (inet_aton(argv[1], (Struktur in_addr *) &dest.sin_addr.s_addr) == 0) { perror(argv[1]); beenden(Fehlernummer); } printf("Adresse erstellt\n"); /* Mit Server verbinden */ wenn (verbinden(sockfd, (Struktur sockaddr *) &dest, sizeof(dest)) != 0) { perror("Verbinden "); beenden(Fehlernummer); } printf("Server verbunden\n"); /* Ein neues SSL basierend auf ctx generieren */ ssl = SSL_neu(ctx); SSL_set_fd(ssl, sockfd); /* SSL-Verbindung herstellen */ wenn (SSL_connect(ssl) == -1) ERR_print_errors_fp(stderr); anders { printf("Verbunden mit %s-Verschlüsselung\n", SSL_get_cipher(ssl)); Zertifikate anzeigen(ssl); } /* Empfangen Sie die von der anderen Partei gesendete Nachricht und empfangen Sie höchstens MAXBUF Bytes*/ bzero(Puffer, MAXBUF + 1); /* Nachrichten vom Server empfangen */ Länge = SSL_read(SSL, Puffer, MAXBUF); wenn (Länge > 0) printf("Nachricht erfolgreich empfangen: '%s', insgesamt %d Bytes an Daten\n", Puffer, Länge); anders { druckenf ("Nachrichtenempfang fehlgeschlagen! Fehlercode ist %d, Fehlermeldung ist '%s'\n", Fehlernummer, Strerror(Fehlernummer)); gehe zum Ende; } bzero(Puffer, MAXBUF + 1); strcpy(Puffer, "von Client->Server"); /* Sende eine Nachricht an den Server */ len = SSL_write(ssl, Puffer, strlen(Puffer)); wenn (Länge < 0) druckenf ("Nachricht '%s' konnte nicht gesendet werden! Fehlercode ist %d, Fehlermeldung ist '%s'\n", Puffer, errno, strerror(errno)); anders printf("Nachricht '%s' wurde erfolgreich gesendet, insgesamt wurden %d Bytes gesendet!\n", Puffer, Länge); beenden: /* Verbindung schließen */ SSL_Shutdown(SSL); SSL_frei(ssl); schließen(sockfd); SSL_CTX_frei(ctx); gebe 0 zurück; } 2.4 Zertifikatserstellung Achten Sie auf drei Punkte Achten Sie zunächst darauf, das Verschlüsselungskennwort für den privaten Schlüssel (Parameter -passout) in Ihr eigenes Kennwort zu ändern; die folgenden Schritte generieren alle privaten Schlüssel mit dem Parameter -passout. Wenn Sie den Parameter -nodes verwenden, muss der letzte Schritt „Verschlüsselten RSA-Schlüssel in unverschlüsselten RSA-Schlüssel umwandeln“ nicht ausgeführt werden. Zweitens werden Zertifikate und Schlüssel in zwei Formen bereitgestellt: direkte Generierung in einem Schritt und schrittweise Generierung. Die beiden Formen sind gleichwertig. Hier verwenden wir die Form der direkten Generierung (die Form der schrittweisen Generierung ist kommentiert). Drittens: Ändern Sie unbedingt die Zertifikatsinformationen in die Informationen Ihrer eigenen Organisation. Die Parameter der Zertifikatsnummer haben folgende Bedeutung: C-----Ländername ST----Name des Staates oder der Provinz L----Stadt (Ortsname) O----Unternehmen (Name der Organisation) OU----Abteilung (Name der Organisationseinheit) CN----Allgemeiner Name emailAddress----E-Mail-Adresse # CA-Zertifikat und Schlüsselgenerierungsmethode 1 - CA-Schlüssel und selbstsigniertes Zertifikat direkt generieren # Wenn Sie beim Lesen der privaten Schlüsseldatei ca_rsa_private.pem in Zukunft kein Kennwort eingeben müssen, d. h. den privaten Schlüssel für die Speicherung nicht verschlüsseln, ersetzen Sie -passout pass:123456 durch -nodes openssl req -newkey rsa:2048 -passout pass:123456 -keyout ca_rsa_private.pem -x509 -days 365 -out ca.crt -subj "/C=CN/ST=GD/L=SZ/O=COM/OU=NSP/CN=CA/[email protected]" # CA-Zertifikat und Schlüsselgenerierungsmethode 2 – CA-Schlüssel und selbstsigniertes Zertifikat Schritt für Schritt generieren: # openssl genrsa -aes256 -passout pass:123456 -out ca_rsa_private.pem 2048 # openssl req -new -x509 -days 365 -key ca_rsa_private.pem -passin pass:123456 -out ca.crt -subj "/C=CN/ST=GD/L=SZ/O=COM/OU=NSP/CN=CA/[email protected]" # Methode 1 zur Generierung von Serverzertifikaten und Schlüsseln - Serverschlüssel und zu signierendes Zertifikat direkt generieren # Wenn Sie beim Lesen der privaten Schlüsseldatei server_rsa_private.pem in Zukunft kein Kennwort eingeben müssen, d. h. den privaten Schlüssel nicht verschlüsseln und speichern möchten, ersetzen Sie -passout pass:server durch -nodes openssl req -newkey rsa:2048 -passout pass:server -keyout server_rsa_private.pem -out server.csr -subj "/C=CN/ST=GD/L=SZ/O=COM/OU=NSP/CN=SERVER/[email protected]" # Methode 2 zur Generierung von Serverzertifikaten und Schlüsseln -- Schrittweises Generieren von Serverschlüssel und zu signierendem Zertifikat# openssl genrsa -aes256 -passout pass:server -out server_rsa_private.pem 2048 # openssl req -new -key server_rsa_private.pem -passin pass:server -out server.csr -subj "/C=CN/ST=GD/L=SZ/O=COM/OU=NSP/CN=SERVER/[email protected]" # Verwenden Sie das CA-Zertifikat und den Schlüssel, um das Serverzertifikat zu signieren: openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca_rsa_private.pem -passin pass:123456 -CAcreateserial -out server.crt # Konvertieren Sie den verschlüsselten RSA-Schlüssel in einen unverschlüsselten RSA-Schlüssel, um nicht bei jedem Lesen das Entschlüsselungskennwort einzugeben. # Das Kennwort ist das beim Generieren der privaten Schlüsseldatei festgelegte Passwort und das beim Lesen der privaten Schlüsseldatei einzugebende Passwort. Geben Sie hier beispielsweise „Server“ ein. openssl rsa -in server_rsa_private.pem -out server_rsa_private.pem.unsecure # Methode 1 zum Generieren von Client-Zertifikat und Schlüssel: Generieren Sie direkt den Client-Schlüssel und das zu signierende Zertifikat. # Wenn Sie beim Lesen der privaten Schlüsseldatei client_rsa_private.pem in Zukunft kein Kennwort mehr eingeben müssen, d. h. den privaten Schlüssel nicht verschlüsseln und speichern möchten, ersetzen Sie -passout pass:client durch -nodes openssl req -newkey rsa:2048 -passout pass:client -keyout client_rsa_private.pem -out client.csr -subj "/C=CN/ST=GD/L=SZ/O=COM/OU=NSP/CN=CLIENT/[email protected]" # Methode 2 zum Generieren von Client-Zertifikat und Schlüssel: Generieren Sie Schritt für Schritt den Client-Schlüssel und das zu signierende Zertifikat: # openssl genrsa -aes256 -passout pass:client -out client_rsa_private.pem 2048 # openssl req -new -key client_rsa_private.pem -passin pass:client -out client.csr -subj "/C=CN/ST=GD/L=SZ/O=COM/OU=NSP/CN=CLIENT/[email protected]" # Verwenden Sie das CA-Zertifikat und den Schlüssel, um das Client-Zertifikat zu signieren: openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca_rsa_private.pem -passin pass:123456 -CAcreateserial -out client.crt # Konvertieren Sie den verschlüsselten RSA-Schlüssel in einen unverschlüsselten RSA-Schlüssel, damit Sie nicht bei jedem Lesen das Entschlüsselungskennwort eingeben müssen. # Das Kennwort ist das beim Generieren der privaten Schlüsseldatei festgelegte Passwort und das beim Lesen der privaten Schlüsseldatei einzugebende Passwort. Geben Sie hier beispielsweise „Client“ ein. openssl rsa -in client_rsa_private.pem -out client_rsa_private.pem.unsecure 2.5 Konfiguration der Entwicklungsumgebung Betriebssystem----Kali-Roaling. Es dient lediglich der Verwendung der aktuellen virtuellen Maschine. Es sollte keinen Unterschied machen, ob Ubuntu, CentOS usw. verwendet wird. IDE----Eclipse. Wenn die Kompilierung direkt im Terminal fehlschlägt, werde ich nicht weiter nachforschen. Wenn es beim Kompilieren in Eclipse kein Problem gibt, werde ich einfach Eclipse verwenden. 2.5.1 Erstellen Sie zwei Projekte im selben Active Directory myclient1----Erstellen Sie einen src-Ordner, um den Client-Code abzulegen myserver1----Erstellen Sie einen src-Ordner zum Speichern des Servercodes (Andere Verzeichnisse werden entweder automatisch generiert oder nach der Kompilierung automatisch generiert. Machen Sie sich also keine Sorgen. Wenn das Projekt einen Fehler meldet, versuchen Sie, Eclipse oder sogar das Betriebssystem mehrmals neu zu starten.) 2.5.2 Festlegen von SSL und Verschlüsselung Klicken Sie mit der rechten Maustaste auf den Projektordner ---- Eigenschaften ---- Geben Sie die Verzeichnisse der SSL-Bibliothek und der Kryptobibliothek an, da die Kompilierung sonst SSL nicht finden kann. Beide Projekte müssen konfiguriert werden 2.5.3 Kompilierung Verwenden Sie zum Kompilieren die Tastenkombination Strg+B. Eclipse kompiliert alle Projekte 2.5.4 Zertifikatskopie Kopieren Sie das zuvor generierte CA-Zertifikat (ca.crt), das Client-Zertifikat (client.crt) und die unverschlüsselte private Client-Schlüsseldatei () in das Debug-Verzeichnis des myclient1-Projekts. Kopieren Sie das zuvor generierte CA-Zertifikat, das Serverzertifikat und die unverschlüsselten privaten Schlüsseldateien auf der Serverseite in das Debug-Verzeichnis des myclient1-Projekts. 2.5.5 Ausführen des Programms Führen Sie zuerst den Server und dann den Client aus
Die Ergebnisse der Operation sind wie folgt, Server: Kunde: Das Obige ist der vollständige Inhalt dieses Artikels. Ich hoffe, er wird für jedermanns Studium hilfreich sein. Ich hoffe auch, dass jeder 123WORDPRESS.COM unterstützen wird. Das könnte Sie auch interessieren:
|
<<: Beispielanalyse der Intervallberechnung von MySQL-Datum und -Uhrzeit
>>: JavaScript-Wissen: Konstruktoren sind auch Funktionen
Inhaltsverzeichnis Erster Schritt: Der zweite Sch...
Inhaltsverzeichnis 1. Was ist eine berechnete Eig...
Container-Lebenszyklus Der Lebenszyklus einer Con...
Nexus bietet RestApi, aber einige APIs müssen noc...
Laden Sie die komprimierte Version von MySQL-5.7....
Detaillierte Erläuterung des Konfigurationsprozes...
Obwohl das W3C einige Standards für HTML festgeleg...
Problem/Fehler/Szenario/Anforderung Die Festplatt...
1: Anmeldeeingang der Baidu-Website Website: http:...
Manche Leute verwenden diese drei Tags auf pervers...
In diesem Artikelbeispiel wird der spezifische JS...
1. Umwelt: MySQL-5.0.41-win32 Windows XP Professi...
1. löschen delete ist die einzige wirkliche Mögli...
1. WebDesignerWall 2. Veerles Blog 3. Lernprogram...
Die Verwendung der internen Funktion instr in MyS...