/*! @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>
namespace pcsc {
std : : string version ( ) ;
}
# ifndef OPENSSL_LOG
# include <iostream>
# if __GNUC__ >= 2
//! Openssl Logging
/*! If you want to change openssl logging mechanism, just
redefine your own OPENSSL_LOG macro before < code > # include
& lt ; openssl . hxx & gt ; < / code > . Define it empty for no logging at
all . By default logs to < code > std : : clog < / code > . */
# define OPENSSL_LOG(X) std::clog<<X<<" @ "<<__PRETTY_FUNCTION__<<std::endl
# else
//! Openssl Logging
/*! If you want to change openssl logging mechanism, just
redefine your own OPENSSL_LOG macro before < code > # include
& lt ; openssl . hxx & gt ; < / code > . Define it empty for no logging at
all . By default logs to < code > std : : clog < / code > . */
# define OPENSSL_LOG(X) std::clog<<X<<" @ "<<__FILE__<<__LINE__<<std::endl
# endif
# endif
/*! @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 ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
~ 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 ) ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class cannot_init : public openssl_error {
public :
cannot_init ( const std : : string & reason ) throw ( ) :
openssl_error ( " openssl initialization: " + reason ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class encryption_error : public openssl_error {
public :
encryption_error ( const std : : string & reason ) throw ( ) :
openssl_error ( " encryption: " + reason ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class pkcs12_error : public openssl_error {
public :
pkcs12_error ( const std : : string & reason ) throw ( ) :
openssl_error ( " pkcs12: " + reason ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class pkcs7_error : public openssl_error {
public :
pkcs7_error ( const std : : string & reason ) throw ( ) :
openssl_error ( " pkcs7: " + reason ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class x509_error : public openssl_error {
public :
x509_error ( const std : : string & reason ) throw ( ) :
openssl_error ( " x509: " + reason ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class key_error : public openssl_error {
public :
key_error ( const std : : string & reason ) throw ( ) :
openssl_error ( " private key: " + reason ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class tcp_error : public openssl_error {
public :
tcp_error ( const std : : string & reason ) throw ( ) :
openssl_error ( " tcp " + reason ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class ssl_error : public openssl_error {
public :
ssl_error ( const std : : string & reason ) throw ( ) :
openssl_error ( " ssl: " + reason ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class cannot_encrypt : public encryption_error {
public :
cannot_encrypt ( std : : string reason ) throw ( ) :
encryption_error ( " cannot encrypt text: " + reason ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class allocation_failed : public x509_error {
public :
allocation_failed ( ) throw ( ) :
x509_error ( " memory allocation failed " ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
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 ) ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class undefined_certificate : public x509_error {
public :
undefined_certificate ( ) throw ( ) :
x509_error ( " certificate must not be 0 " ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class x509_parsing_failed : public x509_error {
public :
x509_parsing_failed ( ) throw ( ) :
x509_error ( " parsing DER encoded certificate failed " ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class x509_copy_failed : public x509_error {
public :
x509_copy_failed ( ) throw ( ) :
x509_error ( " certificate object copy failed " ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class key_copy_failed : public key_error {
public :
key_copy_failed ( ) throw ( ) :
key_error ( " key object copy failed " ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class undefined_key : public key_error {
public :
undefined_key ( ) throw ( ) :
key_error ( " private key must not be 0 " ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
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 ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
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 ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class pkcs12_no_private_key : public pkcs12_error {
public :
pkcs12_no_private_key ( ) throw ( ) : pkcs12_error ( " no private key " ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class pkcs12_no_x509 : public pkcs12_error {
public :
pkcs12_no_x509 ( ) throw ( ) : pkcs12_error ( " no x509 certificate " ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
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 ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class pkcs7_parsing_failed : public pkcs7_error {
public :
pkcs7_parsing_failed ( ) throw ( ) :
pkcs7_error ( " parsing DER encoded p7 failed " ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
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 " ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class pkcs7_no_x509 : public pkcs7_error {
public :
pkcs7_no_x509 ( ) throw ( ) : pkcs7_error ( " no x509 certificate " ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class cannot_open_file : public exception {
public :
cannot_open_file ( const std : : string & file ) throw ( ) :
exception ( " cannot open file: " + file ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class tcp_connection_failed : public tcp_error {
public :
tcp_connection_failed ( const std : : string & hostPort ) throw ( ) :
tcp_error ( " connection failed to: " + hostPort ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class tcp_server_not_specified : public tcp_error {
public :
tcp_server_not_specified ( ) throw ( ) :
tcp_error ( " reconnect without prior connect " ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class tcp_closed_connection : public tcp_error {
public :
tcp_closed_connection ( ) throw ( ) : tcp_error ( " closed connection " ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class tcp_read_error : public tcp_error {
public :
tcp_read_error ( ) throw ( ) : tcp_error ( " read error " ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class tcp_write_error : public tcp_error {
public :
tcp_write_error ( ) throw ( ) : tcp_error ( " write error " ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class ssl_cannot_create_context : public ssl_error {
public :
ssl_cannot_create_context ( ) throw ( ) : ssl_error ( " cannot create context " ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
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 ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
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 ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class ssl_no_connection : public ssl_error {
public :
ssl_no_connection ( ) throw ( ) :
ssl_error ( " no ssl connection " ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
} ;
//----------------------------------------------------------------------------
class ssl_verification_failed : public ssl_error {
public :
ssl_verification_failed ( int num ) throw ( ) :
ssl_error ( " certificate verification failed: " + reason ( num ) ) {
OPENSSL_LOG ( " **** exception **** " ) ;
}
static std : : string reason ( int num ) {
switch ( num ) {
case X509_V_OK :
return " X509_V_OK: Ok the operation was successful. " ;
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT :
return " X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: Unable to get issuer "
" certificate the issuer certificate could not be found: this "
" occurs if the issuer certificate of an untrusted certificate "
" cannot be found. " ;
case X509_V_ERR_UNABLE_TO_GET_CRL : " X509_V_ERR_UNABLE_TO_GET_CRL: "
" unable to get certificate CRL the CRL of a certificate could "
" not be found. Unused. " ;
case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE :
return " 4 X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: Unable to "
" decrypt certificate's signature the certificate signature "
" could not be decrypted. This means that the actual signature "
" value could not be determined rather than it not matching the "
" expected value, this is only meaningful for RSA keys. " ;
case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY :
return " X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: Unable to "
" decode issuer public key the public key in the certificate "
" SubjectPublicKeyInfo could not be read. " ;
case X509_V_ERR_CERT_SIGNATURE_FAILURE :
return " X509_V_ERR_CERT_SIGNATURE_FAILURE: Certificate signature "
" failure the signature of the certificate is invalid. " ;
case X509_V_ERR_CRL_SIGNATURE_FAILURE :
return " X509_V_ERR_CRL_SIGNATURE_FAILURE: Crl signature failure "
" the signature of the certificate is invalid. Unused. " ;
case X509_V_ERR_CERT_NOT_YET_VALID :
return " X509_V_ERR_CERT_NOT_YET_VALID: Certificate is not yet "
" valid the certificate is not yet valid: the notBefore date is "
" after the current time. " ;
case X509_V_ERR_CERT_HAS_EXPIRED :
return " X509_V_ERR_CERT_HAS_EXPIRED: Certificate has expired the "
" certificate has expired: that is the notAfter date is before "
" the current time. " ;
case X509_V_ERR_CRL_NOT_YET_VALID :
return " X509_V_ERR_CRL_NOT_YET_VALID: Crl is not yet valid the CRL "
" is not yet valid. Unused. " ;
case X509_V_ERR_CRL_HAS_EXPIRED :
return " X509_V_ERR_CRL_HAS_EXPIRED: Crl has expired the CRL has "
" expired. Unused. " ;
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD :
return " X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: Format error in "
" certificate's notBefore field the certificate notBefore field "
" contains an invalid time. " ;
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD :
return " X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: Format error in "
" certificate's notAfter field the certificate notAfter field "
" contains an invalid time. " ;
case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD :
return " X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: Format error in "
" CRL's lastUpdate field the CRL lastUpdate field contains an "
" invalid time. Unused. " ;
case X509_V_ERR_OUT_OF_MEM :
return " X509_V_ERR_OUT_OF_MEM: Out of memory an error occurred "
" trying to allocate memory. This should never happen. " ;
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT :
return " X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: Self signed "
" certificate the passed certificate is self signed and the same "
" certificate cannot be found in the list of trusted "
" certificates. " ;
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN :
return " X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: Self signed "
" certificate in certificate chain the certificate chain could "
" be built up using the untrusted certificates but the root "
" could not be found locally. " ;
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY :
return " X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: Unable to "
" get local issuer certificate the issuer certificate of a "
" locally looked up certificate could not be found. This "
" normally means the list of trusted certificates is not "
" complete. " ;
case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE :
return " X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: Unable to "
" verify the first certificate no signatures could be verified "
" because the chain contains only one certificate and it is not "
" self signed. " ;
case X509_V_ERR_CERT_CHAIN_TOO_LONG :
return " X509_V_ERR_CERT_CHAIN_TOO_LONG: Certificate chain too long "
" the certificate chain length is greater than the supplied "
" maximum depth. Unused. " ;
case X509_V_ERR_CERT_REVOKED :
return " X509_V_ERR_CERT_REVOKED: Certificate revoked the "
" certificate has been revoked. Unused. " ;
case X509_V_ERR_INVALID_CA :
return " X509_V_ERR_INVALID_CA: Invalid CA certificate a CA "
" certificate is invalid. Either it is not a CA or its "
" extensions are not consistent with the supplied purpose. " ;
case X509_V_ERR_INVALID_PURPOSE :
return " X509_V_ERR_INVALID_PURPOSE: Unsupported certificate "
" purpose the supplied certificate cannot be used for the "
" specified purpose. " ;
case X509_V_ERR_CERT_UNTRUSTED :
return " X509_V_ERR_CERT_UNTRUSTED: Certificate not trusted the "
" root CA is not marked as trusted for the specified purpose. " ;
case X509_V_ERR_CERT_REJECTED :
return " X509_V_ERR_CERT_REJECTED: Certificate rejected the root CA "
" is marked to reject the specified purpose. " ;
case X509_V_ERR_SUBJECT_ISSUER_MISMATCH :
return " X509_V_ERR_SUBJECT_ISSUER_MISMATCH: Subject issuer "
" mismatch the current candidate issuer certificate was rejected "
" because its subject name did not match the issuer name of the "
" current certificate. Only displayed when the -issuer_checks "
" option is set. " ;
case X509_V_ERR_AKID_SKID_MISMATCH :
return " X509_V_ERR_AKID_SKID_MISMATCH: Authority and subject key "
" identifier mismatch the current candidate issuer certificate "
" was rejected because its subject key identifier was present "
" and did not match the authority key identifier current "
" certificate. Only displayed when the -issuer_checks option is "
" set. " ;
case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH :
return " X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH: Authority and "
" issuer serial number mismatch the current candidate issuer "
" certificate was rejected because its issuer name and serial "
" number was present and did not match the authority key "
" identifier of the current certificate. Only displayed when the "
" -issuer_checks option is set. " ;
case X509_V_ERR_KEYUSAGE_NO_CERTSIGN :
return " X509_V_ERR_KEYUSAGE_NO_CERTSIGN: Ey usage does not include "
" certificate signing the current candidate issuer certificate "
" was rejected because its keyUsage extension does not permit "
" certificate signing. " ;
default : {
std : : stringstream ss ;
ss < < " Unknown certificate validation error code: " < < num ;
return ss . str ( ) ;
}
}
}
} ;
//@}
//! @addtogroup openssllib
//@{
//============================================================================
//! Initializes OpenSSL. Must be instanciated exactly once.
class Init {
public :
Init ( ) {
OPENSSL_LOG ( " log " ) ;
SSL_load_error_strings ( ) ;
ERR_load_BIO_strings ( ) ;
OpenSSL_add_all_algorithms ( ) ;
if ( ! SSL_library_init ( ) ) throw cannot_init ( " cannot init SSL library " ) ;
}
} ;
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 ;
} ;
//! Encrypt a string using DES.
/*! @param txt text to encrypt - size must be a multiple of 8
@ param key1 DES key for encryption */
inline std : : string desEnc ( const std : : string & txt , CBlock8 key1 ) {
OPENSSL_LOG ( " log " ) ;
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 ( 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 ;
}
//! Decrypt a DES encrypted string.
/*! @param txt text to decrypt - size must be a multiple of 8
@ param key1 DES key for decryption */
inline std : : string desDec ( const std : : string & txt , CBlock8 key1 ) {
OPENSSL_LOG ( " log " ) ;
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 ( 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 ;
}
//! DES CBC Encryption
/*! @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 ) {
OPENSSL_LOG ( " log " ) ;
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 ;
}
//! DES CBC Encryption with empty vector
/*! @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 ) {
OPENSSL_LOG ( " log " ) ;
CBlock8 ivec ;
return desCbcEnc ( txt , key , ivec ) ;
}
//! DES CBC Decryption
/*! @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 ) {
OPENSSL_LOG ( " log " ) ;
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 ;
}
//! DES CBC Decryption with empty vector
/*! @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 ) {
OPENSSL_LOG ( " log " ) ;
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 ( ) ) {
OPENSSL_LOG ( " log " ) ;
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 ( ) ) {
OPENSSL_LOG ( " log " ) ;
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 ) {
OPENSSL_LOG ( " log " ) ;
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 ) {
OPENSSL_LOG ( " log " ) ;
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 ;
}
//============================================================================
//! X509 Certificate
class X509 {
public :
//! Construct empty certificate.
X509 ( ) : _x509 ( X509_new ( ) ) {
OPENSSL_LOG ( " log " ) ;
if ( ! _x509 ) throw allocation_failed ( ) ;
}
//! Initialize from DER encoded cerificate.
X509 ( const std : : string & der ) : _x509 ( 0 ) {
OPENSSL_LOG ( " log " ) ;
unsigned char * c ( ( 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 ) {
OPENSSL_LOG ( " log " ) ;
unsigned char * d ( 0 ) ;
int len ( i2d_X509 ( o . _x509 , & d ) ) ;
if ( ! len ) throw x509_copy_failed ( ) ;
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 ) {
OPENSSL_LOG ( " log " ) ;
if ( ! _x509 ) throw undefined_certificate ( ) ;
}
~ X509 ( ) {
OPENSSL_LOG ( " log " ) ;
X509_free ( _x509 ) ;
}
X509 & operator = ( const X509 & o ) {
OPENSSL_LOG ( " log " ) ;
X509_free ( _x509 ) ;
_x509 = 0 ;
unsigned char * d ( 0 ) ;
int len ( i2d_X509 ( o . _x509 , & d ) ) ;
if ( ! len ) throw x509_copy_failed ( ) ;
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 {
OPENSSL_LOG ( " log " ) ;
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 {
OPENSSL_LOG ( " log " ) ;
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 {
OPENSSL_LOG ( " log " ) ;
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 {
OPENSSL_LOG ( " log " ) ;
/* @bug tcp://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 ? ! ? tcp : //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 {
OPENSSL_LOG ( " log " ) ;
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 {
OPENSSL_LOG ( " log " ) ;
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 {
OPENSSL_LOG ( " log " ) ;
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 {
OPENSSL_LOG ( " log " ) ;
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 {
OPENSSL_LOG ( " log " ) ;
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 {
OPENSSL_LOG ( " log " ) ;
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 ( ) {
OPENSSL_LOG ( " log " ) ;
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 {
OPENSSL_LOG ( " log " ) ;
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 {
OPENSSL_LOG ( " log " ) ;
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 ;
} ;
//============================================================================
//! Private certificate key
class PrivateKey {
public :
PrivateKey ( ) : _key ( EVP_PKEY_new ( ) ) {
OPENSSL_LOG ( " log " ) ;
if ( ! _key ) throw allocation_failed ( ) ;
}
PrivateKey ( const PrivateKey & o ) : _key ( 0 ) {
OPENSSL_LOG ( " log " ) ;
copy ( o ) ;
}
PrivateKey ( EVP_PKEY * k ) : _key ( k ) {
OPENSSL_LOG ( " log " ) ;
if ( ! _key ) throw undefined_key ( ) ;
}
~ PrivateKey ( ) {
OPENSSL_LOG ( " log " ) ;
EVP_PKEY_free ( _key ) ;
}
PrivateKey & operator = ( const PrivateKey & o ) {
OPENSSL_LOG ( " log " ) ;
copy ( o ) ;
return * this ;
}
std : : string modulus ( ) const {
OPENSSL_LOG ( " log " ) ;
return string ( rsa ( ) - > n ) ;
}
std : : string publicExponent ( ) const {
OPENSSL_LOG ( " log " ) ;
return string ( rsa ( ) - > e ) ;
}
std : : string privateExponent ( ) const {
OPENSSL_LOG ( " log " ) ;
return string ( rsa ( ) - > d ) ;
}
std : : string prime1 ( ) const {
OPENSSL_LOG ( " log " ) ;
return string ( rsa ( ) - > p ) ;
}
std : : string prime2 ( ) const {
OPENSSL_LOG ( " log " ) ;
return string ( rsa ( ) - > q ) ;
}
std : : string exponent1 ( ) const {
OPENSSL_LOG ( " log " ) ;
return string ( rsa ( ) - > dmp1 ) ;
}
std : : string exponent2 ( ) const {
OPENSSL_LOG ( " log " ) ;
return string ( rsa ( ) - > dmq1 ) ;
}
std : : string coefficient ( ) const {
OPENSSL_LOG ( " log " ) ;
return string ( rsa ( ) - > iqmp ) ;
}
private :
void copy ( const PrivateKey & o ) {
OPENSSL_LOG ( " log " ) ;
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 {
OPENSSL_LOG ( " log " ) ;
std : : string res ( BN_num_bytes ( a ) , ' 0 ' ) ;
BN_bn2bin ( a , ( unsigned char * ) res . begin ( ) . operator - > ( ) ) ;
return res ;
}
void rsa ( const PrivateKey & o ) {
OPENSSL_LOG ( " log " ) ;
//! @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 ) {
OPENSSL_LOG ( " log " ) ;
DSA * tmp ( o . dsa ( ) ) ;
if ( tmp & & ! EVP_PKEY_set1_DSA ( _key , tmp ) ) throw key_copy_failed ( ) ;
}
void dh ( const PrivateKey & o ) {
OPENSSL_LOG ( " log " ) ;
DH * tmp ( o . dh ( ) ) ;
if ( tmp & & ! EVP_PKEY_set1_DH ( _key , tmp ) ) throw key_copy_failed ( ) ;
}
/* Not available on mac osx
void ec ( const PrivateKey & o ) {
OPENSSL_LOG ( " log " ) ;
EC_KEY * tmp ( o . ec ( ) ) ;
if ( tmp & & ! EVP_PKEY_set1_EC_KEY ( _key , tmp ) ) throw key_copy_failed ( ) ;
}
*/
RSA * rsa ( ) const {
OPENSSL_LOG ( " log " ) ;
//! @todo throw exception if 0?
return EVP_PKEY_get1_RSA ( _key ) ;
}
DSA * dsa ( ) const {
OPENSSL_LOG ( " log " ) ;
return EVP_PKEY_get1_DSA ( _key ) ;
}
DH * dh ( ) const {
OPENSSL_LOG ( " log " ) ;
return EVP_PKEY_get1_DH ( _key ) ;
}
/* Not available on mac osx
EC_KEY * ec ( ) const {
OPENSSL_LOG ( " log " ) ;
return EVP_PKEY_get1_EC_KEY ( _key ) ;
} */
EVP_PKEY * _key ;
} ;
//============================================================================
//! PKCS#12 certificate file handler
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 ) {
OPENSSL_LOG ( " log " ) ;
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 ( ) {
OPENSSL_LOG ( " log " ) ;
delete _key ;
delete _cert ;
for ( X509List : : iterator it ( _ca . begin ( ) ) ; it ! = _ca . end ( ) ; + + it )
delete * it ;
}
bool hasPrivateKey ( ) const {
OPENSSL_LOG ( " log " ) ;
return _key ;
}
bool hasCert ( ) const {
OPENSSL_LOG ( " log " ) ;
return _cert ;
}
const PrivateKey & privateKey ( ) const {
OPENSSL_LOG ( " log " ) ;
if ( ! _key ) throw pkcs12_no_private_key ( ) ;
return * _key ;
} ;
const X509 & x509 ( ) const {
OPENSSL_LOG ( " log " ) ;
if ( ! _cert ) throw pkcs12_no_x509 ( ) ;
return * _cert ;
} ;
const X509List & ca ( ) const {
OPENSSL_LOG ( " log " ) ;
return _ca ;
}
private :
PrivateKey * _key ;
X509 * _cert ;
X509List _ca ;
} ;
//============================================================================
//! PKCS#7 certificate file handler
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 ) {
OPENSSL_LOG ( " log " ) ;
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 ( ) {
OPENSSL_LOG ( " log " ) ;
for ( X509List : : iterator it ( _certs . begin ( ) ) ; it ! = _certs . end ( ) ; + + it )
delete * it ;
}
const X509List & certs ( ) const {
OPENSSL_LOG ( " log " ) ;
return _certs ;
}
private :
X509List _certs ;
} ;
//============================================================================
//! TCP Connection
class TCP {
private :
TCP ( const TCP & ) ;
TCP & operator = ( const TCP & ) ;
public :
TCP ( ) : _bio ( 0 ) {
OPENSSL_LOG ( " log " ) ;
}
TCP ( const std : : string & hostPort ) : _bio ( 0 ) {
OPENSSL_LOG ( " log " ) ;
connect ( hostPort ) ;
}
virtual ~ TCP ( ) {
OPENSSL_LOG ( " log " ) ;
try {
close ( ) ;
} catch ( . . . ) {
if ( ! std : : uncaught_exception ( ) ) throw ;
}
}
virtual TCP & connect ( const std : : string & hostPort ) {
OPENSSL_LOG ( " log " ) ;
close ( ) ;
if ( ! ( _bio = BIO_new_connect ( const_cast < char * > ( hostPort . c_str ( ) ) ) ) | |
BIO_do_connect ( _bio ) < = 0 )
throw tcp_connection_failed ( hostPort ) ;
_hostPort = hostPort ;
return * this ;
}
virtual TCP & connect ( ) {
if ( ! _hostPort . size ( ) ) throw tcp_server_not_specified ( ) ;
close ( ) ;
connect ( _hostPort ) ;
return * this ;
}
operator bool ( ) const {
OPENSSL_LOG ( " log " ) ;
return _bio > 0 ;
}
TCP & operator > > ( std : : string & s ) {
OPENSSL_LOG ( " log " ) ;
s + = read ( ) ;
return * this ;
}
TCP & operator < < ( const std : : string & s ) {
OPENSSL_LOG ( " log " ) ;
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 ( ) {
OPENSSL_LOG ( " log " ) ;
if ( _bio < = 0 ) throw tcp_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 {
close ( ) ;
if ( x = = 0 ) return std : : string ( ) ;
else throw tcp_read_error ( ) ;
}
else if ( x = = 1024 ) return std : : string ( buff , x ) + read ( ) ;
return std : : string ( buff , x ) ;
}
TCP & write ( const std : : string & s ) {
OPENSSL_LOG ( " log " ) ;
if ( _bio < = 0 ) throw tcp_closed_connection ( ) ;
int x ( BIO_write ( _bio , s . begin ( ) . operator - > ( ) , s . size ( ) ) ) ;
if ( x < = 0 )
if ( BIO_should_retry ( _bio ) ) return write ( s ) ;
else {
close ( ) ;
throw tcp_write_error ( ) ;
}
else if ( x < s . size ( ) ) return write ( s . substr ( x ) ) ;
return * this ;
}
virtual TCP & close ( ) {
OPENSSL_LOG ( " log " ) ;
if ( _bio > 0 ) BIO_free_all ( _bio ) ;
_bio = 0 ;
return * this ;
}
protected :
: : BIO * _bio ;
std : : string _hostPort ;
} ;
//! Read authorized certificate from a single pem file.
class TrustStore {
public :
TrustStore ( const std : : string & pathToPemFile ) :
_file ( pathToPemFile ) {
OPENSSL_LOG ( " log " ) ;
}
const std : : string & file ( ) const {
OPENSSL_LOG ( " log " ) ;
return _file ;
}
private :
std : : string _file ;
} ;
class CertificateFolder {
public :
CertificateFolder ( const std : : string & certificateFolder ) :
_path ( certificateFolder ) {
OPENSSL_LOG ( " log " ) ;
}
const std : : string & path ( ) const {
OPENSSL_LOG ( " log " ) ;
return _path ;
}
private :
std : : string _path ;
} ;
class SSL : public TCP {
private :
SSL ( ) ;
SSL ( const SSL & ) ;
SSL & operator = ( const SSL & ) ;
public :
SSL ( const TrustStore & file ) :
_ctx ( SSL_CTX_new ( SSLv23_client_method ( ) ) ) ,
_ssl ( 0 ) {
OPENSSL_LOG ( " log " ) ;
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 ) {
OPENSSL_LOG ( " log " ) ;
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 ( ) {
OPENSSL_LOG ( " log " ) ;
close ( ) ;
SSL_CTX_free ( _ctx ) ;
}
virtual SSL & connect ( const std : : string & hostPort ) {
OPENSSL_LOG ( " log " ) ;
close ( ) ;
if ( ! ( _bio = BIO_new_ssl_connect ( _ctx ) ) )
throw tcp_connection_failed ( hostPort ) ;
BIO_get_ssl ( _bio , & _ssl ) ;
if ( ! _ssl ) throw ssl_no_connection ( ) ;
SSL_set_mode ( _ssl , SSL_MODE_AUTO_RETRY ) ;
BIO_set_conn_hostname ( _bio , const_cast < char * > ( hostPort . c_str ( ) ) ) ;
if ( BIO_do_connect ( _bio ) < = 0 )
throw tcp_connection_failed ( hostPort ) ;
verify ( ) ;
return * this ;
}
virtual SSL & close ( ) {
OPENSSL_LOG ( " log " ) ;
TCP : : close ( ) ;
_ssl = 0 ; //! @todo is this correct? <--
return * this ;
}
protected :
void verify ( ) {
OPENSSL_LOG ( " log " ) ;
if ( ! _ssl ) throw ssl_no_connection ( ) ;
int res ( SSL_get_verify_result ( _ssl ) ) ;
if ( res ! = X509_V_OK ) throw ssl_verification_failed ( res ) ;
}
private :
SSL_CTX * _ctx ;
: : SSL * _ssl ;
} ;
//@}
}
inline std : : ostream & operator < < ( std : : ostream & os , openssl : : TCP & is ) {
return os < < is . read ( ) ;
}
inline std : : istream & operator > > ( std : : istream & is , openssl : : TCP & os ) {
std : : string s ;
is > > s ;
os < < s ;
return is ;
}
//@}
# endif