PEM_read_X509() exits with code 0 - c++

I am trying to use OpenSSL in Visual Studio 2010 to read a .pem file and parse an x509 certificate. I got my code example form This tutorial the certificate is formatted in base 64, is named 'secondtry.pem' and looks like this:
-----BEGIN CERTIFICATE-----
MIIDHjCCAtygAwIBAgIEIDJHfjALBgcqhkjOOAQDBQAwYTELMAkGA1UEBhMCVVMxCzAJBgNVBAgT
AkZMMRIwEAYDVQQHEwlNZWxib3VybmUxDjAMBgNVBAoTBU1vbnRoMQwwCgYDVQQLEwNEYXkxEzAR
BgNVBAMTCkp1bHkgRWlnaHQwHhcNMTUwNzA4MTMwNDA2WhcNMTUxMDA2MTMwNDA2WjBhMQswCQYD
VQQGEwJVUzELMAkGA1UECBMCRkwxEjAQBgNVBAcTCU1lbGJvdXJuZTEOMAwGA1UEChMFTW9udGgx
DDAKBgNVBAsTA0RheTETMBEGA1UEAxMKSnVseSBFaWdodDCCAbcwggEsBgcqhkjOOAQBMIIBHwKB
gQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeB
O4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1
864rYdcq7/IiAxmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4
V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyN
KOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQBTDv+z0kq
A4GEAAKBgDH20wAdrFDjcp2hJm2M9y/tm+VCnQP5sL8knITNrDoJXrj6NEkDNkjIlJrXbrPpWuM+
uNbmFOZQAusHNX4gyRfoJCWRAIyOAQ5RsUOEb7isdjnxplbRipFF81NQXJ4XVsZ8wzTZ5quUFhq8
TWOb7Nw6GuaM9BA5tcQZgPMNrZo9oyEwHzAdBgNVHQ4EFgQUtPDQywlbEA/oCiMwN7OXBaxYqoow
CwYHKoZIzjgEAwUAAy8AMCwCFE8buS4tUi3zdlKJzZrnjmFVp8jrAhRnGgZ5/sxU9cTg+1IWZPHx
kBMc7A==
-----END CERTIFICATE-----
I have a function to open the .pem file and assign it to an X509 object for later parsing. Here is the function:
X509* openPemFile(char* filename)
{
X509* cert;
FILE* certfile = fopen(filename, "rb");
if(!certfile)
{
fprintf( stdout, "Unable to open file %s\n", filename);
return NULL;
}
PEM_read_X509(certfile, &cert, 0, NULL);
if(!cert)
{
fprintf(stdout, "Unable to parse certificate in: %s\n", filename);
fclose(certfile);
return NULL;
}
return cert;
}
I am calling the function like so:
X509* cert = openPemFile("secondtry.pem");
the file opens without error, but when my code gets to the line:
PEM_read_X509(certfile, &cert, 0, NULL);
it exits with "Native' has exited with code 0 (0x0)."
I have tried calling the PEM_read_x509 function several different ways:
X509* cert = PEM_read_X509(certfile, NULL, NULL, NULL);
PEM_read_X509(certfile, &cert, NULL, NULL);
I have also tried opening the file with the 'r' flag instead of 'rb'. I have also trawled google and SO for several hours. All of this to no avail. There is no error and the function does not return NULL, it just exits with code 0. How can I get this to work properly so that I end up with a X509 object?

Well, I still don't know why the PEM_read_x509 function doesn't work, but I believe that I have found a workaround.
X509* openPemFile(char* filename)
{
X509* cert = X509_new();
BIO* bio_cert = BIO_new_file(filename, "rb");
PEM_read_bio_X509(bio_cert, &cert, NULL, NULL);
return cert;
}
This returns a certificate that I can get data from. The two main differences are the initializing of cert with a new() call, and the use of the PEM_read_bio_X509 function. I'm not sure why, but this gives me a useable certificate object. I tried the pervious function, the PEM_read_X509, and it did not work, even with the initialized object.

Those reading functions have some rules about how they use the passed pointer-to-pointer argument:
If x is NULL then the parameter is ignored. If x is not NULL but *x is
NULL then the structure returned will be written to *x. If neither x
nor *x is NULL then an attempt is made to reuse the structure at *x
(but see BUGS and EXAMPLES sections). Irrespective of the value of x a
pointer to the structure is always returned (or NULL if an error
occurred).
In you case you have an uninitialized pointer
X509* cert;
which is likely to have some random non-NULL value. You then pass the address of the pointer to the reading function as &cert.
The library sees that x is not NULL (address of your pointer) and *x is not NULL (uninitialized value of the pointer) and so it treats value of *x as a pointer to an existing X509 structure, which is obviously not the case.
There are several ways to fix the problem.
Simply pass NULL instead of &cert to the reading function and use the returned address to initialize the cert value:
X509* cert = PEM_read_X509(certfile, NULL, 0, NULL);
Initialize the value of cert before passing it to the reading function:
X509* cert = NULL;
bool success = PEM_read_X509(certfile, &cert, 0, NULL) != NULL;
Or precreate the X509 structure as you do in your own answer, but note that some versions of OpenSSL may not correctly reuse the existing structure due to bugs.
Conclusion:
Always initialize your variables before usage (ideally at the point of declaration).
RTFM :)

Related

Writing RSA Private Key to PEM File with Passphrase

I am writing a C++ function to create an RSA key pair and store the private key in a PEM file. I use code as in the following example:
static unsigned char passphrase[] = "0123456789";
int keySize = 2048;
int keyExponent = 65537;
BIGNUM *bn;
. . .
bn = BN_new();
BN_set_word(bn, keyExponent);
rsa = RSA_new();
RSA_generate_key_ex(rsa, keySize, bn, nullptr);
FILE* fp = fopen(fileName, "w");
PEM_write_RSAPrivateKey(fp, rsa, EVP_des_ede3_cbc(), passphrase, 10, nullptr, nullptr);
This code works as expected but now I have been asked to store the private key without passphrase protection. Is there a way to get function PEM_write_RSAPrivateKey not to use a passphrase? One option I tried is to change the function call as follows:
outcome = PEM_write_RSAPrivateKey(fp, rsa, EVP_des_ede3_cbc(), nullptr, 0, nullptr, nullptr);
But this results in a prompt to the user to enter a passphrase (which is exactly as stated in the documentation of function PEM_write_RSAPrivateKey). I also tried using a passphrase call-back which returns zero (to signify that there is no passphrase) but this results in PEM_write_RSAPrivateKey returning an error.
What else can I do to have a private key stored in a PEM file without passphrase? I assume that this must somehow be possible because I can do it using openssl commands such as: openssl genrsa -out tmsPrivKey.pem 2048.
The third parameter is the encryption cipher to use to encrypt the private key with. So all you need to do is pass a nullptr to the function.
e.g.
outcome = PEM_write_RSAPrivateKey(fp, rsa, nullptr, nullptr, 0, nullptr, nullptr);
From the documentation:
The PEM functions which write private keys take an enc parameter which
specifies the encryption algorithm to use, encryption is done at the
PEM level. If this parameter is set to NULL then the private key is
written in unencrypted form.

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.

MapViewOfFile returns different addresses with same handle

I'm trying to implement IPC for a school assignment by sharing memory.
I made a class called SharedMemoryBuffer to deal with creating file mappings and views.
My Init() function looks like this:
BYTE * SharedMemoryBuffer::Init(const wchar_t * name, size_t bufferSize)
{
FileMapHandle = OpenFileMapping(
FILE_MAP_ALL_ACCESS, // read/write access
FALSE, // do not inherit the name
name); // name of mapping object
if (FileMapHandle == NULL)
{
FileMapHandle =
CreateFileMapping(INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
bufferSize,
name);
pBuf = (BYTE*)MapViewOfFile(FileMapHandle, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
bufferSize);
}
else
{
pBuf = (BYTE*)MapViewOfFile(
FileMapHandle,
FILE_MAP_ALL_ACCESS,
0,
0,
bufferSize
);
}
return this->GetBuffer();
}
Essentially, I pass it a name and size and it tries to open a mapping with this name. If it fails, it creates it instead.
I call it like so
this->ringBuffer.Init(widestr.c_str(), buffSize);
After this is done (I call Init 4 times for 2 buffers, from the same process) I print out the addresses of the buffers (pBuf from Init()) but theyre all different addresses.
I cant for the love of my life figure out why the addresses would be different!
I have made sure that the second time i call Init() with the same name that it does indeed open the file mapping successfully.
source: https://github.com/RavioliFinoli/SharedMemory
You are mapping the same region twice in your process. You will get two distinct addresses, but they are backed by the same physical memory. Writing into the buffer pointed by the first address modifies the buffer pointed to by the second address, since they are really the same memory.

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.

Creating a temporary client certificate (including a private key)

As part of an application I am writing, I wish for my program to temporarily install a certificate on the local machine and for WinHTTP to use it as the client certificate when connecting to a web server. The aim of this is help protect the web server from unauthorised access (this certificate is only a layer of the security - I know someone could extract it from the .exe). I do not want the user to have to install the certificate and I do not want the certificate to be left on the PC when the application is not running.
At the moment, I'm trying this:
Install the certificate manually from a .p12 file
Use a C++ application to get the binary data out of the local certificate and into a C array in my application (using CryptExportKey and PCCERT_CONTEXT::pbCertEncoded)
Uninstall the certificate
When the application boots:
Open a temporary store for the certificate
m_certificateStoreHandle = CertOpenStore( CERT_STORE_PROV_MEMORY, 0, NULL, 0, NULL );
Call CertAddEncodedCertificateToStore to add the certificate
CertAddEncodedCertificateToStore( m_certificateStoreHandle,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
reinterpret_cast< const BYTE * >( certificateData ),
dataSize,
CERT_STORE_ADD_REPLACE_EXISTING,
&m_clientCertificate )
Call CryptAcquireContext to get somewhere to store the private key (I change the name of the key on each run for the moment - ideally I plan to use CRYPT_VERIFYCONTEXT to make the key non-persistant, but that's something to ignore for now)
HCRYPTPROV cryptProvider = NULL;
HCRYPTKEY cryptKey = NULL;
CryptAcquireContext( &cryptProvider, "MyTestKeyNumber123", NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET )
Call CryptImportKey to load the private key into the key store
CryptImportKey( cryptProvider, reinterpret_cast< BYTE * >( privateKey ), keySize, 0, CRYPT_EXPORTABLE, &cryptKey )
Call CertSetCertificateContextProperty to link the certificate to the private key
char containerName[128];
DWORD containerNameSize = ARRAY_NUM_BYTES(containerName);
char providerName[128];
DWORD providerNameSize = ARRAY_NUM_BYTES(providerName);
CryptGetProvParam(cryptProvider, PP_CONTAINER, reinterpret_cast<byte *>(containerName), &containerNameSize, 0)
CryptGetProvParam(cryptProvider, PP_NAME, reinterpret_cast<byte *>(providerName), &providerNameSize, 0)
WCHAR containerNameWide[128];
convertCharToWChar(containerNameWide, containerName);
WCHAR providerNameWide[128];
convertCharToWChar(providerNameWide, providerName);
CRYPT_KEY_PROV_INFO privateKeyData;
neMemZero(&privateKeyData, sizeof(privateKeyData));
privateKeyData.pwszContainerName = containerNameWide;
privateKeyData.pwszProvName = providerNameWide;
privateKeyData.dwProvType = 0;
privateKeyData.dwFlags = CRYPT_SILENT;
privateKeyData.dwKeySpec = AT_KEYEXCHANGE;
if ( CertSetCertificateContextProperty( m_clientCertificate, CERT_KEY_PROV_INFO_PROP_ID, 0, &privateKeyData ) )
Verify I can access the private key (Works, outputs exactly the same data as I have hardcoded into the application)
byte privateKeyBuffer[2048];
DWORD privateKeyBufferSize = ARRAY_NUM_BYTES(privateKeyBuffer);
memZero(privateKeyBuffer, privateKeyBufferSize);
if(CryptExportKey(cryptKey, 0, PRIVATEKEYBLOB, 0, privateKeyBuffer, &privateKeyBufferSize))
{
TRACE("Got private key!");
LOG_BUFFER(privateKeyBuffer, privateKeyBufferSize);
}
Attempt to verify the client certificate works as expected
char certNameBuffer[128] = "";
char certUrlBuffer[128] = "";
CertGetNameString(testValue, CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, NULL, certNameBuffer, ARRAY_NUM_BYTES(certNameBuffer));
CertGetNameString(testValue, CERT_NAME_URL_TYPE , 0, NULL, certUrlBuffer, ARRAY_NUM_BYTES(certUrlBuffer));
TRACE("SSL Certificate %s [%s]", certNameBuffer, certUrlBuffer);
HCRYPTPROV_OR_NCRYPT_KEY_HANDLE privateKey;
DWORD privateKeyType;
BOOL freeKeyAfter = false;
if(CryptAcquireCertificatePrivateKey(testValue, CRYPT_ACQUIRE_NO_HEALING, NULL, &privateKey, &privateKeyType, &freeKeyAfter))
{
HCRYPTPROV privateKeyProvider = static_cast<HCRYPTPROV>(privateKey);
HCRYPTKEY privateKeyHandle;
if(CryptGetUserKey(privateKeyProvider, privateKeyType, &privateKeyHandle))
{
NEbyte privateKeyBuffer[2048];
DWORD privateKeyBufferSize = NE_ARRAY_NUM_BYTES(privateKeyBuffer);
neMemZero(privateKeyBuffer, privateKeyBufferSize);
if(CryptExportKey(privateKeyHandle, 0, PRIVATEKEYBLOB, 0, privateKeyBuffer, &privateKeyBufferSize))
{
NE_TRACE("Got private key!");
HTTP_LOG_BUFFER(neGetGlobalTraceLog(), "Key", "", privateKeyBuffer, privateKeyBufferSize);
}
At this stage, the private key is found but the call to CryptExportKey fails with NTE_BAD_KEY_STATE. When I try to use the client certificate with WinHTTP, I get ERROR_WINHTTP_CLIENT_CERT_NO_ACCESS_PRIVATE_KEY. If anyone is wondering, I tell WinHTTP to use the client certificate with this code:
if ( !WinHttpSetOption( handle,
WINHTTP_OPTION_CLIENT_CERT_CONTEXT,
const_cast<PCERT_CONTEXT>(m_clientCertificate),
sizeof( CERT_CONTEXT ) ) )
{
HTTP_LOG_ERROR( getLog(), "Setting the client certificate failed with error code %x", GetLastError() );
}
The way I see it, until I can somehow link the private key and the certificate together and make it so that I can use CryptAcquireCertificatePrivateKey with CryptExportKey to get the key data back out, WinHTTP doesn't stand a chance of being succesful.
Any thoughts as to why I can't seem to get my certificate to use the private key?
I didn't manage to get this approach working. Instead, I ended up using PFXImportCertStore along with the raw .p12 file containing the certificate and private key I wanted to use.
From what I've seen, it seems that PFXImportCertStore will create a new store in memory. The only thing I haven't been able to find out is if the private key is also stored in memory or if it ends up permenently on the PC somewhere. If I find out either way, I'll update this answer.
m_clientCertificateStoreHandle = PFXImportCertStore(&pfxData, certificatePassword, 0);
if(NULL != m_clientCertificateStoreHandle)
{
m_clientCertificateHandle = CertFindCertificateInStore( m_clientCertificateStoreHandle, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL );
}