2016-05-13 354 views
1

我已經創建了一個DSAPI篩選器來使用客戶端證書對用戶進行身份驗證。用戶通過代理連接,代理將用戶的證書添加到請求標頭。PEM_read_bio_X509(有時)失敗(OpenSSL 1.0.1p)

#define HDR_SSL_CLIENT_CERT     "SSL_CLIENT_CERT" 

我使用Apache作爲HTTPS代理;客戶使用NGINX。我已經發現,NGINX增加了,而不是空間TABS,我也確信,該證書數據的格式是否正確,然後再交由我的代碼

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

certData解析包含證書的Base64表示(TABS和空間被\ n替換)

-----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----- 

然後我用下面的代碼從一個certData X509:

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; 
    } 

我們還沒有看到當u任何問題用Apache唱DSAPI; NGINX也有效。但有時,PEM_read_bio_X509失敗,並且沒有創建clientCert。

我的代碼有什麼明顯的錯誤嗎?

PEM_read_bio_X509和NGINX是否存在已知問題?

我目前使用openSSL 1.0.1p。

更新:這裏是取代了製表和空格

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

包含數據提交代理

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代碼:好的和壞的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

只是爲了得到諒解的問題:代理實際上會驗證TLS連接中的客戶端證書與預期的CA之間的關係,然後將驗證的證書添加到HTTP請求標頭中以供進一步處理。您的(未示出)代碼從請求頭中提取此證書,然後將其提供給您的代碼。你是否證實提取過程正確?你能提供一個PEM_read_bio_X509失敗的情況下的certData的內容嗎? –

+0

代理實際驗證與預期CA TLS連接內部的客戶端證書,然後添加驗證證書,以作進一步處理 HTTP請求頭這是正確的 是否已確認..? 是的,在失敗發生之前多次解析相同的證書而沒有任何問題。它與導致失敗的那個完全相同。 您能否提供certData的內容.. 解析沒有問題的certData和以錯誤結尾的數據沒有區別。這是讓我發瘋的原因。 –

+1

實際上,您正在使用的PEM_read_bio_X509與示例中不同,請參閱https://www.openssl.org/docs/manmaster/crypto/pem.html。在示例中,clientCert參數應該爲NULL或用NULL初始化,即不是分配的X509_new對象。你應該檢查PEM_read_bio_X509的回報。 –

回答

1

函數運行後可以顯示PEM文本以替換空格嗎?

這是可疑的:

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

,因爲你實際上並沒有檢查是否有在該位置的空間。你可能會覆蓋一些不是SPACE的東西。

這是可疑的:

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

因爲這樣易引起兩個新行後端到回來,如果剛好有換行符之前的一個空間,例如。

在一行後跟一個換行符的後面加上一個空格是確定的,但在PEM數據的中間一排兩個新行是行不通的,並會導致:

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

例如。一個簡單的:

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

在這裏工作很好。

編輯:我看過NGINX在PEM的每一行前放置空格 - 我不記得細節。但是我會在NGINX上使用HTTP_SSL_CLIENT_RAW_CERT。

編輯2:通過HTTP_SSL_CLIENT_RAW_CERT,我的意思是,從$ nginx使用$ ssl_client_raw_cert而不是$ ssl_client_cert。使用$ ssl_client_cert,您必須從每行PEM數據的開頭刪除TAB字符。

+0

>>但我會在NGINX上使用HTTP_SSL_CLIENT_RAW_CERT。 你能解釋一下嗎?我找不到任何關於它的事情。 –

+1

看這裏:http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_client_certificate - 在底部列出變量。比較$ ssl_client_cert和$ ssl_client_raw_cert。我認爲HTTP_是CGI的前綴。參看http://blog.honeybadger.io/how-cookies-and-other-http-headers-get-passed-from-nginx-to-rack-and-into-rails/ for。 –

+0

Thx,Jim。會嘗試。 –

0

感謝您的所有提示和建議。

我改寫了我的代碼;不確定,如果這能真正解決客戶方面的問題。

現在,我使用boost庫格式化PEM數據

#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"); 

,然後得到一個(有效的)證書與

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) { 

...