Loading src/event/epoll.cpp +2 −1 Original line number Diff line number Diff line Loading @@ -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); Loading src/socket.h +6 −7 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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) Loading @@ -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(); } Loading Loading @@ -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; Loading src/ssl.cpp +76 −107 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -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); Loading Loading @@ -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; } Loading @@ -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; }; Loading @@ -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 Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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"); Loading Loading @@ -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; Loading Loading @@ -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) + Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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; Loading Loading @@ -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; } } } Loading Loading @@ -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; Loading Loading @@ -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"; Loading @@ -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 && Loading @@ -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; } } Loading @@ -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); } Loading @@ -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; } } Loading Loading @@ -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); Loading Loading @@ -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"); Loading @@ -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 Loading test/https_certs.h +8 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes test/https_test.cpp +64 −14 Original line number Diff line number Diff line Loading @@ -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"; Loading @@ -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 { Loading @@ -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; Loading Loading
src/event/epoll.cpp +2 −1 Original line number Diff line number Diff line Loading @@ -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); Loading
src/socket.h +6 −7 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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) Loading @@ -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(); } Loading Loading @@ -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; Loading
src/ssl.cpp +76 −107 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -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); Loading Loading @@ -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; } Loading @@ -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; }; Loading @@ -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 Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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"); Loading Loading @@ -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; Loading Loading @@ -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) + Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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; Loading Loading @@ -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; } } } Loading Loading @@ -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; Loading Loading @@ -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"; Loading @@ -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 && Loading @@ -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; } } Loading @@ -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); } Loading @@ -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; } } Loading Loading @@ -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); Loading Loading @@ -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"); Loading @@ -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 Loading
test/https_test.cpp +64 −14 Original line number Diff line number Diff line Loading @@ -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"; Loading @@ -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 { Loading @@ -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; Loading