RTSPS(RTSP over TLS/SSL)本质就是:把明文的 RTSP 协议,套上一层 TLS/SSL 的加密隧道传输。所有 RTSP 的核心业务逻辑完全不需要改, 只需要改「传输层」的配置,不改「应用层」的 RTSP 协议逻辑

RTSP(明文)RTSPs (加密)
本质明文RTSP协议RTSP + TLS/SSL 加密隧道
默认端口TCP/UDP 554仅 TCP 322 (标准端口)
传输协议支持 TCP / UDP强制只能用 TCP
数据传输所有报文明文传输所有报文 TLS 加密传输
URL 前缀 rtsp://rtsps://
安全性低(易抓包 / 篡改)高(加密防窃听 / 篡改)

修改SETUP报文,Transport字段必须为TCP,不支持UDP

Transport: RTP/AVP/TCP;unicast;interleaved=%d-%d

socket通讯改造

  1. connect成功后,通过SSL_connect握手
  2. SSL_write()/SSL_read() 替代原有的 send()/recv() 收发 RTSP 报文

由于socket是非阻塞的, 使用epoll的事件连接状态,所以SSL_connect也需要设置为非阻塞的,并且需要放到epoll的socket连接成功后调用, 封装:


#ifndef CRYPTO_SSL_SOCKET_H_
#define CRYPTO_SSL_SOCKET_H_

#include <unordered_map>
#include "openssl/ssl.h"
#include <memory>
#include "Singleton.h"
#include <mutex>
#include <atomic>
#include "openssl/ssl.h"
#include "openssl/err.h"

// SSL连接状态
typedef enum {
    SSL_STATE_NOT_STARTED = 0,    // SSL未开始
    SSL_STATE_CONNECTING = 1,     // SSL连接进行中
    SSL_STATE_CONNECTED = 2,      // SSL连接已建立
    SSL_STATE_FAILED = -1,        // SSL连接失败
} SSLConnectionState;

// SSL配置参数
typedef struct {
    bool verify_peer = false;             // 是否验证对端证书
    const char* ca_cert_path = nullptr;     // CA证书路径
    const char* cipher_list = nullptr;      // 密码套件列表
} SSLConfig;


// SSL连接上下文
struct SSLConnection {
    std::atomic<int> socket_fd{-1};                  // Socket文件描述符
    std::recursive_mutex ssl_mutex;
    SSL* ssl = nullptr;                       // SSL对象
    SSL_CTX* ssl_ctx = nullptr;               // SSL上下文
    std::atomic<SSLConnectionState> state{SSL_STATE_NOT_STARTED};       // 当前状态
    SSLConfig config;               // SSL配置
};

class SSLSocket
{

    friend class CSingleton<SSLSocket>;
public:
    bool startAsyncConnect(int socket_fd);

    void closeConnection(int socket_fd);

    bool isSupportSSL(int socket_fd);

    // 主动推进SSL握手
    bool connect(int socket_fd);

    int recvData(int socket_fd, void* buffer, size_t length);

    int sendData(int socket_fd, const void* data, size_t length);

    int getConnectState(int socket_fd);

private:
    SSLSocket();
    ~SSLSocket();
    SSLSocket(const SSLSocket&) = delete;
    SSLSocket& operator=(const SSLSocket&) = delete;
    bool createSSLContext(std::shared_ptr<SSLConnection> conn); // 创建ssl上下文
    bool setupSSLConnection(std::shared_ptr<SSLConnection> conn); // 设置异步ssl连接
    bool performHandshakeStep(std::shared_ptr<SSLConnection> conn); // 握手
    void cleanupConnection(std::shared_ptr<SSLConnection> conn);

private:
    mutable std::recursive_mutex m_mutexSSLConnect;
    std::unordered_map<int, std::shared_ptr<SSLConnection>> m_sslConnectMap;
};

#endif /* CRYPTO_SSL_SOCKET_H_ */
#include "SSLSocket.h"
#include <iostream>
#include <sstream>
#include <string.h>

void logSSLInfo(std::shared_ptr<SSLConnection> conn,const char* message) {
    std::stringstream ss;
    if (conn) {
        ss << "[SSL FD=" << conn->socket_fd << "] ";
    }
    ss << message;

    // 这里可以替换为你的日志系统
    std::cout << "INFO: " << ss.str() << std::endl;
}

void logSSLErr(const char* message,std::shared_ptr<SSLConnection> conn=nullptr) {
    std::stringstream ss;
    if (conn) {
        ss << "[SSL FD=" << conn->socket_fd << "] ";
    }
    ss << message;

    // 这里可以替换为你的日志系统
    std::cout << "ERROR: " << ss.str() << std::endl;
}

SSLSocket::SSLSocket()
{
    ERR_load_CRYPTO_strings();
    SSL_load_error_strings();
    std::string verison = "OpenSSL Version=" + std::string(OPENSSL_VERSION_TEXT);
    logSSLInfo(nullptr,verison.c_str());
}

SSLSocket::~SSLSocket()
{
    std::lock_guard<std::recursive_mutex> lock(m_mutexSSLConnect);
    // 清理所有连接
    for (auto& pair : m_sslConnectMap) {
        cleanupConnection(pair.second);
    }
    m_sslConnectMap.clear();
}


bool SSLSocket::startAsyncConnect(int socket_fd)
{
    if (socket_fd < 0) {
        logSSLErr("Invalid socket file descriptor");
        return false;
    }

    // 创建连接上下文
    auto conn = std::make_shared<SSLConnection>();
    conn->socket_fd = socket_fd;
    conn->state = SSL_STATE_NOT_STARTED;

    {
        std::lock_guard<std::recursive_mutex> lock(conn->ssl_mutex);
        // 创建SSL上下文
        if (!createSSLContext(conn)) {
            logSSLErr("Failed to create SSL context");
            return false;
        }

        // 设置SSL连接
        if (!setupSSLConnection(conn)) {
            // 释放SSL上下文
            if (conn->ssl_ctx) {
                SSL_CTX_free(conn->ssl_ctx);
                conn->ssl_ctx = nullptr;
            }
            logSSLErr("Failed to setup SSL connection");
            return false;
        }
    }

    // 保存连接
    {
        std::lock_guard<std::recursive_mutex> lock(m_mutexSSLConnect);
        auto it = m_sslConnectMap.find(socket_fd);
        if( it != m_sslConnectMap.end())
        {
            cleanupConnection(it->second);
        }
        m_sslConnectMap[socket_fd] = conn;
    }

    logSSLInfo(conn, "SSL async connection started");
    return true;
}

void SSLSocket::closeConnection(int socket_fd)
{
    std::lock_guard<std::recursive_mutex> lock(m_mutexSSLConnect);
    auto it = m_sslConnectMap.find(socket_fd);
    if (it != m_sslConnectMap.end()) {
        cleanupConnection(it->second);
    }
}

bool SSLSocket::isSupportSSL(int socket_fd)
{
    std::lock_guard<std::recursive_mutex> lock(m_mutexSSLConnect);
    auto it = m_sslConnectMap.find(socket_fd);
    if (it != m_sslConnectMap.end()) {
        return true;
    }
    return false;
}

bool SSLSocket::createSSLContext(std::shared_ptr<SSLConnection> conn)
{
    // 使用兼容性最好的方法
    const SSL_METHOD* method = TLS_client_method();
    if (!method) {
        logSSLErr("Failed to create TLS client method");
        return false;
    }

    conn->ssl_ctx = SSL_CTX_new(method);
    if (!conn->ssl_ctx) {
        logSSLErr("Failed to create SSL_CTX");
        return false;
    }

    // 配置SSL上下文
    SSL_CTX_set_verify(conn->ssl_ctx, SSL_VERIFY_NONE, NULL);  // 不验证证书
    SSL_CTX_set_verify_depth(conn->ssl_ctx, 0);

    // 设置密码套件
    if (conn->config.cipher_list && strlen(conn->config.cipher_list) > 0) {
        if (!SSL_CTX_set_cipher_list(conn->ssl_ctx, conn->config.cipher_list)) {
            logSSLErr("Failed to set cipher list, using default",conn);
        }
    }

    // 设置CA证书(如果提供)
    if (conn->config.verify_peer && conn->config.ca_cert_path) {
        if (!SSL_CTX_load_verify_locations(conn->ssl_ctx,
                                           conn->config.ca_cert_path,
                                           NULL)) {
            logSSLErr("Failed to load CA certificates",conn);
        }
    }

    return true;
}

bool SSLSocket::setupSSLConnection(std::shared_ptr<SSLConnection> conn)
{
    conn->ssl = SSL_new(conn->ssl_ctx);
    if (!conn->ssl) {
        logSSLErr("Failed to create SSL object");
        return false;
    }

    // 关联socket
    if (SSL_set_fd(conn->ssl, conn->socket_fd) != 1) {
        logSSLErr("Failed to set SSL fd");
        SSL_free(conn->ssl);
        conn->ssl = nullptr;
        return false;
    }

    // 设置非阻塞模式
    SSL_set_mode(conn->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
    return true;
}

void SSLSocket::cleanupConnection(std::shared_ptr<SSLConnection> conn)
{
    if (!conn) return;

    {
        std::lock_guard<std::recursive_mutex> lock(conn->ssl_mutex);
        // 关闭SSL连接
        if (conn->ssl) {
            SSL_shutdown(conn->ssl);
            SSL_free(conn->ssl);
            conn->ssl = nullptr;
        }

        // 释放SSL上下文
        if (conn->ssl_ctx) {
            SSL_CTX_free(conn->ssl_ctx);
            conn->ssl_ctx = nullptr;
        }
    }

    // 从连接表中移除
    if (conn->socket_fd > 0) {
        std::lock_guard<std::recursive_mutex> lock(m_mutexSSLConnect);
        m_sslConnectMap.erase(conn->socket_fd);
    }
}

bool SSLSocket::connect(int socket_fd)
{
    std::shared_ptr<SSLConnection> conn = nullptr;
    {
        std::lock_guard<std::recursive_mutex> lock(m_mutexSSLConnect);
        auto it = m_sslConnectMap.find(socket_fd);
        if (it == m_sslConnectMap.end()) {
            logSSLErr(std::string("Connection not found for fd: " + std::to_string(socket_fd)).c_str());
            return false;
        }
        conn = it->second;
    }
    switch (conn->state) {
    case SSL_STATE_NOT_STARTED:
        // 首次握手尝试
        conn->state = SSL_STATE_CONNECTING;
        logSSLInfo(conn, "Starting SSL handshake");
        // 继续执行握手
    case SSL_STATE_CONNECTING:
        // 执行握手步骤
        return performHandshakeStep(conn);
    case SSL_STATE_CONNECTED:
        // 连接已建立,处理正常数据
        return true;
    default:
        logSSLErr(std::string("Invalid SSL state: " + std::to_string(conn->state)).c_str());
        return false;
    }
    return false;
}

bool SSLSocket::performHandshakeStep(std::shared_ptr<SSLConnection> conn)
{
    std::lock_guard<std::recursive_mutex> lock(conn->ssl_mutex);
    if (!conn || !conn->ssl) {
        logSSLErr("Invalid SSL connection or SSL object is null");
        return false;
    }
    if(conn->state == SSL_STATE_CONNECTED)
    {
        return true;
    }

    int ret = SSL_connect(conn->ssl);
    if (ret == 1) {
        // 握手成功
        conn->state = SSL_STATE_CONNECTED;
        std::stringstream ss;
        ss << "Connect Succesed version: " << SSL_get_version(conn->ssl);
        ss << "  Cipher: " << SSL_get_cipher(conn->ssl);
        logSSLInfo(conn, ss.str().c_str());
        return true;
    } else {
        // 握手需要更多步骤
        int ssl_error = SSL_get_error(conn->ssl, ret);

        switch (ssl_error) {
        case SSL_ERROR_WANT_READ:
            logSSLInfo(conn, "SSL handshake needs more data to read");
            break;
        case SSL_ERROR_WANT_WRITE:
            logSSLInfo(conn, "SSL handshake needs to write more data");
            break;
        case SSL_ERROR_SYSCALL:
            if (ERR_peek_error() == 0) {
                if (ret == 0) {
                    logSSLErr("SSL connection closed by peer");
                } else {
                    logSSLErr(std::string("SSL syscall error: " + std::string(strerror(errno))).c_str());
                }
            } else {
                logSSLErr("SSL syscall error",conn);
            }
            conn->state = SSL_STATE_FAILED;
            break;

        case SSL_ERROR_SSL:
            logSSLErr("SSL protocol error",conn);
            conn->state = SSL_STATE_FAILED;
            break;

        default:
            logSSLErr(std::string("Unknown SSL error: " + std::to_string(ssl_error)).c_str());
            conn->state = SSL_STATE_FAILED;
            break;
        }
    }
    // 握手仍在进行中
    return false;
}

int SSLSocket::recvData(int socket_fd, void* buffer, size_t length)
{
    std::shared_ptr<SSLConnection> conn = nullptr;
    {
        std::lock_guard<std::recursive_mutex> lock(m_mutexSSLConnect);
        auto it = m_sslConnectMap.find(socket_fd);
        if (it == m_sslConnectMap.end() || it->second->state != SSL_STATE_CONNECTED) {
            logSSLErr("SSL connection not ready for receiving");
            return -1;
        }
        conn = it->second;
    }
    {
        std::lock_guard<std::recursive_mutex> lock(conn->ssl_mutex);
        if(!conn->ssl)
        {
            return -1;
        }
        return SSL_read(conn->ssl, buffer, length);
    }
}

int SSLSocket::sendData(int socket_fd, const void* data, size_t length)
{
    std::shared_ptr<SSLConnection> conn = nullptr;
    {
        std::lock_guard<std::recursive_mutex> lock(m_mutexSSLConnect);
        auto it = m_sslConnectMap.find(socket_fd);
        if (it == m_sslConnectMap.end() || it->second->state != SSL_STATE_CONNECTED) {
            logSSLErr("SSL connection not ready for sending");
            return -1;
        }
        conn = it->second;
    }

    {
        std::lock_guard<std::recursive_mutex> lock(conn->ssl_mutex);
        if(!conn->ssl)
        {
            return -1;
        }
        return SSL_write(conn->ssl, data, length);
    }
}

int SSLSocket::getConnectState(int socket_fd)
{
    std::lock_guard<std::recursive_mutex> lock(m_mutexSSLConnect);
    auto it = m_sslConnectMap.find(socket_fd);
    if (it == m_sslConnectMap.end()) {
        return -1;
    }
    return it->second->state;
}
本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:alixiixcom@163.com