2016-05-13 6 views
1

Ich habe einen DSAPI-Filter erstellt, um einen Benutzer mit einem Clientzertifikat zu authentifizieren. Der Benutzer stellt eine Verbindung über einen Proxy her, und der Proxy fügt dem Anforderungsheader das Zertifikat des Benutzers hinzu.PEM_read_bio_X509 (manchmal) schlägt fehl (OpenSSL 1.0.1p)

#define HDR_SSL_CLIENT_CERT     "SSL_CLIENT_CERT" 

Ich verwende Apache als HTTPS-Proxy; Ein Kunde nutzt NGINX. Ich habe bereits festgestellt, dass NGINX TABS fügt statt SPACES und ich habe auch dafür gesorgt, dass die Zertifikatsdaten das richtige Format haben, bevor es von meinem Code

#define BUFFER_SIZE 4096 
char certData[BUFFER_SIZE+1] = {0,}; 

CertData enthält die Base64 Darstellung des Zertifikats (TABS analysiert wird und SPACES werden durch \ n ersetzt) ​​

-----BEGIN CERTIFICATE----- 
MIIDOTCCAiGgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBGMQswCQYDVQQGEwJERTEO 
MAwGA1UECgwFRE9TWVMxETAPBgNVBAsMCFRSQVZFTEVSMRQwEgYDVQQDDAtUUkFW 
RUxFUiBDQTAeFw0xNjA0MTgxMzA2MjdaFw0yNjA0MTgxMzA2MjdaMBUxEzARBgNV 
BAMMCkdlb3JnIER1bWEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCG 
ctEsl++/4LgK8RJId2NUPJgjFKDl76jp38GNMtcCqt+ADUPvR+suoy/zeuRXs7hw 
25YAx49U/FYFlu3Xlmb57ACyPtbhLPpV2Y8fJ0EXD2pY1G3oEWlKWWk6ErT2vg7V 
ppOajckkx3EmkVrALhQgdOqQDHJ6Y2xQSgpKWGORmoEtYQepJ/LGWBfE4muZjUJk 
euUf0fmHFehMw8X0ErPDFxDuAH+d7kjjUl+EqSQCqLqqrg50GMrM0vKIqyqqbUQF 
wLQYyFllYkj0h1VQ+KhyxwVkq2snR+Z2EJe1A7xsUwY5D/9dVK5ih6xeIrpgvgCd 
6Amx2KF9lh8yEZi1NMPPAgMBAAGjYzBhMB8GA1UdEQQYMBaBFEdlb3JnRHVtYUBz 
dGFkdGRvLmRlMB0GA1UdDgQWBBRIX2fz2ahSFgOCf03W4pn9t/BomjAfBgNVHSME 
GDAWgBR7RJ1HsYOVlc4TOAzeqIqETopeCTANBgkqhkiG9w0BAQsFAAOCAQEALWre 
gJYsSD6i3e4MhJOhR0FFincqdnVEeEoVMr4GDSZRMUPSTjNMTdGLLMFHpU9p/cGZ 
4b30k7dQWhIao7aLIgDOXaATr14fLXrZqRM/MXusd27nFKQRZf1ktrxr0vIZqnw4 
SuniS3NP7SuVEbUeTWU8nVub17aUWX8T4C8yAHKmancSSgMXwFhXTNq0aIvwRzIv 
TzyK0SDXSc68kQkf3evTRvKfvlmQGWXL6BukTGJS1870x3IrDK19Phi5PUYXQtZV 
uwaRg1fRUyPno0GCIZiMxCY4rWy+AaM3CO7Ua5+KEiAdWKrBP6Jd24hZuH8ZhuZ/ 
9u5SSvUA1bGAT02eqQ== 
-----END CERTIFICATE----- 

ich dann den folgenden Code verwenden, um eine X509 von CertData zu bekommen:

BIO * bio = BIO_new(BIO_s_mem()); 
    X509 * clientCert = X509_new(); 
    bio = BIO_new_mem_buf(certData, -1); 
    PEM_read_bio_X509(bio, &clientCert, 0, NULL); 

    if (clientCert == NULL) { 
     debugOut("PEM_read_bio_X509 failed...\n"); 

     if(bio) { 
      BIO_free(bio); 
     } 

     return false; 
    } 

Wir haben keine Probleme gesehen, wenn u sing die DSAPI mit Apache; Auch NGINX funktioniert. Von Zeit zu Zeit schlägt jedoch PEM_read_bio_X509 fehl und es wird keine clientCert erstellt.

Gibt es etwas offensichtlich falsch mit meinem Code?

Gibt es ein bekanntes Problem mit PEM_read_bio_X509 und NGINX?

Ich benutze OpenSSL 1.0.1p im Moment.

UPDATE: Hier ist der Code, der die TABS und SPACES ersetzt

char szHeaderAuthToken[MAX_BUF_LEN+1] = {0,}; 

die Daten enthält als

vom Proxy vorgelegt
size_t last = certLen - lastblank; 

    while (szHeaderClientCert[j] != '\0') { 
     c = szHeaderClientCert[j]; 
     // skip first and last 'space' char 
     if (j == 10 || j == last) { 
      c = ' '; 
     } else { 
      if (isspace(c) || ('\t' == c)) c = '\n'; 
     } 
     certData[j] = c; 

     if (DEBUGOUT) { 
      putchar (c); 
      ofs << c; 
     } 

     j++; 
    } 

    certData[j+1] = '\0'; 

UPDATE2: Gute und schlechte CertData

20160512_145926 GOOD 

-----BEGIN CERTIFICATE----- 
MIIDOTCCAiGgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBGMQswCQYDVQQGEwJERTEO 
MAwGA1UECgwFRE9TWVMxETAPBgNVBAsMCFRSQVZFTEVSMRQwEgYDVQQDDAtUUkFW 
RUxFUiBDQTAeFw0xNjA0MTgxMzA2MjdaFw0yNjA0MTgxMzA2MjdaMBUxEzARBgNV 
BAMMCkdlb3JnIER1bWEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCG 
ctEsl++/4LgK8RJId2NUPJgjFKDl76jp38GNMtcCqt+ADUPvR+suoy/zeuRXs7hw 
25YAx49U/FYFlu3Xlmb57ACyPtbhLPpV2Y8fJ0EXD2pY1G3oEWlKWWk6ErT2vg7V 
ppOajckkx3EmkVrALhQgdOqQDHJ6Y2xQSgpKWGORmoEtYQepJ/LGWBfE4muZjUJk 
euUf0fmHFehMw8X0ErPDFxDuAH+d7kjjUl+EqSQCqLqqrg50GMrM0vKIqyqqbUQF 
wLQYyFllYkj0h1VQ+KhyxwVkq2snR+Z2EJe1A7xsUwY5D/9dVK5ih6xeIrpgvgCd 
6Amx2KF9lh8yEZi1NMPPAgMBAAGjYzBhMB8GA1UdEQQYMBaBFEdlb3JnRHVtYUBz 
dGFkdGRvLmRlMB0GA1UdDgQWBBRIX2fz2ahSFgOCf03W4pn9t/BomjAfBgNVHSME 
GDAWgBR7RJ1HsYOVlc4TOAzeqIqETopeCTANBgkqhkiG9w0BAQsFAAOCAQEALWre 
gJYsSD6i3e4MhJOhR0FFincqdnVEeEoVMr4GDSZRMUPSTjNMTdGLLMFHpU9p/cGZ 
4b30k7dQWhIao7aLIgDOXaATr14fLXrZqRM/MXusd27nFKQRZf1ktrxr0vIZqnw4 
SuniS3NP7SuVEbUeTWU8nVub17aUWX8T4C8yAHKmancSSgMXwFhXTNq0aIvwRzIv 
TzyK0SDXSc68kQkf3evTRvKfvlmQGWXL6BukTGJS1870x3IrDK19Phi5PUYXQtZV 
uwaRg1fRUyPno0GCIZiMxCY4rWy+AaM3CO7Ua5+KEiAdWKrBP6Jd24hZuH8ZhuZ/ 
9u5SSvUA1bGAT02eqQ== 
-----END CERTIFICATE----- 


20160512_150227 FAIL 

-----BEGIN CERTIFICATE----- 
MIIDOTCCAiGgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBGMQswCQYDVQQGEwJERTEO 
MAwGA1UECgwFRE9TWVMxETAPBgNVBAsMCFRSQVZFTEVSMRQwEgYDVQQDDAtUUkFW 
RUxFUiBDQTAeFw0xNjA0MTgxMzA2MjdaFw0yNjA0MTgxMzA2MjdaMBUxEzARBgNV 
BAMMCkdlb3JnIER1bWEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCG 
ctEsl++/4LgK8RJId2NUPJgjFKDl76jp38GNMtcCqt+ADUPvR+suoy/zeuRXs7hw 
25YAx49U/FYFlu3Xlmb57ACyPtbhLPpV2Y8fJ0EXD2pY1G3oEWlKWWk6ErT2vg7V 
ppOajckkx3EmkVrALhQgdOqQDHJ6Y2xQSgpKWGORmoEtYQepJ/LGWBfE4muZjUJk 
euUf0fmHFehMw8X0ErPDFxDuAH+d7kjjUl+EqSQCqLqqrg50GMrM0vKIqyqqbUQF 
wLQYyFllYkj0h1VQ+KhyxwVkq2snR+Z2EJe1A7xsUwY5D/9dVK5ih6xeIrpgvgCd 
6Amx2KF9lh8yEZi1NMPPAgMBAAGjYzBhMB8GA1UdEQQYMBaBFEdlb3JnRHVtYUBz 
dGFkdGRvLmRlMB0GA1UdDgQWBBRIX2fz2ahSFgOCf03W4pn9t/BomjAfBgNVHSME 
GDAWgBR7RJ1HsYOVlc4TOAzeqIqETopeCTANBgkqhkiG9w0BAQsFAAOCAQEALWre 
gJYsSD6i3e4MhJOhR0FFincqdnVEeEoVMr4GDSZRMUPSTjNMTdGLLMFHpU9p/cGZ 
4b30k7dQWhIao7aLIgDOXaATr14fLXrZqRM/MXusd27nFKQRZf1ktrxr0vIZqnw4 
SuniS3NP7SuVEbUeTWU8nVub17aUWX8T4C8yAHKmancSSgMXwFhXTNq0aIvwRzIv 
TzyK0SDXSc68kQkf3evTRvKfvlmQGWXL6BukTGJS1870x3IrDK19Phi5PUYXQtZV 
uwaRg1fRUyPno0GCIZiMxCY4rWy+AaM3CO7Ua5+KEiAdWKrBP6Jd24hZuH8ZhuZ/ 
9u5SSvUA1bGAT02eqQ== 
-----END CERTIFICATE----- 
20160512_150227 PEM_read_bio_X509 failed... 
+0

einfach ein Verständnis zu bekommen des Problems: Der Proxy überprüft das Clientzertifikat innerhalb der TLS-Verbindung tatsächlich anhand der erwarteten Zertifizierungsstelle und fügt dann das verifizierte Zertifikat dem HTTP-Anforderungsheader zur weiteren Verarbeitung hinzu. Ihr (nicht gezeigter) Code extrahiert dieses Zertifikat aus dem Anfrage-Header und füttert es dann in Ihren angezeigten Code. Haben Sie verifiziert, dass die Extraktion korrekt durchgeführt wurde? Können Sie den Inhalt von certData für einen Fall bereitstellen, in dem PEM_read_bio_X509 fehlschlägt? –

+0

Der Proxy überprüft tatsächlich das Clientzertifikat innerhalb der TLS-Verbindung mit der erwarteten CA und fügt dann das verifizierte Zertifikat dem HTTP-Anforderungsheader zur weiteren Verarbeitung hinzu. Haben Sie das verifiziert? Ja, das gleiche Zertifikat wird mehrmals ohne jedes Problem analysiert, bevor der Fehler auftritt. Es ist absolut identisch mit dem, das zu einem Fehler führt. Können Sie den Inhalt von certData bereitstellen .. Es besteht kein Unterschied zwischen den CertData, die ohne Probleme analysiert wird und den Daten, die mit einem Fehler enden. Das macht mich verrückt. –

+1

Tatsächlich verwenden Sie PEM_read_bio_X509 nicht wie in den Beispielen, siehe https://www.openssl.org/docs/ manmaster/crypto/pem.html. In den Beispielen sollte das clientCert-Argument entweder NULL sein oder mit NULL initialisiert sein, d. H. Kein zugewiesenes X509_new-Objekt sein. Und Sie sollten die Rückkehr von PEM_read_bio_X509 überprüfen. –

Antwort

1

Können Sie den PEM-Text anzeigen, nachdem die Funktion zum Ersetzen von Leerzeichen ausgeführt wurde?

Dies ist verdächtig:

if (j == 10 || j == last) { 
    c = ' '; 

, weil Sie nicht tatsächlich überprüft, ob es ein SPACE in dieser Position ist. Sie könnten etwas überschreiben, das kein SPACE ist.

Und das ist verdächtig:

if (isspace(c) || ('\t' == c)) c = '\n'; 

weil diese zwei Zeilenumbrüche leicht back-to-back verursachen könnte, wenn es passiert, ein Leerzeichen vor Newline sein, zum Beispiel.

ein Leerzeichen am Ende einen von einem einzigen Newline gefolgt Linie ist in Ordnung, aber zwei neue Zeilen in einer Reihe in der Mitte der PEM-Daten nicht funktionieren würden und in Folge hätte:

unable to load certificate 
27748:error:0906B06B:PEM routines:PEM_get_EVP_CIPHER_INFO:not proc type:pem_lib.c:446: 

zum Beispiel .Eine einfache:

$ echo "...your bad cert output from above..." | openssl x509 -noout -text 

funktioniert hier gut.

Edit: Ich habe gesehen, NGINX Leerzeichen vor jeder Zeile der PEM setzen - ich erinnere mich nicht an die Details. Aber ich würde HTTP_SSL_CLIENT_RAW_CERT mit NGINX verwenden.

Edit 2: Mit HTTP_SSL_CLIENT_RAW_CERT, meine ich, $ ssl_client_raw_cert von nginx anstelle von $ ssl_client_cert verwenden. Mit $ ssl_client_cert müssen Sie TAB-Zeichen am Anfang jeder Zeile von PEM-Daten entfernen.

+0

>> Aber ich würde HTTP_SSL_CLIENT_RAW_CERT mit NGINX verwenden. Könnten Sie erklären? Ich kann nichts darüber finden. –

+1

Schauen Sie hier: http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_client_certificate - unten listet es Variablen auf. Vergleichen Sie $ ssl_client_cert und $ ssl_client_raw_cert. Ich denke, das HTTP_ wird für CGI vorangestellt. Vgl. http://blog.honeybadger.io/how-cookies-and-other-http-headers-get-passed-from-nginx-to-rack-and-into-rails/ für in. –

+0

Thx, Jim. Werde versuchen. –

0

Danke für alle Hinweise und Vorschläge.

Ich habe meinen Code umgeschrieben; nicht sicher, ob dies das Problem auf der Kostenseite wirklich lösen wird.

Ich bin jetzt Boost-Bibliothek unter Verwendung der PEM-Daten

#include <boost/algorithm/string.hpp> 
#include <boost/algorithm/string/trim_all.hpp> 

std::string cert_data(szHeaderClientCert); 
boost::erase_all(cert_data, "-----BEGIN CERTIFICATE-----"); 
boost::erase_all(cert_data, "-----END CERTIFICATE-----"); 

if (boost::contains(cert_data, "\t")) 
     boost::replace_all(cert_data, "\t", " "); 

boost::trim_all(cert_data); 
boost::replace_all(cert_data, " ", "\n"); 

std::vector<std::string> vec; 
vec.push_back("-----BEGIN CERTIFICATE-----"); 
vec.push_back(cert_data); 
vec.push_back("-----END CERTIFICATE-----"); 

std::string szCertData = boost::algorithm::join(vec, "\n"); 

zu formatieren und dann ein (gültiges) Zertifikat erhält mit

BIO * bio = BIO_new(BIO_s_mem()); 
BIO_puts(bio, szCertData.c_str()); 

X509 * clientCert; 

clientCert = PEM_read_bio_X509(bio, NULL, 0, NULL); 
     if (clientCert == NULL) { 

...