问题描述
我有问题,我允许用来上传证书和私钥文件.现在,假设我有一个文件的扩展名被弄乱了(有意或无意地更改了),说要破坏系统..我需要放置一个可以检查并可以告诉我给定文件是有效证书文件的验证,或者私钥文件或其他文件.
我当时的情况是:检查文件内容中的-- BEGIN CERTIFICATE --
,-- BEGIN RSA PRIVATE KEY --
.
谢谢
我将用C回答这个问题,因为OpenSSL是一个C库.其他人可能可以将其翻译为 pyOpenSSL
,在这种情况下,他们可能会有更好的选择比我的答案.
这里有两个答案.一个用于证书,第二个用于私钥.首先显示私钥,因为它用于验证证书(因此首先访问它很有意义).
此外,调用*_check_key
例程也很重要,因为OpenSSL仅检查密钥是否编码正确;并且不会检查其实际有效.例如,请参见 openssl生成的私钥不满足n = p * q .
在OpenSSL中,您将使用以下命令来验证私钥:
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);
如果pkey
是NULL
,则存在问题,并且err
保留原因码.否则,您将拥有正确编码的私钥(但不一定有效).
PasswordCallback
可以简单地在缓冲区中提供密码,或者可以提示用户并在缓冲区中返回密码.有关PasswordCallback
的更多信息,请参见加载PEM格式证书.
如果密钥编码正确,则可以检查私钥的类型并使用以下命令对其进行验证.
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
不是OpenSSL的一部分.这是我的实现方式:
int EVP_PKEY_get_type(EVP_PKEY *pkey)
{
ASSERT(pkey);
if (!pkey)
return NID_undef;
return EVP_PKEY_type(pkey->type);
}
在OpenSSL中,您将使用以下内容来验证证书:
FILE* file = fopen(...);
X509* x509 = PEM_read_X509(file, NULL, NULL, NULL);
unsigned long err = ERR_get_error();
如果x509
是NULL
,则存在问题,并且err
保留原因码.否则,您将拥有正确编码的证书(但不一定有效).
然后您可以使用以下方法验证证书:
/* See above on validating the private key */
EVP_PKEY* pkey = ReadPrivateKey(...);
int rc = X509_verify(x509, pkey);
err = ERR_get_error();
如果为rc != 1
,则存在问题,并且err
保留原因码.否则,您将具有有效的证书和私钥对.如果证书有效,则不能使用err
,因为err
仅在出现问题时才有效.
如果您的证书是由颁发者(例如,CA或中间机构)签名的,则您需要使用X509_STORE
来验证证书上颁发者的签名(省略许多错误检查):
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 */
I have a problem, I am allowing the used to upload certificate and private key files. Now suppose I have a file whose extension is messed-up (changed knowingly or unknowingly) say to break the system.. I need to put a validation in place that can check and can tell me that the given file is a valid certificate file or private key file or some other file..
What i was thing was : to check for -- BEGIN CERTIFICATE --
, -- BEGIN RSA PRIVATE KEY --
in the file content.. Please tell what I am thing is good, or there is some other better solution to this problem..
Thanks
I'm going to answer this in C because OpenSSL is a C library. Others may be able to translate it into pyOpenSSL
, in which case they probably have a better answer than mine.
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:
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).
PasswordCallback
can simply provide a password in the buffer, or it can prompt the user and return the password in the buffer. For more on PasswordCallback
, see Loading a PEM format certificate.
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:
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 */
这篇关于检查文件是证书还是密钥的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!