How to add PFS to socket server written in c and openssl - c

I try to add PFS (perfect forward secrecy) to my client-server application.
When I run a server with the following command:
openssl s_server -key ./key.pem -cert ./cert.pem -accept 443 -cipher ECDHE-RSA-AES128-SHA -tls1_2
I am able to connect with my client given the following ctx:
SSL_CTX* initCTX() {
SSL_METHOD *method;
SSL_CTX *ctx;
SSL_library_init();
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
method = TLSv1_2_client_method();
ctx = SSL_CTX_new(method);
if(ctx == NULL) {
ERR_print_errors_fp(stderr);
return NULL;
}
SSL_CTX_set_cipher_list(ctx, "ECDHE-RSA-AES128-SHA");
return ctx;
}
When I run my server application with the following ctx:
SSL_CTX* init_ssl_ctx() {
SSL_METHOD const *method;
SSL_CTX *ctx;
SSL_library_init();
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
method = TLSv1_2_server_method();
ctx = SSL_CTX_new(method);
if(ctx == NULL) {
ERR_print_errors_fp(stderr);
abort();
}
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
SSL_CTX_set_cipher_list(ctx, "ECDHE-RSA-AES128-SHA");
// ADDITIONAL CTX MODIFICATIONS TO ENABLE ECDHE
SSL_CTX_use_certificate_file(ctx, "./cert.pem", SSL_FILETYPE_PEM);
SSL_CTX_use_PrivateKey_file(ctx, "./key.pem", SSL_FILETYPE_PEM);
return ctx;
}
and try to connect with the client, then I get an no shared cipher error.
The private key has been created with openssl genrsa.
Well my question is: How do I have to modify the ctx to add ECDHE support. I guess that I have to select a curve and I probably need to create and exchange keys for every connection.
Do I still need the private key file? And when yes — what is it used for?

Well what I actually missed was the configuration of the Diffie-Hellman parameters and the Elliptic curve Diffie-Hellman. If you do not configure them ...
the PFS cipher suites will be silently ignored.
More information and examples on how to configure and include Diffie-Hellman parameters and Elliptic curve Diffie-Hellman in your C socket server can be found here: http://wiki.openssl.org/index.php/Diffie-Hellman_parameters

... then I get an no shared cipher error.
How do I have to modify the ctx to add ECDHE support?
The cipher suite is a product of both client and server capabilities. I find that I need to add 12 to 16 to ensure most clients can be accommodated.
Here's the cipher list I use. It includes the upcoming ChaCha and Poly cipher suites, and include the downlevel client suites. If you only want ECDHE, the rdeuce the list even further.
// *_CHACHA20_POLY1305 are 3x to 4x faster than existing cipher suites.
// http://googleonlinesecurity.blogspot.com/2014/04/speeding-up-and-strengthening-https.html
// Use them if available. Normative names can be found at (TLS spec depends on IPSec spec):
// http://tools.ietf.org/html/draft-nir-ipsecme-chacha20-poly1305-01
// http://tools.ietf.org/html/draft-mavrogiannopoulos-chacha-tls-02
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:"
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305:"
"TLS_ECDHE_ECDSA_WITH_CHACHA20_SHA:"
"TLS_ECDHE_RSA_WITH_CHACHA20_SHA:"
"TLS_DHE_RSA_WITH_CHACHA20_POLY1305:"
"TLS_RSA_WITH_CHACHA20_POLY1305:"
"TLS_DHE_RSA_WITH_CHACHA20_SHA:"
"TLS_RSA_WITH_CHACHA20_SHA:"
// Done with bleeding edge, back to TLS v1.2 and below
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:"
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:"
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:"
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:"
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:"
"TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:"
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:"
"TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:"
// TLS v1.0 (with some SSLv3 interop)
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA384:"
"TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:"
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA:"
"TLS_DHE_DSS_WITH_AES_128_CBC_SHA:"
"TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:"
"TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:"
"SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA:"
"SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA:"
// RSA key transport sucks, but they are needed as a fallback.
// For example, microsoft.com fails under all versions of TLS
// if they are not included. If only TLS 1.0 is available at
// the client, then google.com will fail too. TLS v1.3 is
// trying to deprecate them, so it will be interesteng to see
// what happens.
"TLS_RSA_WITH_AES_256_CBC_SHA256:"
"TLS_RSA_WITH_AES_256_CBC_SHA:"
"TLS_RSA_WITH_AES_128_CBC_SHA256:"
"TLS_RSA_WITH_AES_128_CBC_SHA:"
The ChaCha/Poly cipher suites are available in OpenSSL 1.0.2. So you can test Google's implementation if interested.
The RFCs do not specify who picks the cipher. By convention, the server usually honors the client's preference. To ensure your server picks the cipher suite, you should add SSL_OP_CIPHER_SERVER_PREFERENCE to the server's context options. See SSL_CTX_set_options(3).

Related

How to obtain the key agreement algorithm used in TLS 1.3 handshake using OpenSSL API?

I was modifying the example client code given on the OpenSSL s_client page to understand a few functions. I added the following lines after the chain verification part :
const SSL_CIPHER* curr_cipher = SSL_get_current_cipher(ssl);
printf("SSL_CIPHER_standard_name: %s\n", SSL_CIPHER_standard_name(curr_cipher));
printf("SSL_get_cipher_version: %s\n", SSL_get_cipher_version(ssl));
int kx_nid = SSL_CIPHER_get_kx_nid(curr_cipher);
printf("SSL_CIPHER_get_kx_nid: %d\n", kx_nid);
printf("OBJ_nid2ln(%d): %s\n", kx_nid, OBJ_nid2ln(kx_nid));
printf("SSL_CIPHER_description: %s\n", SSL_CIPHER_description(curr_cipher, NULL, 0));
Instead of getting the key exchange algorithm used, I got the following output after connecting to random.org using TLS 1.3 :
SSL_CIPHER_standard_name: TLS_AES_256_GCM_SHA384
SSL_get_cipher_version: TLSv1.3
SSL_CIPHER_get_kx_nid: 1063
OBJ_nid2ln(1063): kx-any
SSL_CIPHER_description: TLS_AES_256_GCM_SHA384 TLSv1.3 Kx=any Au=any Enc=AESGCM(256) Mac=AEAD
The man page for SSL_CIPHER_get_kx_nid says it returns NID_kx_any for TLS 1.3. How can I programatically get the actual key agreement (NID_kx_ecdhe) and authentication protocol (NID_auth_rsa) used in the handshake from the SSL object?

ESP-32 can't connect to MQTT broker: mqtt_client: Error network response

I am trying to connect my ESP32 which runs using the ESP-IDF framework to MQTT. I have imported this MQTT library successfully and have set up the configuration to look like this:
static void mqtt_app_start(void)
{
const esp_mqtt_client_config_t mqtt_cfg = {
// .host = "m15.cloudmqtt.com",
.uri = "mqtt://rxarkckf:smNb81Ppfe7T#m15.cloudmqtt.com:10793", // uri in the format (username:password#domain:port)
// .host = "m15.cloudmqtt.com", // config with host, port, user, password seperated
// .port = 10793,
// .username = "rxarkckf",
// .password = "smNb81Ppfe7T",
.event_handle = mqtt_event_handler,
// .user_context = (void *)your_context
};
esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
esp_mqtt_client_start(client);
}
I call mqtt_app_start(); in my app_main function. After uploading the code my ESP-32 doesn't connect to the MQTT broker and outputs this:
␛[0;32mI (12633410) MQTT_CLIENT: Sending MQTT CONNECT message, type: 1, id: 0000␛[0m
␛[0;31mE (12633710) MQTT_CLIENT: Error network response␛[0m
␛[0;32mI (12633710) MQTT_CLIENT: Error MQTT Connected␛[0m
␛[0;32mI (12633710) MQTT_CLIENT: Reconnect after 10000 ms␛[0m
␛[0;32mI (12633710) MQTT_SAMPLE: MQTT_EVENT_DISCONNECTED␛[0m
I have double checked that the values for the host, username, password, and port are all correct. When I look at the logs on the web interface hosted at cloudmqtt.com, I can see this output:
2018-11-17 03:50:53: New connection from 73.94.66.49 on port 10793.
2018-11-17 03:50:53: Invalid protocol "MQIs�" in CONNECT from 73.94.66.49.
2018-11-17 03:50:53: Socket error on client <unknown>, disconnecting.
2018-11-17 03:51:20: New connection from 73.94.66.49 on port 10793.
I had similar experience using mosquitto.
Adding this line to mqtt_config.h made my mqtt working.
#define CONFIG_MQTT_PROTOCOL_311
I think the more correct way to set this configuration is in sdkconfig.h, either manually or using "make menuconfig"
The problem is very simple. The library you are using implements the MQTT 3.1 protocol. The server you are trying to connect to implements the MQTT 3.1.1 protocol or higher.
As specified in the document (https://www.oasis-open.org/committees/download.php/55095/mqtt-diffs-v1.0-wd01.doc):
4.1 Protocol Name
The Protocol Name is present in the variable header of a MQTT CONNECT control packet. The Protocol Name is a UTF-8 encoded
string. In MQTT 3.1 the protocol name is "MQISDP". In MQTT 3.1.1 the
protocol name is represented as "MQTT".
For technical info:
https://mqtt.org/mqtt-specification/

Microsoft RPC SChannel not encrypted

I have been struggling to get a secure RPC client/server using Microsoft RPC. I am not using COM, just straight C.
I have created a Root CA certificate and created a certificate signed by this cert for the server. The certs are installed into cert stores.
RPC works fine unencrypted and it will even work with SCHANNEL setup, just not encrypted.
ServerCode:
RpcServerUseProtseqEpW(
L"ncacn_ip_tcp",
RPC_C_LISTEN_MAX_CALLS_DEFAULT,
port,
NULL);
CertOpenStore(
CERT_STORE_PROV_SYSTEM,
0,
(HCRYPTPROV)NULL,
CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_READONLY_FLAG,
L"MY")
CertFindCertificateInStore(
hStore,
X509_ASN_ENCODING|PKCS_7_ASAN_ENCODING,
CERT_FIND_SUBJEECT_STR,
subject,
NULL);
RpcCertGeneratePrincipalNameW(
ccert_ctx_server,
RPC_C_FULL_CERT_CHAIN,
serverPrincName);
schannel.dwVersion = SCHANNEL_CRED_VERSION;
schannel.cCreds = 1;
schannel.paCred = &ccert_ctx_server;
RpcServerRegisterAuthInfoW(
serverPrincName,
RPC_C_AUTHN_GSS_SCHANNEL,
NULL,
&schannel);
RpcServerRegisterIf2(
h_v1_ifspec,
NULL,
NULL,
RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH // seems to be required by SCHANNEL
RPC_C_LISTEN_MAX_CALLS_DEFAULT,
(unsigned)-1,
SecurityCallback); SecurityCallback returns RPC_S_OK
Client Code:
RpcStringBindingComposeW(
NULL,
L"ncacn_ip_tcp",
address,
port,
NULL,
&stringBinding);
RpcBindingFronStringBindingW(
stringBinding,
hBind);
CertOpenStore(
CERT_STORE_PROV_SYSTEM,
0,
(HCRYPTPROV)NULL,
CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_READONLY_FLAG,
L"MY")
CertFindCertificateInStore(
hStore,
X509_ASN_ENCODING|PKCS_7_ASAN_ENCODING,
CERT_FIND_SUBJEECT_STR,
subject,
NULL);
RpcCertGeneratePrincipalNameW(
ccert_ctx_server,
RPC_C_FULL_CERT_CHAIN,
serverPrincName);
schannel.dwVersion = SCHANNEL_CRED_VERSION;
schannel.cCreds = 1;
schannel.paCred = &ccert_ctx_client;
RpcServerRegisterAuthInfoW(
serverPrincName,
RPC_C_AUTHN_GSS_SCHANNEL,
NULL,
&schannel);
RpcBindingSetAuthInfo(
hBind,
serverPrincName,
RPC_C_AUTHN_LEVEL_PKT_INTEGRITY,
RPC_C_AUTHN_GSS_SCHANNEL,
&schannelCred,
RPC_C_AUTHZ_NONE)
Given all of this RPC will work, but it will not be encrypted as verified with wireshark. I have worked this with a very minimal SCHANNEL structure definition and not using qos structure. Nothing makes much difference. the only thing that really makes a difference is if I change RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH to RPC_IF_ALLOW_SECURE_ONLY. Then I get an access denied when making an RPC call. From what I understand this is the normal functionality of SCHANNEL and you must provide your own authentication within the Security Callback.
When I call RpcBindingInqAuthClient in my security callback I recieve the error 1746: The binding does not contain any authenticatioin information.
I have looked through MSDN, a the few various links scattered on the web, but there is little to know help on getting SCHANNEL working.
My choice for SCHANNEL is I can't rely on kerberos or ntlm. I am running tcp over the internet so certificates are what work for me. I can't use http because I can't setup IIS on my server, DCE seems to be even less documented than schannel.
Thanks!

SSL Cipher help in C

I am trying to Use SSL on top of tcp/ip to send an HTTPS request to a site using C. I have no access to curl or other standard libraries. Pretend like i can't load any libraries at all.
I need to set an SSL Profile Cipher. When I successfully use curl on my linux box to talk with the server I see: SSL Connection using ECDHE-RSA-AES128-SHA
If my options for setting the cipher are:
SSL_kRSA (RSA Key Exchange)
SSL_kEDH (tmp DH key no DH cert)
SSL_aRSA (Authenticate with RSA)
SSL-aDSS (Authenticate with DSS)
SSL_DES (DES)
SSL_3DES (3DES)
SSL_RC4 (RC4)
SSL_RC2 (RC2)
SSL_AES (AES)
SSL_MD5 (MD5)
SSL_SHA1 (SHA1)
SSL_SHA256 (SHA256)
SSL_SHA384 (SHA384)
SSL_RSA ([SSL_kRSA|SSL_aRSA] RSA)
SSL_DSS ([SSL_aDSS] Authenticate with DSS)
I can set multiple things by something like:
SSL_RSA | SSL_AES
Protocol is TLSv1.2
What should my cipher look like?
"Pretend like i can't load any libraries at all." If that is true, you will need to implement the cipher itself plus the SSL handling layer ^_^.
Assuming you are using OpenSSL and have TCP established with socket_fd, you need to create a SSL_CTX with SSL_CTX_new (SSLv23_client_method()). Normally, to set the cipher list, you use SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL:!eNULL:#STRENGTH"), see http://openssl.org/docs/apps/ciphers.html for all available options, you may specific a particular cipher.
Then create a SSL session with SSL_new(ctx) and SSL_set_fd (ssl, socket_fd), after that use SSL_connect(...), SSL_read(...)/SSL_write(...) to communicate with server.
After all have been done, SSL_shutdown(...) and SSL_Free(...), SSL_CTX_Free(...).

How to run SSL with mongoose webserver API

I use the Mongoose webserver API in C to implement a small HTTP API. It's working fine so far but if I want to use SSL, the server rejects the requests.
To generate the ssl key and certificate, I used the following approach:
# openssl version
OpenSSL 1.0.0-beta2 21 Apr 2009
# openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 1000 -nodes
# cat key.pem > ssl.pem; cat cert.pem >> ssl.pem
According to this manual, mongoose wants the key and cert in one file.
I also set the NS_ENABLE_SSL compile flag to enable the ssl part of mongoose.
To initialize mongoose I do the following in my C source:
struct mg_server *server;
server = mg_create_server(NULL, ev_handler);
printf("%s",mg_set_option(server, "document_root", "/var/www"));
printf("%s",mg_set_option(server, "listening_port", "80"));
printf("%s",mg_set_option(server, "ssl_certificate", "ssl.pem"));
My event handler does nothing but logging the requests on the console at the moment:
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
switch(ev){
case MG_AUTH: printf("MG_AUTH event handler called with uri = '%s'\n", conn->uri);
break;
case MG_REQUEST:
printf("MG_REQUEST event handler called with uri = '%s'\n", conn->uri);
break;
case MG_POLL:
printf("MG_POLL event handler called with uri = '%s'\n", conn->uri);
break;
case MG_HTTP_ERROR:
printf("MG_HTTP_ERROR event handler called with uri = '%s'\n", conn->uri);
break;
default:
DBG("MG_CLOSE event handler called with uri = '%s'\n", conn->uri);
break;
}
return MG_FALSE;
}
If I try to open a HTML file from the document root in the browser, the server closes the connection without delivering content.
Console output:
(null)
(null)
(null)
MG_POLL event handler called with uri = '(null)'
MG_CLOSE event handler called with uri = '(null)'
The first three (null) are the return values from the mg_set_option which are NULL if no problem occurs while setting an option.
What's going wrong here?
Make sure you're using "https://my.ip:80" when connecting to the server. Otherwise, browser is going to use HTTP protocol, and mongoose will reject the connection because it expects SSL handshake, and gets plain HTTP data. I think that is what's going on.
You're returning MG_FALSE on MG_AUTH event, which tells mongoose "connection is not authenticated, close it immediately."
Solution:
return MG_TRUE on MG_AUTH event. For other events, returning MG_FALSE is fine.
use listening_port 443, and https:// when connecting to the server

Resources