From 522fa3b4f1356dbd29f17f17c976ab28fb1f61da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20W=C3=A4ckerlin?= Date: Wed, 3 Mar 2010 15:37:44 +0000 Subject: [PATCH] TCP- and SSL-Connection Examples --- doc/examples/makefile.am | 18 +- doc/examples/openssl-ssl-demo.cxx | 28 ++ doc/examples/openssl-tcp-demo.cxx | 25 ++ src/openssl.hxx | 547 +++++++++++++++++++++++++----- 4 files changed, 526 insertions(+), 92 deletions(-) create mode 100644 doc/examples/openssl-ssl-demo.cxx create mode 100644 doc/examples/openssl-tcp-demo.cxx diff --git a/doc/examples/makefile.am b/doc/examples/makefile.am index 4cf9412..d4092f9 100644 --- a/doc/examples/makefile.am +++ b/doc/examples/makefile.am @@ -3,7 +3,7 @@ ## 1 2 3 4 5 6 7 8 ## 45678901234567890123456789012345678901234567890123456789012345678901234567890 -noinst_PROGRAMS = pcsc-demo cryptoki-demo +noinst_PROGRAMS = pcsc-demo cryptoki-demo openssl-tcp-demo openssl-ssl-demo AM_CPPFLAGS = -I${top_srcdir}/src if !MINGW32 @@ -27,4 +27,20 @@ else cryptoki_demo_LDADD += -ldl -lpthread -lssl endif +openssl_tcp_demo_SOURCES = openssl-tcp-demo.cxx +openssl_tcp_demo_LDFLAGS = -L${top_builddir}/src +if MINGW32 +openssl_tcp_demo_LDADD = -leay32 +else +openssl_tcp_demo_LDADD = -ldl -lpthread -lssl +endif + +openssl_ssl_demo_SOURCES = openssl-ssl-demo.cxx +openssl_ssl_demo_LDFLAGS = -L${top_builddir}/src +if MINGW32 +openssl_ssl_demo_LDADD = -leay32 +else +openssl_ssl_demo_LDADD = -ldl -lpthread -lssl +endif + MAINTAINERCLEANFILES = makefile.in diff --git a/doc/examples/openssl-ssl-demo.cxx b/doc/examples/openssl-ssl-demo.cxx new file mode 100644 index 0000000..9b1a76e --- /dev/null +++ b/doc/examples/openssl-ssl-demo.cxx @@ -0,0 +1,28 @@ +/*! @file + + @id $Id$ +*/ +// 1 2 3 4 5 6 7 8 +// 45678901234567890123456789012345678901234567890123456789012345678901234567890 + +#define OPENSSL_LOG(X) +#include +#include + +int main(int argc, char** argv) try { + openssl::Init init; + std::string host(argc>1?argv[1]:"dev.swisssign.com"); + std::string port(argc>2?argv[2]:"443"); + std::string path(argc>3?argv[3]:"/"); + std::cout<<"Connect to: "< +#include + +int main(int argc, char** argv) try { + openssl::Init init; + std::string host(argc>1?argv[1]:"swisssign.com"); + std::string port(argc>2?argv[2]:"80"); + std::cout<<"Connect to: "< #include -#include //! @todo remove (debug only) +#ifndef OPENSSL_LOG + #include + #if __GNUC__ >= 2 + //! Openssl Logging + /*! If you want to change openssl logging mechanism, just + redefine your own OPENSSL_LOG macro before #include + <openssl.hxx>. Define it empty for no logging at + all. By default logs to std::clog. */ + #define OPENSSL_LOG(X) std::clog<#include + <openssl.hxx>. Define it empty for no logging at + all. By default logs to std::clog. */ + #define OPENSSL_LOG(X) std::clog<()+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()+pos; - out.text = res.begin().operator->()+pos; - DES_ecb_encrypt(&in.array, &out.array, &ks1, DES_DECRYPT); - } - return res; - } - class CBlock8 { public: CBlock8() { @@ -356,10 +545,56 @@ namespace openssl { 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()+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()+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; @@ -369,18 +604,22 @@ namespace openssl { 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; @@ -390,10 +629,12 @@ namespace openssl { 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); } @@ -404,6 +645,7 @@ namespace openssl { 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; @@ -420,6 +662,7 @@ namespace openssl { 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; @@ -433,6 +676,7 @@ namespace openssl { //! @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"); @@ -450,6 +694,7 @@ namespace openssl { //! @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"); @@ -465,20 +710,24 @@ namespace openssl { } //============================================================================ + //! 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"); 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) { + OPENSSL_LOG("log"); unsigned char* d(0); int len(i2d_X509(o._x509, &d)); if (!len) throw x509_copy_failed(); @@ -489,12 +738,15 @@ namespace openssl { } //! 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); @@ -507,6 +759,7 @@ namespace openssl { } //! 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); @@ -515,6 +768,7 @@ namespace openssl { } //! 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); @@ -523,6 +777,7 @@ namespace openssl { } //! 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); @@ -531,7 +786,8 @@ namespace openssl { } //! Get serial number. std::string serial() const { - /* @bug http://albistechnologies.com reports: «could be a + 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);» @@ -540,7 +796,7 @@ namespace openssl { return std::string((char*)ser->data, ser->length); @endcode - requires memory free? - - ser->type?!? http://albistechnologies.com prepends + - 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)); @@ -550,6 +806,7 @@ namespace openssl { } //! 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, @@ -558,6 +815,7 @@ namespace openssl { } //! 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 @@ -568,6 +826,7 @@ namespace openssl { } //! 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 @@ -578,6 +837,7 @@ namespace openssl { } //! 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 @@ -588,6 +848,7 @@ namespace openssl { } //! 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 @@ -599,6 +860,7 @@ namespace openssl { } //! 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 @@ -610,6 +872,7 @@ namespace openssl { } //! 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) @@ -618,6 +881,7 @@ namespace openssl { } //! 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 @@ -629,6 +893,7 @@ namespace openssl { } //! 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) { @@ -648,99 +913,124 @@ namespace openssl { }; //============================================================================ + //! 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);*/ + /*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 @@ -753,6 +1043,7 @@ namespace openssl { //! 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)); @@ -776,6 +1067,7 @@ namespace openssl { } ~PKCS12() { + OPENSSL_LOG("log"); delete _key; delete _cert; for (X509List::iterator it(_ca.begin()); it!=_ca.end(); ++it) @@ -783,24 +1075,29 @@ namespace openssl { } 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; } @@ -811,6 +1108,7 @@ namespace openssl { }; //============================================================================ + //! PKCS#7 certificate file handler class PKCS7 { //...............................................................typedefs @@ -842,6 +1140,7 @@ namespace openssl { //! 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); @@ -859,11 +1158,13 @@ namespace openssl { } ~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; } @@ -872,18 +1173,27 @@ namespace openssl { }; //============================================================================ - class BIO { + //! TCP Connection + class TCP { private: - BIO(const BIO&); - BIO& operator=(const BIO&); + TCP(const TCP&); + TCP& operator=(const TCP&); public: - BIO(): _bio(0) {} + TCP(): _bio(0) { + OPENSSL_LOG("log"); + } - ~BIO() { + TCP(const std::string& hostPort): _bio(0) { + OPENSSL_LOG("log"); + connect(hostPort); + } + + virtual ~TCP() { + OPENSSL_LOG("log"); try { close(); } catch (...) { @@ -891,20 +1201,36 @@ namespace openssl { } } - BIO& connect(const std::string& hostPort) { + virtual TCP& connect(const std::string& hostPort) { + OPENSSL_LOG("log"); close(); if (!(_bio=BIO_new_connect(const_cast(hostPort.c_str()))) || BIO_do_connect(_bio)<=0) - throw bio_connection_failed(hostPort); + 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; + } - BIO& operator>>(std::string& s) { + TCP& operator>>(std::string& s) { + OPENSSL_LOG("log"); s += read(); return *this; } - BIO& operator<<(const std::string& s) { + TCP& operator<<(const std::string& s) { + OPENSSL_LOG("log"); return write(s); } @@ -913,34 +1239,47 @@ namespace openssl { server is waiting for next request, but connection is still open? */ std::string read() { - if (!_bio) throw bio_closed_connection(); + 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 throw bio_read_error(); + 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); } - BIO& write(const std::string& s) { + 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 throw bio_write_error(); - else - if (x0) BIO_free_all(_bio); + _bio = 0; + return *this; } - private: + protected: - friend class SSL; ::BIO* _bio; + std::string _hostPort; }; @@ -949,8 +1288,10 @@ namespace openssl { public: TrustStore(const std::string& pathToPemFile): _file(pathToPemFile) { + OPENSSL_LOG("log"); } const std::string& file() const { + OPENSSL_LOG("log"); return _file; } private: @@ -961,15 +1302,17 @@ namespace openssl { 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 { + class SSL: public TCP { private: SSL(); SSL(const SSL&); @@ -978,6 +1321,7 @@ namespace openssl { 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()); @@ -985,42 +1329,63 @@ namespace openssl { 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); } - BIO& connect(const std::string& hostPort) { + virtual SSL& connect(const std::string& hostPort) { + OPENSSL_LOG("log"); close(); - if (!(_bio._bio=BIO_new_ssl_connect(_ctx))) - throw bio_connection_failed(hostPort); - BIO_get_ssl(_bio._bio, &_ssl); - if (!_ssl) + 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._bio, const_cast(hostPort.c_str())); - if (BIO_do_connect(_bio._bio)<=0) throw bio_connection_failed(hostPort); - return _bio; + BIO_set_conn_hostname(_bio, const_cast(hostPort.c_str())); + if (BIO_do_connect(_bio)<=0) + throw tcp_connection_failed(hostPort); + verify(); + return *this; } - SSL& close() { - _bio.close(); + virtual SSL& close() { + OPENSSL_LOG("log"); + TCP::close(); _ssl = 0; //! @todo is this correct? <-- return *this; } - bool verifyResult() { - return _ssl && SSL_get_verify_result(_ssl)==X509_V_OK; + 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; - BIO _bio; }; //@} } + +std::ostream& operator<<(std::ostream& os, openssl::TCP& is) { + return os<>(std::istream& is, openssl::TCP& os) { + std::string s; + is>>s; + os<