Added an OpenSSL certificate class

This commit is contained in:
fpagliughi
2025-01-21 23:51:53 -05:00
parent 88654ea5ad
commit cff72d00c6
7 changed files with 262 additions and 28 deletions

View File

@@ -36,18 +36,18 @@
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// --------------------------------------------------------------------------
#include <getopt.h>
#include <fstream>
#include <iostream>
#include <string>
#include "sockpp/tcp_connector.h"
#include "sockpp/tls/connector.h"
#include "sockpp/tls/context.h"
#include "sockpp/tls/error.h"
#include "sockpp/version.h"
#include <getopt.h>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main(int argc, char* argv[]) {
@@ -141,13 +141,27 @@ int main(int argc, char* argv[]) {
cout << "Successful connection to " << addr << endl;
if (auto cert = conn.peer_certificate(); cert.empty()) {
if (auto opt_cert = conn.peer_certificate(); !opt_cert) {
cout << "No peer certificate" << endl;
}
else {
ofstream fil("peer.cer", ios::binary);
fil.write(reinterpret_cast<const char*>(cert.data()), cert.size());
cout << "Wrote peer certificate to peer.cer" << endl;
const auto& cert = opt_cert.value();
cout << "\nCertificate info:\n"
<< " Subject: " << cert.subject_name() << '\n'
<< " Issuer: " << cert.issuer_name() << '\n'
<< " Valid dates: " << cert.not_before_str() << " - " << cert.not_after_str()
<< endl;
ofstream derfil("peer.cer", ios::binary);
auto der = cert.to_der();
derfil.write(reinterpret_cast<const char*>(der.data()), der.size());
cout << "\nWrote peer certificate to peer.cer" << endl;
ofstream pemfil("peer.pem");
auto pem = cert.to_pem();
pemfil.write(pem.data(), pem.size());
cout << "\nWrote peer certificate to peer.pem" << endl;
}
if (auto res = conn.write("HELO"); !res) {

View File

@@ -0,0 +1,111 @@
/**
* @file openssl_certificate.h
*
* Socket type for OpenSSL TLS/SSL sockets.
*
* @author Frank Pagliughi
* @date January 2025
*/
// --------------------------------------------------------------------------
// This file is part of the "sockpp" C++ socket library.
//
// Copyright (c) 2025 Frank Pagliughi
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// --------------------------------------------------------------------------
#ifndef __sockpp_tls_openssl_certificate_h
#define __sockpp_tls_openssl_certificate_h
#include <openssl/ssl.h>
#include "sockpp/types.h"
namespace sockpp {
// Forward declaration
class tls_socket;
/////////////////////////////////////////////////////////////////////////////
/**
* An X509 certificate implemented with OpenSSL.
*/
class tls_certificate
{
/** The certificate library struct */
X509* cert_;
friend class tls_socket;
/** Object takes ownership of the pointer */
tls_certificate(X509* cert) : cert_{cert} {}
public:
/**
* Destructor.
*/
~tls_certificate() { ::X509_free(cert_); }
/**
* Gets the subject name for the certificate.
* @return The subject name for the certificate.
*/
string subject_name() const;
/**
* Gets the subject name for the certificate.
* @return The subject name for the certificate.
*/
string issuer_name() const;
/**
* Gets the certificate's "not before" date as a string.
* @return The certificate's "not before" date as a string.
*/
string not_before_str() const;
/**
* Gets the certificate's "not after" date as a string.
* @return The certificate's "not after" date as a string.
*/
string not_after_str() const;
/**
* Gets the certificate as a DER binary blob.
* @return The certificate as a DER binary blob.
*/
binary to_der() const;
/**
* Gets the certificate as a PEM string.
* @return The certificate as a PEM string.
*/
string to_pem() const;
};
/////////////////////////////////////////////////////////////////////////////
} // namespace sockpp
#endif // __sockpp_tls_openssl_certificate_h

View File

@@ -46,7 +46,10 @@
#include <openssl/ssl.h>
#include <optional>
#include "sockpp/stream_socket.h"
#include "sockpp/tls/openssl_certificate.h"
#include "sockpp/tls/openssl_context.h"
#include "sockpp/tls/openssl_error.h"
#include "sockpp/types.h"
@@ -121,10 +124,11 @@ public:
tls_socket& operator=(tls_socket&& rhs);
/**
* Returns the peer's X.509 certificate data, in binary DER format.
* Returns the peer's X.509 certificate.
*/
binary peer_certificate();
std::optional<tls_certificate> peer_certificate();
#if 0
/**
*
*/
@@ -134,6 +138,7 @@ public:
* certificate.
*/
string peer_certificate_status_message();
#endif
// I/O primitives

View File

@@ -48,8 +48,8 @@
#define __sockpp_types_h
#include <chrono>
#include <string>
#include <cstdint>
#include <string>
namespace sockpp {

View File

@@ -68,6 +68,7 @@ endif()
if(SOCKPP_WITH_OPENSSL)
list(APPEND SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/tls/openssl_certificate.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tls/openssl_context.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tls/openssl_socket.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tls/openssl_error.cpp

View File

@@ -0,0 +1,112 @@
// openssl_certificate.cpp
//
// --------------------------------------------------------------------------
// This file is part of the "sockpp" C++ socket library.
//
// Copyright (c) 2025 Frank Pagliughi
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// --------------------------------------------------------------------------
#include "sockpp/tls/openssl_certificate.h"
#include <openssl/bio.h>
#include <openssl/pem.h>
#include <memory>
namespace sockpp {
/////////////////////////////////////////////////////////////////////////////
string tls_certificate::subject_name() const {
auto name = X509_get_subject_name(cert_);
if (!name)
return string{};
const char* name_str = X509_NAME_oneline(name, NULL, 0);
return (name_str) ? string{name_str} : string{};
}
// int X509_set_subject_name(X509 *x, const X509_NAME *name);
string tls_certificate::issuer_name() const {
auto name = X509_get_issuer_name(cert_);
if (!name)
return string{};
const char* name_str = X509_NAME_oneline(name, NULL, 0);
return (name_str) ? string{name_str} : string{};
}
// int X509_set_issuer_name(X509 *x, const X509_NAME *name);
string tls_certificate::not_before_str() const {
auto tm = X509_get0_notBefore(cert_);
return (tm && tm->data) ? string{(const char*)tm->data} : string{};
}
string tls_certificate::not_after_str() const {
auto tm = X509_get0_notAfter(cert_);
return (tm && tm->data) ? string{(const char*)tm->data} : string{};
}
binary tls_certificate::to_der() const {
if (!cert_)
return binary{};
uint8_t* buf = nullptr;
int len = i2d_X509(cert_, &buf);
// TODO: Return an error result on <0?
if (len <= 0)
return binary{};
binary certBin{buf, size_t(len)};
::OPENSSL_free(buf);
return certBin;
}
string tls_certificate::to_pem() const {
BIO* bio = ::BIO_new(BIO_s_mem());
if (!bio || !::PEM_write_bio_X509(bio, cert_)) {
::BIO_vfree(bio);
return string{};
}
size_t keylen = BIO_pending(bio);
std::unique_ptr<char[]> key(new char[keylen]);
int len = ::BIO_read(bio, key.get(), (int)keylen);
::BIO_vfree(bio);
return (len > 0) ? string{key.get(), (size_t)len} : string{};
}
/////////////////////////////////////////////////////////////////////////////
} // namespace sockpp

View File

@@ -73,26 +73,17 @@ tls_socket& tls_socket::operator=(tls_socket&& rhs) {
return *this;
}
binary tls_socket::peer_certificate() {
std::optional<tls_certificate> tls_socket::peer_certificate() {
// TODO: Implement this
X509* cert = SSL_get0_peer_certificate(ssl_);
X509* cert = SSL_get1_peer_certificate(ssl_);
if (!cert)
return binary{};
return std::nullopt;
uint8_t* buf = nullptr;
int len = i2d_X509(cert, &buf);
// TODO: Return an error result on <0?
if (len <= 0)
return binary{};
binary certBin{buf, size_t(len)};
OPENSSL_free(buf);
return certBin;
return tls_certificate{cert};
}
#if 0
uint32_t tls_socket::peer_certificate_status() {
// TODO: Implement this?
return 0;
@@ -104,7 +95,7 @@ string tls_socket::peer_certificate_status_message() {
// TODO: Implement this?
return string{};
}
#endif
result<size_t> tls_socket::read(void* buf, size_t n) {
size_t nx;