I have set up my client-server communication using OpenSSL and my server is sending it's certificate. Now, I want to make my client send a certificate to the server as well. On my client side, i have the following code:
ctx = InitCTX();
LoadCertificates(ctx, "clientCert.pem", "clientCert.pem"); /* load certs */
server = OpenConnection(hostname, atoi(portnum));
ssl = SSL_new(ctx); /* create new SSL connection state */
SSL_set_fd(ssl, server); /* attach the socket descriptor */
and this is my LoadCertificates function:
void LoadCertificates(SSL_CTX* ctx, char* CertFile, char* KeyFile)
{
if ( SSL_CTX_use_certificate_file(ctx, CertFile, SSL_FILETYPE_PEM) <= 0 )
{
ERR_print_errors_fp(stderr);
abort();
}
/* set the private key from KeyFile (may be the same as CertFile) */
if ( SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0 )
{
ERR_print_errors_fp(stderr);
abort();
}
/* verify private key */
if ( !SSL_CTX_check_private_key(ctx) )
{
fprintf(stderr, "Private key does not match the public certificate\n");
abort();
}
printf("Certificate attached.\n");
}
I have the same LoadCertificates function on the server side, and that seems to be working perfectly.
However, my client-side certificate is not getting detected on the server side. Is there anything different I need to do on the client side to send a certificate across?
I made modifications to the client code using the code from here as base: http://simplestcodings.blogspot.in/2010/08/secure-server-client-using-openssl-in-c.html
... my client-side certificate is not getting detected on the server side. Is there anything different I need to do on the client side to send a certificate across?
The server needs to call SSL_CTX_set_client_CA_list. It tells the server to send a list of Distinguished Names (DN) which it accepts for client authentication.
The server will need to call SSL_CTX_load_verify_locations (or friends). This is where the server actually trusts the CAs in the list sent to the client.
Finally, the server should call SSL_CTX_set_verify and set both SSL_VERIFY_PEER and SSL_VERIFY_FAIL_IF_NO_PEER_CERT.
I guess that the certificate is sent but was not accepted by the server, that is it could not be verified against the trusted CA at the server. The error message on the server side about no certificate returned might be misleading, see https://stackoverflow.com/a/27951490/3081018.
I would suggest to check with wireshark or openssl s_server to see if the certificate is not sent or if it is just not accepted by the peer.
Related
I want to connect two F746ZG boards so that they can communicate via TCP. I am using the STM implementation of LwIP with the netconn API. The IP address is supplied via DHCP, but it is always the same address. Also, the address matches the expected value. The problem I am facing is that the client seemingly can't establish a connection. I am binding the connection to port 8880. Since I ran into this issue, I have written a debug client that should just periodically send a predefined message to a server. Here is the code for the client:
static void tcpecho_client_thread(void const *arg)
{
struct netconn *xNetConn = NULL;
err_t bind_err, connect_err;
char* b_data = "OK"; // Data to be sent
uint16_t b_len = sizeof ( b_data );
IP4_ADDR(&local_ip, IP_ADDR0_CLIENT, IP_ADDR1_CLIENT, IP_ADDR2_CLIENT, IP_ADDR3_CLIENT);
IP4_ADDR(&pc_ip, IP_ADDR0_PC, IP_ADDR0_PC, IP_ADDR2_PC, IP_ADDR3_PC);
xNetConn = netconn_new ( NETCONN_TCP );
if (xNetConn != NULL){
bind_err = netconn_bind ( xNetConn, &local_ip, TCP_PORT_NETCONN );
if(bind_err == ERR_OK){
// Try to connect to server
for(;;){
connect_err = netconn_connect ( xNetConn, &pc_ip, TCP_PORT_NETCONN);
if (connect_err == ERR_OK){
// We are connected
while(1){
BSP_LED_On(LED1);
netconn_write(xNetConn, b_data, b_len, NETCONN_COPY);
vTaskDelay(1000); // To see the result easily in Comm Operator
}
}
}
}else{
// Failed to bind the connection
BSP_LED_On(LED3);
}
}else{
// Failed to allocate a new connection
BSP_LED_On(LED3);
}
}
When I debug this, netconn_connect never manages to actually connect to something. Since I am able to ping the board and get a response, I am confused, what is going wrong here. I have tried to use Hercules to set up a TCP server on my PC so that the board can connect to that, but that also doesn't work. Using Wireshark, I can see the responses to my ping command coming in, but I don't see anything that would indicate the board trying to connect to my PC.
I have tested the corresponding server on the second board, but that runs fine. I can connect to it with Hercules and send data, so I doubt there is anything fundamentally wrong with the LwIP stack.
What I could guess is that I messed up the netconn_bind, I am not 100% sure what IP you are supposed to bind the connection to. The way it currently is, is how I read the documentation. For the server, I have bound it to IP_ADDR_ANY. Besides that, my implementation mostly matches with the examples you can find online (e.g. LwIP Wiki).
I have figured out the problem. After I delete the netconn_bind call, everything works fine for me.
I'm hoping someone can help with my OpenSSL(1.1.1k) issue I'm having when trying to validate a certificate on the client against a server. The certificate contents are specified in text and not in a .PEM file on the client side. Some sample code is here:
bool setCert(SSL_CTX* ctx, LPCSTR cert)
{
//cert is the text of the certificate e.g. -----BEGIN CERTIFICATE----- blah -----END CERTIFICATE-----
bool bOk = true;
if (!cert.empty()) // no cert on disk
{
BIO *bioCert = BIO_new_mem_buf((void*)cert, -1);
X509* pCert = PEM_read_bio_X509(bioCert, NULL, 0, NULL);
if (SSL_CTX_use_certificate(ctx, pCert) != 1)
{
//log errror, "Invalid certificate"
bOk = false;
}
BIO_free_all(bioCert);
X509_free(pCert);
}
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3); // other options not used atm SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
return bOk;
}
After this method returns, I create an SSL object based on the context, with SSL_new(ctx).
I then call SSL_Connect(ssl)
The handshake occurs and is accepted even if I pass in an invalid certificate.
My assumption here is that it's doing this because I do not set the verify method like:
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
My understanding is that I cannot use SSL_VERIFY_PEER unless there is a cert / .PEM file on disk on the client side to validate against.
How can I validate the handshake when keeping the certificate contents in memory?
If I change the code to use a PEM file, call SSL_CTX_use_certificate_file instead of SSL_CTX_use_certificate, it works. In that case I did set SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
Any help here is greatly appreciated. Thanks
SSL_CTX_use_certificate is used for setting up the certificate which should be used locally to authenticate against the peer. It needs to be accompanied with a private key, setup with SSL_CTX_use_PrivateKey. But authenticating against a peer, i.e. using a client certificate, is not what you intend to do in your client.
Instead you want to setup a certificate for use as a trusted CA when validating the server certificate. The correct function for this would be X509_STORE_add_cert instead:
X509_STORE_add_cert(SSL_CTX_get_cert_store(ctx), pCert);
I am writing a client-side FTP program, and so far, after a successful connection the server will run in extended passive mode. Using the port number returned from the EPSV command, I can create client-side sockets like this:
void create_data_channel() {
if ((data_sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("Cannot create client socket for data connection :(");
exit(1);
}
data_server_addr.sin_family = AF_INET;
data_server_addr.sin_port = htons(port);
data_server_addr.sin_addr = *((struct in_addr *)ftp_server->h_addr);
bzero(&(data_server_addr.sin_zero),8);
// Connect to the ftp server at given port for data connection
if (connect(data_sock, (struct sockaddr *)&data_server_addr,
sizeof(struct sockaddr)) == -1) {
perror("Cannot connect to the ftp server for data connection :(");
exit(1);
}
}
Now, whenever I want to send a command involving the data channel (e.g. LIST), I can first open a new socket using the method above, and get/send whatever data I need from/to the ftp server. Then, I close the data connection using close(data_sock).
This works well for the first LIST command. However, if I were to try to run two or more LIST command, the program fails with my error message "Cannot connect to the ftp server for data connection :(". Why is this so? What am I missing here?
Typically a FTP server does not accept multiple connections to the same dynamic port. Therefore the PASV or EPSV commands need to be done before each data transfer so that the server creates a new listen socket and returns its port number to the client.
I'm using the Eclipse Paho MQTT C client to connect to a mosquitto broker with TLS using openssl. This is part of my code:
MQTTClient client;
MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
MQTTClient_message pubmsg = MQTTClient_message_initializer;
MQTTClient_SSLOptions sslOptions = MQTTClient_SSLOptions_initializer;
MQTTClient_deliveryToken token;
int rc;
MQTTClient_create(&client, ADDRESS, CLIENTID,
MQTTCLIENT_PERSISTENCE_NONE, NULL);
conn_opts.keepAliveInterval = 20;
conn_opts.cleansession = 1;
/* TLS */
sslOptions.enableServerCertAuth = 0;
sslOptions.trustStore = "ca_rsp.crt";
conn_opts.ssl = &sslOptions;
if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS)
{
printf("Failed to connect, return code %d\n", rc);
exit(EXIT_FAILURE);
}
Actually every time I reconnect to the broker, the client make a full handshake. I would like to use the TLS session resumption to reduce the overhead. I've search around the web but I haven't found any example of how o implement that in a simple way.
Any suggestion?
Thanks
This came up recently on the mosquitto dev mailing list here https://dev.eclipse.org/mhonarc/lists/mosquitto-dev/msg01606.html
The following excerpt seams to imply it may not be possible just yet with the code as it is.
How can I use Mosquitto / OpenSSL C API to leverage session tickets in an
MQTT C client ?
Not at the moment, this needs code changes that are a bit more
involved - it looks like we need to use SSL_set_session() to apply a
saved session to your client and SSL_CTX_sess_set_new_cb() to save the
session out.
Is there any way I could persist session tickets on the clients, so they
would remain valid across reboot ?
With the above changes, yes.
Make conn_opts.cleansession = 0;
Disabling the cleansession flag in PAHO-client programs enables session resumption.
I have already verified it with wireshark.
With session Resumption, 1st packet transmission
We can see 4 times communication between server and client in 1 image and even certificates are transferred.
With session Resumption ,screenshot taken for 2nd packet transmission
Observe both images carefully , there is only 3 times communication between server and client in2 image, hence the server negotiates not to perform full handshake.
Session resumption time limit is 7200 seconds.
But setting the cleansession flag to 1 will always perform full handshake which means no session resumption.
I feel it was a good decision taken by PAHO people who made clean session flag linked with session resumption because mosquitto client provided in github lacks this inbuilt feature of session resumption.
Go through the specification of MQTT v3.1.1
Or refer MQTT specification in their website
I want to get multiple connections with OpenSSL.
Should I create new SSL_CTX context for every new connection or accept all connections with one context?
Should I do additional actions with memory or something with start/stop connection, except
close(_socket); //socket which accept the connection
SSL_shutdown(_ssl); //_ssl — SSL connection
SSL_free (_ssl);
Should I create new SSL_CTX context for every new connection or accept all connections with one context?
It depends on the number of server names and distinct certificates.
If you have one server name and one certificate, then use one default context.
If you have multiple server names and one certificate, then use one default context.
If you have multiple server names and multiple certificate, then see below on the SNI or servername callback and swapping in a context.
If your server listens for foo.com and bar.com with distinct certifcates, then you will need three contexts. One default context is for non-SNI clients, one context is for foo.com, and one context is for bar.com.
Effectively, the only thing that will likely change between the two sites is the certificate that's served. So you listen with the default context. If the client provided a servername via SNI, then you swap-in one of the other two contexts in the servername callback and SSL_set_SSL_CTX. Here's how it would look:
static int ServerNameCallback(SSL *ssl, int *ad, void *arg)
{
UNUSED(ad);
UNUSED(arg);
ASSERT(ssl);
if (ssl == NULL)
return SSL_TLSEXT_ERR_NOACK;
const char* servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
ASSERT(servername && servername[0]);
if (!servername || servername[0] == '\0')
return SSL_TLSEXT_ERR_NOACK;
/* Need a certificate and context for this domain */
SSL_CTX* ctx = GetServerContext(servername);
ASSERT(ctx != NULL);
if (ctx == NULL)
return SSL_TLSEXT_ERR_NOACK;
/* We should not be peeking into the object like this... */
ASSERT(ctx != ssl->ctx);
/* Useless return value */
SSL_CTX* v = SSL_set_SSL_CTX(ssl, ctx);
return SSL_TLSEXT_ERR_OK;
}
GetServerContext just provides the context for foo.com or bar.com. It creates them once, and then reuses the same ones.
For the default context, you set the servername callback with SSL_CTX_set_tlsext_servername_callback. There's no need to set it for non-default contexts.
SSL_CTX_set_tlsext_servername_callback(ctx, ServerNameCallback);
Contexts are referenced counted, so you can reuse them.
Should I do additional actions with memory or something with
start/stop connection, except
close(_socket); //socket which accept the connection
SSL_shutdown(_ssl); //_ssl — SSL connection
SSL_free (_ssl);
Here, you should seek a treatment of the subject. For example, you should not call close before SSL_shutdown. And the first call to SSL_shutdown may fail, so you need to know what to do next.
For a treatment of the subject, see Eric Rescorla's tutorials: An Introduction to OpenSSL Programming, Part I of II and An Introduction to OpenSSL Programming, Part II of II. Or get the book: Network Security with OpenSSL.