【问题标题】:Programmatically read private key file with OpenSSL使用 OpenSSL 以编程方式读取私钥文件
【发布时间】:2016-10-02 02:34:04
【问题描述】:

我对使用 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:error:0B080074:lib(11):func(128):reason(116):x509_cmp.c:330:

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

  X509err(X509_F_X509_CHECK_PRIVATE_KEY,X509_R_KEY_VALUES_MISMATCH);

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

【问题讨论】:

    标签: openssl x509 pki ca


    【解决方案1】:

    我想我在这里有点迷失 - 私钥文件和对应于私钥的证书文件是完全独立的文件,当我将私钥文件提供给 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
    

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

    【讨论】:

    • 我只是像这样创建我的私钥文件:openssl genrsa -aes256 -out intermediate/private/mydomain.com.key.pem 2048
    • “mydomain.com.key.pem”在上面的例子中可以看作是“private_key_file”
    • 证书链文件似乎没问题:$ openssl verify -CAfile certificate_chain_file private_key_file.cert.pem private_key_file.cert.pem: OK $
    • 顺便说一句 - 在这个 SSL_CTX_use_PrivateKey_file 中进行了哪些检查?它没有说明对openssl.org/docs/manmaster/ssl/SSL_CTX_use_certificate.html 的任何检查,而且还有一个似乎对证书进行验证的 SSL_CTX_check_private_key。
    • "... 私钥文件和对应于私钥的证书文件是完全独立的文件,当我将私钥文件提供给 SSL_CTX_use_PrivateKey_file 函数时,我认为它不能了解证书文件” - 您必须知道与服务器证书一起使用的私钥。
    【解决方案2】:

    现在,在调用 SSL_CTX_use_PrivateKey_file 时,它​​会失败并显示以下错误打印输出:

    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
    

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

    certificate_chain_file 应该是 PEM 编码证书的串联,包括 (1) 根 CA 签名的中间证书,和 (2) 中间 CA 签名的服务器证书。 private_key_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 编码的密钥;以及带有 ----- BEGIN XXX---------- END XXX----- 的 Base64 PEM 编码密钥。


    SSL_METHOD const * method = SSLv3_server_method();
    

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

    有关一些建议,请参阅 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服务器证书的背景信息,以及各种规则的来源。


    更新(来自 cmets):

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

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

    【讨论】:

    • 答案太长,请看下文。
    • 好的,对这个 UI 以及您通过编辑以前的帖子来回复帖子的方式有点困惑。到目前为止,我想对我有帮助的是您在 jww 上的更新。谢谢
    【解决方案3】:

    我不确定我的问题/解释是否太长,因为我没有得到答案。现在的简短问题是:

    使用时

    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: 
    

    我什么时候输入正确的密码?

    旧帖:


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

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

    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
    

    根 CA 私钥:~/ca/private/ca.key.pem 根 CA 证书:~/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 配置文件!)

    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 配置文件!!!) 注意:有效期较短,使用 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
    

    中间 CA 私钥:~/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
    

    证书链文件:~/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
    

    对于网站管理员:以下行被视为“代码” - 为什么?上面类似的行不是。 客户端私钥:~/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
    

    但显然不是——为什么?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-05-16
      • 1970-01-01
      • 1970-01-01
      • 2014-10-11
      • 1970-01-01
      • 2017-12-18
      • 2010-12-02
      相关资源
      最近更新 更多