/*! @file
@ id $ Id $
*/
// 1 2 3 4 5 6 7 8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
# ifndef __OPENSSL_HXX__
# define __OPENSSL_HXX__
# include <openssl/pkcs12.h>
# include <openssl/x509.h>
# include "openssl/bio.h"
# include "openssl/ssl.h"
# include <openssl/err.h>
# include <cstdio>
namespace openssl {
//============================================================================
class exception : public std : : exception {
public :
exception ( const std : : string & reason ) throw ( ) :
_what ( " openssl: " + reason ) {
}
~ exception ( ) throw ( ) { }
const char * what ( ) const throw ( ) {
return _what . c_str ( ) ;
}
private :
std : : string _what ;
} ;
//----------------------------------------------------------------------------
class openssl_error : public exception {
public :
openssl_error ( const std : : string & reason ) throw ( ) :
exception ( reason + ' \n ' + ERR_error_string ( ERR_get_error ( ) , 0 ) ) {
}
} ;
//----------------------------------------------------------------------------
class pkcs12_error : public openssl_error {
public :
pkcs12_error ( const std : : string & reason ) throw ( ) :
openssl_error ( " pkcs12: " + reason ) {
}
} ;
//----------------------------------------------------------------------------
class x509_error : public openssl_error {
public :
x509_error ( const std : : string & reason ) throw ( ) :
openssl_error ( " x509: " + reason ) {
}
} ;
//----------------------------------------------------------------------------
class bio_error : public openssl_error {
public :
bio_error ( const std : : string & reason ) throw ( ) :
openssl_error ( " bio: " + reason ) {
}
} ;
//----------------------------------------------------------------------------
class allocation_failed : public x509_error {
public :
allocation_failed ( ) throw ( ) :
x509_error ( " memory allocation failed " ) {
}
} ;
//----------------------------------------------------------------------------
class undefined_certificate : public x509_error {
public :
undefined_certificate ( ) throw ( ) :
x509_error ( " certificate must not be 0 " ) {
}
} ;
//----------------------------------------------------------------------------
class x509_parsing_failed : public x509_error {
public :
x509_parsing_failed ( ) throw ( ) :
x509_error ( " parsing DER encoded certificate failed " ) {
}
} ;
//----------------------------------------------------------------------------
class x509_copy_failed : public x509_error {
public :
x509_copy_failed ( ) throw ( ) :
x509_error ( " certificate object copy failed " ) {
}
} ;
//----------------------------------------------------------------------------
class pkcs12_reading_failed : public pkcs12_error {
public :
pkcs12_reading_failed ( const std : : string & file ) throw ( ) :
pkcs12_error ( " reading DER encoded p12 file failed: " + file ) {
}
} ;
//----------------------------------------------------------------------------
class pkcs12_parsing_failed : public pkcs12_error {
public :
pkcs12_parsing_failed ( const std : : string & file ) throw ( ) :
pkcs12_error ( " parsing DER encoded p12 file failed: " + file ) {
}
} ;
//----------------------------------------------------------------------------
class pkcs12_no_private_key : public pkcs12_error {
public :
pkcs12_no_private_key ( ) throw ( ) : pkcs12_error ( " no private key " ) { }
} ;
//----------------------------------------------------------------------------
class pkcs12_no_x509 : public pkcs12_error {
public :
pkcs12_no_x509 ( ) throw ( ) : pkcs12_error ( " no x509 certificate " ) { }
} ;
//----------------------------------------------------------------------------
class cannot_open_file : public exception {
public :
cannot_open_file ( const std : : string & file ) throw ( ) :
exception ( " cannot open file: " + file ) {
}
} ;
//----------------------------------------------------------------------------
class bio_connection_failed : public bio_error {
public :
bio_connection_failed ( const std : : string & hostPort ) throw ( ) :
bio_error ( " connection failed to: " + hostPort ) {
}
} ;
//============================================================================
class Init {
public :
Init ( ) {
SSL_load_error_strings ( ) ;
ERR_load_BIO_strings ( ) ;
OpenSSL_add_all_algorithms ( ) ;
}
} ;
//============================================================================
class X509 {
public :
//! Construct empty certificate.
X509 ( ) : _x509 ( X509_new ( ) ) {
if ( ! _x509 ) throw allocation_failed ( ) ;
}
X509 ( const X509 & o ) : _x509 ( 0 ) {
unsigned char * d ( 0 ) ;
int len ( i2d_X509 ( o . _x509 , & d ) ) ;
if ( ! len ) throw x509_copy_failed ( ) ;
const unsigned char * d2 ( d ) ;
_x509 = d2i_X509 ( 0 , & d2 , len ) ;
free ( d ) ;
if ( ! _x509 ) throw x509_copy_failed ( ) ;
}
//! Take over OpenSSL allocated certificate.
X509 ( : : X509 * x509 ) : _x509 ( x509 ) {
if ( ! _x509 ) throw undefined_certificate ( ) ;
}
~ X509 ( ) {
X509_free ( _x509 ) ;
}
X509 & operator = ( const X509 & o ) {
X509_free ( _x509 ) ;
_x509 = 0 ;
unsigned char * d ( 0 ) ;
int len ( i2d_X509 ( o . _x509 , & d ) ) ;
if ( ! len ) throw x509_copy_failed ( ) ;
const unsigned char * d2 ( d ) ;
free ( d ) ;
_x509 = d2i_X509 ( 0 , & d2 , len ) ;
if ( ! _x509 ) throw x509_copy_failed ( ) ;
}
private :
: : X509 * _x509 ;
} ;
//============================================================================
class PrivateKey {
public :
PrivateKey ( EVP_PKEY * ) {
}
} ;
//============================================================================
class PKCS12 {
//...............................................................typedefs
public :
typedef std : : vector < X509 * > X509List ;
//................................................................methods
public :
//! Read from a PKCS#12 (.p12) file.
PKCS12 ( std : : string filename , std : : string password ) :
_key ( 0 ) , _cert ( 0 ) {
FILE * file ( fopen ( filename . c_str ( ) , " rb " ) ) ;
if ( ! file ) throw cannot_open_file ( filename ) ;
: : PKCS12 * p12 ( d2i_PKCS12_fp ( file , 0 ) ) ;
fclose ( file ) ;
if ( ! p12 ) throw pkcs12_reading_failed ( filename ) ;
try {
EVP_PKEY * pkey ( 0 ) ;
: : X509 * cert ( 0 ) ;
STACK_OF ( X509 ) * ca ( 0 ) ;
if ( ! PKCS12_parse ( p12 , password . c_str ( ) , & pkey , & cert , & ca ) )
throw pkcs12_parsing_failed ( filename ) ;
if ( pkey ) _key = new PrivateKey ( pkey ) ;
if ( cert ) _cert = new X509 ( cert ) ;
for ( int i ( sk_num ( ca ) ) ; i > 0 ; - - i )
_ca . push_back ( new X509 ( ( : : X509 * ) sk_pop ( ca ) ) ) ;
PKCS12_free ( p12 ) ;
} catch ( . . . ) {
PKCS12_free ( p12 ) ;
throw ;
}
}
~ PKCS12 ( ) {
delete _key ;
delete _cert ;
for ( X509List : : iterator it ( _ca . begin ( ) ) ; it ! = _ca . end ( ) ; + + it )
delete * it ;
}
bool hasPrivateKey ( ) {
return _key ;
}
bool hasCert ( ) {
return _cert ;
}
const PrivateKey & privateKey ( ) {
if ( ! _key ) throw pkcs12_no_private_key ( ) ;
return * _key ;
} ;
const X509 & x509 ( ) {
if ( ! _cert ) throw pkcs12_no_x509 ( ) ;
return * _cert ;
} ;
const X509List & ca ( ) const {
return _ca ;
}
private :
PrivateKey * _key ;
X509 * _cert ;
X509List _ca ;
} ;
class BIO {
public :
BIO ( ) { }
~ BIO ( ) {
try {
close ( ) ;
} catch ( . . . ) {
if ( ! std : : uncaught_exception ( ) ) throw ;
}
}
void connect ( const std : : string & hostPort ) {
close ( ) ;
if ( _bio = BIO_new_connect ( const_cast < char * > ( hostPort . c_str ( ) ) ) )
throw bio_connection_failed ( hostPort ) ;
// ...
}
void close ( ) {
//! @todo tbd
}
private :
: : BIO * _bio ;
} ;
}
# endif