/*! @file @id $Id$ This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) This product includes cryptographic software written by Eric Young (eay@cryptsoft.com) */ // 1 2 3 4 5 6 7 8 // 45678901234567890123456789012345678901234567890123456789012345678901234567890 #ifndef __OPENSSL_HXX__ #define __OPENSSL_HXX__ #include #include #include #include "openssl/bio.h" #include "openssl/des.h" #ifdef DATADIR // DATADIR is defined in autoconf/automake #undef DATADIR // but DATADIR is also a variablename in ssl.h #endif #include "openssl/ssl.h" #include #include #if OPENSSL_VERSION_NUMBER < 0x10100000L inline const unsigned char* ASN1_STRING_get0_data(ASN1_STRING *x) { return ASN1_STRING_data(x); } #endif #include // BASIC_CONSTRAINTS #include #include #include #include #include /*! @defgroup gopenssl C++ Wrapper around OpenSSL API Support for SSL-connections, engines, keys and certificates. */ //@{ //! @defgroup openssllib OpenSSL C++ Library //! @defgroup opensslcompat OpenSSL Compatibility Wrapper //! @defgroup opensslexceptions OpenSSL Exceptions //! @ref gopenssl @copydoc gpcsc namespace openssl { //============================================================================ //! @addtogroup opensslexceptions //@{ //---------------------------------------------------------------------------- class exception: public std::exception { public: exception(const std::string& reason) noexcept: _what("openssl: "+reason) { CRYPTOLOG("**** exception ****"); } ~exception() noexcept {} const char* what() const noexcept { return _what.c_str(); } private: std::string _what; }; //---------------------------------------------------------------------------- class openssl_error: public exception { public: openssl_error(const std::string& reason) noexcept: exception(reason+' '+ERR_error_string(ERR_get_error(), 0)) { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class cannot_init: public openssl_error { public: cannot_init(const std::string& reason) noexcept: openssl_error("openssl initialization: "+reason) { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class encryption_error: public openssl_error { public: encryption_error(const std::string& reason) noexcept: openssl_error("encryption: "+reason) { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class pkcs12_error: public openssl_error { public: pkcs12_error(const std::string& reason) noexcept: openssl_error("pkcs12: "+reason) { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class pkcs7_error: public openssl_error { public: pkcs7_error(const std::string& reason) noexcept: openssl_error("pkcs7: "+reason) { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class x509_error: public openssl_error { public: x509_error(const std::string& reason) noexcept: openssl_error("x509: "+reason) { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class key_error: public openssl_error { public: key_error(const std::string& reason) noexcept: openssl_error("private key: "+reason) { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class tcp_error: public openssl_error { public: tcp_error(const std::string& reason) noexcept: openssl_error("tcp "+reason) { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class ssl_error: public openssl_error { public: ssl_error(const std::string& reason) noexcept: openssl_error("ssl: "+reason) { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class cannot_encrypt: public encryption_error { public: cannot_encrypt(std::string reason) noexcept: encryption_error("cannot encrypt text: "+reason) { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class allocation_failed: public x509_error { public: allocation_failed() noexcept: x509_error("memory allocation failed") { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class x509_decoding_failed: public x509_error { public: x509_decoding_failed(const std::string& der) noexcept: x509_error("certificate decoding failed: "+crypto::readable(der)) { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class undefined_certificate: public x509_error { public: undefined_certificate() noexcept: x509_error("certificate must not be 0") { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class x509_parsing_failed: public x509_error { public: x509_parsing_failed() noexcept: x509_error("parsing DER encoded certificate failed") { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class x509_copy_failed: public x509_error { public: x509_copy_failed() noexcept: x509_error("certificate object copy failed") { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class key_copy_failed: public key_error { public: key_copy_failed() noexcept: key_error("key object copy failed") { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class undefined_key: public key_error { public: undefined_key() noexcept: key_error("private key must not be 0") { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class pkcs12_reading_failed: public pkcs12_error { public: pkcs12_reading_failed(const std::string& file) noexcept: pkcs12_error("reading DER encoded p12 file failed: "+file) { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class pkcs12_parsing_failed: public pkcs12_error { public: pkcs12_parsing_failed(const std::string& file) noexcept: pkcs12_error("parsing DER encoded p12 file failed: "+file) { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class pkcs12_no_private_key: public pkcs12_error { public: pkcs12_no_private_key() noexcept: pkcs12_error("no private key") { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class pkcs12_no_x509: public pkcs12_error { public: pkcs12_no_x509() noexcept: pkcs12_error("no x509 certificate") { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class pkcs7_reading_failed: public pkcs7_error { public: pkcs7_reading_failed(const std::string& file) noexcept: pkcs7_error("reading DER encoded p7 file failed: "+file) { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class pkcs7_parsing_failed: public pkcs7_error { public: pkcs7_parsing_failed() noexcept: pkcs7_error("parsing DER encoded p7 failed") { CRYPTOLOG("**** exception ****"); } pkcs7_parsing_failed(const std::string& file) noexcept: pkcs7_error("parsing DER encoded p7 file failed: "+file) { } }; //---------------------------------------------------------------------------- class pkcs7_unsupported_format: public pkcs7_error { public: pkcs7_unsupported_format() noexcept: pkcs7_error("format not supported") { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class pkcs7_no_x509: public pkcs7_error { public: pkcs7_no_x509() noexcept: pkcs7_error("no x509 certificate") { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class cannot_open_file: public exception { public: cannot_open_file(const std::string& file) noexcept: exception("cannot open file: "+file) { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class tcp_connection_failed: public tcp_error { public: tcp_connection_failed(const std::string& hostPort) noexcept: tcp_error("connection failed to: "+hostPort) { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class tcp_server_not_specified: public tcp_error { public: tcp_server_not_specified() noexcept: tcp_error("reconnect without prior connect") { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class tcp_closed_connection: public tcp_error { public: tcp_closed_connection() noexcept: tcp_error("closed connection") { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class tcp_read_error: public tcp_error { public: tcp_read_error() noexcept: tcp_error("read error") { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class tcp_write_error: public tcp_error { public: tcp_write_error() noexcept: tcp_error("write error") { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class ssl_cannot_create_context: public ssl_error { public: ssl_cannot_create_context() noexcept: ssl_error("cannot create context") { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class ssl_certificate_file_not_loaded: public ssl_error { public: ssl_certificate_file_not_loaded(const std::string& file) noexcept: ssl_error("certificate file not loaded: "+file) { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class ssl_certificate_folder_not_loaded: public ssl_error { public: ssl_certificate_folder_not_loaded(const std::string& path) noexcept: ssl_error("certificate folder not loaded: "+path) { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class ssl_no_connection: public ssl_error { public: ssl_no_connection() noexcept: ssl_error("no ssl connection") { CRYPTOLOG("**** exception ****"); } }; //---------------------------------------------------------------------------- class ssl_verification_failed: public ssl_error { public: ssl_verification_failed(long num) noexcept: ssl_error("certificate verification failed: "+reason(num)) { CRYPTOLOG("**** exception ****"); } static std::string reason(long 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: return "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: "<()); return res; } private: BigNum(const BigNum& o); BigNum& operator=(const BigNum& o); private: BIGNUM* _bn; }; 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((const std::string::value_type*)_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) { CRYPTOLOG("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) { CRYPTOLOG("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. @param key block key @param ivec vector */ inline std::string desCbcEnc(std::string txt, CBlock8 key, CBlock8& ivec) { CRYPTOLOG("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. @param key block key */ inline std::string desCbcEnc(const std::string& txt, const CBlock8& key) { CRYPTOLOG("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. @param key block key @param ivec vector */ inline std::string desCbcDec(std::string txt, CBlock8 key, CBlock8& ivec) { CRYPTOLOG("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. @param key block key */ inline std::string desCbcDec(const std::string& txt, const CBlock8& key) { CRYPTOLOG("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. @param key1 block key 1 @param key2 block key 2 @param ivec vector */ inline std::string des2edeCbcEnc(std::string txt, CBlock8 key1, CBlock8 key2, CBlock8 ivec = CBlock8()) { CRYPTOLOG("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. @param key1 block key 1 @param key2 block key 2 @param ivec vector */ inline std::string des2edeCbcDec(std::string txt, CBlock8 key1, CBlock8 key2, CBlock8 ivec = CBlock8()) { CRYPTOLOG("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) { CRYPTOLOG("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()); 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) { CRYPTOLOG("log"); _x509 = X509_dup(o._x509); // unsigned char* d(0); // int len(i2d_X509(o._x509, &d)); // if (!len) throw x509_copy_failed(); // V0_CONST unsigned char* d2(d); // _x509 = d2i_X509(0, &d2, len); // OPENSSL_free(d); if (!_x509) throw x509_copy_failed(); } //! Take over OpenSSL allocated certificate. X509(::X509 *x509): _x509(x509) { CRYPTOLOG("log"); if (!_x509) throw undefined_certificate(); } ~X509() { CRYPTOLOG("log"); X509_free(_x509); } X509& operator=(const X509& o) { CRYPTOLOG("log"); X509_free(_x509); _x509 = 0; _x509 = X509_dup(o._x509); // unsigned char* d(0); // int len(i2d_X509(o._x509, &d)); // if (!len) throw x509_copy_failed(); // V0_CONST unsigned char* d2(d); // _x509 = d2i_X509(0, &d2, len); // OPENSSL_free(d); if (!_x509) throw x509_copy_failed(); return *this; } //! Get a copy of the OpenSSL low level certificate /*! @note you are responsible to free the certificate */ ::X509* lowLevelCopy() const { return X509_dup(_x509); } //! Get DER encoded subject. std::string subjectDER() const { CRYPTOLOG("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 { CRYPTOLOG("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 { CRYPTOLOG("log"); unsigned char* c(0); int len(i2d_X509(_x509, &c)); std::string res((char*)c, len); OPENSSL_free(c); return res; } //! Get DER encoded serial number. std::string serialDER() const { CRYPTOLOG("log"); unsigned char* c(0); int len(i2d_ASN1_INTEGER(X509_get_serialNumber(_x509), &c)); std::string res((char*)c, len); OPENSSL_free(c); return res; } //! Get serial number. std::string serial() const { CRYPTOLOG("log"); unsigned char* c(0); int len(i2d_X509(_x509, &c)); if (len<15 || len<15+c[14]) { OPENSSL_free(c); return std::string(); } std::string res((char*)c+15, c[14]); OPENSSL_free(c); return res; } //! Get id. std::string id() const { CRYPTOLOG("log"); unsigned char c[SHA_DIGEST_LENGTH]; const unsigned char *pk(0); int ppklen(0); X509_PUBKEY_get0_param(0, &pk, &ppklen, 0, X509_get_X509_PUBKEY(_x509)); SHA1(pk, ppklen, c); return std::string((char*)c, SHA_DIGEST_LENGTH); } //! Get common name. std::string commonName() const { CRYPTOLOG("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((const char*)ASN1_STRING_get0_data(cn), ASN1_STRING_length(cn)); } //! Get country name. std::string countryName() const { CRYPTOLOG("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((const char*)ASN1_STRING_get0_data(cn), ASN1_STRING_length(cn)); } //! Get locality name. std::string localityName() const { CRYPTOLOG("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((const char*)ASN1_STRING_get0_data(cn), ASN1_STRING_length(cn)); } //! Get state or province name. std::string stateOrProvinceName() const { CRYPTOLOG("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((const char*)ASN1_STRING_get0_data(cn), ASN1_STRING_length(cn)); } //! Get organization name. std::string organizationName() const { CRYPTOLOG("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((const char*)ASN1_STRING_get0_data(cn), ASN1_STRING_length(cn)); } //! Check whether it's a CA certificate. bool isCa() { CRYPTOLOG("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 { CRYPTOLOG("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((const char*)ASN1_STRING_get0_data(cn), ASN1_STRING_length(cn)); } //! Get key usage flags. int keyUsageFlags() const { CRYPTOLOG("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((const char*)ASN1_STRING_get0_data(ku), 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()) { CRYPTOLOG("log"); if (!_key) throw allocation_failed(); } PrivateKey(const PrivateKey& o): _key(0) { CRYPTOLOG("log"); copy(o); } PrivateKey(EVP_PKEY* k): _key(k) { CRYPTOLOG("log"); if (!_key) throw undefined_key(); } ~PrivateKey() { CRYPTOLOG("log"); EVP_PKEY_free(_key); } PrivateKey& operator=(const PrivateKey& o) { CRYPTOLOG("log"); copy(o); return *this; } std::string modulus() const { CRYPTOLOG("log"); const BIGNUM **n(0); RSA_get0_key(rsa(), n, 0, 0); return BigNum::string(*n); } std::string publicExponent() const { CRYPTOLOG("log"); const BIGNUM **e(0); RSA_get0_key(rsa(), 0, e, 0); return BigNum::string(*e); } std::string privateExponent() const { CRYPTOLOG("log"); const BIGNUM **d(0); RSA_get0_key(rsa(), 0, 0, d); return BigNum::string(*d); } std::string prime1() const { CRYPTOLOG("log"); const BIGNUM **p(0); RSA_get0_factors(rsa(), p, 0); return BigNum::string(*p); } std::string prime2() const { CRYPTOLOG("log"); const BIGNUM **q(0); RSA_get0_factors(rsa(), 0, q); return BigNum::string(*q); } std::string exponent1() const { CRYPTOLOG("log"); const BIGNUM **dmp1(0); RSA_get0_crt_params(rsa(), dmp1, 0, 0); return BigNum::string(*dmp1); } std::string exponent2() const { CRYPTOLOG("log"); const BIGNUM **dmq1(0); RSA_get0_crt_params(rsa(), 0, dmq1, 0); return BigNum::string(*dmq1); } std::string coefficient() const { CRYPTOLOG("log"); const BIGNUM **iqmp(0); RSA_get0_crt_params(rsa(), 0, 0, iqmp); return BigNum::string(*iqmp); } private: void copy(const PrivateKey& o) { CRYPTOLOG("log"); EVP_PKEY_free(_key); if (!(_key=EVP_PKEY_new())) throw allocation_failed(); rsa(o); dsa(o); dh(o); /*ec(o);*/ } void rsa(const PrivateKey& o) { CRYPTOLOG("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) { CRYPTOLOG("log"); DSA* tmp(o.dsa()); if (tmp&&!EVP_PKEY_set1_DSA(_key, tmp)) throw key_copy_failed(); } void dh(const PrivateKey& o) { CRYPTOLOG("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) { CRYPTOLOG("log"); EC_KEY* tmp(o.ec()); if (tmp&&!EVP_PKEY_set1_EC_KEY(_key, tmp)) throw key_copy_failed(); } */ RSA* rsa() const { CRYPTOLOG("log"); //! @todo throw exception if 0? return EVP_PKEY_get0_RSA(_key); } DSA* dsa() const { CRYPTOLOG("log"); return EVP_PKEY_get0_DSA(_key); } DH* dh() const { CRYPTOLOG("log"); return EVP_PKEY_get0_DH(_key); } /* Not available on mac osx EC_KEY* ec() const { CRYPTOLOG("log"); return EVP_PKEY_get0_EC_KEY(_key); }*/ EVP_PKEY* _key; }; //============================================================================ //! PKCS#12 certificate file handler class PKCS12 { //...............................................................typedefs public: typedef std::vector X509List; //................................................................methods public: //! Read from a PKCS#12 (.p12) file. PKCS12(std::string filename, std::string password): _key(0), _cert(0) { CRYPTOLOG("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) *cal(0); if (!PKCS12_parse(p12, password.c_str(), &pkey, &cert, &cal)) throw pkcs12_parsing_failed(filename); if (pkey) _key = new PrivateKey(pkey); if (cert) _cert = new X509(cert); for (int i(sk_num(CV_STACK cal)); i>0; --i) _ca.push_back(new X509((::X509*)sk_pop(CV_STACK cal))); PKCS12_free(p12); } catch (...) { PKCS12_free(p12); throw; } } ~PKCS12() { CRYPTOLOG("log"); delete _key; delete _cert; for (X509List::iterator it(_ca.begin()); it!=_ca.end(); ++it) delete *it; } bool hasPrivateKey() const { CRYPTOLOG("log"); return _key; } bool hasCert() const { CRYPTOLOG("log"); return _cert; } const PrivateKey& privateKey() const { CRYPTOLOG("log"); if (!_key) throw pkcs12_no_private_key(); return *_key; }; const X509& x509() const { CRYPTOLOG("log"); if (!_cert) throw pkcs12_no_x509(); return *_cert; }; const X509List& ca() const { CRYPTOLOG("log"); return _ca; } private: PrivateKey *_key; X509 *_cert; X509List _ca; }; //============================================================================ //! PKCS#7 certificate file handler class PKCS7 { //...............................................................typedefs public: typedef std::vector 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) { CRYPTOLOG("log"); BIO* mem(BIO_new_mem_buf(const_cast((const void*)memory.data()), (int)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 (OPENSSL_sk_num(CV_STACK p7->d.sign->cert)>0) _certs.push_back(new X509((::X509*)sk_pop(CV_STACK 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() { CRYPTOLOG("log"); for (X509List::iterator it(_certs.begin()); it!=_certs.end(); ++it) delete *it; } const X509List& certs() const { CRYPTOLOG("log"); return _certs; } private: X509List _certs; }; //============================================================================ //! TCP Connection class TCP { private: TCP(const TCP&); TCP& operator=(const TCP&); public: TCP(): _bio(0) { CRYPTOLOG("log"); } TCP(const std::string& hostPort): _bio(0) { CRYPTOLOG("log"); connect(hostPort); } virtual ~TCP() noexcept(false) { CRYPTOLOG("log"); try { close(); } catch (...) { if (!std::uncaught_exception()) throw; } } virtual TCP& connect(const std::string& hostPort) { CRYPTOLOG("log"); close(); if (!(_bio=BIO_new_connect(const_cast(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 { CRYPTOLOG("log"); return _bio; } TCP& operator>>(std::string& s) { CRYPTOLOG("log"); s += read(); return *this; } TCP& operator<<(const std::string& s) { CRYPTOLOG("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() { CRYPTOLOG("log"); if (!_bio) 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) { CRYPTOLOG("log"); if (!_bio) throw tcp_closed_connection(); unsigned int x(BIO_write(_bio, s.begin().operator->(), (int)s.size())); if (x<=0) if (BIO_should_retry(_bio)) return write(s); else { close(); throw tcp_write_error(); } else if (x(hostPort.c_str())); if (BIO_do_connect(_bio)<=0) throw tcp_connection_failed(hostPort); verify(); return *this; } virtual SSL& close() { CRYPTOLOG("log"); TCP::close(); _ssl = 0; //! @todo is this correct? <-- return *this; } protected: void verify() { CRYPTOLOG("log"); if (!_ssl) throw ssl_no_connection(); long 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<>(std::istream& is, openssl::TCP& os) { std::string s; is>>s; os<