DEV Community

Mohamed Barakat
Mohamed Barakat

Posted on

Working with self-signed Certificates

I was working on building software to communicate with external device. The connection should be secured. Here comes to use ssl certificates.

To do so, I needed to 3 things:

1) Create all needed certificates for Certificate Authority(CA), server and client to simulate the connection.

2) Create server and client using Nodejs to test the certificate.

3) In my c++ project, I was using CPP Rest API. I wanted it to embedded my client certificate in the connection to the server to authorize the connection and manage to communicate to the server successfully.

I will go through each step:

Create Certificates

1) Download and install Openssl

  • For more detailed information, please check here

2) Create certificate authority[CA] configuration file

It is optional step but it is easy to pass the information to openssl using a file rather than inserting that each time.

I tried to create a simple example here

  • You can check the file format here

3) Create CA certifcate and key

openssl req -new -x509 -config cert-authority.cnf -keyout cert-authority-key.pem -out cert-authority-crt.pem 

Output: cert-authority-key.pem, cert-authority-crt.pem

Server

1) Create server private key

openssl genrsa -out server-key.pem 4096 

Output: server-key.pem

2) Create server configuration file

I tried to create a simple example here

3) Create server certifacate signing request

openssl req -new -config server.cnf -key server-key.pem -out server-csr.pem 

Output: server-csr.pem

4) Sign server certificate

openssl x509 -req -extfile server.cnf -passin "pass:12345" -in server-csr.pem -CA cert-authority-crt.pem -CAkey cert-authority-key.pem -CAcreateserial -out server-crt.pem 

Client

1) Create client private key

openssl genrsa -out client-key.pem 4096 

Output: client-key.pem

2) Create client configuration file

I tried to create a simple example here

3) Create client certifacate signing request

openssl req -new -config client.cnf -key client-key.pem -out client-csr.pem 

Output: client-csr.pem

4) Sign client certificate

openssl x509 -req -extfile client.cnf -passin "pass:12345" -in client-csr.pem -CA cert-authority-crt.pem -CAkey cert-authority-key.pem -CAcreateserial -out client-crt.pem 

5) Verify client certificate

you can verify client certificate using CA or server certificates as following:

openssl verify -CAfile cert-authority-crt.pem client-crt.pem 

Use Nodejs to create server and client

Server

var fs = require('fs'); var https = require('https'); var options = { key: fs.readFileSync('server-key.pem'), cert: fs.readFileSync('server-crt.pem'), ca: fs.readFileSync('cert-authority-crt.pem'), strictSSL: true, requestCert: true, rejectUnauthorized: true }; var srv = https.createServer(options, function (req, res) { console.log('Recieve an request from authorized client!'); res.writeHead(200); res.end("Hello secured world!"); }).listen(3000, function() { console.log('Hello! I am at https://localhost:'+ srv.address().port); }); 

Client

var fs = require('fs'); var https = require('https'); var options = { hostname: 'localhost', port: 3000, path: '/', method: 'GET', key: fs.readFileSync(__dirname +'/client-key.pem'), cert: fs.readFileSync(__dirname +'/client-crt.pem'), ca: fs.readFileSync(__dirname +'/cert-authority-crt.pem') }; var req = https.request(options, function(res) { res.on('data', function(data) { process.stdout.write(data); }); }); req.end(); req.on('error', function(e) { console.error(e); }); 

C++ Code

1) Notes before going into the code

After you create your certificates we need to install your certificate authority into your machine.

openssl pkcs12 -export -out cert-authority.p12 -inkey cert-authority-key.pem -in cert-authority-cert.pem 

Double-click on cert-authority.p12 and install the authourity under "Trusted Root Certification Authorities"

Now convert your client certificate using the same way to loaded later in c++:

openssl pkcs12 -export -out client.p12 -inkey client-key.pem -in client-cert.pem 

Finally do not include the following library in you linker

Crypt32.lib winhttp.lib 

2) Load certificate Function

void loadOrFindCertificate() { if (_pCertContext) return; HANDLE _certFileHandle = NULL; /*Open File*/ _certFileHandle = CreateFile(L"client.p12", GENERIC_READ, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); if (INVALID_HANDLE_VALUE == _certFileHandle) return; DWORD certEncodedSize = GetFileSize(_certFileHandle, NULL); /*Check if file size */ if (!certEncodedSize) { CloseHandle(_certFileHandle); return; } BYTE* certEncoded = new BYTE[(int)certEncodedSize]; /*Read File */ auto result = ReadFile(_certFileHandle, certEncoded, certEncodedSize, &certEncodedSize, 0); if (!result) { CloseHandle(_certFileHandle); return; } CRYPT_DATA_BLOB data; data.cbData = certEncodedSize; data.pbData = certEncoded; // Convert key-pair data to the in-memory certificate store WCHAR pszPassword[] = L"12345"; HCERTSTORE hCertStore = PFXImportCertStore(&data, pszPassword, 0); SecureZeroMemory(pszPassword, sizeof(pszPassword)); if (!hCertStore) { CloseHandle(_certFileHandle); return; } //get handle of loaded certificate _pCertContext = CertFindCertificateInStore (hCertStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL); CloseHandle(_certFileHandle); } 

3) Test with sending request to server

// Create http_client configuration. web::http::client::http_client_config config; config.set_timeout(std::chrono::seconds(2)); config.set_validate_certificates(true); auto func = [&](web::http::client::native_handle handle) { loadOrFindCertificate(); //Attach certificate with request if (_pCertContext) WinHttpSetOption(handle, WINHTTP_OPTION_CLIENT_CERT_CONTEXT, (LPVOID)_pCertContext, sizeof(CERT_CONTEXT)); }; config.set_nativehandle_options(func); // Create http_client to send the request. web::http::client::http_client client(U("https://localhost:4150"), config); // Build request URI and start the request. auto requestTask = client.request(web::http::methods::GET) // Handle response headers arriving. .then([=](web::http::http_response response) { auto status(response.status_code()); printf("Received response status code:%u\n", status); /* Extract plain text only if status code signals success */ if (status >= 200 && status < 300) return response.extract_string(true); else return Concurrency::task<utility::string_t>([=] { return utility::string_t(); }); }).then([=](utility::string_t val) { printf("Received response message:%s\n", utility::conversions::to_utf8string(val).c_str()); }); // Wait for all the outstanding I/O to complete and handle any exceptions try { requestTask.wait(); } catch (const std::exception &e) { printf("Error exception:%s\n", e.what()); } 

Source Code

Create server/client Certificates using openssl
Create server and client using NodeJS
Load certificate with CPP REST API

Top comments (0)