PEM_read_bio_X509(有时)失败(OpenSSL 1.0.1p)

时间:2016-05-13 06:34:14

标签: visual-studio visual-studio-2010 ssl proxy openssl

我创建了一个DSAPI过滤器,用于使用客户端证书对用户进行身份验证。用户通过代理连接,代理将用户的证书添加到请求标头。

#define HDR_SSL_CLIENT_CERT                 "SSL_CLIENT_CERT"

我使用Apache作为HTTPS代理;客户使用NGINX。我已经发现NGINX添加了TABS而不是SPACES,并且在我的代码解析之前,我还确保证书数据具有正确的格式

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

certData包含证书的Base64表示形式(TABS和SPACES由\ 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;
    } 

我们在使用DSAPI和Apache时没有遇到任何问题; NGINX也有效。但有时,PEM_read_bio_X509失败,并且没有创建clientCert。

我的代码有什么明显的错误吗?

PEM_read_bio_X509和NGINX是否存在已知问题?

我目前正在使用openSSL 1.0.1p。

更新:以下是替换TABS和SPACES的代码

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:Good and BAD 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...

2 个答案:

答案 0 :(得分:1)

在函数运行后可以显示PEM文本以替换空格吗?

这很可疑:

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

因为您实际上没有检查该位置是否有空格。你可能会覆盖一些不是空间的东西。

这是可疑的:

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的每一行前面放置了空格 - 我不记得细节。但我会使用HTTP_SSL_CLIENT_RAW_CERT和NGINX。

编辑2:通过HTTP_SSL_CLIENT_RAW_CERT,我的意思是,使用来自nginx的$ ssl_client_raw_cert而不是$ ssl_client_cert。使用$ ssl_client_cert,您必须从每行PEM数据的开头删除TAB字符。

答案 1 :(得分: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) { 

...