Self sign with SAN with OpenSSL in Chrome and Firefox - c++

I created a minimal code that serves as an HTTPS server with CA created as follows:
$ openssl req -config openssl.conf -x509 -sha256 -nodes -extensions v3_ca -days 3650 -subj '/CN=OpenSSL CA/O=Example Company/C=SE' -newkey rsa:4096 -keyout ca.key -out ca.pem
I added the CA to the Trusted Certificate in Chrome and Firefox, and I added a new line to hosts file to resolve test.com as 127.0.0.1.
In Firefox it works as expected, but Chrome returns a 'NET::ERR_CERT_COMMON_NAME_INVALID
' error. It makes sense that Chrome starting version 58 no longer supports a common name and instead needs to use subjectAltName.
So I added a snippet of code (currently comment-out) to support this, then Firefox returns the error SEC_ERROR_BAD_SIGNATURE, and Chrome returns the NET::ERR_CERT_AUTHORITY_INVALID.
#include <openssl/err.h>
#include <openssl/conf.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
#include <openssl/ssl.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <openssl/x509v3.h>
#define RSA_KEY_BITS (4096)
#define REQ_DN_C "SE"
#define REQ_DN_ST ""
#define REQ_DN_L ""
#define REQ_DN_O "Example Company"
#define REQ_DN_OU ""
#define REQ_DN_CN "test.com"
static void crt_to_pem(X509 *crt, uint8_t **crt_bytes, size_t *crt_size);
static int generate_key_csr(EVP_PKEY **key, X509_REQ **req);
static int generate_set_random_serial(X509 *crt);
static int generate_signed_key_pair(EVP_PKEY *ca_key, X509 *ca_crt, EVP_PKEY **key, X509 **crt);
static void key_to_pem(EVP_PKEY *key, uint8_t **key_bytes, size_t *key_size);
static int load_ca(const char *ca_key_path, EVP_PKEY **ca_key, const char *ca_crt_path, X509 **ca_crt);
static void print_bytes(uint8_t *data, size_t size);
int create_socket() {
int sockfd, portno;
char buffer[256];
struct sockaddr_in serv_addr{}, cli_addr{};
int n;
/* First call to socket() function */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("ERROR opening socket");
exit(1);
}
portno = 8000;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
/* Now bind the host address using bind() call.*/
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR on binding");
exit(1);
}
listen(sockfd,5);
return sockfd;
}
SSL_CTX *create_context()
{
const SSL_METHOD *method;
SSL_CTX *ctx;
method = TLS_server_method();
ctx = SSL_CTX_new(method);
if (!ctx) {
exit(EXIT_FAILURE);
}
return ctx;
}
int main(int argc, char **argv)
{
char *ca_key_path = "ca.key";
char *ca_crt_path = "ca.pem";
/* Load CA key and cert. */
EVP_PKEY *ca_key = NULL;
X509 *ca_crt = NULL;
if (!load_ca(ca_key_path, &ca_key, ca_crt_path, &ca_crt)) {
fprintf(stderr, "Failed to load CA certificate and/or key!\n");
return 1;
}
/* Generate keypair and then print it byte-by-byte for demo purposes. */
EVP_PKEY *key = NULL;
X509 *crt = NULL;
int ret = generate_signed_key_pair(ca_key, ca_crt, &key, &crt);
if (!ret) {
fprintf(stderr, "Failed to generate key pair!\n");
return 1;
}
/*
X509_EXTENSION *cert_ex = X509V3_EXT_conf_nid(nullptr, nullptr, NID_subject_alt_name, "DNS:test.com");
X509_add_ext(crt, cert_ex, -1);
*/
/* Convert key and certificate to PEM format. */
uint8_t *key_bytes = NULL;
uint8_t *crt_bytes = NULL;
size_t key_size = 0;
size_t crt_size = 0;
key_to_pem(key, &key_bytes, &key_size);
crt_to_pem(crt, &crt_bytes, &crt_size);
/* Print key and certificate. */
print_bytes(key_bytes, key_size);
print_bytes(crt_bytes, crt_size);
auto ctx = create_context();
auto lfd = create_socket();
while (true) {
int clilen = 0;
struct sockaddr_in serv_addr{}, cli_addr{};
auto s = accept(lfd, (struct sockaddr *)&cli_addr, reinterpret_cast<socklen_t *>(&clilen));
auto ssl = SSL_new(ctx);
SSL_set_fd(ssl, s);
SSL_use_certificate(ssl, crt);
SSL_use_PrivateKey(ssl, key);
SSL_accept(ssl);
char msg_200ok[] = "HTTP/1.1 404 \n"
"Content-Length: 0\n\n";
SSL_write(ssl, msg_200ok, sizeof msg_200ok - 1);
}
/* Free stuff. */
EVP_PKEY_free(ca_key);
EVP_PKEY_free(key);
X509_free(ca_crt);
X509_free(crt);
free(key_bytes);
free(crt_bytes);
return 0;
}
void crt_to_pem(X509 *crt, uint8_t **crt_bytes, size_t *crt_size)
{
/* Convert signed certificate to PEM format. */
BIO *bio = BIO_new(BIO_s_mem());
PEM_write_bio_X509(bio, crt);
*crt_size = BIO_pending(bio);
*crt_bytes = (uint8_t *)malloc(*crt_size + 1);
BIO_read(bio, *crt_bytes, *crt_size);
BIO_free_all(bio);
}
int generate_signed_key_pair(EVP_PKEY *ca_key, X509 *ca_crt, EVP_PKEY **key, X509 **crt)
{
/* Generate the private key and corresponding CSR. */
X509_REQ *req = NULL;
if (!generate_key_csr(key, &req)) {
fprintf(stderr, "Failed to generate key and/or CSR!\n");
return 0;
}
/* Sign with the CA. */
*crt = X509_new();
X509_set_version(*crt, 2); /* Set version to X509v3 */
/* Generate random 20 byte serial. */
generate_set_random_serial(*crt);
/* Set issuer to CA's subject. */
X509_set_issuer_name(*crt, X509_get_subject_name(ca_crt));
/* Set validity of certificate to 2 years. */
X509_gmtime_adj(X509_get_notBefore(*crt), 0);
X509_gmtime_adj(X509_get_notAfter(*crt), (long)2*365*24*3600);
/* Get the request's subject and just use it (we don't bother checking it since we generated
* it ourself). Also take the request's public key. */
X509_set_subject_name(*crt, X509_REQ_get_subject_name(req));
EVP_PKEY *req_pubkey = X509_REQ_get_pubkey(req);
X509_set_pubkey(*crt, req_pubkey);
EVP_PKEY_free(req_pubkey);
/* Now perform the actual signing with the CA. */
if (X509_sign(*crt, ca_key, EVP_sha256()) == 0) goto err;
X509_REQ_free(req);
return 1;
err:
EVP_PKEY_free(*key);
X509_REQ_free(req);
X509_free(*crt);
return 0;
}
int generate_key_csr(EVP_PKEY **key, X509_REQ **req)
{
*key = NULL;
*req = NULL;
RSA *rsa = NULL;
BIGNUM *e = NULL;
*key = EVP_PKEY_new();
*req = X509_REQ_new();
rsa = RSA_new();
e = BN_new();
BN_set_word(e, 65537);
RSA_generate_key_ex(rsa, RSA_KEY_BITS, e, NULL);
EVP_PKEY_assign_RSA(*key, rsa);
X509_REQ_set_pubkey(*req, *key);
/* Set the DN of the request. */
X509_NAME *name = X509_REQ_get_subject_name(*req);
X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (const unsigned char*)REQ_DN_C, -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "ST", MBSTRING_ASC, (const unsigned char*)REQ_DN_ST, -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "L", MBSTRING_ASC, (const unsigned char*)REQ_DN_L, -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (const unsigned char*)REQ_DN_O, -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "OU", MBSTRING_ASC, (const unsigned char*)REQ_DN_OU, -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (const unsigned char*)REQ_DN_CN, -1, -1, 0);
/* Self-sign the request to prove that we posses the key. */
if (!X509_REQ_sign(*req, *key, EVP_sha256())) goto err;
BN_free(e);
return 1;
err:
EVP_PKEY_free(*key);
X509_REQ_free(*req);
RSA_free(rsa);
BN_free(e);
return 0;
}
int generate_set_random_serial(X509 *crt)
{
/* Generates a 20 byte random serial number and sets in certificate. */
unsigned char serial_bytes[20];
if (RAND_bytes(serial_bytes, sizeof(serial_bytes)) != 1) return 0;
serial_bytes[0] &= 0x7f; /* Ensure positive serial! */
BIGNUM *bn = BN_new();
BN_bin2bn(serial_bytes, sizeof(serial_bytes), bn);
ASN1_INTEGER *serial = ASN1_INTEGER_new();
BN_to_ASN1_INTEGER(bn, serial);
X509_set_serialNumber(crt, serial); // Set serial.
ASN1_INTEGER_free(serial);
BN_free(bn);
return 1;
}
void key_to_pem(EVP_PKEY *key, uint8_t **key_bytes, size_t *key_size)
{
/* Convert private key to PEM format. */
BIO *bio = BIO_new(BIO_s_mem());
PEM_write_bio_PrivateKey(bio, key, NULL, NULL, 0, NULL, NULL);
*key_size = BIO_pending(bio);
*key_bytes = (uint8_t *)malloc(*key_size + 1);
BIO_read(bio, *key_bytes, *key_size);
BIO_free_all(bio);
}
int load_ca(const char *ca_key_path, EVP_PKEY **ca_key, const char *ca_crt_path, X509 **ca_crt)
{
BIO *bio = NULL;
*ca_crt = NULL;
*ca_key = NULL;
/* Load CA public key. */
bio = BIO_new(BIO_s_file());
if (!BIO_read_filename(bio, ca_crt_path)) goto err;
*ca_crt = PEM_read_bio_X509(bio, NULL, NULL, NULL);
if (!*ca_crt) goto err;
BIO_free_all(bio);
/* Load CA private key. */
bio = BIO_new(BIO_s_file());
if (!BIO_read_filename(bio, ca_key_path)) goto err;
*ca_key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
if (!ca_key) goto err;
BIO_free_all(bio);
return 1;
err:
BIO_free_all(bio);
X509_free(*ca_crt);
EVP_PKEY_free(*ca_key);
return 0;
}
void print_bytes(uint8_t *data, size_t size)
{
for (size_t i = 0; i < size; i++) {
printf("%c", data[i]);
}
}
What is the problem? How can this be debugged?
Thanks

You are trying to add the extension to the certificate after it was signed. This makes the CA's signature invalid. You have to do that before signing, e.g.:
X509_EXTENSION *cert_ex = X509V3_EXT_conf_nid(nullptr, nullptr, NID_subject_alt_name, "DNS:test.com");
X509_add_ext(*crt, cert_ex, -1);
/* Now perform the actual signing with the CA. */
if (X509_sign(*crt, ca_key, EVP_sha256()) == 0) goto err;
With that it works for me both on Firefox and Chrome.

Related

OpenSSL verify server certificate

I am new in openssl and i have TLS client code but doubt about certificate validation. In this code nothing about certificate validation. It is hidden process which executed behind the scene or just code is bad?
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include <sys/socket.h>
#include <resolv.h>
#include <netdb.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#define FAIL -1
int OpenConnection(const char *hostname, int port)
{ int sd;
struct hostent *host;
struct sockaddr_in addr;
if ( (host = gethostbyname(hostname)) == NULL )
{
perror(hostname);
abort();
}
sd = socket(PF_INET, SOCK_STREAM, 0);
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = *(long*)(host->h_addr);
if ( connect(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 )
{
close(sd);
perror(hostname);
abort();
}
return sd;
}
SSL_CTX* InitCTX(void)
{ SSL_METHOD *method;
SSL_CTX *ctx;
OpenSSL_add_all_algorithms(); /* Load cryptos, et.al. */
SSL_load_error_strings(); /* Bring in and register error messages */
method = TLSv1_2_client_method(); /* Create new client-method instance */
ctx = SSL_CTX_new(method); /* Create new context */
if ( ctx == NULL )
{
ERR_print_errors_fp(stderr);
abort();
}
return ctx;
}
void ShowCerts(SSL* ssl)
{ X509 *cert;
char *line;
cert = SSL_get_peer_certificate(ssl); /* get the server's certificate */
if ( cert != NULL )
{
printf("Server certificates:\n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("Subject: %s\n", line);
free(line); /* free the malloc'ed string */
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("Issuer: %s\n", line);
free(line); /* free the malloc'ed string */
X509_free(cert); /* free the malloc'ed certificate copy */
}
else
printf("Info: No client certificates configured.\n");
}
int main(int count, char *strings[])
{ SSL_CTX *ctx;
int server;
SSL *ssl;
char buf[1024];
int bytes;
char *hostname, *portnum;
if ( count != 3 )
{
printf("usage: %s <hostname> <portnum>\n", strings[0]);
exit(0);
}
SSL_library_init();
hostname=strings[1];
portnum=strings[2];
ctx = InitCTX();
server = OpenConnection(hostname, atoi(portnum));
ssl = SSL_new(ctx); /* create new SSL connection state */
SSL_set_fd(ssl, server); /* attach the socket descriptor */
if ( SSL_connect(ssl) == FAIL ) /* perform the connection */
ERR_print_errors_fp(stderr);
else
{ char *msg = "Hello???";
printf("Connected with %s encryption\n", SSL_get_cipher(ssl));
ShowCerts(ssl); /* get any certs */
SSL_write(ssl, msg, strlen(msg)); /* encrypt & send message */
bytes = SSL_read(ssl, buf, sizeof(buf)); /* get reply & decrypt */
buf[bytes] = 0;
printf("Received: \"%s\"\n", buf);
SSL_free(ssl); /* release connection state */
}
close(server); /* close socket */
SSL_CTX_free(ctx); /* release context */
return 0;
}
Hope anyone read this code and give me some information for understanding certificate validation processes.

TLS 1.2 implementation in C++ Windows Application using OpenSSL

I have a windows application that works as a client and installed on multiple device and they can communicate with each other, Now I want to encrypt the communication. I tried to found out some methods like Microsoft's SSPI library and SChannel but didn't find an easy implementation, So I am trying with the OpenSSL.
I am trying to create a client and server using winsocket and enabling TLS communication using OpenSSL in Visual Studio 2019. THe problem is it gets connect to the server, the server prints the message connected to client and also client code show connected, but on SSL_Connect method it fails and shows no error in the client code! I also created a normal TCP socket server and try to connect it to this as well than also it fails after connecting.
What is going wrong! Here is my code:
Client:
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <errno.h>
#include <string.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <WS2tcpip.h>
#include <string>
#include <iostream>
#pragma comment (lib, "ws2_32.lib")
#define FAIL -1
//Added the LoadCertificates how in the server-side makes.
void LoadCertificates(SSL_CTX* ctx, char* CertFile, char* KeyFile)
{
/* set the local certificate from CertFile */
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();
}
}
int OpenConnection(const char* hostname, int port)
{
int sd;
struct hostent* host;
struct sockaddr_in addr;
WSAData data;
WORD ver = MAKEWORD(2, 2);
int wsResult = WSAStartup(ver, &data);
if (wsResult != 0)
{
printf("winsock error");
return 0;
}
if ((host = gethostbyname(hostname)) == NULL)
{
perror(hostname);
abort();
}
sd = socket(PF_INET, SOCK_STREAM, 0);
ZeroMemory(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = *(long*)(host->h_addr);
if (connect(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0)
{
closesocket(sd);
perror(hostname);
abort();
}
return sd;
}
SSL_CTX* InitCTX(void)
{
const SSL_METHOD* method = TLS_client_method(); /* Create new client-method instance */
SSL_CTX* ctx;
OpenSSL_add_all_algorithms(); /* Load cryptos, et.al. */
SSL_load_error_strings(); /* Bring in and register error messages */
// method = SSLv3_client_method(); /* Create new client-method instance */
ctx = SSL_CTX_new(method); /* Create new context */
if (ctx == NULL)
{
ERR_print_errors_fp(stderr);
abort();
}
return ctx;
}
void ShowCerts(SSL* ssl)
{
X509* cert;
char* line;
cert = SSL_get_peer_certificate(ssl); /* get the server's certificate */
if (cert != NULL)
{
printf("Server certificates:\n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("Subject: %s\n", line);
free(line); /* free the malloc'ed string */
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("Issuer: %s\n", line);
free(line); /* free the malloc'ed string */
X509_free(cert); /* free the malloc'ed certificate copy */
}
else
printf("No certificates.\n");
}
int main()
{
SSL_CTX* ctx;
int server;
SSL* ssl;
char buf[1024];
int bytes;
char hostname[] = "127.0.0.1";
char portnum[] = "54000";
char CertFile[] = "C:/Users/cert/Documents/testing/ec_crt.pem";
char KeyFile[] = "C:/Users/cert/Documents/testing/private-key.pem";
SSL_library_init();
ctx = InitCTX();
LoadCertificates(ctx, CertFile, KeyFile);
printf("clinet certificate loaded");
server = OpenConnection(hostname, atoi(portnum));
ssl = SSL_new(ctx); /* create new SSL connection state */
SSL_set_fd(ssl, server); /* attach the socket descriptor */
if (SSL_connect(ssl) == FAIL) /* perform the connection */ {
printf("Connection failed");
ERR_print_errors_fp(stderr);
}
else
{
const char* msg = "Hello???";
printf("Connected with %s encryption\n", SSL_get_cipher(ssl));
ShowCerts(ssl); /* get any certs */
SSL_write(ssl, msg, strlen(msg)); /* encrypt & send message */
bytes = SSL_read(ssl, buf, sizeof(buf)); /* get reply & decrypt */
buf[bytes] = 0;
printf("Received: \"%s\"\n", buf);
SSL_free(ssl); /* release connection state */
}
closesocket(server); /* close socket */
SSL_CTX_free(ctx); /* release context */
return 0;
}
SERVER:
//SSL-Server.c
#include <errno.h>
#include <malloc.h>
#include <string.h>
#include <sys/types.h>
#include "openssl/ssl.h"
#include "openssl/err.h"
#include <WS2tcpip.h>
#include <string>
#pragma comment (lib, "ws2_32.lib")
#define FAIL -1
int OpenListener(int port)
{
int sd;
struct sockaddr_in addr;
WSAData data;
WORD ver = MAKEWORD(2, 2);
int wsResult = WSAStartup(ver, &data);
if (wsResult != 0)
{
printf("winsock error");
return 0;
}
sd = socket(PF_INET, SOCK_STREAM, 0);
ZeroMemory(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0)
{
perror("can't bind port");
abort();
}
if (listen(sd, 10) != 0)
{
perror("Can't configure listening port");
abort();
}
listen(sd, SOMAXCONN);
return sd;
}
SSL_CTX* InitServerCTX(void)
{
const SSL_METHOD* method = TLS_client_method(); /* Create new client-method instance */
SSL_CTX* ctx;
OpenSSL_add_all_algorithms(); /* load & register all cryptos, etc. */
SSL_load_error_strings(); /* load all error messages */
ctx = SSL_CTX_new(method); /* create new context from method */
if (ctx == NULL)
{
ERR_print_errors_fp(stderr);
abort();
}
return ctx;
}
void LoadCertificates(SSL_CTX* ctx, char* CertFile, char* KeyFile)
{
//New lines
if (SSL_CTX_load_verify_locations(ctx, CertFile, KeyFile) != 1)
ERR_print_errors_fp(stderr);
if (SSL_CTX_set_default_verify_paths(ctx) != 1)
ERR_print_errors_fp(stderr);
//End new lines
/* set the local certificate from CertFile */
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();
}
//New lines - Force the client-side have a certificate
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
SSL_CTX_set_verify_depth(ctx, 4);
//End new lines
}
void ShowCerts(SSL* ssl)
{
X509* cert;
char* line;
cert = SSL_get_peer_certificate(ssl); /* Get certificates (if available) */
if (cert != NULL)
{
printf("Server certificates:\n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("Subject: %s\n", line);
free(line);
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("Issuer: %s\n", line);
free(line);
X509_free(cert);
}
else
printf("No certificates.\n");
}
void Servlet(SSL* ssl) /* Serve the connection -- threadable */
{
char buf[1024];
char reply[1024];
int sd, bytes;
const char* HTMLecho = "<html><body><pre>%s</pre></body></html>\n\n";
if (SSL_accept(ssl) == FAIL) /* do SSL-protocol accept */
ERR_print_errors_fp(stderr);
else
{
ShowCerts(ssl); /* get any certificates */
bytes = SSL_read(ssl, buf, sizeof(buf)); /* get request */
if (bytes > 0)
{
buf[bytes] = 0;
printf("Client msg: \"%s\"\n", buf);
// sprintf(reply, HTMLecho, buf); /* construct reply */
SSL_write(ssl, reply, strlen(reply)); /* send reply */
}
else
ERR_print_errors_fp(stderr);
}
sd = SSL_get_fd(ssl); /* get socket connection */
SSL_free(ssl); /* release SSL state */
closesocket(sd); /* close connection */
}
int main()
{
SSL_CTX* ctx;
int server;
char portnum[] = "54000";
char CertFile[] = "C:/Users/cert/Documents/testing/ec_crt.pem";
char KeyFile[] = "C:/Users/cert/Documents/testing/private-key.pem";
SSL_library_init();
ctx = InitServerCTX(); /* initialize SSL */
LoadCertificates(ctx, CertFile, KeyFile); /* load certs */
printf("Certificate loaded");
server = OpenListener(atoi(portnum)); /* create server socket */
while (1)
{
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
SSL* ssl;
int client = accept(server, (struct sockaddr*)&addr, &len); /* accept connection as usual */
//printf("Connection: %s:%d\n", inet_ntop(addr.sin_addr), ntohs(addr.sin_port));
printf("Connected");
ssl = SSL_new(ctx); /* get new SSL state with context */
SSL_set_fd(ssl, client); /* set connection socket to SSL state */
Servlet(ssl); /* service connection */
}
closesocket(server); /* close server socket */
SSL_CTX_free(ctx); /* release context */
}
Maybe you need to use:
const SSL_METHOD* method = TLS_server_method();
in InitServerCtx()?

C++ openssl - Generate RSA Keypair and read

I am trying to generate RSA keypair using openssl library and then read the same keys later. However, it fails. Sometimes it gives me this error:
error:0906D06C:PEM routines:PEM_read_bio:no start line
And sometimes, it gives me this error:
error:0906D06C:lib(9):func(109):reason(108)
What is the correct way to generate the keypair and later be able to read it ? Here is my code. If you run it, you will find that it correctly generates the RSA key pair but not able read them later.
#include <stdio.h>
#include <iostream>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <exception>
bool generate_key() {
size_t pri_len; // Length of private key
size_t pub_len; // Length of public key
char *pri_key; // Private key in PEM
char *pub_key; // Public key in PEM
int ret = 0;
RSA *r = NULL;
BIGNUM *bne = NULL;
BIO *bp_public = NULL, *bp_private = NULL;
int bits = 2048;
unsigned long e = RSA_F4;
EVP_PKEY *evp_pbkey = NULL;
EVP_PKEY *evp_pkey = NULL;
BIO *pbkeybio = NULL;
BIO *pkeybio = NULL;
// 1. generate rsa key
bne = BN_new();
ret = BN_set_word(bne, e);
if (ret != 1) {
goto free_all;
}
r = RSA_new();
ret = RSA_generate_key_ex(r, bits, bne, NULL);
if (ret != 1) {
goto free_all;
}
// 2. save public key
//bp_public = BIO_new_file("public.pem", "w+");
bp_public = BIO_new(BIO_s_mem());
ret = PEM_write_bio_RSAPublicKey(bp_public, r);
if (ret != 1) {
goto free_all;
}
// 3. save private key
//bp_private = BIO_new_file("private.pem", "w+");
bp_private = BIO_new(BIO_s_mem());
ret = PEM_write_bio_RSAPrivateKey(bp_private, r, NULL, NULL, 0, NULL, NULL);
//4. Get the keys are PEM formatted strings
pri_len = BIO_pending(bp_private);
pub_len = BIO_pending(bp_public);
pri_key = (char*) malloc(pri_len + 1);
pub_key = (char*) malloc(pub_len + 1);
BIO_read(bp_private, pri_key, pri_len);
BIO_read(bp_public, pub_key, pub_len);
pri_key[pri_len] = '\0';
pub_key[pub_len] = '\0';
printf("\n%s\n%s\n", pri_key, pub_key);
//verify if you are able to re-construct the keys
pbkeybio = BIO_new_mem_buf((void*) pub_key, -1);
if (pbkeybio == NULL) {
return -1;
}
evp_pbkey = PEM_read_bio_PUBKEY(pbkeybio, &evp_pbkey, NULL, NULL);
if (evp_pbkey == NULL) {
char buffer[120];
ERR_error_string(ERR_get_error(), buffer);
printf("Error reading public key:%s\n", buffer);
}
pkeybio = BIO_new_mem_buf((void*) pri_key, -1);
if (pkeybio == NULL) {
return -1;
}
evp_pkey = PEM_read_bio_PrivateKey(pkeybio, &evp_pkey, NULL, NULL);
if (evp_pbkey == NULL) {
char buffer[120];
ERR_error_string(ERR_get_error(), buffer);
printf("Error reading private key:%s\n", buffer);
}
BIO_free(pbkeybio);
BIO_free(pkeybio);
// 4. free
free_all:
BIO_free_all(bp_public);
BIO_free_all(bp_private);
RSA_free(r);
BN_free(bne);
return (ret == 1);
}
int main(int argc, char* argv[]) {
generate_key();
return 0;
}
Looks good to me. Except on reloading; I would have used PEM_read_bio_RSAPublicKey in stead of PEM_read_bio_PUBKEY.
I am not sure it is the best way to do it though.
--- /tmp/stack_openssl.cpp.back 2018-05-25 12:53:12.366488025 +0000
+++ /tmp/stack_openssl.cpp 2018-05-25 13:57:20.614066828 +0000
## -18,6 +18,8 ##
int bits = 2048;
unsigned long e = RSA_F4;
+ RSA *pb_rsa = NULL;
+ RSA *p_rsa = NULL;
EVP_PKEY *evp_pbkey = NULL;
EVP_PKEY *evp_pkey = NULL;
## -66,27 +68,32 ##
printf("\n%s\n%s\n", pri_key, pub_key);
//verify if you are able to re-construct the keys
- pbkeybio = BIO_new_mem_buf((void*) pub_key, -1);
+ pbkeybio = BIO_new_mem_buf((void*) pub_key, pub_len);
if (pbkeybio == NULL) {
return -1;
}
- evp_pbkey = PEM_read_bio_PUBKEY(pbkeybio, &evp_pbkey, NULL, NULL);
- if (evp_pbkey == NULL) {
+ pb_rsa = PEM_read_bio_RSAPublicKey(pbkeybio, &pb_rsa, NULL, NULL);
+ if (pb_rsa == NULL) {
char buffer[120];
ERR_error_string(ERR_get_error(), buffer);
printf("Error reading public key:%s\n", buffer);
}
+ evp_pbkey = EVP_PKEY_new();
+ EVP_PKEY_assign_RSA(evp_pbkey, pb_rsa);
- pkeybio = BIO_new_mem_buf((void*) pri_key, -1);
+ pkeybio = BIO_new_mem_buf((void*) pri_key, pri_len);
if (pkeybio == NULL) {
return -1;
}
- evp_pkey = PEM_read_bio_PrivateKey(pkeybio, &evp_pkey, NULL, NULL);
- if (evp_pbkey == NULL) {
+ p_rsa = PEM_read_bio_RSAPrivateKey(pkeybio, &p_rsa, NULL, NULL);
+ if (p_rsa == NULL) {
char buffer[120];
ERR_error_string(ERR_get_error(), buffer);
printf("Error reading private key:%s\n", buffer);
}
+ evp_pkey = EVP_PKEY_new();
+ EVP_PKEY_assign_RSA(evp_pkey, p_rsa);
BIO_free(pbkeybio);
BIO_free(pkeybio);

On make make install, building application getting ISO c does not permit named variadic

/.configure, make and make install
This is code for status.h
#define DEBUG(level, args...) debug(level, __location__, __FUNCTION__, args)
typedef enum {
ST_OK = 0,
ST_GENERAL_FAILURE = 1,
ST_NO_SUCH_OBJECT = 2,
ST_READ_ERROR = 3,
ST_WRITE_ERROR = 4,
ST_PARSE_ERROR = 5,
ST_LOG_ERR = 117,
ST_DATABASE_FAILURE = 118,
ST_BIND_FAILURE = 119,
ST_SOCKET_FAILURE = 120,
ST_CONFIGURATION_ERROR = 121,
ST_ASSERTION_FAILED = 122,
ST_NOT_IMPLEMENTED = 123,
ST_OUT_OF_MEMORY = 124,
} STATUS;
#define NO_MEM_RETURN(ptr) {if (ptr == NULL) { DEBUG(0, "Out of memory"); return ST_OUT_OF_MEMORY; }}
#define NO_MEM_RETURN_RV(ptr, rv) {if (ptr == NULL) { DEBUG(0, "Out of memory"); return rv; }}
STATUS debug(int loglevel, const char *location, const char *function, ...);
This is code for code for chirond.c and currently running on gcc version 4.7.2 (Debian 4.7.2-5)
#include "includes.h"
#include "build/ndr_chiron.h"
#include "build/chiron.h"
#include <nettle/md5.h>
#include <nettle/arcfour.h>
#define CHIRON_PORT "53165"
struct chiron_context {
int clientfd;
struct sockaddr *clientaddr;
char *account_code;
char *device_id;
uint8_t md5_last_out[0x10];
uint8_t rc4key[0x10];
};
/* FIXME This function is a nasty little hack. */
char *ndr_print_chiron_msg_type_enum(TALLOC_CTX *mem_ctx, enum chiron_msg_type msg_type) {
char *ret;
struct ndr_print *ndr_print = talloc_zero(mem_ctx, struct ndr_print);
ndr_print->print = ndr_print_string_helper;
ndr_print->depth = 0;
ndr_print_chiron_msg_type(ndr_print, "", msg_type);
ret = talloc_steal(mem_ctx, ndr_print->private_data);
talloc_free(ndr_print);
return ret;
}
STATUS handle_chiron_msg_response(struct chiron_context *ctx, struct chiron_message *msg) {
#if 0 // TLV, move to ASN.1 parsing
DATA_BLOB crypted, decrypted;
enum ndr_err_code ndr_err;
struct chiron_msg_inner_response *inner_response;
struct arcfour_ctx rc4;
char *deviceid_string;
if (memcmp(msg->msg.response.md5_check, ctx->md5_last_out, 0x10)) {
DEBUG(0, "MD5 does not match!\n");
return ST_PARSE_ERROR;
}
DEBUG(0, "Handling the response");
inner_response = talloc(msg, struct chiron_msg_inner_response);
NO_MEM_RETURN(inner_response);
/* Copy packet to crypted data blob */
crypted.length = msg->msg.response.length - MD5_HASH_LEN;
crypted.data = talloc_memdup(msg, msg->msg.response.payload, crypted.length);
NO_MEM_RETURN(crypted.data);
decrypted.data = talloc_array(msg, uint8_t, crypted.length);
NO_MEM_RETURN(decrypted.data);
decrypted.length = crypted.length;
arcfour_set_key(&rc4, MD5_HASH_LEN, ctx->rc4key);
arcfour_crypt(&rc4, crypted.length, decrypted.data, crypted.data);
/* Parse the packet */
ndr_err = ndr_pull_struct_blob_all(&decrypted, inner_response, inner_response, (ndr_pull_flags_fn_t)ndr_pull_chiron_msg_inner_response);
if (ndr_err != NDR_ERR_SUCCESS) {
DEBUG(0, "Could not parse the inner response");
return ST_PARSE_ERROR;
}
STATUS handle_chiron_msg_response(struct chiron_context *ctx, struct chiron_message *msg) {
#if 0 // TLV, move to ASN.1 parsing
DATA_BLOB crypted, decrypted;
enum ndr_err_code ndr_err;
struct chiron_msg_inner_response *inner_response;
struct arcfour_ctx rc4;
char *deviceid_string;
if (memcmp(msg->msg.response.md5_check, ctx->md5_last_out, 0x10)) {
DEBUG(0, "MD5 does not match!\n");
return ST_PARSE_ERROR;
}
DEBUG(0, "Handling the response");
inner_response = talloc(msg, struct chiron_msg_inner_response);
NO_MEM_RETURN(inner_response);
/* Copy packet to crypted data blob */
crypted.length = msg->msg.response.length - MD5_HASH_LEN;
crypted.data = talloc_memdup(msg, msg->msg.response.payload, crypted.length);
NO_MEM_RETURN(crypted.data);
decrypted.data = talloc_array(msg, uint8_t, crypted.length);
NO_MEM_RETURN(decrypted.data);
decrypted.length = crypted.length;
arcfour_set_key(&rc4, MD5_HASH_LEN, ctx->rc4key);
arcfour_crypt(&rc4, crypted.length, decrypted.data, crypted.data);
/* Parse the packet */
ndr_err = ndr_pull_struct_blob_all(&decrypted, inner_response, inner_response, (ndr_pull_flags_fn_t)ndr_pull_chiron_msg_inner_response);
if (ndr_err != NDR_ERR_SUCCESS) {
DEBUG(0, "Could not parse the inner response");
return ST_PARSE_ERROR;
}
DEBUG(0, "%s", ndr_print_struct_string(msg,(ndr_print_fn_t)ndr_print_chiron_msg_inner_response, "chiron payload", inner_response));
deviceid_string = talloc_zero_array(msg, char, inner_response->dev_len + 1);
memcpy(deviceid_string, inner_response->deviceid, inner_response->dev_len);
DEBUG(0, "Remote device: %s", deviceid_string);
#endif
//send_chiron_msg_key
return ST_OK;
}
STATUS send_chiron_msg_challenge(struct chiron_context *ctx, struct chiron_message *in) {
struct chiron_message *out = talloc_zero(in, struct chiron_message);
struct md5_ctx md5;
uint8_t *md5input;
enum ndr_err_code ndr_err;
DATA_BLOB raw_out;
NO_MEM_RETURN(out);
DEBUG(0, "Sending out a challenge");
out->msg_type = CHIRON_CHALLENGE;
out->seq = in->seq;
out->flags = in->flags;
/* Make an md5 hash of the account code with the seq byte appended. */
md5input = talloc_array(in, uint8_t, in->msg.account.length + 1);
NO_MEM_RETURN(md5input);
memcpy(md5input, in->msg.account.account_code, in->msg.account.length);
md5input[in->msg.account.length] = in->seq;
out->msg.challenge.md5_check = talloc_array(out, uint8_t, MD5_HASH_LEN);
NO_MEM_RETURN(out->msg.challenge.md5_check);
md5_init(&md5);
md5_update(&md5, in->msg.account.length + 1, md5input);
md5_digest(&md5, MD5_HASH_LEN, out->msg.challenge.md5_check);
talloc_free(md5input);
/* FIXME This should be random, but that is annoying for testing purposes */
out->msg.challenge.length = MD5_HASH_LEN + CHALLENGE_LEN;
out->msg.challenge.challenge = talloc_zero_array(out, uint8_t, CHALLENGE_LEN);
NO_MEM_RETURN(out->msg.challenge.challenge);
out->msg.challenge.challenge[0] = 0xd0;
out->msg.challenge.challenge[1] = 0x8b;
out->msg.challenge.challenge[2] = 0x29;
out->msg.challenge.challenge[3] = 0xd3;
out->msg.challenge.challenge[4] = 0x7c;
out->msg.challenge.challenge[5] = 0xfd;
out->msg.challenge.challenge[6] = 0xb5;
out->msg.challenge.challenge[7] = 0xc6;
out->msg.challenge.challenge[8] = 0x1e;
ndr_err = ndr_push_struct_blob(&raw_out, out, out, (ndr_push_flags_fn_t)ndr_push_chiron_message);
if (ndr_err != NDR_ERR_SUCCESS) {
DEBUG(0, "Error writing NDR data blob.");
return ST_WRITE_ERROR;
}
/* Update the md5 check for the next message (last 9 bytes with the seq byte appended). */
md5input = talloc_array(in, uint8_t, CHALLENGE_LEN + 1);
NO_MEM_RETURN(md5input);
memcpy(md5input, &raw_out.data[MSG_HDR_LEN + MD5_HASH_LEN], CHALLENGE_LEN);
md5input[CHALLENGE_LEN] = in->seq;
md5_init(&md5);
md5_update(&md5, CHALLENGE_LEN + 1, md5input);
md5_digest(&md5, MD5_HASH_LEN, ctx->md5_last_out);
/* Update the rc4 crypto key, which is seq+challenge */
md5input[0] = in->seq;
memcpy(&md5input[1], &raw_out.data[MSG_HDR_LEN + MD5_HASH_LEN], CHALLENGE_LEN);
md5_init(&md5);
md5_update(&md5, CHALLENGE_LEN + 1, md5input);
md5_digest(&md5, MD5_HASH_LEN, ctx->rc4key);
DEBUG(0, "The expected md5sum for the next entry is %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
ctx->md5_last_out[0], ctx->md5_last_out[1], ctx->md5_last_out[2], ctx->md5_last_out[3],
ctx->md5_last_out[4], ctx->md5_last_out[5], ctx->md5_last_out[6], ctx->md5_last_out[9],
ctx->md5_last_out[8], ctx->md5_last_out[9], ctx->md5_last_out[10], ctx->md5_last_out[11],
ctx->md5_last_out[12], ctx->md5_last_out[13], ctx->md5_last_out[14], ctx->md5_last_out[15]);
write(ctx->clientfd, raw_out.data, raw_out.length);
talloc_free(out);
return ST_OK;
}
STATUS handle_chiron_msg_account(struct chiron_context *ctx, struct chiron_message *msg) {
ctx->account_code = talloc_memdup(msg, msg->msg.account.account_code, msg->msg.account.length);
NO_MEM_RETURN(ctx->account_code);
send_chiron_msg_challenge(ctx, msg);
return ST_OK;
}
STATUS handle_connection(struct chiron_context *ctx) {
int n;
struct chiron_message *msg;
enum ndr_err_code ndr_err;
char buf[1024]; /* Purposefully static length */
DATA_BLOB data;
STATUS status;
DEBUG(0, "The expected md5sum for the next entry is %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
ctx->md5_last_out[0], ctx->md5_last_out[1], ctx->md5_last_out[2], ctx->md5_last_out[3],
ctx->md5_last_out[4], ctx->md5_last_out[5], ctx->md5_last_out[6], ctx->md5_last_out[9],
ctx->md5_last_out[8], ctx->md5_last_out[9], ctx->md5_last_out[10], ctx->md5_last_out[11],
ctx->md5_last_out[12], ctx->md5_last_out[13], ctx->md5_last_out[14], ctx->md5_last_out[15]);
write(ctx->clientfd, raw_out.data, raw_out.length);
talloc_free(out);
return ST_OK;
}
STATUS handle_chiron_msg_account(struct chiron_context *ctx, struct chiron_message *msg) {
ctx->account_code = talloc_memdup(msg, msg->msg.account.account_code, msg->msg.account.length);
NO_MEM_RETURN(ctx->account_code);
send_chiron_msg_challenge(ctx, msg);
return ST_OK;
}
STATUS handle_connection(struct chiron_context *ctx) {
int n;
struct chiron_message *msg;
enum ndr_err_code ndr_err;
char buf[1024]; /* Purposefully static length */
DATA_BLOB data;
STATUS status;
while ((n = read(ctx->clientfd, buf, sizeof(buf)))) {
if (n < 0) {
DEBUG( 0, "Error when storing packet in buffer!");
return ST_PARSE_ERROR;
} else if (n == sizeof(buf)) {
DEBUG(0, "Maximum packet size exceeded!");
return ST_PARSE_ERROR;
}
msg = talloc(ctx, struct chiron_message);
NO_MEM_RETURN(msg);
/* Copy packet to data blob */
data.length = n;
data.data = talloc_memdup(msg, buf, n);
NO_MEM_RETURN(data.data);
/* Parse the packet */
ndr_err = ndr_pull_struct_blob_all(&data, msg, msg, (ndr_pull_flags_fn_t)ndr_pull_chiron_message);
if (ndr_err != NDR_ERR_SUCCESS) {
DEBUG(0, "Could not parse this message");
return ST_PARSE_ERROR;
}
DEBUG(0, "%s", ndr_print_struct_string(msg,(ndr_print_fn_t)ndr_print_chiron_message, "chiron message", msg));
switch (msg->msg_type) {
case CHIRON_ACCOUNT:
status = handle_chiron_msg_account(ctx, msg);
break;
case CHIRON_RESPONSE:
status = handle_chiron_msg_response(ctx, msg);
break;
default:
DEBUG(0, "Got unexpected message type: %s.",
ndr_print_chiron_msg_type_enum(msg, msg->msg_type));
break;
}
if (status != ST_OK) {
return status;
}
talloc_free(msg);
}
return ST_OK;
}
static STATUS daemonize(char *pid_file) {
FILE *pidfile;
pid_t pid;
fclose(stdin);
fclose(stdout);
fclose(stderr);
if ((pid = fork())) {
/* Write PID file */
pidfile = fopen(pid_file, "w");
if (pidfile < 0)
exit(1);
fprintf(pidfile, "%d\n", pid);
fclose(pidfile);
exit(0);
}
return ST_OK;
}
static STATUS listen_server(TALLOC_CTX *mem_ctx, const char *bindaddr, const char *bindport, const char *protocol, STATUS (*dispatcher)(struct chiron_context *)) {
int sock;
socklen_t clientlen;
struct addrinfo hints, *server, *first_server;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_flags = AI_PASSIVE;
getaddrinfo(bindaddr, bindport, &hints, &server);
first_server = server;
while (server) {
sock = socket(server->ai_family, SOCK_STREAM, 0);
if (sock >= 0) {
int optval = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
if (bind(sock, server->ai_addr, server->ai_addrlen) < 0) {
close(sock);
sock = -1;
} else {
{
break;
}
}
server = server->ai_next;
}
if (sock < 0) {
DEBUG(0, "Could not create socket in server");
return ST_SOCKET_FAILURE;
}
listen(sock, 128);
freeaddrinfo(first_server);
DEBUG(0, "Started %s and waiting for Chiron messages on port %s",
get_process_name(), CHIRON_PORT);
/*
* Wait for connections
*/
clientlen = sizeof(struct addrinfo);
while (1) {
int clientfd;
struct sockaddr_storage clientaddr;
char clienthost[NI_MAXHOST];
char clientservice[NI_MAXSERV];
clientfd = accept(sock, (struct sockaddr *)&clientaddr, &clientlen);
getnameinfo((struct sockaddr *)&clientaddr, clientlen,
clienthost, sizeof(clienthost),
clientservice, sizeof(clientservice),
NI_NUMERICHOST | NI_NUMERICSERV);
DEBUG(3, "Received connection from %s:%s", clienthost, clientservice);
//if (fork()) {
// continue;
//} else {
{
struct chiron_context *client_ctx = talloc_zero(mem_ctx, struct chiron_context);
NO_MEM_RETURN(client_ctx);
client_ctx->clientaddr = (struct sockaddr *)&clientaddr;
client_ctx->clientfd = clientfd;
dispatcher(client_ctx);
shutdown(client_ctx->clientfd, SHUT_RDWR);
close(client_ctx->clientfd);
talloc_free(client_ctx);
exit(0);
}
}
shutdown(sock, SHUT_RDWR);
close(sock);
}
int main (int argc, char **argv) {
TALLOC_CTX *mem_ctx;
STATUS rv;
const configuration *conf;
set_process_name(argv[0]);
/* Initialize a memory context */
mem_ctx = talloc_init("chirond");
/* Read the configuration file */
rv = read_configuration_file(mem_ctx);
if (rv != ST_OK)
return rv;
conf = get_conf();
/* Daemonize if we're not supposed to run in foreground mode */
if (!conf->foreground) {
daemonize(conf->pid_file);
}
/*
* Open up a TCP socket the Chiron port
*/
listen_server(mem_ctx, "::", CHIRON_PORT, "tcp", handle_connection);
return 0;
}
getting error on make, ISO c does not permit named variadic macros on ubutnu
In file included from ../includes.h:43:0,
from ../chirond.c:18:
../status.h:19:26: error: ISO C does not permit named variadic macros [-Werror=variadic-macros]
Have to upgrade GCC to 4.9 and if you are to lower version using follow instructions on this page.
[https://solarianprogrammer.com/2015/01/13/raspberry-pi-raspbian-install-gcc-compile-cpp-14-programs/][1]
put this lines to avoid warnings
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wvariadic-macros"
// Your code and/or include files
// (No variadic warnings here)
#pragma GCC diagnostic pop
if you have so much warnings remove -werror from CFLAGS. you can able to build.

OpenSSL client verification - Need client side validation requirements

i have a C tcp client which as of now works with SSLV3
need guidance on what and i am missing and what i need to do extra in the client side
i am still not so clear on implementation and trying to read documentation and understand
request people to show some light
(have cut many things from the orginal code)
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include "socketApp.h"
#include "stdio.h"
#include "string.h"
#include "functions.h"
#include "logger.h"
#include <errno.h>
#include <fcntl.h>
#include <malloc.h>
#include <resolv.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#define TCP_KEEPALIVE 0x2
fd_set readSet, actualReadSet;
static int xSocket,ySocket,zSocket;
static char debugBuf[200];
char str[4][20] = {"INVALID","x","y","z"};
char xLogPrint=0;
#define FAIL -1
char CertFile[] = "SocketCert.pem";
char KeyFile[] = "SocketPrivateKey.pem"; //current no key implemetation is done
SSL_CTX *ctx;
int server;
static int sslStatus;
SSL *ssl;
SSL_CTX* InitCTX(void);
int setupSSL(int server){
SSL_library_init();
ctx = InitCTX();
LoadCertificates(ctx, CertFile, KeyFile);
ssl = SSL_new(ctx); /* create new SSL connection state */
SSL_set_fd(ssl, server); /* attach the socket descriptor */
if ( SSL_connect(ssl) == FAIL ){ /* perform the connection */
ERR_print_errors_y(stderr);
return -1;
}else{
sprintf(debugBuf,"Connected with %s encryption\n", SSL_get_cipher(ssl));
debug_log(debugBuf,TRACE_LOG);
setSSLContext(ssl,sslStatus);
return 0;
}
}
int LoadCertificates(SSL_CTX* ctx, char* CertFile, char* KeyFile)
{
if ( SSL_CTX_use_certificate_file(ctx, CertFile, SSL_FILETYPE_PEM) <= 0 )/* set the local certificate from CertFile */
{
ERR_print_errors_y(stderr);
debug_log("Certificate Load error",TRACE_LOG);
return -1;
}
//printf("Server certificates:\n");
// line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
//printf("Subject: %s\n", line);
/* 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_y(stderr);
abort();
}*/
/* verify private key
if ( !SSL_CTX_check_private_key(ctx) )
{
yrintf(stderr, "Private key does not match the public certificate\n");
printf("abort at SSL_CTX_check_private_key");
abort();
}*/
debug_log("Certificate Load success",TRACE_LOG);
return 0;
}
SSL_CTX* InitCTX(void)
{ SSL_METHOD *method;
SSL_CTX *ctx;
OpenSSL_add_all_algorithms(); /* Load cryptos, et.al. */
SSL_load_error_strings(); /* Bring in and register error messages */
method = SSLv3_client_method(); /* Create new client-method instance */
ctx = SSL_CTX_new(method); /* Create new context */
if ( ctx == NULL )
{
//ERR_print_errors_y(stderr);
debug_log("SSL context load failure",TRACE_LOG);
sprintf(debugBuf,"SYSTEM:SSL_SOCKET:creation Failed: %d %s\n",stderr,ERR_print_errors_y(stderr));
debug_log(debugBuf,TRACE_LOG);
//abort();
return ctx;
}
SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL);
SSL_CTX_load_verify_locations(ctx,CertFile,NULL);
return ctx;
}
int socketfdInit(void)
{
FD_ZERO(&readSet);
return 0;
}
int connectToServer(int server,int serverIP, int serverPort)
{
long arg = 0;
int *serverSock= NULL;
int retVal, keepalive=5000,sockOpt=1;
struct sockaddr_in localAddr, serverAddr;
int valopt;
struct timeval tv;
socklen_t opt_len;
int sock_err =0;
fd_set tempSet;
sslStatus=getSSLEnableStatus();
sprintf(debugBuf,"SYSTEM:x_SOCKET:sslStatus: %d \n",sslStatus);
debug_log(debugBuf,TRACE_LOG);
if ((*serverSock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
sprintf(debugBuf,"SYSTEM:%s_SOCKET:creation Failed: %d %s\n",str[server],errno,strerror(errno));
debug_log(debugBuf,TRACE_LOG);
return -1;
}
if (sslStatus == 1){
retVal=setupSSL(*serverSock);
if (retVal != 0){
return retVal;
}
else{
FD_SET(*serverSock, &readSet);
FD_SET(*serverSock, &actualReadSet);
return 0;
}
}
}
int SendToSock( int msgLen,char *msg)
{
int i,numBytesSent;
int sock_err =0;
int sock_errl = sizeof(sock_err);
int serverSock = 0;
if (sslStatus == 1){
numBytesSent = SSL_write(ssl, msg,msgLen+2);
}
else{
numBytesSent = send(serverSock, msg,msgLen+2, MSG_NOSIGNAL);
}
}
int ReceiveFromSock(char *msg,int Rxtimeout)
{
// time val for select expiry
struct timeval tv;
int opt_len; // for querying getsocket opt -->value of length of result
int sock_err=0; // placeholder integer where result is passed by getsockOpt
int bytesRecvd=0;
int selectRetVal;
int tempBytesRecvd;
int status;
fd_set errSet ;
int serverSock = 0;
int len;
char lenStr[3];
if (sslStatus == 1){
bytesRecvd = SSL_read(ssl, (char *)lenStr, 2);
SSL_get_error(ssl,status);
sprintf(debugBuf,"SYSTEM:x_SOCKET:recv: get error value %d",bytesRecvd);
debug_log(debugBuf,TRACE_LOG);
}else
bytesRecvd = recv(serverSock, (char *)lenStr, 2, 0); //receive the length in EBCDIC
}
Just an FYI, Another way to solve this problem may be to run your C program under sslclient, which basically works similar to DJB's tcpclient, but with SSL - i.e. sslclient will spawn your program and open an SSL connection to the server, and pipe your program's stdout to the server, and pipe output from the server to your program's stdin. The nice thing about doing it this way is that you can let sslclient to all the heavy lifting as far as negotiating SSL protocols with the server and doing the actual encryption, and you can focus on the core function of your program. See http://www.superscript.com/ucspi-ssl/sslclient.html for more info.