以编程方式使用OpenSSL读取私钥文件

时间:2016-08-11 13:04:26

标签: openssl x509 pki ca

我对使用OpenSSL API进行编程非常陌生,只是通过阅读openssl的文档很难理解工作原理(现在,为什么我不能在帖子中放置尽可能多的链接? ??)。

我已按照OpenSSL Certificate Authority | Create the root pairOpenSSL Certificate Authority | Sign server and client certificates创建密钥和证书,现在我希望我的应用程序使用服务器私钥和证书与客户端进行通信。这就是我到目前为止(请随意评论您认为此代码错误的每一个细节)。

SSL_library_init();
SSL_METHOD const * method = SSLv3_server_method();
if (!method)
{
    ERR_print_errors_fp(stderr);
    exit(EXIT_FAILURE);
}

SSL_CTX * ctx = SSL_CTX_new(method);
if (!ctx)
{
    ERR_print_errors_fp(stderr);
    exit(EXIT_FAILURE);
}
if (!SSL_CTX_use_certificate_chain_file(ctx, certificate_chain_file))
{
    ERR_print_errors_fp(stderr);
    exit(EXIT_FAILURE);
}
SSL_CTX_set_default_passwd_cb_userdata(ctx, (void *) private_key_file_password);
SSL_CTX_set_default_passwd_cb(ctx, pem_passwd_cb);

if (SSL_CTX_use_PrivateKey_file(ctx, private_key_file, SSL_FILETYPE_PEM) != 1)
{
    ERR_print_errors_fp(stderr);
    exit(EXIT_FAILURE);
}

现在,在调用SSL_CTX_use_PrivateKey_file时,它失败并显示以下错误:

  

139649166755520:错误:0B080074:LIB(11):函数(128):原因(116):x509_cmp.c:330:

我已经下载了相应的OpenSSL源代码,并在x509_cmp.c的第330行下载了:

  X509err(X509_F_X509_CHECK_PRIVATE_KEY,X509_R_KEY_VALUES_MISMATCH);

当谷歌搜索时,我看到有人说这意味着密码不正确,但绝对不是(因为当我输入任何其他密码时,我得到另一堆错误的痕迹)。可能是造成此错误的原因是什么?

3 个答案:

答案 0 :(得分:0)

  

现在,在调用SSL_CTX_use_PrivateKey_file时,它失败并显示以下错误:

<IfModule mod_auth_pam.c>
  AuthPAM off
</IfModule>

您没有阅读问题。这似乎工作正常。

完整的错误字符串是:

139649166755520:error:0B080074:lib(11):func(128):reason(116):x509_cmp.c:330:

您提供的私钥未与 服务器的 证书中的公钥配对。

$ openssl errstr 0x0B080074 error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch 应该是PEM编码证书的串联,包括(1)由根CA签名的中间证书,以及(2)由中间CA签名的服务器证书。 certificate_chain_file应该是服务器证书的私钥;而不是其中一个CA.为了完整起见,客户必须信任您的根CA,而不是在链中发送。

对于“... PEM编码证书的串联......”,请参阅(例如)Adding an intermediate certificates to a pkcs12 fileIs it possible to include the private key in a .CER certificate file?

  

以编程方式读取私钥文件...

为了完整起见,这里是如何以ASN.1 / DER和PEM格式读取和写入它们,但我不相信它的问题:Use OpenSSL RSA key with .Net

不要让.Net欺骗你。答案是在C / C ++中,它向您展示了如何检查二进制ASN.1 / DER编码键;以及 private_key_file ----- BEGIN XXX----- 的Base64 PEM编码密钥。

----- END XXX-----

相关,这可能还有改进的余地。

请参阅OpenSSL wiki上TLS Client的设置代码以获取一些建议。

  

我已按照OpenSSL Certificate Authority | Create the root pairOpenSSL Certificate Authority | Sign server and client certificates创建密钥和证书......

相关的,您可能需要查看How do you sign Certificate Signing Request with your Certification AuthorityHow to create a self-signed certificate with openssl?它提供了有关X.509服务器证书的大量背景信息,以及各种规则的来源。

更新 (来自评论):

  

“...私钥文件和与私钥对应的证书文件是完全独立的文件,当我将私钥文件提供给SSL_CTX_use_PrivateKey_file函数时,我认为它无法知道证书文件”

您必须知道与服务器证书一起使用的私钥。这是您必须加载的私钥。根证书和中间证书不需要私钥,因为您不对它们执行私钥操作。

答案 1 :(得分:0)

我觉得我有点失落 - 私钥文件和与私钥对应的证书文件是完全独立的文件,当我将私钥文件提供给SSL_CTX_use_PrivateKey_file函数时,我认为它可以不知道证书文件。我知道私钥文件的密钥是正确的。当我输入错误的密码时,会出现以下内容

139818423899840:error:06065064:lib(6):func(101):reason(100):evp_enc.c:539:
139818423899840:error:0906A065:lib(9):func(106):reason(101):pem_lib.c:483:
139818423899840:error:140B0009:lib(20):func(176):reason(9):ssl_rsa.c:669:

这转化为

$ openssl errstr 140B0009
error:140B0009:SSL routines:SSL_CTX_use_PrivateKey_file:PEM lib
$ openssl errstr 0906A065
error:0906A065:PEM routines:PEM_do_header:bad decrypt
$ openssl errstr 06065064
error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt

所以它必须是别的东西,就像我不理解的一些加密东西。

答案 2 :(得分:0)

由于我没有回答,我不确定我的问题/解释是否太久了。现在的简短问题是:

使用

SSL_CTX_use_PrivateKey_file

功能;为什么我得到

139923876902592:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt:evp_enc.c:539:
139923876902592:error:0906A065:PEM routines:PEM_do_header:bad decrypt:pem_lib.c:483:
139923876902592:error:140B0009:SSL routines:SSL_CTX_use_PrivateKey_file:PEM lib:ssl_rsa.c:669:
ERROR: Failed to set private key file /home/jocke/ca/intermediate/private/xxx.key.pem. ERROR: 

当我输错密码时,

39814590265024:error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch:x509_cmp.c:330:
ERROR: Failed to set private key file /home/jocke/ca/intermediate/private/xxx.key.pem. ERROR: 

当我提供正确的密码?

旧帖子:

对不起这里的布局,但工具说的是代码但没有格式化为代码,但我不知道它是什么,所以我会“编码”所有内容。

好吧,我以为我做到了。让我准确地说明我在做什么,并解释为什么我认为我是对的(尽管我显然不是):

1)生成根CA密钥/证书

目录设置

mkdir ~/ca/
cd ~/ca
mkdir certs crl newcerts private
chmod 700 private

index.txt文件是OpenSSL ca工具存储证书数据库的位置。请勿手动删除或编辑此文件。它现在应该包含一个引用中间证书的行。

touch index.txt
echo 1000 > serial

创建〜/ ca /​​ openssl.cnf,如下面的“根CA配置文件”,并确保dir值正确

openssl genrsa -aes256 -out private/ca.key.pem 4096
chmod 400 private/ca.key.pem

警告:每当使用req工具时,必须指定要与-config选项一起使用的配置文件,否则OpenSSL将默认为/etc/pki/tls/openssl.cnf。 注意:到期时间长,使用20年(7300天)

openssl req -config openssl.cnf -key private/ca.key.pem -new -x509 -days 7300 -sha256 -extensions v3_ca -out certs/ca.cert.pem
chmod 444 certs/ca.cert.pem

验证证书:

openssl x509 -noout -text -in certs/ca.cert.pem

ROOT CA PRIVATE KEY:〜/ ca /​​ private / ca.key.pem ROOT CA CERTIFICATE:〜/ ca /​​ certs / ca.cert.pem

2)生成中间CA密钥/证书

目录设置

mkdir ~/ca/indermediate
cd ~/ca/indermediate
mkdir certs crl csr newcerts private
chmod 700 private
touch index.txt
echo 1000 > serial
echo 1000 > ~/ca/intermediate/crlnumber

创建〜/ ca /​​ intermediate / openssl.cnf,如下面的“中间CA配置文件”,并确保dir值正确

cd ~/ca

创建中间CA私钥:

openssl genrsa -aes256 -out intermediate/private/intermediate.key.pem 4096
chmod 400 intermediate/private/intermediate.key.pem

创建证书签名请求(CSR):(确保指定中间CA CONF文件!)

cd ~/ca
openssl req -config intermediate/openssl.cnf -new -sha256 -key intermediate/private/intermediate.key.pem -out intermediate/csr/intermediate.csr.pem

使用根证书和CSR创建中间CA证书:(确保指定 ROOT CA CONF FILE !!!) 注意:更短的到期时间,使用10年(3650天)

openssl ca -config openssl.cnf -extensions v3_intermediate_ca -days 3650 -notext -md sha256 -in intermediate/csr/intermediate.csr.pem -out intermediate/certs/intermediate.cert.pem
chmod 444 intermediate/certs/intermediate.cert.pem

验证中间证书:

openssl x509 -noout -text -in intermediate/certs/intermediate.cert.pem

INTERMEDIATE CA PRIVATE KEY:〜/ ca /​​ intermediate / private / intermediate.key.pem 中级CA证书:〜/ ca /​​ intermediate / certs / intermediate.cert.pem

3)创建证书链文件

当应用程序(例如,Web浏览器)尝试验证由中间CA签名的证书时,它还必须根据根证书验证中间证书。 要完成信任链,请创建CA证书链以呈现给应用程序。 要创建CA证书链,请将中间证书和根证书连接在一起。 我们稍后将使用此文件来验证由中间CA签名的证书。

cat intermediate/certs/intermediate.cert.pem certs/ca.cert.pem > intermediate/certs/ca-chain.cert.pem
chmod 444 intermediate/certs/ca-chain.cert.pem

CERTIFICATE CHAIN FILE:〜/ ca /​​ intermediate / certs / ca-chain.cert.pem

4)签署服务器和客户端证书

使用中间CA签署证书。 创建密钥:

cd ~/ca/
openssl genrsa -aes256 -out intermediate/private/myinternetaddr.key.pem 2048
chmod 400 intermediate/private/myinternetaddr.key.pem

创建证书签名请求(CSR):

openssl req -config intermediate/openssl.cnf -key intermediate/private/myinternetaddr.key.pem -keyform PEM -new -sha256 -out intermediate/csr/myinternetaddr.csr.pem

创建服务器证书:

openssl ca -config intermediate/openssl.cnf -extensions server_cert -days 375 -notext -md sha256 -in intermediate/csr/myinternetaddr.csr.pem -out intermediate/certs/myinternetaddr.cert.pem
chmod 444 intermediate/certs/myinternetaddr.cert.pem
cat index.txt

对于WEBMASTERS:以下行被视为“代码” - 为什么?上面类似的行不是。     客户端私钥:〜/ ca /​​ intermediate / private / myinternetaddr.key.pem 客户证书签名请求:〜/ ca /​​ intermediate / csr / myinternetaddr.csr.pem 客户证书:〜/ ca /​​ intermediate / certs / myinternetaddr.cert.pem

验证证书:

openssl x509 -noout -text -in intermediate/certs/myinternetaddr.cert.pem

使用我们之前创建的CA证书链文件(ca-chain.cert.pem)来验证新证书是否具有有效的信任链。

$ openssl verify -CAfile intermediate/certs/ca-chain.cert.pem intermediate/certs/myinternetaddr.cert.pem
intermediate/certs/myinternetaddr.cert.pem: OK

然后我运行程序,如下所示。正如您所看到的,我正在使用证书链文件(已经过验证,具有服务器证书 - 请参阅底部)以及相应的私钥文件到服务器证书。

SSL_library_init();
SSL_METHOD const * method = SSLv3_server_method();
if (!method)
{
    ERR_print_errors_fp(stderr);
    exit(EXIT_FAILURE);
}

SSL_CTX * ctx = SSL_CTX_new(method);
if (!ctx)
{
    ERR_print_errors_fp(stderr);
    exit(EXIT_FAILURE);
}
if (!SSL_CTX_use_certificate_chain_file(ctx, "~/ca/intermediate/certs/ca-chain.cert.pem"))
{
    ERR_print_errors_fp(stderr);
    exit(EXIT_FAILURE);
}
SSL_CTX_set_default_passwd_cb_userdata(ctx, (void *) private_key_file_password);
SSL_CTX_set_default_passwd_cb(ctx, pem_passwd_cb);

if (SSL_CTX_use_PrivateKey_file(ctx, "~/ca/intermediate/private/myinternetaddr.key.pem", SSL_FILETYPE_PEM) != 1)
{
    ERR_print_errors_fp(stderr);
    exit(EXIT_FAILURE);
}

密码回调:

static int pem_passwd_cb(char * buf, int size, int rwflag, void * userdata)
{
    char const * const password = (char const * const) userdata;
    Logger & logger = Logger::get_instance();
    logger << "Setting password to [" << password << "]";
    logger.log_info();
    strncpy(buf, (char *) password, size);
    buf[size - 1] = '\0';
    fprintf(stdout, "BUFLEN: %d\nBUF: [%s]\n", (int) strlen(buf), buf);
    return strlen(buf);
}

现在,当我完成以下操作时,我认为一切都应该没问题。

$ openssl req -config intermediate/openssl.cnf -key intermediate/private/myinternetaddr.key.pem -new -sha256 -out intermediate/csr/myinternetaddr.csr.pem
Enter pass phrase for intermediate/private/myinternetaddr.key.pem:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [GB]:SE
State or Province Name [England]:Sweden
Locality Name []:NA
Organization Name [Alice Ltd]:NA
Organizational Unit Name []:NA
Common Name []:Jocke
Email Address []:yyy@hotmail.com
$ openssl ca -config intermediate/openssl.cnf -extensions server_cert -days 375 -notext -md sha256 -in intermediate/csr/myinternetaddr.csr.pem -out intermediate/certs/myinternetaddr.cert.pem
Using configuration from intermediate/openssl.cnf
Enter pass phrase for ~/ca/intermediate/private/intermediate.key.pem:
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 4098 (0x1002)
        Validity
            Not Before: Aug 13 20:58:46 2016 GMT
            Not After : Aug 23 20:58:46 2017 GMT
        Subject:
            countryName               = SE
            stateOrProvinceName       = Sweden
            localityName              = NA
            organizationName          = NA
            organizationalUnitName    = NA
            commonName                = Jocke
            emailAddress              = yyy@hotmail.com
        X509v3 extensions:
            X509v3 Basic Constraints: 
                CA:FALSE
            Netscape Cert Type: 
                SSL Server
            Netscape Comment: 
                OpenSSL Generated Server Certificate
            X509v3 Subject Key Identifier: 
                    D5:D6:F4:38:24:18:41:F7:F0:29:9F:99:6C:D3:08:38:CE:35:B8:43
                X509v3 Authority Key Identifier: 
                    keyid:2C:EB:99:69:BE:00:EE:C2:FD:86:B7:CF:6C:AD:47:4E:65:AA:90:5A
                    DirName:/C=SE/ST=Sweden/L=/O=Joachim Person/CN=Joachim Person/emailAddress=xxx@gmail.com
                    serial:10:00

                X509v3 Key Usage: critical
                    Digital Signature, Key Encipherment
                X509v3 Extended Key Usage: 
                    TLS Web Server Authentication
    Certificate is to be certified until Aug 23 20:58:46 2017 GMT (375 days)
    Sign the certificate? [y/n]:y


    1 out of 1 certificate requests certified, commit? [y/n]y
    Write out database with 1 new entries
    Data Base Updated

但显然不是 - 为什么?