TCP- and SSL-Connection Examples

master
Marc Wäckerlin 15 years ago
parent 234bb55b7b
commit 522fa3b4f1
  1. 18
      doc/examples/makefile.am
  2. 28
      doc/examples/openssl-ssl-demo.cxx
  3. 25
      doc/examples/openssl-tcp-demo.cxx
  4. 547
      src/openssl.hxx

@ -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

@ -0,0 +1,28 @@
/*! @file
@id $Id$
*/
// 1 2 3 4 5 6 7 8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
#define OPENSSL_LOG(X)
#include <openssl.hxx>
#include <iostream>
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: "<<host<<':'<<port<<std::endl;
openssl::SSL ssl
(openssl::TrustStore("/usr/lib/ssl/certs/SwissSign_Gold_CA_-_G2.pem"));
ssl.connect(host+':'+port)<<"GET "<<path<<" HTTP/1.1\n"
<<"Host: "<<host<<"\n"
<<"Connection: Close\n\n";
while (ssl) std::cout<<ssl;
return 0;
} catch (const std::exception& x) {
std::cerr<<"**** ERROR: "<<x.what()<<std::endl;
return 1;
}

@ -0,0 +1,25 @@
/*! @file
@id $Id$
*/
// 1 2 3 4 5 6 7 8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
//#define OPENSSL_LOG(X)
#include <openssl.hxx>
#include <iostream>
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: "<<host<<':'<<port<<std::endl;
openssl::TCP ssl(host+':'+port);
ssl<<"GET / HTTP/1.1\x0D\x0AHost: "<<host
<<"\x0D\x0A\x43onnection: Close\x0D\x0A\x0D\x0A";
while (ssl) std::cout<<ssl;
return 0;
} catch (const std::exception& x) {
std::cerr<<"**** ERROR: "<<x.what()<<std::endl;
return 1;
}

@ -23,7 +23,25 @@
#include <cstdio>
#include <cassert>
#include <iostream> //! @todo remove (debug only)
#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 */
//@{
@ -43,6 +61,7 @@ namespace openssl {
public:
exception(const std::string& reason) throw():
_what("openssl: "+reason) {
OPENSSL_LOG("**** exception ****");
}
~exception() throw() {}
const char* what() const throw() {
@ -56,6 +75,15 @@ namespace openssl {
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 ****");
}
};
//----------------------------------------------------------------------------
@ -63,6 +91,7 @@ namespace openssl {
public:
encryption_error(const std::string& reason) throw():
openssl_error("encryption: "+reason) {
OPENSSL_LOG("**** exception ****");
}
};
//----------------------------------------------------------------------------
@ -70,6 +99,7 @@ namespace openssl {
public:
pkcs12_error(const std::string& reason) throw():
openssl_error("pkcs12: "+reason) {
OPENSSL_LOG("**** exception ****");
}
};
//----------------------------------------------------------------------------
@ -77,6 +107,7 @@ namespace openssl {
public:
pkcs7_error(const std::string& reason) throw():
openssl_error("pkcs7: "+reason) {
OPENSSL_LOG("**** exception ****");
}
};
//----------------------------------------------------------------------------
@ -84,6 +115,7 @@ namespace openssl {
public:
x509_error(const std::string& reason) throw():
openssl_error("x509: "+reason) {
OPENSSL_LOG("**** exception ****");
}
};
//----------------------------------------------------------------------------
@ -91,13 +123,15 @@ namespace openssl {
public:
key_error(const std::string& reason) throw():
openssl_error("private key: "+reason) {
OPENSSL_LOG("**** exception ****");
}
};
//----------------------------------------------------------------------------
class bio_error: public openssl_error {
class tcp_error: public openssl_error {
public:
bio_error(const std::string& reason) throw():
openssl_error("bio: "+reason) {
tcp_error(const std::string& reason) throw():
openssl_error("tcp "+reason) {
OPENSSL_LOG("**** exception ****");
}
};
//----------------------------------------------------------------------------
@ -105,6 +139,7 @@ namespace openssl {
public:
ssl_error(const std::string& reason) throw():
openssl_error("ssl: "+reason) {
OPENSSL_LOG("**** exception ****");
}
};
//----------------------------------------------------------------------------
@ -112,6 +147,7 @@ namespace openssl {
public:
cannot_encrypt(std::string reason) throw():
encryption_error("cannot encrypt text: "+reason) {
OPENSSL_LOG("**** exception ****");
}
};
//----------------------------------------------------------------------------
@ -119,6 +155,7 @@ namespace openssl {
public:
allocation_failed() throw():
x509_error("memory allocation failed") {
OPENSSL_LOG("**** exception ****");
}
};
//----------------------------------------------------------------------------
@ -126,6 +163,7 @@ namespace openssl {
public:
x509_decoding_failed(const std::string& der) throw():
x509_error("certificate decoding failed:\n"+crypto::readable(der)) {
OPENSSL_LOG("**** exception ****");
}
};
//----------------------------------------------------------------------------
@ -133,6 +171,7 @@ namespace openssl {
public:
undefined_certificate() throw():
x509_error("certificate must not be 0") {
OPENSSL_LOG("**** exception ****");
}
};
//----------------------------------------------------------------------------
@ -140,6 +179,7 @@ namespace openssl {
public:
x509_parsing_failed() throw():
x509_error("parsing DER encoded certificate failed") {
OPENSSL_LOG("**** exception ****");
}
};
//----------------------------------------------------------------------------
@ -147,6 +187,7 @@ namespace openssl {
public:
x509_copy_failed() throw():
x509_error("certificate object copy failed") {
OPENSSL_LOG("**** exception ****");
}
};
//----------------------------------------------------------------------------
@ -154,6 +195,7 @@ namespace openssl {
public:
key_copy_failed() throw():
key_error("key object copy failed") {
OPENSSL_LOG("**** exception ****");
}
};
//----------------------------------------------------------------------------
@ -161,6 +203,7 @@ namespace openssl {
public:
undefined_key() throw():
key_error("private key must not be 0") {
OPENSSL_LOG("**** exception ****");
}
};
//----------------------------------------------------------------------------
@ -168,6 +211,7 @@ namespace openssl {
public:
pkcs12_reading_failed(const std::string& file) throw():
pkcs12_error("reading DER encoded p12 file failed: "+file) {
OPENSSL_LOG("**** exception ****");
}
};
//----------------------------------------------------------------------------
@ -175,23 +219,29 @@ namespace openssl {
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") {}
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") {}
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 ****");
}
};
//----------------------------------------------------------------------------
@ -199,6 +249,7 @@ namespace openssl {
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) {
@ -207,52 +258,75 @@ namespace openssl {
//----------------------------------------------------------------------------
class pkcs7_unsupported_format: public pkcs7_error {
public:
pkcs7_unsupported_format() throw(): pkcs7_error("format not supported") {}
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") {}
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 bio_connection_failed: public bio_error {
class tcp_server_not_specified: public tcp_error {
public:
bio_connection_failed(const std::string& hostPort) throw():
bio_error("connection failed to: "+hostPort) {
tcp_server_not_specified() throw():
tcp_error("reconnect without prior connect") {
OPENSSL_LOG("**** exception ****");
}
};
//----------------------------------------------------------------------------
class bio_closed_connection: public bio_error {
class tcp_closed_connection: public tcp_error {
public:
bio_closed_connection() throw(): bio_error("closed connection") {}
tcp_closed_connection() throw(): tcp_error("closed connection") {
OPENSSL_LOG("**** exception ****");
}
};
//----------------------------------------------------------------------------
class bio_read_error: public bio_error {
class tcp_read_error: public tcp_error {
public:
bio_read_error() throw(): bio_error("read error") {}
tcp_read_error() throw(): tcp_error("read error") {
OPENSSL_LOG("**** exception ****");
}
};
//----------------------------------------------------------------------------
class bio_write_error: public bio_error {
class tcp_write_error: public tcp_error {
public:
bio_write_error() throw(): bio_error("write error") {}
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") {}
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 ****");
}
};
//----------------------------------------------------------------------------
@ -260,6 +334,154 @@ namespace openssl {
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();
}
}
}
};
//@}
@ -268,51 +490,18 @@ namespace openssl {
//@{
//============================================================================
//! 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");
}
};
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() {
@ -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<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;
@ -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<char*>(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 (x<s.size()) return write(s.substr(x));
else {
close();
throw tcp_write_error();
}
else if (x<s.size()) return write(s.substr(x));
return *this;
}
void close() {
BIO_free_all(_bio);
virtual TCP& close() {
OPENSSL_LOG("log");
if (_bio>0) 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<char*>(hostPort.c_str()));
if (BIO_do_connect(_bio._bio)<=0) throw bio_connection_failed(hostPort);
return _bio;
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;
}
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<<is.read();
}
std::istream& operator>>(std::istream& is, openssl::TCP& os) {
std::string s;
is>>s;
os<<s;
return is;
}
//@}
#endif

Loading…
Cancel
Save