/*! @file
@ id $ Id $
*/
// 1 2 3 4 5 6 7 8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
# ifndef __OPENSSL_HXX__
# define __OPENSSL_HXX__
# include <openssl/pkcs12.h>
# include <openssl/pkcs7.h>
# include <openssl/x509.h>
# include "openssl/bio.h"
# include "openssl/des.h"
# include "openssl/ssl.h"
# include <openssl/err.h>
# include <vector>
# include <openssl/x509v3.h> // BASIC_CONSTRAINTS
# include <openssl/bn.h>
# include <cryptaux.hxx>
# include <cstdio>
# include <cassert>
# include <iostream> //! @todo remove (debug only)
/*! @defgroup gopenssl C++ Wrapper around OpenSSL API */
//@{
//! @defgroup openssllib OpenSSL C++ Library
//! @defgroup opensslexceptions OpenSSL Exceptions
//! @see gopenssl
namespace openssl {
//============================================================================
//! @addtogroup opensslexceptions
//@{
//----------------------------------------------------------------------------
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 encryption_error : public openssl_error {
public :
encryption_error ( const std : : string & reason ) throw ( ) :
openssl_error ( " encryption: " + reason ) {
}
} ;
//----------------------------------------------------------------------------
class pkcs12_error : public openssl_error {
public :
pkcs12_error ( const std : : string & reason ) throw ( ) :
openssl_error ( " pkcs12: " + reason ) {
}
} ;
//----------------------------------------------------------------------------
class pkcs7_error : public openssl_error {
public :
pkcs7_error ( const std : : string & reason ) throw ( ) :
openssl_error ( " pkcs7: " + reason ) {
}
} ;
//----------------------------------------------------------------------------
class x509_error : public openssl_error {
public :
x509_error ( const std : : string & reason ) throw ( ) :
openssl_error ( " x509: " + reason ) {
}
} ;
//----------------------------------------------------------------------------
class key_error : public openssl_error {
public :
key_error ( const std : : string & reason ) throw ( ) :
openssl_error ( " private key: " + reason ) {
}
} ;
//----------------------------------------------------------------------------
class bio_error : public openssl_error {
public :
bio_error ( const std : : string & reason ) throw ( ) :
openssl_error ( " bio: " + reason ) {
}
} ;
//----------------------------------------------------------------------------
class ssl_error : public openssl_error {
public :
ssl_error ( const std : : string & reason ) throw ( ) :
openssl_error ( " ssl: " + reason ) {
}
} ;
//----------------------------------------------------------------------------
class cannot_encrypt : public encryption_error {
public :
cannot_encrypt ( std : : string reason ) throw ( ) :
encryption_error ( " cannot encrypt text: " + reason ) {
}
} ;
//----------------------------------------------------------------------------
class allocation_failed : public x509_error {
public :
allocation_failed ( ) throw ( ) :
x509_error ( " memory allocation failed " ) {
}
} ;
//----------------------------------------------------------------------------
class x509_decoding_failed : public x509_error {
public :
x509_decoding_failed ( const std : : string & der ) throw ( ) :
x509_error ( " certificate decoding failed: \n " + crypto : : readable ( der ) ) {
}
} ;
//----------------------------------------------------------------------------
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 key_copy_failed : public key_error {
public :
key_copy_failed ( ) throw ( ) :
key_error ( " key object copy failed " ) {
}
} ;
//----------------------------------------------------------------------------
class undefined_key : public key_error {
public :
undefined_key ( ) throw ( ) :
key_error ( " private key must not be 0 " ) {
}
} ;
//----------------------------------------------------------------------------
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 pkcs7_reading_failed : public pkcs7_error {
public :
pkcs7_reading_failed ( const std : : string & file ) throw ( ) :
pkcs7_error ( " reading DER encoded p7 file failed: " + file ) {
}
} ;
//----------------------------------------------------------------------------
class pkcs7_parsing_failed : public pkcs7_error {
public :
pkcs7_parsing_failed ( ) throw ( ) :
pkcs7_error ( " parsing DER encoded p7 failed " ) {
}
pkcs7_parsing_failed ( const std : : string & file ) throw ( ) :
pkcs7_error ( " parsing DER encoded p7 file failed: " + file ) {
}
} ;
//----------------------------------------------------------------------------
class pkcs7_unsupported_format : public pkcs7_error {
public :
pkcs7_unsupported_format ( ) throw ( ) : pkcs7_error ( " format not supported " ) { }
} ;
//----------------------------------------------------------------------------
class pkcs7_no_x509 : public pkcs7_error {
public :
pkcs7_no_x509 ( ) throw ( ) : pkcs7_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 bio_closed_connection : public bio_error {
public :
bio_closed_connection ( ) throw ( ) : bio_error ( " closed connection " ) { }
} ;
//----------------------------------------------------------------------------
class bio_read_error : public bio_error {
public :
bio_read_error ( ) throw ( ) : bio_error ( " read error " ) { }
} ;
//----------------------------------------------------------------------------
class bio_write_error : public bio_error {
public :
bio_write_error ( ) throw ( ) : bio_error ( " write error " ) { }
} ;
//----------------------------------------------------------------------------
class ssl_cannot_create_context : public ssl_error {
public :
ssl_cannot_create_context ( ) throw ( ) : ssl_error ( " cannot create context " ) { }
} ;
//----------------------------------------------------------------------------
class ssl_certificate_file_not_loaded : public ssl_error {
public :
ssl_certificate_file_not_loaded ( const std : : string & file ) throw ( ) :
ssl_error ( " certificate file not loaded: " + file ) {
}
} ;
//----------------------------------------------------------------------------
class ssl_certificate_folder_not_loaded : public ssl_error {
public :
ssl_certificate_folder_not_loaded ( const std : : string & path ) throw ( ) :
ssl_error ( " certificate folder not loaded: " + path ) {
}
} ;
//@}
//! @addtogroup openssllib
//@{
//============================================================================
class Init {
public :
Init ( ) {
SSL_load_error_strings ( ) ;
ERR_load_BIO_strings ( ) ;
OpenSSL_add_all_algorithms ( ) ;
}
} ;
inline std : : string desEnc ( const std : : string & txt , DES_cblock key1 ) {
std : : string res ( txt ) ;
if ( txt . size ( ) % 8 ! = 0 )
throw cannot_encrypt ( " text size must be a multiple of eight " ) ;
DES_key_schedule ks1 ;
DES_set_key_unchecked ( & ( DES_cblock & ) key1 , & ks1 ) ;
for ( std : : string : : size_type pos ( 0 ) ; pos < txt . size ( ) ; pos + = 8 ) {
union {
DES_cblock array ;
const char * text ;
} in , out ;
in . text = txt . begin ( ) . operator - > ( ) + pos ;
out . text = res . begin ( ) . operator - > ( ) + pos ;
DES_ecb_encrypt ( & in . array , & out . array , & ks1 , DES_ENCRYPT ) ;
}
return res ;
}
inline std : : string desDec ( const std : : string & txt , DES_cblock key1 ) {
std : : string res ( txt ) ;
if ( txt . size ( ) % 8 ! = 0 )
throw cannot_encrypt ( " text size must be a multiple of eight " ) ;
DES_key_schedule ks1 ;
DES_set_key_unchecked ( & ( DES_cblock & ) key1 , & ks1 ) ;
for ( std : : string : : size_type pos ( 0 ) ; pos < txt . size ( ) ; pos + = 8 ) {
union {
DES_cblock array ;
const char * text ;
} in , out ;
in . text = txt . begin ( ) . operator - > ( ) + pos ;
out . text = res . begin ( ) . operator - > ( ) + pos ;
DES_ecb_encrypt ( & in . array , & out . array , & ks1 , DES_DECRYPT ) ;
}
return res ;
}
class CBlock8 {
public :
CBlock8 ( ) {
_cb [ 0 ] = _cb [ 1 ] = _cb [ 2 ] = _cb [ 3 ] =
_cb [ 4 ] = _cb [ 5 ] = _cb [ 6 ] = _cb [ 7 ] = 0 ;
}
CBlock8 ( unsigned char c1 , unsigned char c2 , unsigned char c3 ,
unsigned char c4 , unsigned char c5 , unsigned char c6 ,
unsigned char c7 , unsigned char c8 ) {
_cb [ 0 ] = c1 ;
_cb [ 1 ] = c2 ;
_cb [ 2 ] = c3 ;
_cb [ 3 ] = c4 ;
_cb [ 4 ] = c5 ;
_cb [ 5 ] = c6 ;
_cb [ 6 ] = c7 ;
_cb [ 7 ] = c8 ;
}
//! String must contain exactly 8 char.
CBlock8 ( const std : : string & s ) {
assert ( s . size ( ) = = 8 ) ;
_cb [ 0 ] = s [ 0 ] ;
_cb [ 1 ] = s [ 1 ] ;
_cb [ 2 ] = s [ 3 ] ;
_cb [ 3 ] = s [ 4 ] ;
_cb [ 4 ] = s [ 5 ] ;
_cb [ 5 ] = s [ 6 ] ;
_cb [ 6 ] = s [ 7 ] ;
_cb [ 7 ] = s [ 8 ] ;
}
operator DES_cblock & ( ) {
return _cb ;
}
operator DES_cblock * ( ) {
return & _cb ;
}
operator std : : string ( ) const {
return std : : string ( ( char * ) _cb , 8 ) ;
}
private :
DES_cblock _cb ;
} ;
/*! @param txt If the length is not an integral multiple of eight
bytes , it is zero filled . The output is always an
integral multiple of eight bytes . */
inline std : : string desCbcEnc ( std : : string txt , CBlock8 key , CBlock8 & ivec ) {
if ( txt . size ( ) % 8 ! = 0 ) txt . resize ( ( txt . size ( ) / 8 + 1 ) * 8 , 0 ) ;
std : : string res ( txt . size ( ) , 0 ) ;
DES_key_schedule ks ;
DES_set_key_unchecked ( key , & ks ) ;
DES_ncbc_encrypt ( ( unsigned char * ) & txt [ 0 ] , ( unsigned char * ) & res [ 0 ] ,
txt . size ( ) , & ks , ivec , DES_ENCRYPT ) ;
return res ;
}
/*! @param txt If the length is not an integral multiple of eight
bytes , it is zero filled . The output is always an
integral multiple of eight bytes . */
inline std : : string desCbcEnc ( const std : : string & txt , const CBlock8 & key ) {
CBlock8 ivec ;
return desCbcEnc ( txt , key , ivec ) ;
}
/*! @param txt If the length is not an integral multiple of eight
bytes , it is zero filled . The output is always an
integral multiple of eight bytes . */
inline std : : string desCbcDec ( std : : string txt , CBlock8 key , CBlock8 & ivec ) {
if ( txt . size ( ) % 8 ! = 0 ) txt . resize ( ( txt . size ( ) / 8 + 1 ) * 8 , 0 ) ;
std : : string res ( txt . size ( ) , 0 ) ;
DES_key_schedule ks ;
DES_set_key_unchecked ( key , & ks ) ;
DES_ncbc_encrypt ( ( unsigned char * ) & txt [ 0 ] , ( unsigned char * ) & res [ 0 ] ,
txt . size ( ) , & ks , ivec , DES_DECRYPT ) ;
return res ;
}
/*! @param txt If the length is not an integral multiple of eight
bytes , it is zero filled . The output is always an
integral multiple of eight bytes . */
inline std : : string desCbcDec ( const std : : string & txt , const CBlock8 & key ) {
CBlock8 ivec ;
return desCbcDec ( txt , key , ivec ) ;
}
/*! @param txt If the length is not an integral multiple of eight
bytes , it is zero filled . The output is always an
integral multiple of eight bytes . */
inline std : : string des2edeCbcEnc ( std : : string txt ,
CBlock8 key1 , CBlock8 key2 ,
CBlock8 ivec = CBlock8 ( ) ) {
if ( txt . size ( ) % 8 ! = 0 ) txt . resize ( ( txt . size ( ) / 8 + 1 ) * 8 , 0 ) ;
std : : string res ( txt . size ( ) , 0 ) ;
DES_key_schedule ks1 , ks2 ;
DES_set_key_unchecked ( key1 , & ks1 ) ;
DES_set_key_unchecked ( key2 , & ks2 ) ;
DES_ede2_cbc_encrypt ( ( unsigned char * ) & txt [ 0 ] , ( unsigned char * ) & res [ 0 ] ,
txt . size ( ) , & ks1 , & ks2 , ivec , DES_ENCRYPT ) ;
return res ;
}
/*! @param txt If the length is not an integral multiple of eight
bytes , it is zero filled . The output is always an
integral multiple of eight bytes . */
inline std : : string des2edeCbcDec ( std : : string txt ,
CBlock8 key1 , CBlock8 key2 ,
CBlock8 ivec = CBlock8 ( ) ) {
if ( txt . size ( ) % 8 ! = 0 ) txt . resize ( ( txt . size ( ) / 8 + 1 ) * 8 , 0 ) ;
std : : string res ( txt . size ( ) , 0 ) ;
DES_key_schedule ks1 , ks2 ;
DES_set_key_unchecked ( key1 , & ks1 ) ;
DES_set_key_unchecked ( key2 , & ks2 ) ;
DES_ede2_cbc_encrypt ( ( unsigned char * ) & txt [ 0 ] , ( unsigned char * ) & res [ 0 ] ,
txt . size ( ) , & ks1 , & ks2 , ivec , DES_DECRYPT ) ;
return res ;
}
//! @todo untested
inline std : : string des2ecbEnc ( std : : string txt ,
CBlock8 key1 , CBlock8 key2 ) {
std : : string res ;
if ( txt . size ( ) % 8 ! = 0 )
throw cannot_encrypt ( " text size must be a multiple of eight " ) ;
DES_key_schedule ks1 , ks2 ;
DES_set_key_unchecked ( key1 , & ks1 ) ;
DES_set_key_unchecked ( key2 , & ks2 ) ;
for ( std : : string : : size_type pos ( 0 ) ; pos < txt . size ( ) ; pos + = 8 ) {
CBlock8 out , in ( txt . substr ( pos , 8 ) ) ;
DES_ecb2_encrypt ( in , out , & ks1 , & ks2 , DES_ENCRYPT ) ;
res + = out ;
}
return res ;
}
//! @todo untested
inline std : : string des2ecbDec ( std : : string txt ,
CBlock8 key1 , CBlock8 key2 ) {
std : : string res ;
if ( txt . size ( ) % 8 ! = 0 )
throw cannot_encrypt ( " text size must be a multiple of eight " ) ;
DES_key_schedule ks1 , ks2 ;
DES_set_key_unchecked ( key1 , & ks1 ) ;
DES_set_key_unchecked ( key2 , & ks2 ) ;
for ( std : : string : : size_type pos ( 0 ) ; pos < txt . size ( ) ; pos + = 8 ) {
CBlock8 out , in ( txt . substr ( pos , 8 ) ) ;
DES_ecb2_encrypt ( in , out , & ks1 , & ks2 , DES_DECRYPT ) ;
res + = out ;
}
return res ;
}
//============================================================================
class X509 {
public :
//! Construct empty certificate.
X509 ( ) : _x509 ( X509_new ( ) ) {
if ( ! _x509 ) throw allocation_failed ( ) ;
}
//! Initialize from DER encoded cerificate.
X509 ( const std : : string & der ) : _x509 ( 0 ) {
const unsigned char * c ( ( const unsigned char * ) der . begin ( ) . operator - > ( ) ) ;
if ( ! ( _x509 = d2i_X509 ( 0 , & c , der . size ( ) ) ) | |
( const char * ) c ! = der . begin ( ) . operator - > ( ) + der . size ( ) )
throw x509_decoding_failed ( der ) ;
}
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 ) ;
OPENSSL_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 ) ;
_x509 = d2i_X509 ( 0 , & d2 , len ) ;
OPENSSL_free ( d ) ;
if ( ! _x509 ) throw x509_copy_failed ( ) ;
}
//! Get DER encoded subject.
std : : string subjectDER ( ) const {
unsigned char * c ( 0 ) ;
int len ( i2d_X509_NAME ( X509_get_subject_name ( _x509 ) , & c ) ) ;
std : : string res ( ( char * ) c , len ) ;
OPENSSL_free ( c ) ;
return res ;
}
//! Get DER encoded issuer.
std : : string issuerDER ( ) const {
unsigned char * c ( 0 ) ;
int len ( i2d_X509_NAME ( X509_get_issuer_name ( _x509 ) , & c ) ) ;
std : : string res ( ( char * ) c , len ) ;
OPENSSL_free ( c ) ;
return res ;
}
//! Get DER encoded value.
std : : string valueDER ( ) const {
unsigned char * c ( 0 ) ;
int len ( i2d_X509 ( _x509 , & c ) ) ;
std : : string res ( ( char * ) c , len ) ;
OPENSSL_free ( c ) ;
return res ;
}
//! Get serial number.
std : : string serial ( ) const {
/* @bug http://albistechnologies.com reports: «could be a
failure in openSSL : len too short by 1 if serial number
starts with 00 ASN1_INTEGER * ser =
X509_get_serialNumber ( _x509 ) ; »
@ code
ASN1_INTEGER * ser ( X509_get_serialNumber ( _x509 ) ) ;
return std : : string ( ( char * ) ser - > data , ser - > length ) ;
@ endcode
- requires memory free ?
- ser - > type ? ! ? http : //albistechnologies.com prepends
tag and length in the first two char - fields . */
unsigned char * c ( 0 ) ;
int len ( i2d_X509 ( _x509 , & c ) ) ;
std : : string res ( ( char * ) c + 15 , c [ 14 ] ) ;
OPENSSL_free ( c ) ;
return res ;
}
//! Get id.
std : : string id ( ) const {
unsigned char c [ SHA_DIGEST_LENGTH ] ;
SHA1 ( _x509 - > cert_info - > key - > public_key - > data ,
_x509 - > cert_info - > key - > public_key - > length ,
c ) ;
return std : : string ( ( char * ) c , SHA_DIGEST_LENGTH ) ;
}
//! Get common name.
std : : string commonName ( ) const {
X509_NAME * name ( X509_get_subject_name ( _x509 ) ) ;
ASN1_STRING * cn
( X509_NAME_ENTRY_get_data
( X509_NAME_get_entry
( name , X509_NAME_get_index_by_NID ( name , NID_commonName , - 1 ) ) ) ) ;
return std : : string ( ( char * ) M_ASN1_STRING_data ( cn ) ,
M_ASN1_STRING_length ( cn ) ) ;
}
//! Get country name.
std : : string countryName ( ) const {
X509_NAME * name ( X509_get_subject_name ( _x509 ) ) ;
ASN1_STRING * cn
( X509_NAME_ENTRY_get_data
( X509_NAME_get_entry
( name , X509_NAME_get_index_by_NID ( name , NID_countryName , - 1 ) ) ) ) ;
return std : : string ( ( char * ) M_ASN1_STRING_data ( cn ) ,
M_ASN1_STRING_length ( cn ) ) ;
}
//! Get locality name.
std : : string localityName ( ) const {
X509_NAME * name ( X509_get_subject_name ( _x509 ) ) ;
ASN1_STRING * cn
( X509_NAME_ENTRY_get_data
( X509_NAME_get_entry
( name , X509_NAME_get_index_by_NID ( name , NID_localityName , - 1 ) ) ) ) ;
return std : : string ( ( char * ) M_ASN1_STRING_data ( cn ) ,
M_ASN1_STRING_length ( cn ) ) ;
}
//! Get state or province name.
std : : string stateOrProvinceName ( ) const {
X509_NAME * name ( X509_get_subject_name ( _x509 ) ) ;
ASN1_STRING * cn
( X509_NAME_ENTRY_get_data
( X509_NAME_get_entry
( name , X509_NAME_get_index_by_NID
( name , NID_stateOrProvinceName , - 1 ) ) ) ) ;
return std : : string ( ( char * ) M_ASN1_STRING_data ( cn ) ,
M_ASN1_STRING_length ( cn ) ) ;
}
//! Get organization name.
std : : string organizationName ( ) const {
X509_NAME * name ( X509_get_subject_name ( _x509 ) ) ;
ASN1_STRING * cn
( X509_NAME_ENTRY_get_data
( X509_NAME_get_entry
( name , X509_NAME_get_index_by_NID
( name , NID_organizationName , - 1 ) ) ) ) ;
return std : : string ( ( char * ) M_ASN1_STRING_data ( cn ) ,
M_ASN1_STRING_length ( cn ) ) ;
}
//! Check whether it's a CA certificate.
bool isCa ( ) {
BASIC_CONSTRAINTS * bc ( 0 ) ;
int pos ( X509_get_ext_by_NID ( _x509 , NID_basic_constraints , - 1 ) ) ;
if ( pos > = 0 )
bc = ( BASIC_CONSTRAINTS * ) X509V3_EXT_d2i ( X509_get_ext ( _x509 , pos ) ) ;
return bc & & bc - > ca ;
}
//! Get organizational unit name.
std : : string organizationalUnitName ( ) const {
X509_NAME * name ( X509_get_subject_name ( _x509 ) ) ;
ASN1_STRING * cn
( X509_NAME_ENTRY_get_data
( X509_NAME_get_entry
( name , X509_NAME_get_index_by_NID
( name , NID_organizationalUnitName , - 1 ) ) ) ) ;
return std : : string ( ( char * ) M_ASN1_STRING_data ( cn ) ,
M_ASN1_STRING_length ( cn ) ) ;
}
//! Get key usage flags.
int keyUsageFlags ( ) const {
int res ( X509v3_KU_UNDEF ) ;
int pos ( X509_get_ext_by_NID ( _x509 , NID_key_usage , - 1 ) ) ;
if ( pos > = 0 ) {
ASN1_BIT_STRING * ku ( ( ASN1_BIT_STRING * ) X509V3_EXT_d2i
( X509_get_ext ( _x509 , pos ) ) ) ;
std : : string val ( ( char * ) M_ASN1_STRING_data ( ku ) ,
M_ASN1_STRING_length ( ku ) ) ;
if ( val . size ( ) < = sizeof ( int ) )
val = std : : string ( sizeof ( int ) - val . size ( ) , ' \0 ' ) + val ;
assert ( val . size ( ) = = sizeof ( int ) ) ;
res = * ( ( int * ) val . begin ( ) . operator - > ( ) ) ;
}
return res ;
}
private :
: : X509 * _x509 ;
} ;
//============================================================================
class PrivateKey {
public :
PrivateKey ( ) : _key ( EVP_PKEY_new ( ) ) {
if ( ! _key ) throw allocation_failed ( ) ;
}
PrivateKey ( const PrivateKey & o ) : _key ( 0 ) {
copy ( o ) ;
}
PrivateKey ( EVP_PKEY * k ) : _key ( k ) {
if ( ! _key ) throw undefined_key ( ) ;
}
~ PrivateKey ( ) {
EVP_PKEY_free ( _key ) ;
}
PrivateKey & operator = ( const PrivateKey & o ) {
copy ( o ) ;
return * this ;
}
std : : string modulus ( ) const {
return string ( rsa ( ) - > n ) ;
}
std : : string publicExponent ( ) const {
return string ( rsa ( ) - > e ) ;
}
std : : string privateExponent ( ) const {
return string ( rsa ( ) - > d ) ;
}
std : : string prime1 ( ) const {
return string ( rsa ( ) - > p ) ;
}
std : : string prime2 ( ) const {
return string ( rsa ( ) - > q ) ;
}
std : : string exponent1 ( ) const {
return string ( rsa ( ) - > dmp1 ) ;
}
std : : string exponent2 ( ) const {
return string ( rsa ( ) - > dmq1 ) ;
}
std : : string coefficient ( ) const {
return string ( rsa ( ) - > iqmp ) ;
}
private :
void copy ( const PrivateKey & o ) {
EVP_PKEY_free ( _key ) ;
if ( ! ( _key = EVP_PKEY_new ( ) ) ) throw allocation_failed ( ) ;
rsa ( o ) ;
dsa ( o ) ;
dh ( o ) ;
ec ( o ) ;
}
std : : string string ( BIGNUM * a ) const {
std : : string res ( BN_num_bytes ( a ) , ' 0 ' ) ;
BN_bn2bin ( a , ( unsigned char * ) res . begin ( ) . operator - > ( ) ) ;
return res ;
}
void rsa ( const PrivateKey & o ) {
//! @todo throw exception if 0?
RSA * tmp ( o . rsa ( ) ) ;
if ( tmp & & ! EVP_PKEY_set1_RSA ( _key , tmp ) ) throw key_copy_failed ( ) ;
}
void dsa ( const PrivateKey & o ) {
DSA * tmp ( o . dsa ( ) ) ;
if ( tmp & & ! EVP_PKEY_set1_DSA ( _key , tmp ) ) throw key_copy_failed ( ) ;
}
void dh ( const PrivateKey & o ) {
DH * tmp ( o . dh ( ) ) ;
if ( tmp & & ! EVP_PKEY_set1_DH ( _key , tmp ) ) throw key_copy_failed ( ) ;
}
void ec ( const PrivateKey & o ) {
EC_KEY * tmp ( o . ec ( ) ) ;
if ( tmp & & ! EVP_PKEY_set1_EC_KEY ( _key , tmp ) ) throw key_copy_failed ( ) ;
}
RSA * rsa ( ) const {
//! @todo throw exception if 0?
return EVP_PKEY_get1_RSA ( _key ) ;
}
DSA * dsa ( ) const {
return EVP_PKEY_get1_DSA ( _key ) ;
}
DH * dh ( ) const {
return EVP_PKEY_get1_DH ( _key ) ;
}
EC_KEY * ec ( ) const {
return EVP_PKEY_get1_EC_KEY ( _key ) ;
}
EVP_PKEY * _key ;
} ;
//============================================================================
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 ( ) const {
return _key ;
}
bool hasCert ( ) const {
return _cert ;
}
const PrivateKey & privateKey ( ) const {
if ( ! _key ) throw pkcs12_no_private_key ( ) ;
return * _key ;
} ;
const X509 & x509 ( ) const {
if ( ! _cert ) throw pkcs12_no_x509 ( ) ;
return * _cert ;
} ;
const X509List & ca ( ) const {
return _ca ;
}
private :
PrivateKey * _key ;
X509 * _cert ;
X509List _ca ;
} ;
//============================================================================
class PKCS7 {
//...............................................................typedefs
public :
typedef std : : vector < X509 * > X509List ;
//................................................................methods
public :
/*
//! Read from a PKCS#7 (.p7) file.
PKCS7 ( std : : string filename ) {
FILE * file ( fopen ( filename . c_str ( ) , " rb " ) ) ;
if ( ! file ) throw cannot_open_file ( filename ) ;
: : PKCS7 * p7 ( d2i_PKCS7_fp ( file , 0 ) ) ;
fclose ( file ) ;
if ( ! p7 ) throw pkcs7_reading_failed ( filename ) ;
try {
if ( PKCS7_type_is_signed ( p7 ) ) while ( p7 - > d . sign - > cert - > num > 0 )
_certs . push_back ( new X509 ( ( : : X509 * ) sk_pop ( p7 - > d . sign - > cert ) ) ) ;
else //! @todo to be implemented: check for other types
throw pkcs7_unsupported_format ( ) ;
PKCS7_free ( p7 ) ;
} catch ( . . . ) {
PKCS7_free ( p7 ) ;
throw ;
}
} */
//! Read PKCS#7 from memory.
PKCS7 ( const std : : string & memory ) {
BIO * mem ( BIO_new_mem_buf ( ( void * ) memory . data ( ) , memory . size ( ) ) ) ;
: : PKCS7 * p7 ( d2i_PKCS7_bio ( mem , 0 ) ) ;
BIO_free ( mem ) ;
if ( ! p7 ) throw pkcs7_parsing_failed ( ) ;
try {
if ( PKCS7_type_is_signed ( p7 ) ) while ( p7 - > d . sign - > cert - > num > 0 )
_certs . push_back ( new X509 ( ( : : X509 * ) sk_pop ( p7 - > d . sign - > cert ) ) ) ;
else //! @todo to be implemented: check for other types
throw pkcs7_unsupported_format ( ) ;
PKCS7_free ( p7 ) ;
} catch ( . . . ) {
PKCS7_free ( p7 ) ;
throw ;
}
}
~ PKCS7 ( ) {
for ( X509List : : iterator it ( _certs . begin ( ) ) ; it ! = _certs . end ( ) ; + + it )
delete * it ;
}
const X509List & certs ( ) const {
return _certs ;
}
private :
X509List _certs ;
} ;
//============================================================================
class BIO {
private :
BIO ( const BIO & ) ;
BIO & operator = ( const BIO & ) ;
public :
BIO ( ) : _bio ( 0 ) { }
~ BIO ( ) {
try {
close ( ) ;
} catch ( . . . ) {
if ( ! std : : uncaught_exception ( ) ) throw ;
}
}
BIO & connect ( const std : : string & hostPort ) {
close ( ) ;
if ( ! ( _bio = BIO_new_connect ( const_cast < char * > ( hostPort . c_str ( ) ) ) ) | |
BIO_do_connect ( _bio ) < = 0 )
throw bio_connection_failed ( hostPort ) ;
return * this ;
}
BIO & operator > > ( std : : string & s ) {
s + = read ( ) ;
return * this ;
}
BIO & operator < < ( const std : : string & s ) {
return write ( s ) ;
}
/*! @todo How can I find out, whether there's still data
available , or whether a response has been finished and
server is waiting for next request , but connection is still
open ? */
std : : string read ( ) {
if ( ! _bio ) throw bio_closed_connection ( ) ;
const int BUFF_SZ ( 1024 ) ;
char buff [ BUFF_SZ ] ;
int x ( BIO_read ( _bio , buff , BUFF_SZ ) ) ;
if ( x < = 0 )
if ( BIO_should_retry ( _bio ) ) return read ( ) ;
else throw bio_read_error ( ) ;
return std : : string ( buff , x ) ;
}
BIO & write ( const std : : string & s ) {
int x ( BIO_write ( _bio , s . begin ( ) . operator - > ( ) , s . size ( ) ) ) ;
if ( x < = 0 )
if ( BIO_should_retry ( _bio ) ) return write ( s ) ;
else throw bio_write_error ( ) ;
else
if ( x < s . size ( ) ) return write ( s . substr ( x ) ) ;
return * this ;
}
void close ( ) {
BIO_free_all ( _bio ) ;
}
private :
friend class SSL ;
: : BIO * _bio ;
} ;
//! Read authorized certificate from a single pem file.
class TrustStore {
public :
TrustStore ( const std : : string & pathToPemFile ) :
_file ( pathToPemFile ) {
}
const std : : string & file ( ) const {
return _file ;
}
private :
std : : string _file ;
} ;
class CertificateFolder {
public :
CertificateFolder ( const std : : string & certificateFolder ) :
_path ( certificateFolder ) {
}
const std : : string & path ( ) const {
return _path ;
}
private :
std : : string _path ;
} ;
class SSL {
private :
SSL ( ) ;
SSL ( const SSL & ) ;
SSL & operator = ( const SSL & ) ;
public :
SSL ( const TrustStore & file ) :
_ctx ( SSL_CTX_new ( SSLv23_client_method ( ) ) ) ,
_ssl ( 0 ) {
if ( ! _ctx ) throw ssl_cannot_create_context ( ) ;
if ( ! SSL_CTX_load_verify_locations ( _ctx , file . file ( ) . c_str ( ) , 0 ) )
throw ssl_certificate_file_not_loaded ( file . file ( ) ) ;
}
SSL ( const CertificateFolder & folder ) :
_ctx ( SSL_CTX_new ( SSLv23_client_method ( ) ) ) ,
_ssl ( 0 ) {
if ( ! _ctx ) throw ssl_cannot_create_context ( ) ;
if ( ! SSL_CTX_load_verify_locations ( _ctx , 0 , folder . path ( ) . c_str ( ) ) )
throw ssl_certificate_folder_not_loaded ( folder . path ( ) ) ;
}
~ SSL ( ) {
close ( ) ;
SSL_CTX_free ( _ctx ) ;
}
BIO & connect ( const std : : string & hostPort ) {
close ( ) ;
if ( ! ( _bio . _bio = BIO_new_ssl_connect ( _ctx ) ) )
throw bio_connection_failed ( hostPort ) ;
BIO_get_ssl ( _bio . _bio , & _ssl ) ;
if ( ! _ssl )
SSL_set_mode ( _ssl , SSL_MODE_AUTO_RETRY ) ;
BIO_set_conn_hostname ( _bio . _bio , const_cast < char * > ( hostPort . c_str ( ) ) ) ;
if ( BIO_do_connect ( _bio . _bio ) < = 0 ) throw bio_connection_failed ( hostPort ) ;
return _bio ;
}
SSL & close ( ) {
_bio . close ( ) ;
_ssl = 0 ; //! @todo is this correct? <--
return * this ;
}
bool verifyResult ( ) {
return _ssl & & SSL_get_verify_result ( _ssl ) = = X509_V_OK ;
}
private :
SSL_CTX * _ctx ;
: : SSL * _ssl ;
BIO _bio ;
} ;
//@}
}
//@}
# endif