Wednesday, April 16, 2008

Proxy HTTP request


Today I was working with HTTP protocol, I had to connect to web server over proxy using C and Sockets.
For this task you have to connect with the proxy and it'll redirect your request to your requested web server, if you are authenticated.
My Proxy uses Basic Authorization then I had to send the login information (userid and password) in base64 format. For this task I used OpenSSL one more time.
Once you are authenticated, and you have send a GET request correctly formated, proxy will send response to you.

Bellow I write a necesary code to do this connection. I hope it'll be usefull for you.


// Proxy.c : HTTP/HTTPS Request example
//

#include <stdio.h>
#include <memory.h>
#include <errno.h>
#include <sys/types.h>

#ifdef WIN32
#include <winsock.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#endif

#include <openssl/sha.h>
#include <openssl/hmac.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>

#include "applink.c"

// OpenSSL functions
void OpenSSL_add_all_algorithms();
void ERR_load_crypto_strings();
void ENGINE_load_builtin_engines();

// my functions
char *base64(const unsigned char *input, int length);
BOOL sendGet(const char* host, const char* dir, const char* proxy,
unsigned short proxyPort, const char* uid, const char* pwd);
char* GetIpAddress(char* host);
long getLen(char* cadena, long *bytes);

#define MAXLEN 10000

int main (int argc, char* argv[])
{
    char ret[2500]="";
    boolean r=FALSE;

    // OpenSSL Init
    _fmode=_O_BINARY;
    CRYPTO_malloc_init();
    ERR_load_crypto_strings();
    OpenSSL_add_all_algorithms();
    ENGINE_load_builtin_engines();

vERR_load_crypto_strings();

    // HTTPS connection example
    r= sendGet("www.ingdirect.es:443", "CONNECT www.ingdirect.es:443 ", "myproxy.es", 8080, "uid", "pwd");
    // Ejemplo HTTP
    r= sendGet("www.elmundo.es", "GET /", "myproxy.es", 8080, "uid", "pwd");

    return 0;
}


/*
*
* Sends a HTTP request
*
*/

BOOL sendGet(const char* host, const char* dir, const char* proxy,
unsigned short proxyPort, const char* uid, const char* pwd)
{
    int iProtocolPort = 0;
    char szBuffer[MAXLEN] = "";
    SOCKET sd;
    struct sockaddr_in sa;
    char proxySrv[16]="";
    char login[200]="";
    boolean loop=TRUE;
    int ret=0;
    FILE *file=NULL;
    int len=0;
    long bytes=0;

    #ifdef WIN32
        int err=0;
        WORD wVersionRequested;
        WSADATA wsaData;

        wVersionRequested = MAKEWORD( 1, 1 );
        err = WSAStartup(wVersionRequested, &wsaData );
        if ( err != 0 )
        {
            return FALSE;
        }
    #endif

    // Translate hostname to ip address
    strcpy(proxySrv, GetIpAddress((char*)proxy));

    // creating Socket
    sd = socket(PF_INET, SOCK_STREAM, 0);
    if (sd == INVALID_SOCKET)
    {
        // Can't open the socket
        return FALSE;
    }


    memset (&sa, '\0', sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_addr.s_addr = inet_addr (proxySrv); /* Proxy Ip */
    sa.sin_port = htons(proxyPort); /* Proxy Port */


    // Connecting to Proxy
    if(connect(sd, (struct sockaddr*) &sa, sizeof(sa)))
    {
        // Error connecting proxy!
        return FALSE;
    }

    // Init Request
    sprintf(szBuffer,"%s HTTP/1.1\r\nHost:%s\r\n", dir,host);

    // setting login information
    strcat(szBuffer,"Proxy-authorization:Basic ");
    strcpy(login,uid);
    strcat(login, ":");
    strcat(login,pwd);
    strcpy(login, base64(login, strlen(login)));
    strcat(szBuffer,login);
    strcat(szBuffer, "\r\n\r\n");

    // Sending request
    send(sd,szBuffer,strlen(szBuffer),0);

    // Proxy response
    bytes=recv(sd, szBuffer, sizeof(szBuffer), 0);
    file=fopen("c:\\index.html","w");
    len=getLen(szBuffer, &bytes);


    fprintf(file, "%s", szBuffer);
    fflush(file);

    if(bytes>0 && strncmp(szBuffer+strlen(szBuffer)-4,"\r\n\r\n",4)!=0 && (len>0 && len>bytes)){
        // Writing proxy response
        do{
            ret=recv(sd, szBuffer, sizeof(szBuffer), 0);
            bytes+=ret;
            if(ret<0 || strncmp(szBuffer+strlen(szBuffer)-4,"\r\n\r\n",4)==0 || (len>0 && len<bytes)){
                loop=FALSE;
            }
            fprintf(file, "%s",szBuffer);
            fflush(file);
        }while(loop);
    }
    fclose(file);

    // closing connection
    #ifdef WIN32
        closesocket (sd);
        // free resources
        WSACleanup();

    #else
        close(sd);
    #endif

    return TRUE;
}

/*
*
* Returns a Base64 string
*
*/

char *base64(const unsigned char *input, int length)
{
    BIO *bmem, *b64;
    BUF_MEM *bptr;
    char *buff="";

    b64 = BIO_new(BIO_f_base64());
    bmem = BIO_new(BIO_s_mem());
    b64 = BIO_push(b64, bmem);
    BIO_write(b64, input, length);
    BIO_flush(b64);
    BIO_get_mem_ptr(b64, &bptr);

    buff = (char *)malloc(bptr->length);
    memcpy(buff, bptr->data, bptr->length-1);
    buff[bptr->length-1] = 0;

    BIO_free_all(b64);

    return buff;
}

/*
*
* Translate hostname to ip address
*
*/

char* GetIpAddress(char *hostname)
{

    struct sockaddr_in SocketAddress;
    struct hostent *pHost = 0;
    char aszIPAddresses[16];
    int iCnt=0;

    pHost = gethostbyname(hostname);
    if(!pHost)
        return "";

    // Recupero la primera ip del Servidor
    memcpy(&SocketAddress.sin_addr, pHost->h_addr_list[0], pHost->h_length);
    strcpy(aszIPAddresses, inet_ntoa(SocketAddress.sin_addr));

    // Retorno la ip
    return aszIPAddresses;

}


/*
*
* Returns length data from Content-Length header
*
*/

long getLen(char* cadena, long *bytes){
    int t=0;
    char len[10];

    while(t<MAXLEN){
        if(strncmp(cadena+t,"Content-Length:",15)==0){
            int f=t+16;
            while(cadena[f]!='\r')
                f++;
            memcpy(len, cadena+t+16,f-(t+16));
            while(strncmp(cadena+f,"\r\n\r\n",4)!=0)
                f++;
            *bytes=*bytes-f;
            return atol(len);
        }
        t++;
    }

    return -1;

}

Monday, March 31, 2008

Socket eMail Example

Hi Friends, today We need to send a simple alert mail from our application and I have wanted to send this email from C using sockets.

It isn't difficult, you need to open a socket connection to the server using the port of email service and exchange some messages to send the alert message.

All messages you have to send, should be terminated by "\r\n" and after each message you have to call to recv function to get the response from the server.

The first message to send to the Server is the HELO %s\r\n message, replacing %s with the server ip address.

After this message you have to send the EHLO\r\n message, this message will be responsed by the Server with all operations permited by the server.

If you need to send some login information to your email server, you'll need to send the auth\r\n message and after this, you'll have to send the user uid\r\n and pwd\r\n in base64 format.
To do this, I've used Openssl again.

At this point you have to send the information about the from address, to address, subject and body.

For ending the comunication with the server you need to send a blank line and after this blank line send a quit\r\n message.

Bellow I paste the code I've used to do this, I hope it'll be useful for you



// Mail.c : email function example
//


#include <stdio.h>
#include <memory.h>
#include <errno.h>
#includee <sys/types.h>
#ifdef WIN32
  #include <winsock.h>
#else
  #include <sys/socket.h>
  #include <netinet/in.h>
#endif

#include <openssl/bio.h>
#include <openssl/evp.h>
#include "applink.c"

BIO_METHOD * BIO_f_base64(void);

#include <openssl/sha.h>
#include <openssl/hmac.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>

char *base64(const unsigned char *input, int length);


int main (int argc, char* argv[])
{
  char ret[2500]="";

  // Init OpenSSL
  _fmode=_O_BINARY;
  CRYPTO_malloc_init();
  ERR_load_crypto_strings();
  OpenSSL_add_all_algorithms();
  ENGINE_load_builtin_engines();

  ERR_load_crypto_strings();

  return SendMail("uid@server.es","uidTo@server.es", "MailServerIp", "Subject", "UID","PWD", "Message");

}


/*
*
* Send Text Mail from user requested to the target user, using the server and the specified credentials.
*
*/

BOOL SendMail(const char* _From, const char* _To, const char* host,
const char* _Subject, const char* uid, const char* pwd, const char* _MessageFormat, ...)
{
  int iProtocolPort = 0;
  char szBuffer[4096] = "";
  SOCKET sd;
  struct sockaddr_in sa;
  LPSERVENT lpServEntry;
  va_list message_parts;
  char message[65536];
  int err=0;
  char _SMTP[16]="";
  #ifdef WIN32
    WORD wVersionRequested;
    WSADATA wsaData;
  #endif

  #ifdef WIN32
    wVersionRequested = MAKEWORD( 1, 1 );
    err = WSAStartup(wVersionRequested, &wsaData );
    if ( err != 0 )
    {
      exit(1);
    }
  #endif

  // traslating namehost to ip address
  strcpy(_SMTP, GetIpAddress((char*)host));

  // creating the Socket
  sd = socket(PF_INET, SOCK_STREAM, 0);
  if (sd == INVALID_SOCKET)
  {
    // Cannot open mail server socket!
    return FALSE;
    }

  // Getting the mail port
  lpServEntry = getservbyname("mail", 0);

  // if we havent't a port, we use the default port to SMTP
  if (!lpServEntry)
    iProtocolPort = htons(IPPORT_SMTP);
  else
    iProtocolPort = lpServEntry->s_port;

  memset (&sa, '\0', sizeof(sa));
  sa.sin_family = AF_INET;
  sa.sin_addr.s_addr = inet_addr (_SMTP); /* Server IP*/
  sa.sin_port = (iProtocolPort); /* Server Port*/


  // Connecting to the Server
  if(connect(sd, (struct sockaddr*) &sa, sizeof(sa)))
  {
    // Error connecting to the Server!
    return FALSE;
  }

  // Initial response from the Server
  recv(sd, szBuffer, sizeof(szBuffer), 0);

  // Init dialog
  sprintf(szBuffer, "HELO %s\r\n", _SMTP);
  send(sd, szBuffer, strlen(szBuffer), 0);
  recv(sd, szBuffer, sizeof(szBuffer), 0);


  sprintf(szBuffer,"ehlo\r\n");
  send(sd,szBuffer,strlen(szBuffer),0);
  recv(sd,szBuffer,sizeof(szBuffer),0);

  // Sending login
  strcpy(szBuffer, "auth login\r\n");
  send(sd, szBuffer, strlen(szBuffer), 0);
  recv(sd, szBuffer, sizeof(szBuffer), 0);

  // Send userid
  sprintf(szBuffer,"%s\r\n", base64(uid, strlen(uid)));
  send(sd, szBuffer, strlen(szBuffer), 0);
  recv(sd, szBuffer, sizeof(szBuffer), 0);


  // Send Pwd
  sprintf(szBuffer,"%s\r\n", base64(pwd, strlen(pwd)));
  send(sd, szBuffer, strlen(szBuffer), 0);
  recv(sd, szBuffer, sizeof(szBuffer), 0);

  // send From
  sprintf(szBuffer, "MAIL FROM:<%s>\r\n", _From);
  send(sd, szBuffer, strlen(szBuffer), 0);
  recv(sd, szBuffer, sizeof(szBuffer), 0);

  // Send To
  // repeat for each target address

  sprintf(szBuffer, "RCPT TO:<%s>\r\n", _To);
  send(sd, szBuffer, strlen(szBuffer), 0);
  recv(sd, szBuffer, sizeof(szBuffer), 0);

  // Send Data
  sprintf(szBuffer, "DATA\r\n");
  send(sd, szBuffer, strlen(szBuffer), 0);
  recv(sd, szBuffer, sizeof(szBuffer), 0);

  // Preparing the string to send
  va_start(message_parts, _MessageFormat);
  vsprintf(message, _MessageFormat, message_parts);
  va_end(message_parts);

  // Setting Subject and Body
  sprintf(szBuffer,"Subject: %s\r\n\r\n%s\r\n.\r\n",_Subject, message);


  // Send a blank line to end the transmision
  send(sd, szBuffer, strlen(szBuffer), 0);
  recv(sd, szBuffer, sizeof(szBuffer), 0);

  // Sending end command
  sprintf(szBuffer, "quit\r\n");
  send(sd, szBuffer, strlen(szBuffer), 0);
  recv(sd, szBuffer, sizeof(szBuffer), 0);

  // closing the connection
  #ifdef WIN32
    closesocket (sd);
  #else
    close(sd);
  #endif

  return TRUE;
}


char *base64(const unsigned char *input, int length)
{
  BIO *bmem, *b64;
  BUF_MEM *bptr;
  char *buff="";

  b64 = BIO_new(BIO_f_base64());
  bmem = BIO_new(BIO_s_mem());
  b64 = BIO_push(b64, bmem);
  BIO_write(b64, input, length);
  BIO_flush(b64);
  BIO_get_mem_ptr(b64, &bptr);

  buff = (char *)malloc(bptr->length);
  memcpy(buff, bptr->data, bptr->length-1);
  buff[bptr->length-1] = 0;

  BIO_free_all(b64);

  return buff;
}

/*
*
* Return Ip from hostname
*
*/

char* GetIpAddress(char *hostname)
{

  struct sockaddr_in SocketAddress;
  struct hostent *pHost = 0;
  char aszIPAddresses[16];
  int iCnt=0;

  pHost = gethostbyname(hostname);
  if(!pHost)
    return "";

  // get first server ip
  memcpy(&SocketAddress.sin_addr, pHost->h_addr_list[0], pHost->h_length);
  strcpy(aszIPAddresses, inet_ntoa(SocketAddress.sin_addr));

  // returning ip
  return aszIPAddresses;

}

Wednesday, March 5, 2008

SSL HTTP POST

Hi, friends

I'm developing a module to connect C programs with WebServices and newly I've used OpenSSL. Actually it hasn't very difficult, although I had some problem. It's very important to set properly the method to use. I'm connecting with a JBoss HTTPs WebService and I had to set this one:


meth = TLSv1_client_method();
ctx = SSL_CTX_new (meth);

I copy bellow the main function I had used, to send the HTTP POST message, I hope it'll be usefull for you:

int sendPOST(void* sd, SSL* ssl, ;char* server, char* port, char* post, char* action, char* csoap, char *response){

int ret=0;
char msg[5000]="";
char buff[5000]="";
int err=0;
int len=0;
char header[200]="";

    // Setting HTTP Headers
    sprintf(msg,"POST %s HTTP/1.1\r\n", post);
    sprintf(header,"Host: %s:%s\r\n", server, port);
    len=strlen(msg) ;
    memcpy(msg + len , header, strlen(header));
    len+=strlen(header);
    strcpy(header,"Content-Type: application/x-www-form-urlencoded; charset=utf-8\r\n");
    memcpy(msg + len, header, strlen(header));
    len+=strlen(header);
    sprintf(header,"Content-Length: %d\r\n", strlen(datos));
    memcpy(msg + len, header, strlen(header));
    len+=strlen(header);
    sprintf(header,"SOAPAction: %s\r\n\r\n",action);
    memcpy(msg + len, header, strlen(header));
    len+=strlen(header);
    // Adding csoap
    memcpy(msg + len, datos, strlen(datos));

    if(sd!=NULL){
        // No SSL Connections

        err=send((int)sd, msg, len + strlen(datos),0);
        if (err == SOCKET_ERROR) {
            printf("send() failed with error: %d\n", WSAGetLastError());
            closesocket((int)sd);
            WSACleanup();
            exit(1);
        }

        do {
            err = recv((int)sd, buff, sizeof(buff) - 1, 0);
            if ( err > 0 )
                printf("Bytes received: %d\n", err);
            else if ( err == 0 )
                printf("Connection closed\n");
            else
                printf("recv failed with error: %d\n", WSAGetLastError());

        } while( err > 0 );


    }else{
        // SSL Connections
        err = SSL_write (ssl, msg, len + strlen(datos)); CHK_SSL(err);

        err = SSL_read (ssl, buff, sizeof(buff) - 1);
        CHK_SSL(err);
        buff[err] = '\0';

        SSL_shutdown (ssl);
    }

    strcpy(respuesta, buff);

    return ret;

}

Thursday, February 7, 2008

Create a certificate based on private key file

Well, this is another example of using OpenSSL API from Visual C++. In this case I needed to create a certificate from a private key file generated from openssl command line.

It isn’t so difficult, because is too similar to another examples have been told here before.

Bellow I list the code necessary to do this:

/* serv.cpp - Little SSL Server for Windows 01/02/2008 ccalvo@indra.es*/
/* working with openssl-0.9.8g */


#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <errno.h>
#include <sys/types.h>
#include <winsock.h>

#include <openssl/rsa.h>
#include <openssl/crypto.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>


#include <openssl/conf.h>
#include <openssl/x509v3.h>
#ifndef OPENSSL_NO_ENGINE
#include <openssl/engine.h>
#endif

int mkcert(X509 **x509p, RSA *rsa, int bits, int serial, int days);
int add_ext(X509 *cert, int nid, char *value);


/* Key Home */
#define HOME "./"
/* Key File */
#define KEYF HOME "mikey.des"

/* Check functions */
#define CHK_NULL(x) if ((x)==NULL) exit (1)
#define CHK_ERR(err,s) if ((err)==-1) { perror(s); exit(1); }
#define CHK_SSL(err) if ((err)==-1) { ERR_print_errors_fp(stderr); exit(2); }


int err;

// Login information Callback
int pass_cb(char *buf, int size, int rwflag, void *password)
{
strncpy(buf, "carlos", size);
buf[size - 1] = '\0';
return(strlen(buf));
}


int main ()
{
int err;
int listen_sd;
int sd;
struct sockaddr_in sa_serv;
struct sockaddr_in sa_cli;
int client_len;
SSL_CTX* ctx;
SSL* ssl;
X509* client_cert;
X509* x509=NULL;
char* str;
char buf [4096];
SSL_METHOD *meth;
WORD wVersionRequested;
WSADATA wsaData;
FILE *privKey=NULL;
RSA *rsaPrivKey = NULL;
BIO *bio_err;
char pass[8]="carlos\n";

/* Open SSL Init */

SSL_load_error_strings();
SSLeay_add_ssl_algorithms();
meth = SSLv23_server_method();
ctx = SSL_CTX_new (meth);
if (!ctx) {
    ERR_print_errors_fp(stderr);
    exit(2);
}

// Reading private Key from file
privKey=fopen(KEYF,"r");
if(privKey){
    rsaPrivKey = PEM_read_RSAPrivateKey(privKey, &rsaPrivKey, (pem_password_cb *)pass_cb, pass);
    fclose(privKey);
}

CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);

bio_err=BIO_new_fp(stderr, BIO_NOCLOSE);

// Creating a certificate
mkcert(&x509,rsaPrivKey,512,0,365);

// Keeping Key and Cert in the ctx
SSL_CTX_use_RSAPrivateKey(ctx, rsaPrivKey);
SSL_CTX_use_certificate(ctx, x509);

// clearing key and cert
X509_free(x509);
RSA_free(rsaPrivKey);

// checking key is valid to cert
if (!SSL_CTX_check_private_key(ctx)) {
    fprintf(stderr,"The private key don’t match with cert public key\n");
    exit(5);
}

/*****************************************************/
/*                                                   */
/* Init Sockets Layer */
/*                                                   */
/*****************************************************/



wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup(wVersionRequested, &wsaData );
if ( err != 0 )
{
    printf("///WSAStartup Error//%d\n",err);
    exit(err);
}

listen_sd = socket (AF_INET, SOCK_STREAM, 0);
printf("socket() return 0x%X (%d)\n", listen_sd, listen_sd);
CHK_ERR(listen_sd, "socket");

memset (&sa_serv, '\0', sizeof(sa_serv));
sa_serv.sin_family = AF_INET;
sa_serv.sin_addr.s_addr = INADDR_ANY;
sa_serv.sin_port = htons (1111); /* Puerto del Servidor */

err = bind(listen_sd, (struct sockaddr*) &sa_serv,
sizeof (sa_serv));
CHK_ERR(err, "bind");

/*****************************************************/
/*                                                   */
/* Setting Listening */
/*                                                   */
/*****************************************************/


printf("listening\n");
err = listen (listen_sd, 5);
CHK_ERR(err, "listen");

client_len = sizeof(sa_cli);
sd = accept (listen_sd, (struct sockaddr*) &sa_cli, &client_len);
CHK_ERR(sd, "accept");
closesocket (listen_sd);

printf ("Connection from %lx, port %x\n",
sa_cli.sin_addr.s_addr, sa_cli.sin_port);

/*****************************************************/
/*                                                   */
/* Setting SSL */
/*                                                   */
/*****************************************************/


ssl = SSL_new (ctx); CHK_NULL(ssl);
SSL_set_fd (ssl, sd);
err = SSL_accept (ssl); CHK_SSL(err);

/* Show cipher information - optional */

printf ("SSL connection using %s\n", SSL_get_cipher (ssl));

/* Getting client cert, if exist - optional */

client_cert = SSL_get_peer_certificate (ssl);
if (client_cert != NULL) {
    printf ("Client Cert:\n");

    str = X509_NAME_oneline (X509_get_subject_name (client_cert), 0, 0);
    CHK_NULL(str);
    printf ("\t subject: %s\n", str);
    OPENSSL_free (str);

    str = X509_NAME_oneline (X509_get_issuer_name (client_cert), 0, 0);
    CHK_NULL(str);
    printf ("\t issuer: %s\n", str);
    OPENSSL_free (str);

    /* Here we can check all we want about client cert, before freeing. */

    X509_free (client_cert);
} else
    printf ("Client hasn’t Cert.\n");

/* Messaging exchange */

err = SSL_read (ssl, buf, sizeof(buf) - 1); CHK_SSL(err);
buf[err] = '\0';
printf ("Read %d characters :'%s'\n", err, buf);

err = SSL_write (ssl, "I’m listening to you.", strlen("I’m listening to you.")); CHK_SSL(err);

/* Freeing resources */

closesocket (sd);
SSL_free (ssl);
SSL_CTX_free (ctx);

CRYPTO_cleanup_all_ex_data();
ERR_remove_state(0);

CRYPTO_mem_leaks_fp(stderr);
#ifndef OPENSSL_NO_ENGINE
    ENGINE_cleanup();
#endif

BIO_free(bio_err);

return(0);
}

void callback(int p, int n, void *arg)
{
char c='B';

if (p == 0) c='.';
if (p == 1) c='+';
if (p == 2) c='*';
if (p == 3) c='\n';
fputc(c,stderr);
}

int mkcert(X509 **x509p, RSA *rsa, int bits, int serial, int days)
{
X509 *x;
EVP_PKEY *pk;

X509_NAME *name=NULL;

if ((pk=EVP_PKEY_new()) == NULL)
{
    abort();
    return(0);
}


if ((x509p == NULL) (*x509p == NULL))
{
    if ((x=X509_new()) == NULL)
        goto err;
}
else
x= *x509p;

if (!EVP_PKEY_assign_RSA(pk,rsa))
{
    abort();
    goto err;
}
rsa=NULL;

X509_set_version(x,2);
ASN1_INTEGER_set(X509_get_serialNumber(x),serial);
X509_gmtime_adj(X509_get_notBefore(x),0);
X509_gmtime_adj(X509_get_notAfter(x),(long)60*60*24*days);
X509_set_pubkey(x,pk);

name=X509_get_subject_name(x);

/* This function creates and adds the entry, working out the
* correct string type and performing checks on its length.
* Normally we'd check the return value for errors...
*/

X509_NAME_add_entry_by_txt(name,"C",
MBSTRING_ASC, (unsigned char*)"ES", -1, -1, 0);
X509_NAME_add_entry_by_txt(name,"CN",
MBSTRING_ASC, (unsigned char*)"MyCompany", -1, -1, 0);

/* Its self signed so set the issuer name to be the same as the
* subject.
*/

X509_set_issuer_name(x,name);

/* Add various extensions: standard extensions */
add_ext(x, NID_basic_constraints, "critical,CA:TRUE");
add_ext(x, NID_key_usage, "critical,keyCertSign,cRLSign");

add_ext(x, NID_subject_key_identifier, "hash");

/* Some Netscape specific extensions */
add_ext(x, NID_netscape_cert_type, "sslCA");

add_ext(x, NID_netscape_comment, "example comment extension");


#ifdef CUSTOM_EXT
/* Maybe even add our own extension based on existing */
{
int nid;
nid = OBJ_create("1.2.3.4", "MyAlias", "My Test Alias Extension");
X509V3_EXT_add_alias(nid, NID_netscape_comment);
add_ext(x, nid, "example comment alias");
}
#endif

if (!X509_sign(x,pk,EVP_md5()))
    goto err;

*x509p=x;
return(0);
err:
return(1);
}

/* Add extension using V3 code: we can set the config file as NULL
* because we wont reference any other sections.
*/


int add_ext(X509 *cert, int nid, char *value)
{
X509_EXTENSION *ex;
X509V3_CTX ctx;
/* This sets the 'context' of the extensions. */
/* No configuration database */

X509V3_set_ctx_nodb(&ctx);
/* Issuer and subject certs: both the target since it is self signed,
* no request and no CRL
*/

X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0);
ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, value);
if (!ex)
    return 0;

X509_add_ext(cert,ex,-1);
X509_EXTENSION_free(ex);
return 1;
}


If you want to download the VC++ proyect click here.

Friday, January 25, 2008

SSL OpenSSL with Visual C++

Today I have another solution to work with SSL from Visual C++ using OpenSSL.

Well, the OpenSSL compilation was described before, so I can’t described here again. Click here if you want to know more about OpenSSL compilation

To work with SSL is easy with OpenSSL, really is a layer to add to our Windows Socket Projects.

The steps you’ll have to do are:

1. Add the OpenSSL libs (libeay32.lib, ssleay32.lib) to your project in Link options. Well, and touch some different options in this sheet, download the projects to see more about this point.
2. Add the OpenSSL header files to your main cpp.
3. And the OpenSSL SSL commands.

Here is the sample server code:


#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <errno.h>
#include <sys/types.h>
#include <winsock.h>

#include <openssl/rsa.h>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>


/* certificates root path */
#define HOME "./"
/* certificate and private key */
#define CERTF HOME "micert.pem"
#define KEYF HOME "mikey.key"

/* checking fuctions */
#define CHK_NULL(x) if ((x)==NULL) exit (1)
#define CHK_ERR(err,s) if ((err)==-1) { perror(s); exit(1); }
#define CHK_SSL(err) if ((err)==-1) { ERR_print_errors_fp(stderr); exit(2); }

int err;


void main ()
{
int err;
int listen_sd;
int sd;
struct sockaddr_in sa_serv;
struct sockaddr_in sa_cli;
int client_len;
SSL_CTX* ctx;
SSL* ssl;
X509* client_cert;
char* str;
char buf [4096];
SSL_METHOD *meth;
WORD wVersionRequested;
WSADATA wsaData;

/* SSL init. Keep the Key and the certificate in the context. */

SSL_load_error_strings();
SSLeay_add_ssl_algorithms();
meth = SSLv23_server_method();
ctx = SSL_CTX_new (meth);
if (!ctx) {
    ERR_print_errors_fp(stderr);
    exit(2);
}

if (SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0) {
    ERR_print_errors_fp(stderr);
    exit(3);
}
if (SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) {
    ERR_print_errors_fp(stderr);
    exit(4);
}

if (!SSL_CTX_check_private_key(ctx)) {
    fprintf(stderr,"The private Key don’t match with the certificate\n");
    exit(5);
}

/*****************************************************/
/*                                                   */
/* Socket Initialitation */
/*                                                   */
/*****************************************************/



wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup(wVersionRequested, &wsaData );
if ( err != 0 )
{
    printf("///Error in WSAStartup//%d\n",err);
    exit(err);

}

listen_sd = socket (AF_INET, SOCK_STREAM, 0);
printf("socket() return 0x%X (%d)\n", listen_sd, listen_sd);
CHK_ERR(listen_sd, "socket");

memset (&sa_serv, '\0', sizeof(sa_serv));
sa_serv.sin_family = AF_INET;
sa_serv.sin_addr.s_addr = INADDR_ANY;
sa_serv.sin_port = htons (9000); /* Server listen Port */

err = bind(listen_sd, (struct sockaddr*) &sa_serv,
sizeof (sa_serv));
CHK_ERR(err, "bind");

/*****************************************************/
/*                                                   */
/* setting listening */
/*                                                   */
/*****************************************************/

printf("listening\n");
err = listen (listen_sd, 5); CHK_ERR(err, "listen");

client_len = sizeof(sa_cli);
sd = accept (listen_sd, (struct sockaddr*) &sa_cli, &client_len);
CHK_ERR(sd, "accept");
closesocket (listen_sd);

printf ("Connection from %lx, port %x\n",
sa_cli.sin_addr.s_addr, sa_cli.sin_port);

/*****************************************************/
/*                                                   */
/* preparing SSL with the recived socket */
/*                                                   */
/*****************************************************/


ssl = SSL_new (ctx); CHK_NULL(ssl);
SSL_set_fd (ssl, sd);
err = SSL_accept (ssl); CHK_SSL(err);

/* Showing cipher - optional */

printf ("SSL connection using %s\n", SSL_get_cipher (ssl));

/* Get Client cert, if exist - optional */

client_cert = SSL_get_peer_certificate (ssl);
if (client_cert != NULL) {
    printf ("Client Certificate:\n");

    str = X509_NAME_oneline (X509_get_subject_name (client_cert), 0, 0);
    CHK_NULL(str);
    printf ("\t subject: %s\n", str);
    OPENSSL_free (str);

    str = X509_NAME_oneline (X509_get_issuer_name (client_cert), 0, 0);
    CHK_NULL(str);
    printf ("\t issuer: %s\n", str);
    OPENSSL_free (str);

    /* Here we could check all we want about client certificate. */

    X509_free (client_cert);
} else
    printf ("The Client hasn’t certificate.\n");

/* Exchanging messages */

err = SSL_read (ssl, buf, sizeof(buf) - 1); CHK_SSL(err);
buf[err] = '\0';
printf ("Read %d characters:'%s'\n", err, buf);

err = SSL_write (ssl, "I’m listening.", strlen("I’m listening.")); CHK_SSL(err);

/* Freeing resources */

closesocket (sd);
SSL_free (ssl);
SSL_CTX_free (ctx);
}

Bellow is the SSL Client Code.

#include <stdio.h>
#include <memory.h>
#include <errno.h>
#include <sys/types.h>
#include <winsock.h>

#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>


#define CHK_NULL(x) if ((x)==NULL) exit (1)
#define CHK_ERR(err,s) if ((err)==-1) { perror(s); exit(1); }
#define CHK_SSL(err) if ((err)==-1) { ERR_print_errors_fp(stderr); exit(2); }

void main ()
{
int err=0;
int sd;
struct sockaddr_in sa;
SSL_CTX* ctx;
SSL* ssl;
X509* scert;
char* txt;
char buff [4096];
SSL_METHOD *meth;
WORD wVersionRequested;
WSADATA wsaData;
SSLeay_add_ssl_algorithms();
meth = SSLv2_client_method();
SSL_load_error_strings();
ctx = SSL_CTX_new (meth); CHK_NULL(ctx);

CHK_SSL(err);

/*****************************************************/
/*                                                   */
/* Socket Init and connecting. */
/*                                                   */
/*****************************************************/


wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup(wVersionRequested, &wsaData );
if ( err != 0 )
{
    printf("///Error in WSAStartup //%d\n",err);
    exit(1);

}


sd = socket (AF_INET, SOCK_STREAM, 0);
CHK_ERR(sd, "socket");

memset (&sa, '\0', sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr ("127.0.0.1"); /* Server Ip */
sa.sin_port = htons (9000); /* Server port */

err = connect(sd, (struct sockaddr*) &sa,
sizeof(sa)); CHK_ERR(err, "connection");

/*****************************************************/
/*                                                   */
/* Starting SSL connection */
/*                                                   */
/*****************************************************/

ssl = SSL_new (ctx); CHK_NULL(ssl);
SSL_set_fd (ssl, sd);
err = SSL_connect (ssl); CHK_SSL(err);

/* Get the cipher - optional */

printf ("SSL connection using %s\n", SSL_get_cipher (ssl));

/* Get Server certificate - optional */

scert = SSL_get_peer_certificate (ssl); CHK_NULL(scert);
printf ("Server Certificate:\n");

txt = X509_NAME_oneline (X509_get_subject_name (scert),0,0);
CHK_NULL(txt);
printf ("\t Subject: %s\n", txt);
OPENSSL_free (txt);

txt = X509_NAME_oneline (X509_get_issuer_name (scert),0,0);
CHK_NULL(txt);
printf ("\t Issuer: %s\n", txt);
OPENSSL_free (txt);

/* Here we can check what we wanted about the Server certificate */

X509_free (scert);

/*****************************************************/
/*                                                   */
/* Exchanging messages */
/*                                                   */
/*****************************************************/

err = SSL_write (ssl, "Is someone there?", strlen("Is someone there?")); CHK_SSL(err);

err = SSL_read (ssl, buff, sizeof(buff) - 1); CHK_SSL(err);
buff[err] = '\0';
printf ("Read %d characters:'%s'\n", err, buff);
SSL_shutdown (ssl); /* sending the SSL/TLS end*/

/* Freeing resources */

closesocket (sd);
SSL_free (ssl);
SSL_CTX_free (ctx);
}


If you want to download the client and Server projects, click here. Source Files

Thursday, January 24, 2008

RSA OpenSSL with Microsoft Visual C++

Hi, buddies, how long time? JE JE JE

Here I am newly with another solution.

I have been working with RSA for some time with Java, but now I wanted to work with RSA in pure C.

Well, there are several libraries to work with RSA in C, but I choose OpenSSL.

Another time I only have a Windows System to try, I’m in a customer and here there aren’t UNIX machines.

Well, I downloaded this OpenSSL version

http://www.openssl.org/source/openssl-0.9.8g.tar.gz

This package is a source code package, and you need to compile to work with it.

To compile OpenSSL on Windows with Microsoft Visual C++, you have to read the install instructions (INSTALL.W32), located in the root. You’ll need Perl, and you’ll have to configure Perl correctly to work with Visual C++, in the file INSTALL.W32 you’ll find everything what you need. But, basically you’ll have to run these instructions from the root OpenSSL path:

To configure Perl, don’t forget add the Perl bin path to your PATH environment variable:
>perl Configure VC-WIN32 --prefix=‘c:/some/openssl/dir ‘

If you don't want to use the assembly language files at all then run
> ms\do_ms

And finally, run the compilation:

For get OpenSSL libs, run:
> nmake -f ms\nt.mak

Or run this command to get OpenSSL dlls:
> nmake -f ms\ntdll.mak

If you want to generate the libs or dlls with debugger information, you’ll need to edit do_nt.bat or do_ntdll.bat, to add debug option to the command line in these lines:

perl util\mk1mf.pl no-asm debug VC-NT>ms\nt.mak
perl util\mk1mf.pl dll no-asm debug VC-NT>ms\ntdll.mak

These bats build the makes with DEBUG option; you’ll have to execute the nmake -f ms\nt.mak newly to get the new libraries.

Maybe you’ll need unicows.lib for this last compilation.
If you don’t have it into your PC, it’s here.
http://surfnet.dl.sourceforge.net/sourceforge/libunicows/libunicows-1.1.1-msvc6.zip

Well, after the compilation you’ll get some exes and two dlls or libs, it depends if you compile nt.mak (libs) or ntdlls.mak (dlls), the output directory name begins with out32.

Well, here is a sample source code that shows how to read private and public keys from files (private key file is encrypted), retrieving the keys, encrypting and decrypting.

The bold lines are important, they showed the problems that I found


#include <stdio.h>
#include <string.h>
#include "e_os.h"
#include <openssl\crypto.h>
#include <openssl\err.h>
#include <openssl\rand.h>
#include <openssl\bn.h>
#include "applink.c"
#include "pem.h"
#include <openssl\rsa.h>

// Callback for login information
int pass_cb(char *buf, int size, int rwflag, void *password)
{
strncpy(buf, "carlos", size);
buf[size - 1] = '\0';
return(strlen(buf));
}


int main(int argc, char *argv[])
{

RSA *rsaPrivKey = RSA_new();
RSA *rsaPubKey = RSA_new();
unsigned char inText[256];
FILE *pubKey = NULL;
FILE *privKey = NULL;
unsigned char sigBuffer[256];
int size;
unsigned char plainText[256];
int encSize;
char pass[7]="carlos\n";

// OpenSSL Init
_fmode=_O_BINARY;
CRYPTO_malloc_init();
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
ENGINE_load_builtin_engines();

// End OpenSSL Init

// Opening Files generated with openSSL
pubKey=fopen("J:\\public.key", "r");
// Private Key file, encrypted with 3DESC
privKey=fopen("J:\\priv.key", "r");

// Reading Keys
if(privKey){
    rsaPrivKey = PEM_read_RSAPrivateKey(privKey, &rsaPrivKey,
        (pem_password_cb *)pass_cb, pass);
}
ERR_print_errors_fp(stdout);
if(pubKey)
{
    rsaPubKey = PEM_read_RSA_PUBKEY(pubKey, &rsaPubKey,
        NULL, NULL);
}

// Encrypting
strcpy(inText,"Carlos");
printf("String to encrypting: %s\n", inText );
size = strlen((char*)inText);
encSize=RSA_public_encrypt(size, inText,
sigBuffer, rsaPubKey, RSA_PKCS1_OAEP_PADDING);

// Decrypting
printf("encrypted string size: %d\n" ,encSize );
encSize=RSA_private_decrypt(encSize, sigBuffer, plainText,
rsaPrivKey, RSA_PKCS1_OAEP_PADDING);
memset(plainText+encSize,0,sizeof(plainText)-encSize);
printf("ret %s\n",plainText);
ERR_print_errors_fp(stdout);
printf("Decrypting result: %s\n", plainText );

// Free keys
RSA_free(rsaPubKey);
RSA_free(rsaPrivKey);

// unload OpenSSL
CRYPTO_cleanup_all_ex_data();
ERR_remove_state(0);

CRYPTO_mem_leaks_fp(stderr);

return 0;
}

Here is the Visual C++ Project
Source Files




CSoap and Visual Studio

First of all, I’m sorry for my English, but I want to improve it and this is a good way.

Well, this is my first solution.

Many times I have had to connect a c program to a Web Service, in Windows with MFC is too easy. I have a dll, which connects the c program with whatever Web Service only updating some xml files.

But, when you need to connect a c program running in other OS, you'll need to try other solutions.

The best, for me, it's CSoap.

However when I had to develop that connection I only had Windows Systems and well, I tried to connect CSoap in a Visual Studio Project.

It wasn't easy for me to find examples about CSOAP for Visual C++, and that is why I put that solution here.

Here is the original link to my source files https://sourceforge.net/tracker/download.php?group_id=74977&atid=542567&file_id=250457&aid=1816455