Working with self-signed Certificates
Tags: https, node, cpp, openssl
Published: 2019-05-24 11:42:11

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](https://nodejs.org) to test the certificate.

3) In my c++ project, I was using [CPP Rest API](https://github.com/microsoft/cpprestsdk). 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](https://github.com/openssl/openssl)*

#### 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](https://github.com/mobarakat/mb-create-selfsignedcertificate/blob/master/cert-authority.cnf)

* *You can check the file format [here](https://pages.github.com/)*

#### 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](https://github.com/mobarakat/mb-create-selfsignedcertificate/blob/master/server.cnf)

#### 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](https://github.com/mobarakat/mb-create-selfsignedcertificate/blob/master/client.cnf)

#### 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](https://github.com/mobarakat/mb-create-selfsignedcertificate)
[Create server and client using NodeJS](https://github.com/mobarakat/mb-nodjs-client_server-selfsignedcertificate)
[Load certificate with CPP REST API](https://github.com/mobarakat/mb-cpp-client-selfsignedcertificate)