Mir ist klar, dass dies eine sehr späte (und lange) Antwort ist. Aber wenn man bedenkt, wie gut diese Frage in den Suchmaschinenergebnissen zu rangieren scheint, dachte ich, dass es sich lohnen könnte, eine anständige Antwort zu schreiben.
Vieles, was Sie unten lesen werden, stammt aus dieser Demo und den OpenSSL-Dokumenten. Der folgende Code gilt sowohl für C als auch für C ++.
Bevor wir tatsächlich ein Zertifikat erstellen können, müssen wir einen privaten Schlüssel erstellen. OpenSSL bietet die EVP_PKEY
Struktur zum Speichern eines algorithmisch unabhängigen privaten Schlüssels im Speicher. Diese Struktur ist in deklariert openssl/evp.h
, wird aber von openssl/x509.h
(was wir später benötigen werden) eingeschlossen, sodass Sie den Header nicht wirklich explizit einschließen müssen.
Um eine EVP_PKEY
Struktur zuzuweisen , verwenden wir EVP_PKEY_new
:
EVP_PKEY * pkey;
pkey = EVP_PKEY_new();
Es gibt auch eine entsprechende Funktion zum Freigeben der Struktur - EVP_PKEY_free
-, die ein einziges Argument akzeptiert: die EVP_PKEY
oben initialisierte Struktur.
Jetzt müssen wir einen Schlüssel generieren. In unserem Beispiel generieren wir einen RSA-Schlüssel. Dies geschieht mit der RSA_generate_key
Funktion, die in deklariert ist openssl/rsa.h
. Diese Funktion gibt einen Zeiger auf eine RSA
Struktur zurück.
Ein einfacher Aufruf der Funktion könnte folgendermaßen aussehen:
RSA * rsa;
rsa = RSA_generate_key(
2048,
RSA_F4,
NULL,
NULL
);
Wenn der Rückgabewert von RSA_generate_key
ist NULL
, ist ein Fehler aufgetreten. Wenn nicht, haben wir jetzt einen RSA-Schlüssel, den wir unserer EVP_PKEY
Struktur von früher zuweisen können :
EVP_PKEY_assign_RSA(pkey, rsa);
Die RSA
Struktur wird automatisch freigegeben, wenn die EVP_PKEY
Struktur freigegeben wird.
Nun zum Zertifikat selbst.
OpenSSL verwendet die X509
Struktur, um ein x509-Zertifikat im Speicher darzustellen. Die Definition für diese Struktur ist in openssl/x509.h
. Die erste Funktion, die wir brauchen werden, ist X509_new
. Die Verwendung ist relativ einfach:
X509 * x509;
x509 = X509_new();
Wie bei EVP_PKEY
gibt es eine entsprechende Funktion zum Freigeben der Struktur - X509_free
.
Jetzt müssen wir einige Eigenschaften des Zertifikats mit einigen X509_*
Funktionen festlegen :
ASN1_INTEGER_set(X509_get_serialNumber(x509), 1);
Dies setzt die Seriennummer unseres Zertifikats auf '1'. Einige Open-Source-HTTP-Server lehnen es ab, ein Zertifikat mit der Seriennummer '0' zu akzeptieren. Dies ist die Standardeinstellung. Der nächste Schritt besteht darin, die Zeitspanne anzugeben, in der das Zertifikat tatsächlich gültig ist. Wir machen das mit den folgenden zwei Funktionsaufrufen:
X509_gmtime_adj(X509_get_notBefore(x509), 0);
X509_gmtime_adj(X509_get_notAfter(x509), 31536000L);
In der ersten Zeile wird die notBefore
Eigenschaft des Zertifikats auf die aktuelle Zeit gesetzt. (Die X509_gmtime_adj
Funktion addiert die angegebene Anzahl von Sekunden zur aktuellen Zeit - in diesem Fall keine.) In der zweiten Zeile wird die notAfter
Eigenschaft des Zertifikats auf 365 Tage festgelegt (60 Sekunden * 60 Minuten * 24 Stunden * 365 Tage).
Jetzt müssen wir den öffentlichen Schlüssel für unser Zertifikat mit dem zuvor generierten Schlüssel festlegen:
X509_set_pubkey(x509, pkey);
Da es sich um ein selbstsigniertes Zertifikat handelt, setzen wir den Namen des Ausstellers auf den Namen des Betreffs. Der erste Schritt in diesem Prozess besteht darin, den Betreffnamen zu erhalten:
X509_NAME * name;
name = X509_get_subject_name(x509);
Wenn Sie zuvor ein selbstsigniertes Zertifikat in der Befehlszeile erstellt haben, werden Sie wahrscheinlich nach einem Ländercode gefragt. Hier stellen wir es zusammen mit der Organisation ('O') und dem allgemeinen Namen ('CN') zur Verfügung:
X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
(unsigned char *)"CA", -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC,
(unsigned char *)"MyCompany Inc.", -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
(unsigned char *)"localhost", -1, -1, 0);
(Ich verwende hier den Wert 'CA', weil ich Kanadier bin und das ist unser Ländercode. Beachten Sie auch, dass Parameter 4 explizit in einen umgewandelt werden muss unsigned char *
.)
Jetzt können wir den Namen des Ausstellers tatsächlich festlegen:
X509_set_issuer_name(x509, name);
Und schließlich sind wir bereit, den Signierprozess durchzuführen. Wir rufen X509_sign
mit dem Schlüssel an, den wir zuvor generiert haben. Der Code dafür ist schmerzlich einfach:
X509_sign(x509, pkey, EVP_sha1());
Beachten Sie, dass wir den SHA-1- Hashing-Algorithmus verwenden, um den Schlüssel zu signieren. Dies unterscheidet sich von der mkcert.c
Demo, die ich zu Beginn dieser Antwort erwähnt habe und die MD5 verwendet.
Wir haben jetzt ein selbstsigniertes Zertifikat! Aber wir sind noch nicht fertig - wir müssen diese Dateien auf die Festplatte schreiben. Zum Glück hat OpenSSL uns auch dort mit den PEM_*
Funktionen abgedeckt, die in deklariert sind openssl/pem.h
. Der erste, den wir benötigen, ist das PEM_write_PrivateKey
Speichern unseres privaten Schlüssels.
FILE * f;
f = fopen("key.pem", "wb");
PEM_write_PrivateKey(
f,
pkey,
EVP_des_ede3_cbc(),
"replace_me",
10,
NULL,
NULL
);
Wenn Sie den privaten Schlüssel nicht verschlüsseln möchten, übergeben Sie einfach NULL
den dritten und vierten Parameter oben. In jedem Fall sollten Sie unbedingt sicherstellen, dass die Datei nicht für die Welt lesbar ist. (Für Unix-Benutzer bedeutet dies chmod 600 key.pem
.)
Wütend! Jetzt haben wir nur noch eine Funktion: Wir müssen das Zertifikat auf die Festplatte schreiben. Die Funktion, die wir dafür benötigen, ist PEM_write_X509
:
FILE * f;
f = fopen("cert.pem", "wb");
PEM_write_X509(
f,
x509
);
Und wir sind fertig! Hoffentlich reichen die Informationen in dieser Antwort aus, um Ihnen eine ungefähre Vorstellung davon zu geben, wie alles funktioniert, obwohl wir die Oberfläche von OpenSSL kaum zerkratzt haben.
Für diejenigen, die sehen möchten, wie der gesamte obige Code in einer realen Anwendung aussieht, habe ich einen Gist (in C ++ geschrieben) zusammengestellt, den Sie hier anzeigen können .