This library provides a simple and nice C++ wrapper around these libraries, so that programmers can concentrate on functionality. It offers general support for PCSC-lite, OpenSSL, PKCS#11, plus specific functionality for the SuisseID.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

930 lines
30 KiB

/*! @file
@id $Id$
*/
// 1 2 3 4 5 6 7 8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
#ifndef __OPENSSL_HXX__
#define __OPENSSL_HXX__
#include <openssl/pkcs12.h>
#include <openssl/x509.h>
#include "openssl/bio.h"
#include "openssl/des.h"
#include "openssl/ssl.h"
#include <openssl/err.h>
#include <vector>
15 years ago
#include <openssl/x509v3.h> // BASIC_CONSTRAINTS
#include <openssl/bn.h>
#include <cryptaux.hxx>
#include <cstdio>
#include <cassert>
#include <iostream> //! @todo remove (debug only)
15 years ago
/*! @defgroup gopenssl C++ Wrapper around OpenSSL API */
//@{
//! @defgroup openssllib OpenSSL C++ Library
//! @defgroup opensslexceptions OpenSSL Exceptions
//! @see gopenssl
namespace openssl {
//============================================================================
15 years ago
//! @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 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 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) {
}
};
15 years ago
//@}
//! @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);
}
15 years ago
X509(const X509& o): _x509(0) {
unsigned char* d(0);
int len(i2d_X509(o._x509, &d));
if (!len) throw x509_copy_failed();
15 years ago
const unsigned char* d2(d);
_x509 = d2i_X509(0, &d2, len);
OPENSSL_free(d);
15 years ago
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;
15 years ago
unsigned char* d(0);
int len(i2d_X509(o._x509, &d));
if (!len) throw x509_copy_failed();
15 years ago
const unsigned char* d2(d);
_x509 = d2i_X509(0, &d2, len);
OPENSSL_free(d);
15 years ago
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));
}
15 years ago
//! Check whether it's a CA certificate.
bool isCa() {
static BASIC_CONSTRAINTS* bc(0);
if (!bc) {
int pos(X509_get_ext_by_NID(_x509, NID_basic_constraints, -1));
15 years ago
if (pos>=0)
bc = (BASIC_CONSTRAINTS*)X509V3_EXT_d2i(X509_get_ext(_x509, pos));
}
15 years ago
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) {
15 years ago
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 BIO {
private:
BIO(const BIO&);
BIO& operator=(const BIO&);
public:
BIO(): _bio(0) {}
~BIO() {
try {
15 years ago
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;
15 years ago
::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;
};
15 years ago
//@}
}
15 years ago
//@}
#endif