Commit e5246d63 authored by jan.koester's avatar jan.koester
Browse files

test

parent aae73952
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -220,7 +220,8 @@ namespace netplus {
                    ccon->csock = std::make_unique<udp>(-1);
                else if (_ServerSocket->_Type == sockettype::SSL) {
                    ssl* srv = static_cast<ssl*>(_ServerSocket);
                    ccon->csock = std::make_unique<ssl>(srv->_cert, -1);
                    // ✅ Pass the certificate bundle map (SNI will select appropriate cert per connection)
                    ccon->csock = std::make_unique<ssl>(srv->_cert_map, -1);
                }

                _ServerSocket->accept(ccon->csock, true);
+6 −7
Original line number Diff line number Diff line
@@ -311,13 +311,13 @@ namespace netplus {
		struct CertificateBundle {
			netplus::x509cert    cert;
			std::vector<uint8_t> privateKeyDer;
			netplus::rsa         rsa_key;  // Pre-loaded RSA private key
		};

		using tcp::operator=;

		ssl(const std::map<std::string, CertificateBundle>& certs);
		ssl(const std::map<std::string, CertificateBundle>& certs, int sock);
		ssl(std::shared_ptr<netplus::x509cert> cert, int sock);

		virtual ~ssl() = default;

@@ -340,6 +340,9 @@ namespace netplus {
		bool loadServerPrivateKeyDer(const std::string& keyDerPath);
		bool loadServerPrivateKey(const std::vector<uint8_t>& keyData);

		// ✅ Static helper to load RSA key from DER bytes into rsa object
		static bool _loadRsaFromDer(const std::vector<uint8_t>& derData, netplus::rsa& out_rsa);

		bool hasPendingWrite() const override {
			// Return true if there's queued data not yet sent
			// Check _send_queue (queued but not flushed)
@@ -362,8 +365,8 @@ namespace netplus {

		void resetTLS();

		// Public accessor for certificate (used by event loop for SSL accept)
		std::shared_ptr<netplus::x509cert> getCert() const { return _cert; }
		// SNI accessor - get the requested hostname from client
		const std::string& getRequestedHostname() const { return _requested_hostname; }

		// Check if there's buffered data waiting to be processed
		bool hasBufferedData() const override { return !_rx_tcp_buf.empty(); }
@@ -524,10 +527,6 @@ namespace netplus {
		//debug
		bool _tls13_got_key_share = false;

		netplus::x509cert _peer_cert;
		std::shared_ptr<netplus::x509cert> _cert = nullptr;
		netplus::rsa _rsa;
		netplus::rsa _peer_rsa;  // Server's RSA public key (client mode)
		uint8_t _ec_priv[32] = {0};  // ECC P-256 private key (Big-Endian)
		bool _has_ec_key = false;
		std::string _hostname;
+76 −107
Original line number Diff line number Diff line
@@ -324,7 +324,7 @@ namespace netplus {

// Hilfsfunktion zum Parsen von SNI (server_name) Extension aus ClientHello
static bool extractSNIFromClientHello(const std::vector<uint8_t>& ch, std::string& out_hostname) {
    if (ch.size() < 44) return false;  // Minimum size for ClientHello
    if (ch.size() < 43) return false;  // Minimum size for ClientHello (without type byte)
    if (ch[0] != 0x01) return false;   // Type must be ClientHello

    auto readU16 = [&](size_t& p) -> uint16_t {
@@ -338,7 +338,7 @@ static bool extractSNIFromClientHello(const std::vector<uint8_t>& ch, std::strin
        return ch[p++];
    };

    size_t p = 4;  // Skip type (1) and length (3)
    size_t p = 1;  // Skip type (0x01)

    // legacy_version (2 bytes)
    (void)readU16(p);
@@ -420,7 +420,6 @@ netplus::ssl::ssl(const std::map<std::string, CertificateBundle>& certs) :
    // Wähle das erste Zertifikat als Standard aus
    if (!certs.empty()) {
        _selected_cert_bundle = const_cast<CertificateBundle*>(&certs.begin()->second);
        _cert = std::make_shared<x509cert>(_selected_cert_bundle->cert);
    }
    _Type=sockettype::SSL;
}
@@ -434,7 +433,6 @@ netplus::ssl::ssl(const std::map<std::string, CertificateBundle>& certs, int soc
    // Wähle das erste Zertifikat als Standard aus
    if (!certs.empty()) {
        _selected_cert_bundle = const_cast<CertificateBundle*>(&certs.begin()->second);
        _cert = std::make_shared<x509cert>(_selected_cert_bundle->cert);
    }
    _Type=sockettype::SSL;
};
@@ -448,20 +446,10 @@ netplus::ssl::ssl(const std::map<std::string, CertificateBundle>& certs, const s
    // Wähle das erste Zertifikat als Standard aus
    if (!certs.empty()) {
        _selected_cert_bundle = const_cast<CertificateBundle*>(&certs.begin()->second);
        _cert = std::make_shared<x509cert>(_selected_cert_bundle->cert);
    }
    _Type=sockettype::SSL;
}

netplus::ssl::ssl(std::shared_ptr<netplus::x509cert> cert, int sock) :
    tcp(sock),
    _cert(cert),
    _aes(nullptr),
    _handshakeDone(false)
{
    _Type=sockettype::SSL;
}

std::vector<uint8_t> netplus::ssl::readTlsRecordAsync()
{
    // Caller must hold con::event_mutex to protect _rx_tcp_buf
@@ -1113,9 +1101,12 @@ void netplus::ssl::accept(std::unique_ptr<socket>& csock, bool nonblock)
    // 3) reset TLS state for new connection
    cssock->resetTLS();

    // 4) share cert/rsa/ec key
    cssock->_cert = this->_cert;
    cssock->_rsa  = this->_rsa;  // better: shared_ptr<const rsa>
    // 4) share cert/rsa/ec key (via selected_cert_bundle for SNI)
    // Note: _selected_cert_bundle will be set by SNI selection in READ_CLIENT_HELLO
    // For now, copy the parent's selected bundle
    if (this->_selected_cert_bundle) {
        cssock->_selected_cert_bundle = const_cast<CertificateBundle*>(this->_selected_cert_bundle);
    }
    std::memcpy(cssock->_ec_priv, this->_ec_priv, 32);
    cssock->_has_ec_key = this->_has_ec_key;

@@ -1490,10 +1481,11 @@ std::vector<uint8_t> netplus::ssl::_tls13_read_record_handshake()

std::vector<uint8_t> netplus::ssl::_tls13_build_certificate()
{
    if (!_cert || _cert->derData().empty())
    // ✅ Always use selected bundle's cert
    if (!_selected_cert_bundle || _selected_cert_bundle->cert.derData().empty())
        throw std::runtime_error("TLS1.3: no certificate loaded");

    const auto& rawCert = _cert->derData();
    const auto& rawCert = _selected_cert_bundle->cert.derData();
    uint32_t certLen = (uint32_t)rawCert.size();

    std::vector<uint8_t> out;
@@ -1548,7 +1540,7 @@ std::vector<uint8_t> netplus::ssl::_tls13_build_certificate_verify()

    // TLS 1.3 RFC 8446 Section 4.4.3: For RSA signatures, RSASSA-PSS algorithms MUST be used.
    // rsa_pkcs1_* algorithms are NOT allowed for CertificateVerify in TLS 1.3.
    if (_rsa) {
    if (_selected_cert_bundle && _selected_cert_bundle->rsa_key) {
        // For TLS 1.3, we MUST use RSA-PSS-RSAE-SHA256 (0x0804)
        // PKCS#1 v1.5 (0x0401) is NOT allowed for CertificateVerify in TLS 1.3
        sig = _rsa_pss_sha256_sign(toSign);
@@ -1848,13 +1840,20 @@ void netplus::ssl::handshake_after_accept(){
                auto it = _cert_map.find(sni_hostname);
                if (it != _cert_map.end()) {
                    _selected_cert_bundle = const_cast<CertificateBundle*>(&it->second);
                    _cert = std::make_shared<x509cert>(_selected_cert_bundle->cert);
                    // ✅ Use bundle's cert directly - no need to copy to _cert
                    SSL_LOG("[SNI] Selected cert for domain: " << sni_hostname);
                } else {
                    // Fallback: Verwende das erste Zertifikat
                    SSL_LOG("[SNI] No cert found for domain: " << sni_hostname << ", using default");
                    // Fallback: Verwende das erste Zertifikat
                    if (!_cert_map.empty()) {
                        _selected_cert_bundle = const_cast<CertificateBundle*>(&_cert_map.begin()->second);
                        // ✅ Use bundle's cert directly
                        SSL_LOG("[SNI] Switched to default cert");
                    }
                }
            } else {
                SSL_LOG("[SNI] SNI extension not found in ClientHello, using default cert");
            }

            auto readU16 = [&](size_t& p) -> uint16_t {
                if (p + 2 > ch.size()) throwSSL(NetException::Error, "parse underrun u16");
@@ -2288,7 +2287,11 @@ void netplus::ssl::handshake_after_accept(){

                _sendServerHello_TLS12(this, _secure_reneg, _chosenSuite);

                const auto& rawCert = _cert->derData();
                // ✅ Always use selected bundle's cert for TLS 1.2 certificate
                if (!_selected_cert_bundle)
                    throwSSL(NetException::Error, "No certificate bundle selected");
                
                const auto& rawCert = _selected_cert_bundle->cert.derData();
                uint32_t cLen    = (uint32_t)rawCert.size();
                uint32_t listLen = cLen + 3;

@@ -2519,7 +2522,7 @@ void netplus::ssl::handshake_after_accept(){
            if (off + encLen > msg.size())
                throwSSL(NetException::Error, "ClientKeyExchange truncated ciphertext");

            const size_t kBytes = (_rsa.n.bitLength() + 7) / 8;
            const size_t kBytes = (_selected_cert_bundle->rsa_key.n.bitLength() + 7) / 8;
            if (encLen != kBytes) {
                throwSSL(NetException::Error,
                    "RSA ciphertext length mismatch encLen=" + std::to_string(encLen) +
@@ -2532,7 +2535,7 @@ void netplus::ssl::handshake_after_accept(){
#endif

            rsa::bigInt cipher = rsa::bigIntFromBytesBE(msg.data() + off, encLen);
            rsa::bigInt plainBI = _rsa.decrypt(cipher);
            rsa::bigInt plainBI = _selected_cert_bundle->rsa_key.decrypt(cipher);

#if SSL_DEBUG
            std::cerr << "[SSL] WAIT_CKE: RSA decryption successful" << std::endl;
@@ -2912,8 +2915,6 @@ void netplus::ssl::queueRaw(const uint8_t* p, size_t n) {
    _send_queue.emplace_back(p, p + n);
}

#define SSL_DEBUG 0

void netplus::ssl::flush_out(){
#ifdef Windows
#if SSL_DEBUG
@@ -3046,8 +3047,7 @@ void netplus::ssl::accept(LPFN_ACCEPTEX lpfnAcceptEx, std::unique_ptr<socket>& c
    cssock->resetTLS();

    // Share cert/rsa/ec key from server socket
    cssock->_cert = this->_cert;
    cssock->_rsa  = this->_rsa;
    cssock->_selected_cert_bundle = this->_selected_cert_bundle;
    std::memcpy(cssock->_ec_priv, this->_ec_priv, 32);
    cssock->_has_ec_key = this->_has_ec_key;

@@ -3864,16 +3864,25 @@ void netplus::ssl::handshake_after_connect(){

                    if (off + certLen <= certMsg.size()) {
                        std::vector<uint8_t> derCert(certMsg.begin() + off, certMsg.begin() + off + certLen);
                        _peer_cert.loadFromBuffer(derCert);
                        if (_selected_cert_bundle) {
                            _selected_cert_bundle->cert.loadFromBuffer(derCert);
                        } else {
                            netplus::x509cert temp;
                            temp.loadFromBuffer(derCert);
                        }

                        // Extract RSA public key from certificate
                        netplus::rsa peerRsa;
                        if (!_peer_cert.extractPublicKey(peerRsa)) {
                        netplus::x509cert temp;
                        temp.loadFromBuffer(derCert);
                        if (!temp.extractPublicKey(peerRsa)) {
                            NetException e;
                            e[NetException::Error] << "Failed to extract RSA public key from server certificate";
                            throw e;
                        }
                        _peer_rsa = peerRsa;
                        if (_selected_cert_bundle) {
                            _selected_cert_bundle->rsa_key = peerRsa;
                        }
                    }
                }

@@ -4016,7 +4025,11 @@ void netplus::ssl::connect(const std::string& addr, int port, bool nonblock)

    if (!_handshakeStarted) {
        resetTLS();
        _peer_cert = netplus::x509cert{};
        if (!_selected_cert_bundle) {
        // Create a temporary bundle for client-side connection
        static CertificateBundle temp_bundle;
        _selected_cert_bundle = &temp_bundle;
    }
        _handshakeStarted = true;
        _handshakeDone = false;

@@ -4304,9 +4317,24 @@ bool netplus::ssl::loadServerPrivateKeyDer(const std::string& keyDerPath) {

bool netplus::ssl::loadServerPrivateKey(const std::vector<uint8_t>& der) {
    try {
        // ✅ Extract RSA key data from DER and load into selected bundle's RSA
        if (_selected_cert_bundle) {
            return _loadRsaFromDer(der, _selected_cert_bundle->rsa_key);
        }
        return false;
    } catch (...) {
        throw;
    }
}

// ✅ Static helper: Extract RSA key from DER bytes and populate rsa object
bool netplus::ssl::_loadRsaFromDer(const std::vector<uint8_t>& derData, netplus::rsa& out_rsa) {
    try {
        // Create a temporary x509cert just for parsing ASN.1
        netplus::x509cert temp_cert;

        netplus::ASN1Node root;
        size_t used = _cert->parseInternal(der.data(), der.size(), root); // reuse parser
        size_t used = temp_cert.parseInternal(derData.data(), derData.size(), root);
        if (used == 0) {
            netplus::NetException e;
            e[netplus::NetException::Error] << "failed parsing key DER ASN.1";
@@ -4316,12 +4344,6 @@ bool netplus::ssl::loadServerPrivateKey(const std::vector<uint8_t>& der) {
        std::vector<uint8_t> nBE, eBE, dBE;

        // Detect PKCS#8:
        // SEQUENCE {
        //   INTEGER version,
        //   SEQUENCE algorithmIdentifier,
        //   OCTET STRING privateKey,
        //   [0] attributes OPTIONAL
        // }
        bool looksPkcs8 =
            (root.tag == 0x30 &&
             root.children.size() >= 3 &&
@@ -4329,37 +4351,15 @@ bool netplus::ssl::loadServerPrivateKey(const std::vector<uint8_t>& der) {
             root.children[2].data != nullptr &&
             root.children[2].len > 0);

        std::cerr << "[SSL] loadServerPrivateKeyDer: looksPkcs8=" << looksPkcs8
                  << " children=" << root.children.size() << std::endl;

        bool ok = false;

        // Check if this is an EC key (PKCS#8 with ecPublicKey OID)
        bool isEcKey = false;
        if (looksPkcs8 && root.children.size() >= 2 && root.children[1].tag == 0x30) {
            const auto& algId = root.children[1];
            std::cerr << "[SSL] algId.children.size()=" << algId.children.size() << std::endl;
            if (algId.children.size() >= 1 && algId.children[0].tag == 0x06) {
                // Check algorithm OID
                const auto& oid = algId.children[0];
                std::cerr << "[SSL] Algorithm OID len=" << oid.len << " bytes: ";
                for (size_t i = 0; i < oid.len && i < 16; i++) {
                    std::cerr << std::hex << (int)oid.data[i] << " ";
                }
                std::cerr << std::dec << std::endl;
                isEcKey = isOidEcPublicKey(oid.data, oid.len);
                std::cerr << "[SSL] isEcKey after OID check: " << isEcKey << std::endl;

                // Also check for P-256 curve OID in parameters
                if (isEcKey && algId.children.size() >= 2 && algId.children[1].tag == 0x06) {
                    const auto& curveOid = algId.children[1];
                    std::cerr << "[SSL] Curve OID len=" << curveOid.len << " bytes: ";
                    for (size_t i = 0; i < curveOid.len && i < 16; i++) {
                        std::cerr << std::hex << (int)curveOid.data[i] << " ";
                    }
                    std::cerr << std::dec << std::endl;
                    if (!isOidP256(curveOid.data, curveOid.len)) {
                        // Not P-256, we only support P-256 for now
                        isEcKey = false;
                    }
                }
@@ -4367,53 +4367,22 @@ bool netplus::ssl::loadServerPrivateKey(const std::vector<uint8_t>& der) {
        }

        if (isEcKey) {
            // Parse EC private key from PKCS#8
            // The privateKey OCTET STRING contains ECPrivateKey:
            // SEQUENCE {
            //   INTEGER version (1),
            //   OCTET STRING privateKey (32 bytes for P-256),
            //   [0] parameters OPTIONAL,
            //   [1] publicKey OPTIONAL
            // }
            const auto& oct = root.children[2];
            netplus::ASN1Node ecKey;
            if (_cert->parseInternal(oct.data, oct.len, ecKey) == 0) {
                netplus::NetException e;
                e[netplus::NetException::Error] << "failed parsing EC private key";
                throw e;
            }

            if (ecKey.tag == 0x30 && ecKey.children.size() >= 2) {
                // children[1] should be OCTET STRING with the private key
                const auto& privOct = ecKey.children[1];
                if (privOct.tag == 0x04 && privOct.len == 32) {
                    std::memcpy(_ec_priv, privOct.data, 32);
                    _has_ec_key = true;
                    std::cerr << "[SSL] Loaded EC P-256 private key" << std::endl;
                    return true;
                }
            }

            netplus::NetException e;
            e[netplus::NetException::Error] << "failed extracting EC private key";
            throw e;
            // EC keys not supported in CertificateBundle for now
            // (only RSA for TLS handshake signatures)
            return false;
        }

        bool ok = false;
        if (looksPkcs8) {
            // privateKey OCTET STRING contains RSAPrivateKey DER (PKCS#1)
            netplus::ASN1Node inner;

            const auto& oct = root.children[2];

            if (_cert->parseInternal(oct.data, oct.len, inner) == 0) {
            if (temp_cert.parseInternal(oct.data, oct.len, inner) == 0) {
                netplus::NetException e;
                e[netplus::NetException::Error] << "failed parsing PKCS#8 inner private key";
                throw e;
            }

            ok = parsePkcs1RsaPrivateKeyDer(inner, nBE, eBE, dBE);
        } else {
            // Assume PKCS#1 directly
            ok = parsePkcs1RsaPrivateKeyDer(root, nBE, eBE, dBE);
        }

@@ -4423,16 +4392,16 @@ bool netplus::ssl::loadServerPrivateKey(const std::vector<uint8_t>& der) {
            throw e;
        }

        // Install into server RSA
        _rsa.setRsaKeyFromRaw(nBE, eBE, dBE);
        std::cerr << "[SSL] Loaded RSA private key" << std::endl;
        // Install into output RSA object
        out_rsa.setRsaKeyFromRaw(nBE, eBE, dBE);
        std::cerr << "[SSL] _loadRsaFromDer: Successfully loaded RSA private key" << std::endl;
        return true;

    } catch (netplus::NetException&) {
        throw;
    } catch (...) {
        netplus::NetException e;
        e[netplus::NetException::Error] << "loadServerPrivateKeyDer: unknown exception";
        e[netplus::NetException::Error] << "_loadRsaFromDer: unknown exception";
        throw e;
    }
}
@@ -4799,7 +4768,7 @@ std::vector<uint8_t> netplus::ssl::_tls13_build_server_finished() {

// RSA-PKCS1-v1.5-SHA256 signature for maximum compatibility with TLS 1.3
std::vector<uint8_t> netplus::ssl::_rsa_sha256_pkcs15_sign(const std::vector<uint8_t>& in){
    if (!_rsa) throwSSL(NetException::Error, "TLS1.3: no RSA private key");
    if (!_selected_cert_bundle || !_selected_cert_bundle->rsa_key) throwSSL(NetException::Error, "TLS1.3: no RSA private key");

    // Compute SHA256 hash
    std::vector<uint8_t> mHash = sha256_hash(in);
@@ -4828,7 +4797,7 @@ std::vector<uint8_t> netplus::ssl::_rsa_sha256_pkcs15_sign(const std::vector<uin
    tBuf.insert(tBuf.end(), mHash.begin(), mHash.end());

    // Build PKCS#1 v1.5 encoded message
    size_t modBytes = (_rsa.n.bitLength() + 7) / 8;
    size_t modBytes = (_selected_cert_bundle->rsa_key.n.bitLength() + 7) / 8;

    if (tBuf.size() > modBytes - 11)
        throwSSL(NetException::Error, "Message too long for RSA key");
@@ -4845,15 +4814,15 @@ std::vector<uint8_t> netplus::ssl::_rsa_sha256_pkcs15_sign(const std::vector<uin

    // Perform raw RSA operation: signature = EM^d mod n
    rsa::bigInt m = rsa::bytesToBigIntBE(EM);
    rsa::bigInt s = rsa::modPow(m, _rsa.d, _rsa.n);
    rsa::bigInt s = rsa::modPow(m, _selected_cert_bundle->rsa_key.d, _selected_cert_bundle->rsa_key.n);

    std::vector<uint8_t> sig = rsa::bigIntToBytesBE(s, modBytes);
    return sig;
}

std::vector<uint8_t> netplus::ssl::_rsa_pss_sha256_sign(const std::vector<uint8_t>& in){
    if (!_rsa) throwSSL(NetException::Error, "TLS1.3: no RSA private key");
    return netplus::rsa_pss_sha256::sign(_rsa, in);
    if (!_selected_cert_bundle || !_selected_cert_bundle->rsa_key) throwSSL(NetException::Error, "TLS1.3: no RSA private key");
    return netplus::rsa_pss_sha256::sign(_selected_cert_bundle->rsa_key, in);
}

// ECDSA-SHA256 signature using P-256 curve
+8 −0

File changed.

Preview size limit exceeded, changes collapsed.

+64 −14
Original line number Diff line number Diff line
@@ -22,7 +22,22 @@ class MyServer : public event {
    }
    
    void RequestEvent(con &curcon, const int tid, ULONG_PTR args) override {
        const std::string body = "Hello world (HTTPS)\n";
        // Get the SNI hostname from the SSL socket
        netplus::ssl* sslsock = dynamic_cast<netplus::ssl*>(curcon.csock.get());
        std::string hostname = "unknown";
        if (sslsock) {
            hostname = sslsock->getRequestedHostname();
        }
        
        std::string body;
        if (hostname == "test1.localhost") {
            body = "Hello from test1.localhost (HTTPS)\n";
        } else if (hostname == "test2.localhost") {
            body = "Hello from test2.localhost (HTTPS)\n";
        } else {
            body = "Hello world (HTTPS)\n";
        }
        
        std::string resp;
        resp += "HTTP/1.1 200 OK\r\n";
        resp += "Content-Type: text/plain\r\n";
@@ -38,7 +53,12 @@ class MyServer : public event {
    }

    void ConnectEvent(con &curcon, const int tid, ULONG_PTR args) override {
        std::cout << "New HTTPS connection from peer" << std::endl;
        netplus::ssl* sslsock = dynamic_cast<netplus::ssl*>(curcon.csock.get());
        std::string hostname = "unknown";
        if (sslsock) {
            hostname = sslsock->getRequestedHostname();
        }
        std::cout << "New HTTPS connection from peer (SNI: " << hostname << ")" << std::endl;
    }

    void DisconnectEvent(con &curcon, const int tid, ULONG_PTR args) override {
@@ -58,37 +78,67 @@ int main() {
        std::cout << "=== HTTPS Test Starting ===" << std::endl;
        std::cout.flush();
        
        // Load first certificate
        x509cert cert;
        if (!cert.loadFromBuffer(test_cert_der)) {
             std::cerr << "Failed to load cert from memory!" << std::endl;
             std::cerr.flush();
             return 1;
        }
        std::cout << "Certificate loaded successfully" << std::endl;
        std::cout << "Certificate 1 loaded successfully" << std::endl;
        std::cout.flush();

        // Load second certificate for test2.localhost
        x509cert cert2;
        if (!cert2.loadFromBuffer(test2_cert_der)) {
             std::cerr << "Failed to load cert2 from memory!" << std::endl;
             std::cerr.flush();
             return 1;
        }
        std::cout << "Certificate 2 loaded successfully" << std::endl;
        std::cout.flush();

        // Create SSL socket listening on 127.0.0.1:8443 with SNI support
        std::cout << "Creating SSL socket..." << std::endl;
        std::cout.flush();
        
        // Erstelle Zertifikat-Map für SNI
        // ✅ BETTER STRUCT APPROACH: Pre-load RSA keys into certificate bundles
        std::map<std::string, netplus::ssl::CertificateBundle> certs;
        netplus::ssl::CertificateBundle bundle;
        bundle.cert = cert;
        bundle.privateKeyDer = {};
        certs["localhost"] = bundle;
        certs["127.0.0.1"] = bundle;
        
        ssl serverSock(certs, "127.0.0.1", 8443, 1024, -1);
        std::cout << "SSL socket created successfully" << std::endl;
        // Bundle 1 für test1.localhost, localhost, 127.0.0.1
        netplus::ssl::CertificateBundle bundle1;
        bundle1.cert = cert;
        bundle1.privateKeyDer = std::vector<uint8_t>(test_key_der.begin(), test_key_der.end());
        // Pre-load RSA key into bundle
        if (!netplus::ssl::_loadRsaFromDer(bundle1.privateKeyDer, bundle1.rsa_key)) {
             std::cerr << "Failed to load RSA key 1!" << std::endl;
             std::cerr.flush();
             return 1;
        }
        std::cout << "Bundle 1 RSA key loaded successfully" << std::endl;
        std::cout.flush();
        
        if (!serverSock.loadServerPrivateKey(test_key_der)) {
             std::cerr << "Failed to load key from memory!" << std::endl;
        // Bundle 2 für test2.localhost mit anderem Zertifikat und privatem Schlüssel
        netplus::ssl::CertificateBundle bundle2;
        bundle2.cert = cert2;
        bundle2.privateKeyDer = std::vector<uint8_t>(test2_key_der.begin(), test2_key_der.end());
        // Pre-load RSA key into bundle
        if (!netplus::ssl::_loadRsaFromDer(bundle2.privateKeyDer, bundle2.rsa_key)) {
             std::cerr << "Failed to load RSA key 2!" << std::endl;
             std::cerr.flush();
             return 1;
        }
        std::cout << "Private key loaded successfully" << std::endl;
        std::cout << "Bundle 2 RSA key loaded successfully" << std::endl;
        std::cout.flush();
        
        // Registriere Test-Domains mit ihren jeweiligen Zertifikaten
        certs["test1.localhost"] = bundle1;
        certs["test2.localhost"] = bundle2;
        certs["localhost"] = bundle1;
        certs["127.0.0.1"] = bundle1;
        
        ssl serverSock(certs, "127.0.0.1", 8443, 1024, -1);
        std::cout << "SSL socket created successfully" << std::endl;
        std::cout.flush();

        std::cout << "Starting HTTPS server on 127.0.0.1:8443..." << std::endl;