Programmatically verify a X509 certificate and private key match. Private key has a PEM passphrase
Ci sono due risposte qui. Uno è per il certificato e il secondo è per la chiave privata. La chiave privata viene mostrata per prima perché viene utilizzata per convalidare il certificato (quindi è consigliabile visitarlo prima).
Inoltre, è importante chiamare le routine *_check_key
perché OpenSSL controlla solo che una chiave sia ben codificata; e non controlla che sia effettivamente valido. Vedere, ad esempio, Private key generated by openssl does not satisfy n = p * q.
In OpenSSL, è necessario utilizzare il seguente per verificare la chiave privata è ben codificato:
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);
Se pkey
è NULL
, poi c'è stato un problema e err
in possesso di un codice motivo. Altrimenti, hai una chiave privata correttamente codificata (ma non necessariamente valida).
Se la chiave è correttamente codificata, è possibile verificare il tipo di chiave privata e convalidarla con quanto segue.
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
non fa parte di OpenSSL. Ecco come ho implementato è:
int EVP_PKEY_get_type(EVP_PKEY *pkey)
{
ASSERT(pkey);
if (!pkey)
return NID_undef;
return EVP_PKEY_type(pkey->type);
}
In OpenSSL, è necessario utilizzare il seguente per verificare il certificato è ben codificato:
FILE* file = fopen(...);
X509* x509 = PEM_read_X509(file, NULL, NULL, NULL);
unsigned long err = ERR_get_error();
Se x509
è NULL
, poi c'è stato un problema e err
contiene un codice motivo. Altrimenti, hai un certificato correttamente codificato (ma non necessariamente valido).
Si può quindi verificare il certificato con:
/* See above on validating the private key */
EVP_PKEY* pkey = ReadPrivateKey(...);
int rc = X509_verify(x509, pkey);
err = ERR_get_error();
Se rc != 1
, poi c'è stato un problema e err
tiene un codice motivo. Altrimenti, hai un certificato valido e una coppia di chiavi private.Se il certificato è valido, non è possibile utilizzare err
perché err
è valido solo se c'è un problema.
Se il certificato è firmato da un emittente (per esempio, una CA o intermedio), allora avete bisogno di utilizzare un X509_STORE
per verificare la firma dell'emittente sul certificato (un sacco di controllo degli errori omesso):
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 */
Is there are way to automatically input the password so that I don't get prompted from the console?
Sì. utilizzare la richiamata della password in PEM_read_PrivateKey
. Lo PasswordCallback
può semplicemente fornire una password nel buffer, oppure può richiedere all'utente e restituire la password nel buffer.
La mia password di richiamata è in qualche modo coinvolta. Esegue un singolo hash della password raw prima di passarlo alla libreria. Ciò garantisce che non venga utilizzata una password di "testo normale" (ma non rallenta gli attacchi consueti). Il tuo può richiedere all'utente una stringa o può restituire una stringa codificata.
La richiamata della mia password utilizza un'etichetta. L'etichetta mi consente di ricavare chiavi diverse a seconda dell'uso (anche se viene utilizzato lo stesso segreto "di base"). Specificando un diverso uso o etichetta, ottengo una diversa derivazione dei bit chiave. L'etichetta viene fornita tramite arg
qui sotto e puoi impostarla con 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;
}
Ah, ho rilevato SSL_CTX_set_default_passwd_cb() dopo aver letto la documentazione. Lo proverò e pubblicherò i miei progressi più tardi. – GloriousLemon
Possibile duplicato di [Verificare che un file sia certificato o una chiave] (http://stackoverflow.com/questions/22398477/check-that-a-file-is-certificate-or-a-key) – jww