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

Ulr*_*use 5 ssl proxy openssl visual-studio-2010 visual-studio

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

#define HDR_SSL_CLIENT_CERT                 "SSL_CLIENT_CERT"
Run Code Online (Sandbox Code Playgroud)

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

#define BUFFER_SIZE 4096
char certData[BUFFER_SIZE+1] = {0,};
Run Code Online (Sandbox Code Playgroud)

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-----
Run Code Online (Sandbox Code Playgroud)

然后,我使用以下代码从 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;
    } 
Run Code Online (Sandbox Code Playgroud)

在 Apache 中使用 DSAPI 时,我们没有发现任何问题;NGINX 也可以工作。但有时 PEM_read_bio_X509 会失败并且不会创建 clientCert。

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

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

我现在使用的是 openSSL 1.0.1p。

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

char szHeaderAuthToken[MAX_BUF_LEN+1] = {0,};
Run Code Online (Sandbox Code Playgroud)

包含代理提交的数据

    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';
Run Code Online (Sandbox Code Playgroud)

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...
Run Code Online (Sandbox Code Playgroud)

Jim*_*ood 1

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

这很可疑:

if (j == 10 || j == last) {
    c = ' ';
Run Code Online (Sandbox Code Playgroud)

因为你实际上并没有检查该位置是否有空格。您可能会覆盖非 SPACE 的内容。

这很可疑:

if (isspace(c) || ('\t' == c) ) c = '\n';
Run Code Online (Sandbox Code Playgroud)

因为例如,如果换行符之前恰好有一个空格,这很容易导致连续两个换行符。

行尾的空格后跟单个换行符是可以的,但是 PEM 数据中间的一行中的两个换行符不起作用,并且会导致:

unable to load certificate
27748:error:0906B06B:PEM routines:PEM_get_EVP_CIPHER_INFO:not proc type:pem_lib.c:446:
Run Code Online (Sandbox Code Playgroud)

例如。一个简单的:

$ echo "...your bad cert output from above..." | openssl x509 -noout -text
Run Code Online (Sandbox Code Playgroud)

在这里工作得很好。

编辑:我见过 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 数据的开头删除制表符。