Add CRL number extension to CRL using OpenSSL - c++

For some client testing I need to generate certificates and revocation lists "on the fly". I am able to setup a revocation list with the CRL number extension using OpenSSL console commands and configuration files. However I can't make this work in code.
Here are my relevant functions:
X509* g_certificate[MAX_CERTIFICATE_COUNT];
EVP_PKEY* g_certificateKeyPair[MAX_CERTIFICATE_COUNT];
X509_CRL* g_crl;
UINT16 GenerateCrl(UINT16 issuerCertificateIndex, UINT16 crlNumber, INT32 lastUpdate, INT32 nextUpdate)
{
ASN1_TIME* lastUpdateTime = ASN1_TIME_new();
ASN1_TIME* nextUpdateTime = ASN1_TIME_new();
char crlNumberString[32];
int result = 1;
if (g_crl != NULL) X509_CRL_free(g_crl);
g_crl = X509_CRL_new();
result &= X509_CRL_set_version(g_crl, 1);
result &= X509_CRL_set_issuer_name(g_crl, X509_get_subject_name(g_certificate[issuerCertificateIndex]));
// there are multiple X509 certificate objects stored in memory, which I use to setup certificate chains
ASN1_TIME_set(lastUpdateTime, time(NULL) + lastUpdate);
ASN1_TIME_set(nextUpdateTime, time(NULL) + nextUpdate);
result &= X509_CRL_set1_lastUpdate(g_crl, lastUpdateTime);
result &= X509_CRL_set1_nextUpdate(g_crl, nextUpdateTime);
ASN1_TIME_free(lastUpdateTime);
ASN1_TIME_free(nextUpdateTime);
_itoa_s((int)crlNumber, crlNumberString, 10);
// my CRLs need to have the authority key identifier and CRL number extensions
result &= SetCrlExtension(issuerCertificateIndex, NID_authority_key_identifier, "keyid:always"); // this does add the auth key id extension
result &= SetCrlExtension(issuerCertificateIndex, NID_crl_number, crlNumberString); // this does not add CRL number the extension
result &= X509_CRL_sign(g_crl, g_certificateKeyPair[issuerCertificateIndex], EVP_sha256());
return result;
}
INT16 SetCrlExtension(UINT16 issuerCertificateIndex, INT16 extensionNid, const char* extensionData)
{
X509V3_CTX ctx;
X509_EXTENSION* extension;
lhash_st_CONF_VALUE conf; // actually I have no idea what this is for, probably it is not required here
int result = 1;
X509V3_set_ctx(&ctx, g_certificate[issuerCertificateIndex], NULL, NULL, g_crl, 0);
extension = X509V3_EXT_conf_nid(&conf, &ctx, extensionNid, (char*)extensionData);
result &= X509_CRL_add_ext(g_crl, extension, -1);
return result;
}
void SaveCrlAsPem(const char* fileName)
{
FILE* f;
fopen_s(&f, fileName, "wb");
PEM_write_X509_CRL(f, g_crl);
if (f != NULL) fclose(f);
}
So e.g.
GenerateCrl(1, 1234, -3600, 36000);
SaveCrlAsPem("crl.pem");
should result in a CRL having said extension. But it does only contain the authority key identifier extension. Everything else is fine. Im also adding the certificate extensions in basically the same way and dont't have any issues there.
So how can I get the CRL number attached to my CRL?

You mention that you are able to setup the CRL number extension from OpenSSL command line. You should probably take a look at the source code of the particular command then.
I haven't used CRLs, but I believe you are using the ca command. Looking for NID_crl_number in its source at apps/ca.c (from OpenSSL 1.1.1g) shows the following code:
/* Add any extensions asked for */
if (crl_ext != NULL || crlnumberfile != NULL) {
X509V3_CTX crlctx;
X509V3_set_ctx(&crlctx, x509, NULL, NULL, crl, 0);
X509V3_set_nconf(&crlctx, conf);
if (crl_ext != NULL)
if (!X509V3_EXT_CRL_add_nconf(conf, &crlctx, crl_ext, crl))
goto end;
if (crlnumberfile != NULL) {
tmpser = BN_to_ASN1_INTEGER(crlnumber, NULL);
if (!tmpser)
goto end;
X509_CRL_add1_ext_i2d(crl, NID_crl_number, tmpser, 0, 0);
ASN1_INTEGER_free(tmpser);
crl_v2 = 1;
if (!BN_add_word(crlnumber, 1))
goto end;
}
}
So, it seems you can use X509V3_EXT_CRL_add_nconf or X509_CRL_add1_ext_i2d for the purpose. Please refer the apps/ca.c of the OpenSSL version that you are using.
Another solution:
Maybe not the best approach, but you can probably launch the same OpenSSL commands as processes from code and process their output if it's acceptable.

Related

Will this digital signing code work with a Qualified Digital Certificate?

I was recently asked if our existing signing code will work with a European Qualified Digital Certificate in place of the RSA signing certificate we normally use. As far as I can tell it should, but I haven't found anything that actually confirms this theory.
Short of figuring out how to acquire a qualified certificate and actually testing it, I'm not sure how to answer this question definitively. Anybody happen to have experience with this?
The code in question is shown below. It's a Windows-based C++ application that is using Microsoft's Cryptographic API for signing.
int SignData(
const std::string &data, // Data to be signed
const char *containerName, // Name of key container to use
std::string &signature) // Returns the signature
{
HCRYPTPROV hProv = NULL;
HCRYPTHASH hHash = NULL;
HCRYPTKEY hKey = NULL;
DWORD dwLength;
int status = 0;
// Attempt to open the key container as a LocalMachine key.
if (CryptAcquireContext(
&hProv,
containerName,
NULL,
PROV_RSA_FULL,
CRYPT_MACHINE_KEYSET | CRYPT_SILENT))
{
// Create a SHA-1 hash context.
if (CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash))
{
// Calculate hash of the buffer.
if (CryptHashData(hHash, (const BYTE *)data.data(), (DWORD)data.size(), 0))
{
// Determine the size of the signature and allocate memory.
if (CryptSignHash(hHash, AT_SIGNATURE, 0, 0, NULL, &dwLength))
{
signature.resize(dwLength);
// Sign the hash object.
if (!CryptSignHash(hHash, AT_SIGNATURE, 0, 0, (BYTE*)&signature[0], &dwLength))
status = HandleCryptError("CryptSignHash failed");
}
else
status = HandleCryptError("CryptSignHash failed");
}
else
status = HandleCryptError("CryptHashData failed");
}
else
status = HandleCryptError("CryptCreateHash failed");
}
else
status = HandleCryptError("CryptAcquireContext failed");
return status;
}
The above code is using the default Microsoft software cryptographic provider which doesn't meet the requirements of eIDAS.
If you replace the default Microsoft provider by an eIDAS compliant provider in the above code then this approach of performing signature is OK. Under the hood, the compliant CSP will take care of non-repudiation requirement of the key usage, for example by displaying a PIN popup or if the key is stored remotely in a server by sending a 2FA notification to the key owner.

Verifying message signature produce HEADER TOO LONG error

Background
I am trying to verify signature of a given binary file using openssl. Actual signing of binary hash is done by a 3rd party. Both 3rd party and I have the same exact certificate - they sent me the certificate.
I have verified health of my certificate by running openssl x509 -noout -text -inform DER -in CERT_PATH. This displays contents of cert correctly.
Following is my code so far - I based it on openssl wiki example here:
static std::vector<char> ReadAllBytes(char const* filename){
std::ifstream ifs(filename, std::ios::binary|std::ios::ate);
std::ifstream::pos_type pos = ifs.tellg();
std::vector<char> result(pos);
ifs.seekg(0, std::ios::beg);
ifs.read(result.data(), pos);
return result;
}
int main(int ac, const char * av[]) {
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
// most of error check omitted for brevity
auto foundBinBytes = ReadAllBytes("BINARY_PATH");
auto foundSgnBytes = ReadAllBytes("SIGNATURE_PATH");
auto foundCertBytes = ReadAllBytes("CERT_PATH");
ERR_clear_error();
BIO *b = NULL;
X509 *c;
b = BIO_new_mem_buf(reinterpret_cast<const unsigned char *>(foundCertBytes.data()), foundCertBytes.size());
c = d2i_X509_bio(b, NULL);
EVP_MD_CTX* ctx = NULL;
ctx = EVP_MD_CTX_create();
const EVP_MD* md = EVP_get_digestbyname("SHA256");
int rc = EVP_DigestInit_ex(ctx, md, NULL);
EVP_PKEY *k = NULL;
k = X509_get_pubkey(c);
rc = EVP_DigestVerifyInit(ctx, NULL, md, NULL, k);
rc = EVP_DigestVerifyUpdate(ctx, reinterpret_cast<const unsigned char *>(foundBinBytes.data()), foundBinBytes.size());
ERR_clear_error();
rc = EVP_DigestVerifyFinal(ctx, reinterpret_cast<const unsigned char *>(foundSgnBytes.data()), foundSgnBytes.size());
ERR_print_errors_fp( stdout );
// openssl free functions omitted
if(ctx) {
EVP_MD_CTX_destroy(ctx);
ctx = NULL;
}
return 0;
}
Issue
Running this code produces following errors:
4511950444:error:0D07207B:asn1 encoding routines:ASN1_get_object:header too long:/.../crypto/asn1/asn1_lib.c:152:
4511950444:error:0D068066:asn1 encoding routines:ASN1_CHECK_TLEN:bad object header:/.../crypto/asn1/tasn_dec.c:1152:
4511950444:error:0D07803A:asn1 encoding routines:ASN1_ITEM_EX_D2I:nested asn1 error:/.../crypto/asn1/tasn_dec.c:314:Type=X509_SIG
Question
What is wrong with my setup/code? Did I miss something along the way?
You never check the errors when reading the files. You might have errors there (does the file "CERT_PATH" exist? Do you have read permissions? ...).
If "CERT_PATH" cannot be read, then foundCertBytes.data() is an empty byte array, and this explains the subsequent errors.
If you get these errors in a mosquitto secured server's log, check the config file.
My mosquitto.conf was containing :
require_certificate true
my meross device doesn't send certificate. Turning this to "false" solved my problem after restart

C++ and OpenSSL Library: How can I set subjectAltName (SAN) from code?

I'm trying to create self-signed request with subjectAltName from c++ code (trying to implement dynamic self-signed certificates like this to actual version of OpenResty, but there is not sollution for subjectAltName).
Please, provide some examples of setting SANs from C++/OpenSSL code. I trying some like this:
X509_EXTENSION *ext;
STACK_OF (X509_EXTENSION) * extlist;
char *ext_name = "subjectAltName";
char *ext_value = "DNS:lohsport.com";
extlist = sk_X509_EXTENSION_new_null ();
ext = X509V3_EXT_conf (NULL, NULL, ext_name, ext_value);
if(ext == NULL)
{
*err = "Error creating subjectAltName extension";
goto failed;
}
sk_X509_EXTENSION_push (extlist, ext);
if (!X509_REQ_add_extensions (x509_req, extlist)){
*err = "Error adding subjectAltName to the request";
goto failed;
}
sk_X509_EXTENSION_pop_free (extlist, X509_EXTENSION_free);
It's compiling successfully but not works.
I would be grateful for any help.
UPDATE
Now i trying to work as in selfsing.c demo of OpenSSL Library:
1) I defined a function for adding extensions to CSR:
int add_ext(STACK_OF(X509_EXTENSION) *sk, int nid, char *value)
{
X509_EXTENSION *ex;
ex = X509V3_EXT_conf_nid(NULL, NULL, nid, value);
if (!ex)
return 0;
sk_X509_EXTENSION_push(sk, ex);
return 1;
}
2) Add this block to my function which generates CSR:
char Buffer[512];
// Format the value
sprintf (Buffer, "DNS:%s", info->common_name);
xts = sk_X509_EXTENSION_new_null();
add_ext(exts, NID_subject_alt_name, Buffer);
if(X509_REQ_add_extensions(x509_req, exts) != 1) {
*err = "X509_REQ_add_extensions() failed";
goto failed;
}
sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
The code again compiles correctly, certificates are generated on the fly, but alternative names still don't work, and I get an error in the browser:
NET :: ERR_CERT_COMMON_NAME_INVALID
and I don’t see the alternative name information in the certificate details.
What other solutions can there be for a SAN problem? I can provide all the code for example on githab if it can help.
Hello I have done something like that (but I am working with X509 object not X509_REQ object like you :
static int cs_cert_set_subject_alt_name(X509 *x509_cert)
{
char *subject_alt_name = "IP: 192.168.1.1";
X509_EXTENSION *extension_san = NULL;
ASN1_OCTET_STRING *subject_alt_name_ASN1 = NULL;
int ret = -1;
subject_alt_name_ASN1 = ASN1_OCTET_STRING_new();
if (!subject_alt_name_ASN1) {
goto err;
}
ASN1_OCTET_STRING_set(subject_alt_name_ASN1, (unsigned char*) subject_alt_name, strlen(subject_alt_name));
if (!X509_EXTENSION_create_by_NID(&extension_san, NID_subject_alt_name, 0, subject_alt_name_ASN1)) {
goto err;
}
ASN1_OCTET_STRING_free(subject_alt_name_ASN1);
ret = X509_add_ext(x509_cert, extension_san, -1);
if (!ret) {
goto err;
}
X509_EXTENSION_free(extension_san);
err:
if (subject_alt_name_ASN1) ASN1_OCTET_STRING_free(subject_alt_name_ASN1);
if (extension_san) X509_EXTENSION_free(extension_san);
return -1;
}
It worked for me for the moment, I still have some trouble when I want to update an already existing certificate with a new subject alt name (because of a new ip address).
To see the result and check if the subject alt name is made :
$ openssl x509 -text -in cert.pem
Instead of X509V3_EXT_conf function try with X509V3_EXT_conf_nid function in which you pass the NID instead of name.
ext = X509V3_EXT_conf (NULL, NULL, ext_name, ext_value);
can be
ext = X509V3_EXT_conf_nid(NULL, NULL, NID_subject_alt_name, ext_value);
Your code might not be working because you might not be exactly matching the extension name with the one in OpenSSL code.

How to use X509_verify()

How can we use X509_verify(). I have two certificates. The first certificate is the Root Certificate which signed the next certificate (which is my Certificate). So I want to check if my certificate is signed by the root certificate using x509_verify() in C++. My goal is to keep the code simple and Understandable so I can also put it online.
Signature of X509_verify is
int X509_verify(X509 * x509, EVP_PKEY * pkey);
Suppose of you have root certificate in root and your certificate in mycert;
X509 * root;
X509 * mycert;
//Get root certificate into root
//Get mycert into mycert.
//Get the public key.
EVP_PKEY * pubkey = X509_get_pubkey(root);
//verify. result less than or 0 means not verified or some error.
int result = X509_verify(mycert, pubkey);
//free the public key.
EVP_PKEY_free(pubkey);
I think this would help you.
I think dbasic and Balamurugan answered how to use it. Here's how to interpret the errors you get from it. I find error handling is much more important than business logic because nearly anyone can copy/paste code that works in a benign environment. How you respond to failures, broken/bad inputs and a hostile environments matter more.
The source code for the function is in <openssl dir>/crypto/x509/x_all.c:
int X509_verify(X509 *a, EVP_PKEY *r)
{
return(ASN1_item_verify(ASN1_ITEM_rptr(X509_CINF),a->sig_alg,
a->signature,a->cert_info,r));
}
ASN1_item_verify id defined in <openssl dir>/crypto/asn1/a_verify.c:
int ASN1_item_verify(const ASN1_ITEM *it, X509_ALGOR *alg,
ASN1_BIT_STRING *signature, void *asn, EVP_PKEY *pkey)
{
...
}
The function returns -1 on failure with one of the following error codes:
ERR_R_PASSED_NULL_PARAMETER if pkey is NULL
ASN1_R_UNKNOWN_SIGNATURE_ALGORITHM if alg is unknown using an OID lookup
ASN1_R_WRONG_PUBLIC_KEY_TYPE if the pkey type does not match the pkey->method
ERR_R_MALLOC_FAILURE if a buffer allocation fails
The function returns 0 on failure with one of the following error codes:
ERR_R_EVP_LIB if EVP_DigestVerifyInit fails
ERR_R_EVP_LIB if EVP_DigestVerifyUpdate fails
ERR_R_EVP_LIB if EVP_DigestVerifyFinal fails
On success, the function returns 1 (from around line 220):
...
if (EVP_DigestVerifyFinal(&ctx,signature->data,
(size_t)signature->length) <= 0)
{
ASN1err(ASN1_F_ASN1_ITEM_VERIFY,ERR_R_EVP_LIB);
ret=0;
goto err;
}
ret=1;
err:
EVP_MD_CTX_cleanup(&ctx);
return(ret);
} /* End of function */
From <openssl dir>/crypto/err/err.h, you use ERR_get_error() to retrieve the error code:
err.h:#define ASN1err(f,r) ERR_PUT_error(ERR_LIB_ASN1,(f),(r),__FILE__,__LINE__)
Step1 : Read the certificate and convert the Certificate into X509 structure
// the below will show how to read the certificate from the file (DER or PEM Encoded)
X509* oCertificate=NULL;
FILE *lFp=NULL;
lFp=fopen(iFilePath.c_str(),"rb"); // iFilepath is the string
if(lFp==NULL)
{
oCertificate=NULL;
}
else
{
oCertificate = PEM_read_X509(lFp, NULL, NULL, NULL);
if (oCertificate == NULL )
{
//Certificate may be DER encode
oCertificate = d2i_X509_fp(lFp, NULL);
}
fclose(lFp);
}
// OCertificate contains
Step 2: now read the Root certificate key
(Note check the X509 is NULL or not before use)
Step 3 : use the X509_verify() function.

Simple AES encryption using WinAPI

I need to do simple single-block AES encryption / decryption in my Qt / C++ application. This is a "keep the honest people honest" implementation, so just a basic encrypt(key, data) is necessary--I'm not worried about initialization vectors, etc. My input and key will always be exactly 16 bytes.
I'd really like to avoid another dependency to compile / link / ship with my application, so I'm trying to use what's available on each platform. On the Mac, this was a one-liner to CCCrypt. On Windows, I'm getting lost in the API from WinCrypt.h. Their example of encrypting a file is almost 600 lines long. Seriously?
I'm looking at CryptEncrypt, but I'm falling down the rabbit hole of dependencies you have to create before you can call that.
Can anyone provide a simple example of doing AES encryption using the Windows API? Surely there's a way to do this in a line or two. Assume you already have a 128-bit key and 128-bits of data to encrypt.
Here's the best I've been able to come up with. Suggestions for improvement are welcome!
static void encrypt(const QByteArray &data,
const QByteArray &key,
QByteArray *encrypted) {
// Create the crypto provider context.
HCRYPTPROV hProvider = NULL;
if (!CryptAcquireContext(&hProvider,
NULL, // pszContainer = no named container
NULL, // pszProvider = default provider
PROV_RSA_AES,
CRYPT_VERIFYCONTEXT)) {
throw std::runtime_error("Unable to create crypto provider context.");
}
// Construct the blob necessary for the key generation.
AesBlob128 aes_blob;
aes_blob.header.bType = PLAINTEXTKEYBLOB;
aes_blob.header.bVersion = CUR_BLOB_VERSION;
aes_blob.header.reserved = 0;
aes_blob.header.aiKeyAlg = CALG_AES_128;
aes_blob.key_length = kAesBytes128;
memcpy(aes_blob.key_bytes, key.constData(), kAesBytes128);
// Create the crypto key struct that Windows needs.
HCRYPTKEY hKey = NULL;
if (!CryptImportKey(hProvider,
reinterpret_cast<BYTE*>(&aes_blob),
sizeof(AesBlob128),
NULL, // hPubKey = not encrypted
0, // dwFlags
&hKey)) {
throw std::runtime_error("Unable to create crypto key.");
}
// The CryptEncrypt method uses the *same* buffer for both the input and
// output (!), so we copy the data to be encrypted into the output array.
// Also, for some reason, the AES-128 block cipher on Windows requires twice
// the block size in the output buffer. So we resize it to that length and
// then chop off the excess after we are done.
encrypted->clear();
encrypted->append(data);
encrypted->resize(kAesBytes128 * 2);
// This acts as both the length of bytes to be encoded (on input) and the
// number of bytes used in the resulting encrypted data (on output).
DWORD length = kAesBytes128;
if (!CryptEncrypt(hKey,
NULL, // hHash = no hash
true, // Final
0, // dwFlags
reinterpret_cast<BYTE*>(encrypted->data()),
&length,
encrypted->length())) {
throw std::runtime_error("Encryption failed");
}
// See comment above.
encrypted->chop(length - kAesBytes128);
CryptDestroyKey(hKey);
CryptReleaseContext(hProvider, 0);
}