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.

No comments: