问题描述
我创建使用 EVP_aes_256_cbc()
加密RSA密钥对。私钥是PEM连接codeD,并具有密码。这需要一个口令到由用户输入
下面是创建私钥电话:
//保存私钥
bio_priv = BIO_new_file(full_asymKeyFilePath.c_str(),A +);
如果(PEM_write_bio_RSAPrivateKey(
bio_priv,// BIO手柄
RSA,//钥匙手柄
EVP_aes_256_cbc(),//密码编码格式
PWD,//密码
pwd_len,//密码长度
NULL,//回调
NULL //不知道
)!= 1){
//报告犯错
}
然后我生成的证书和私钥签了名。
// SIGN与生成的密钥证书
如果(!X509_sign(CERT,evpKey,EVP_sha1())){
//报告犯错
}
后来,我想验证此证书此RSA密钥对的比赛。当我 SSL_CTX_check_private_key()
,我按提示输入从控制台密码。
是否有办法自动输入密码,这样我就不会被从控制台提示?
//加载服务器证书,必须不断呼吁使用私钥之前调用
如果(SSL_CTX_use_certificate_file(背景下,full_certFilePath.c_str(),SSL_FILETYPE_PEM)== 0){//将PEM文件导入到SSL_CTX所有证书
//呃
} //加载相应的证书私钥
如果(SSL_CTX_use_PrivateKey_file(背景下,full_asymKeyFilePath.c_str(),SSL_FILETYPE_PEM)== 0){//将PEM文件导入到SSL_CTX所有证书
//文件类型不是PEM或私钥调用此函数之前加载。私钥不匹配证书中的公钥
//呃
} //验证的证书和私钥匹配
如果{//&LT(SSL_CTX_check_private_key(上下文)!); ======提示我输入传递:(
//呃
}
There are two answers here. One is for the certificate, and the second is for the private key. The private key is shown first because it is used to validate the certificate (so it makes sense to visit it first).
Also, its important to call the *_check_key
routines because OpenSSL only checks that a key is well encoded; and it does not check that its actually valid. See, for example, Private key generated by openssl does not satisfy n = p * q.
In OpenSSL, you would use the following to verify the the private key is well encoded:
FILE* file = fopen(...);
EVP_PKEY* pkey = PEM_read_PrivateKey(file, NULL, PasswordCallback, NULL);
unsigned long err = ERR_get_error();
if(pkey)
EVP_PKEY_free(pkey);
If pkey
is NULL
, then there was a problem and err
holds a reason code. Otherwise, you have a properly encoded private key (but not necessarily valid).
If the key is properly encoded, you can check the type of private key and validate it with the following.
int type = EVP_PKEY_get_type(pkey);
switch (type)
{
case EVP_PKEY_RSA:
case EVP_PKEY_RSA2:
RSA* rsa = EVP_PKEY_get1_RSA(pkey);
rc = RSA_check_key(rsa);
ASSERT(rc);
RSA_free(rsa);
break;
case EVP_PKEY_DSA:
case EVP_PKEY_DSA1:
case EVP_PKEY_DSA2:
case EVP_PKEY_DSA3:
case EVP_PKEY_DSA4:
DSA* dsa = EVP_PKEY_get1_DSA(pkey);
rc = DSA_check_key(dsa);
ASSERT(rc);
DSA_free(dsa);
break;
case EVP_PKEY_DH:
DH* dh = EVP_PKEY_get1_DH(pkey);
rc = DH_check_key(dh);
ASSERT(rc);
DH_free(dh);
break;
case EVP_PKEY_EC:
EC_KEY* ec = EVP_PKEY_get1_EC_KEY(pkey);
rc = EC_KEY_check_key(ec);
ASSERT(rc);
EC_KEY_free(ec);
break;
default:
ASSERT(0);
}
EVP_PKEY_get_type
is not part of OpenSSL. Here's how I implemented it:
int EVP_PKEY_get_type(EVP_PKEY *pkey)
{
ASSERT(pkey);
if (!pkey)
return NID_undef;
return EVP_PKEY_type(pkey->type);
}
In OpenSSL, you would use the following to verify the the certificate is well encoded:
FILE* file = fopen(...);
X509* x509 = PEM_read_X509(file, NULL, NULL, NULL);
unsigned long err = ERR_get_error();
If x509
is NULL
, then there was a problem and err
holds a reason code. Otherwise, you have a properly encoded certificate (but not necessarily valid).
You can then verify the certificate with:
/* See above on validating the private key */
EVP_PKEY* pkey = ReadPrivateKey(...);
int rc = X509_verify(x509, pkey);
err = ERR_get_error();
If rc != 1
, then there was a problem and err
holds a reason code. Otherwise, you have a valid certificate and private key pair. If the certificate is valid, then you can't use err
because err
is only valid if there's a problem.
If your certificate is signed by an issuer (for example, a CA or intermediate), then you need to use a X509_STORE
to verify the issuer's signature on your certificate (a lot of error checking omitted):
const char* serverCertFilename = ...;
const char* issuerCertFilename = ...;
X509_STORE* store = X509_STORE_new();
ASSERT(store);
static const long flags = X509_V_FLAG_X509_STRICT | X509_V_FLAG_CHECK_SS_SIGNATURE
| X509_V_FLAG_POLICY_CHECK;
rc = X509_STORE_set_flags(store, flags);
err = ERR_get_error();
ASSERT(rc);
/* Some other object/functions owns 'lookup', but I'm not sure which (perhaps the store) */
X509_LOOKUP* lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
/* err = ERR_get_error(); // Does not set error codes. */
ASSERT(lookup);
/* Cannot load this from memory. No API!!! */
rc = X509_LOOKUP_load_file(lookup, issuerCertFilename, X509_FILETYPE_PEM);
/* err = ERR_get_error(); // Does not set error codes. */
ASSERT(rc);
X509_STORE_CTX* ctx = X509_STORE_CTX_new();
ASSERT(ctx);
X509* serverCert = ReadCertifcate(serverCertFilename);
ASSERT(serverCert);
rc = X509_STORE_CTX_init(ctx, store, serverCert, NULL);
ret = err = ERR_get_error();
ASSERT(rc);
/* Error codes at https://www.openssl.org/docs/crypto/X509_STORE_CTX_get_error.html */
rc = X509_verify_cert(ctx);
err = X509_STORE_CTX_get_error(ctx);
/* Do cleanup, return success/failure */
Yes. use the password callback in PEM_read_PrivateKey
. The PasswordCallback
can simply provide a password in the buffer, or it can prompt the user and return the password in the buffer.
My password callback is somewhat involved. It performs a single hash of the raw password before passing it on to the library. That ensures a "plain text" password is not used (but does not slow down the customary attacks). Yours can prompt the user for a string, or it can return a hard coded string.
My password callback uses a label. The label allows me to derive different keys depending on usage (even though the same 'base' secret is used). By specifying a different usage or label, I get a different derivation of key bits. The label is provided through arg
below, and you can set it with SSL_CTX_set_default_passwd_cb_userdata
.
using EVP_MD_CTX_ptr = std::unique_ptr<EVP_MD_CTX, decltype(&::EVP_MD_CTX_destroy)>;
int PasswordCallback(char *buffer, int size, int rwflag, void *arg)
{
UNUSED(rwflag);
int rc;
unsigned long err;
ostringstream oss;
const char* label = (char*) arg;
size_t lsize = (label ? strlen(label) : 0);
SecureVector sv = config.GetMasterKey();
ASSERT(!sv.empty());
if (sv.empty())
{
...
throw runtime_error(oss.str().c_str());
}
EVP_MD_CTX_ptr ctx(EVP_MD_CTX_create(), ::EVP_MD_CTX_destroy);
ASSERT(ctx.get() != NULL);
const EVP_MD* hash = EVP_sha512();
ASSERT(hash != NULL);
rc = EVP_DigestInit_ex(ctx.get(), hash, NULL);
err = ERR_get_error();
ASSERT(rc == 1);
if (rc != 1)
{
...
throw runtime_error(oss.str().c_str());
}
rc = EVP_DigestUpdate(ctx.get(), sv.data(), sv.size());
err = ERR_get_error();
ASSERT(rc == 1);
if (rc != 1)
{
...
throw runtime_error(oss.str().c_str());
}
if (label && lsize)
{
rc = EVP_DigestUpdate(ctx.get(), label, lsize);
err = ERR_get_error();
ASSERT(rc == 1);
if (rc != 1)
{
...
throw runtime_error(oss.str().c_str());
}
}
int n = std::min(size, EVP_MD_size(hash));
if (n <= 0)
return 0;
rc = EVP_DigestFinal_ex(ctx.get(), (unsigned char*) buffer, (unsigned int*) &n);
err = ERR_get_error();
ASSERT(rc == 1);
if (rc != 1)
{
...
throw runtime_error(oss.str().c_str());
}
return n;
}
这篇关于编程验证X509证书和私钥匹配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!