跳至正文
老丹的足迹 —— 代码写给机器,游记写给自己,感悟写给时间
老丹的足迹 老丹的足迹
老丹的足迹 老丹的足迹
  • 首页
  • 示例页面
  • 首页
  • 示例页面
老丹的足迹 老丹的足迹
老丹的足迹 老丹的足迹
  • 首页
  • 示例页面
  • 首页
  • 示例页面

RSA密码系统深度解析:从数学原理到Botan实践

一、密码学的历史转折

1977年,三位麻省理工学院的年轻学者——Ron Rivest、Adi Shamir和Leonard Adleman——发表了一篇题为《A Method for Obtaining Digital Signatures and Public-Key Cryptosystems》的论文,宣告了RSA算法的诞生。这篇仅数页的论文彻底改变了密码学的面貌。

在此之前,所有加密算法都属于对称加密范畴:加密和解密使用同一个密钥。这带来一个根本性的困境——密钥分发问题。通信双方必须通过某种安全渠道预先共享密钥,而”安全渠道”本身的存在又依赖于密码学,形成了循环悖论。

RSA的革命性贡献在于引入了非对称加密的概念:加密和解密使用不同的密钥,公钥可以公开分发,私钥由接收方独立保管。任何人都可以用公钥加密消息,但只有拥有私钥的人才能解密。这个看似简单的想法,背后却依赖着深厚的数论根基。

二、RSA的数论基石

要理解RSA,需要从几个核心的数论概念开始。

2.1 模运算

模运算(Modular Arithmetic)是整个RSA算法的运算基础。简单来说,”a mod n”表示a除以n得到的余数。例如17 mod 5 = 2,因为17 ÷ 5 = 3余2。

模运算有几个关键性质:

  • (a + b) mod n = (a mod n + b mod n) mod n
  • (a × b) mod n = (a mod n × b mod n) mod n
  • a^b mod n = (a mod n)^b mod n

RSA中的加密运算c = m^e mod n和m = c^d mod n正是基于这些性质。模运算的”单向性”——正向计算容易,逆向求解困难——是RSA安全性的第一道防线。

2.2 欧拉函数 φ(n)

欧拉函数φ(n)是数论中一个极为重要的函数,它表示小于等于n且与n互质的正整数个数。

互质是指两个数的最大公约数为1。例如φ(10) = 4,因为1、3、7、9都与10互质。

欧拉函数有两个关键性质:

性质一:若p是素数,则φ(p) = p – 1。因为素数p与1到p-1的所有数都互质。

性质二:若p和q是两个不同的素数,则φ(p × q) = φ(p) × φ(q) = (p-1)(q-1)。这就是RSA密钥生成的核心公式。

这两个性质为RSA提供了构造”陷阱门”的数学工具——公开的模数n足够大时,计算φ(n)需要对n进行因数分解,而这个难题正是RSA的安全保障。

2.3 欧拉定理

欧拉定理是RSA能够正确解密的数学保证。定理表述为:若a与n互质,则a^φ(n) ≡ 1 (mod n)。

举个例子:n = 10,φ(10) = 4。取a = 3(与10互质),3^4 = 81,81 mod 10 = 1,定理成立。

欧拉定理有一个重要推论:a^(k×φ(n)+1) ≡ a (mod n),对任意整数k成立。这个推论是RSA加密解密互逆性证明的关键。

2.4 扩展欧几里得算法

扩展欧几里得算法用于求解一次同余方程a×x ≡ b (mod n)。在RSA中,它被用来计算私钥指数d,使得e×d ≡ 1 (mod φ(n))。

算法的核心思想是利用辗转相除法求解gcd(a, n),同时回溯出满足a×x + n×y = gcd(a, n)的整数解x和y。当gcd(e, φ(n)) = 1时,x就是所需的私钥指数d。

2.5 大整数因数分解的困难性

RSA的安全性最终归结为以下事实:给定一个大整数n = p × q(p和q是大素数),要恢复p和q在计算上是不可行的。

这个”不可行”的程度是惊人的。一个2048位的RSA模数(约617位十进制数),用目前最快的通用数域筛法(GNFS)分解,需要消耗的算力超过目前全世界所有计算机总运算能力的百万倍。即便考虑摩尔定律的持续进步,这种指数级的困难性仍能保证RSA在可预见的未来是安全的。

三、RSA算法的完整数学推导

现在我们可以构建完整的RSA算法了。

3.1 密钥生成

步骤一:随机选择两个不同的大素数p和q。实际应用中,p和q通常具有相近的位数,但不能太接近(否则易被Fermat方法分解)。

步骤二:计算模数n = p × q。n的长度(如1024位、2048位)决定了密钥的强度。

步骤三:计算欧拉函数φ(n) = (p-1) × (q-1)。

步骤四:选择公钥指数e,满足1 < e < φ(n)且gcd(e, φ(n)) = 1。在实际中,e通常取65537。这是一个经过精心选择的素数,兼顾了安全性和计算效率——它的二进制表示只有两个”1″(10000000000000001),使得模幂运算可以快速完成。

步骤五:计算私钥指数d,使得e × d ≡ 1 (mod φ(n))。这等价于求解d = e^(-1) mod φ(n),可用扩展欧几里得算法高效计算。

最终:

  • 公钥:Public Key = (n, e)
  • 私钥:Private Key = (n, d)

注意:p、q、φ(n)在密钥生成后必须立即销毁,只保留n、e、d。如果φ(n)泄露,任何知道公钥的人都能计算出d。

3.2 加密过程

假设Alice要向Bob发送秘密消息m。Bob已经生成了公钥(n, e)和私钥(n, d)。

预处理:消息m必须是小于n的整数。实际场景中,消息通常远大于n,因此采用混合加密方案(稍后详述)。对于直接加密,需要将消息转换为整数表示,并应用填充方案(如OAEP)。

加密运算:Alice用Bob的公钥计算密文c = m^e mod n。这个运算是模指数运算,虽然涉及大整数,但计算机可以在毫秒级完成。

传输:Alice将密文c发送给Bob。即使攻击者截获了c,由于不知道私钥d,无法恢复m。

3.3 解密过程

Bob收到密文c后,用私钥(n, d)计算:

m’ = c^d mod n

根据欧拉定理,m’ = (m^e)^d mod n = m^(e×d) mod n = m^(k×φ(n)+1) mod n = m mod n = m。

解密成功。这里的关键在于e×d ≡ 1 (mod φ(n)),使得e×d可以表示为k×φ(n)+1的形式,从而应用欧拉定理的推论。

3.4 一个小型数值示例

用具体数字来验证整个流程:

  1. 生成密钥:
  • 选择p = 61,q = 53(实际使用中需要上百位的素数)
  • n = 61 × 53 = 3233
  • φ(n) = 60 × 52 = 3120
  • 选e = 17(gcd(17, 3120) = 1)
  • 计算d = 2753(因为17 × 2753 = 46801,46801 mod 3120 = 1)
  1. 加密:
  • 明文m = 123
  • c = 123^17 mod 3233 = 855
  1. 解密:
  • m’ = 855^2753 mod 3233 = 123 ✓

这个例子清晰展示了模指数运算的”循环性”——经过模数n的模运算,指数d将密文”还原”为明文。

3.5 数字签名的逆向运用

RSA的对称性——公钥加密只能用私钥解密,私钥加密也只能用公钥解密——使得它可以双向使用。

数字签名的流程如下:

  1. 签名生成:Alice用私钥d对消息的哈希值h计算签名s = h^d mod n。
  2. 签名验证:Bob用Alice的公钥e计算h’ = s^e mod n,并与h比较。如果相等,则签名有效。

注意:签名时通常对消息的哈希值签名,而不是对整个消息签名。这样可以大幅提升效率,且哈希算法的单向性进一步增强了安全性。

四、RSA的实际工程约束

4.1 加密长度限制

RSA加密单个消息时,明文长度必须小于n的长度(以位为单位)减去填充开销。

这是因为数学运算c = m^e mod n要求m < n。如果m ≥ n,在模运算中会发生”包装”,导致解密时无法恢复原值。

具体限制为:

  • 1024位密钥:最多加密约117字节(128字节 – 11字节填充)
  • 2048位密钥:最多加密约245字节(256字节 – 11字节填充)
  • 4096位密钥:最多加密约501字节(512字节 – 11字节填充)

这个限制意味着RSA无法直接加密大文件或长消息。

4.2 性能对比

RSA的计算速度远慢于对称加密。下表为典型性能对比(相对值):

算法密钥生成加密解密/签名
RSA-2048慢较慢很慢
AES-256N/A极快极快
RSA/AES比值–约1000倍约10000倍

解密比加密慢,因为私钥指数d通常比公钥指数e大得多。

4.3 混合加密方案

实际应用中,RSA几乎从不单独使用。标准的混合加密流程如下:

  1. Bob生成一个临时对称密钥K(如256位AES密钥)
  2. Bob用Alice的公钥加密K得到K_enc = RSA_Encrypt(K)
  3. Bob用K加密大数据得到C = AES_Encrypt(data)
  4. Bob将(K_enc, C)发送给Alice
  5. Alice用私钥解密K_enc得到K
  6. Alice用K解密C得到原始数据

这个方案广泛应用于TLS/SSL协议、PGP加密等场景。

五、Botan中的RSA实现详解

Botan(植物名)是一个用现代C++编写的加密算法库,被称为”加密界的瑞士军刀”。它提供了完整、高效且易于使用的加密原语,其中RSA的实现尤为成熟。

5.1 Botan的架构特点

Botan的设计有以下几个显著特点:

  • 全面性:支持RSA、ECC、AES、ChaCha20等几乎所有主流算法
  • 模块化:核心功能通过Botan::命名空间下的类组织,接口清晰
  • 安全性:默认使用安全的填充方案(OAEP、PSS),并持续跟进最新的安全实践
  • 跨平台:支持Windows、Linux、macOS及嵌入式系统

在Botan中使用RSA,通常需要包含以下几个核心头文件:

#include <botan/rsa.h>           // RSA算法实现
#include <botan/pk_keys.h>       // 公钥/私钥基类
#include <botan/pkcs8.h>         // 密钥的PEM编码/解码
#include <botan/data_src.h>      // 数据源抽象
#include <botan/hex.h>           // 十六进制编解码
#include <botan/rng.h>           // 随机数生成器
#include <botan/system_rng.h>    // 系统随机数生成器

5.2 密钥生成流程

Botan的RSA密钥生成通过RSA_PrivateKey类完成,其构造函数直接接受随机数生成器和密钥长度:

#include <botan/rsa.h>
#include <botan/system_rng.h>

// 创建系统随机数生成器(使用/dev/urandom或Windows CryptoAPI)
Botan::System_RNG rng;

// 生成2048位RSA密钥对
Botan::RSA_PrivateKey private_key(rng, 2048);

// 从私钥提取公钥(私钥对象包含完整的密钥信息,可安全导出公钥)
const auto& public_key = private_key;

// 密钥信息检查
std::cout << "密钥长度: " << private_key.key_length() << " 位" << std::endl;
std::cout << "估计强度: " << private_key.estimated_strength() << " 位" << std::endl;

estimated_strength()返回的是该密钥能提供的安全强度(以对称加密的位数为参考),2048位RSA对应约112位安全强度。

5.3 密钥的序列化与反序列化

在实际项目中,密钥需要持久化存储。Botan提供了符合PKCS#8标准的PEM格式支持:

#include <botan/pkcs8.h>

// 将私钥编码为PEM字符串(加密存储)
std::string pem_private_key = Botan::PKCS8::PEM_encode(
    private_key, 
    rng, 
    "your-strong-passphrase",  // 加密密码
    "AES-256-GCM"             // 加密算法
);

// 将公钥编码为PEM字符串
std::string pem_public_key = Botan::X509::PEM_encode(public_key);

// 从PEM字符串加载私钥
std::string pem_data = load_file("private_key.pem");
Botan::DataSource_Memory data_source(pem_data);
std::unique_ptr<Botan::Private_Key> loaded_private = 
    Botan::PKCS8::load_key(data_source, rng, "your-strong-passphrase");

// 从PEM字符串加载公钥
std::string pem_pub_data = load_file("public_key.pem");
Botan::DataSource_Memory pub_source(pem_pub_data);
std::unique_ptr<Botan::Public_Key> loaded_public = 
    Botan::X509::load_key(pub_source);

安全提示:密码短语应在使用后立即从内存中清除。Botan的secure_vector类型可以帮助实现这一点。

5.4 加密与解密

Botan通过PK_Encryptor_EME和PK_Decryptor_EME类提供RSA加密/解密操作:

#include <botan/pk_encrypt.h>

// ---- 加密 ----
std::string plaintext = "Hello, RSA!";

// 创建加密器,使用OAEP填充方案
Botan::PK_Encryptor_EME encryptor(
    public_key, 
    rng, 
    "EME-OAEP(SHA-256)"  // 填充方案,可用"EME-PKCS1-v1_5"
);

// 执行加密
Botan::secure_vector<uint8_t> ciphertext = 
    encryptor.encrypt(reinterpret_cast<const uint8_t*>(plaintext.data()), 
                      plaintext.size(), 
                      rng);

// ---- 解密 ----
Botan::PK_Decryptor_EME decryptor(
    private_key, 
    rng, 
    "EME-OAEP(SHA-256)"
);

Botan::secure_vector<uint8_t> decrypted = 
    decryptor.decrypt(ciphertext.data(), ciphertext.size());

// 验证结果
std::string recovered(reinterpret_cast<const char*>(decrypted.data()), 
                      decrypted.size());
assert(plaintext == recovered);

填充方案说明:

  • EME-OAEP(SHA-256):推荐使用,提供选择密文攻击防护
  • EME-PKCS1-v1_5:仅用于兼容旧系统,不推荐新项目使用

5.5 数字签名与验证

Botan通过PK_Signer和PK_Verifier类支持数字签名:

#include <botan/pk_sign.h>

// ---- 签名 ----
std::string message = "Important document content";

// 创建签名器,使用PSS填充
Botan::PK_Signer signer(
    private_key, 
    rng, 
    "EMSA-PSS(SHA-256)",  // 填充方案
    Botan::PK_Signer::UseNonSaltLength::NO,
    32                    // salt长度(字节)
);

// 更新数据
signer.update(reinterpret_cast<const uint8_t*>(message.data()), 
              message.size());

// 生成签名
Botan::secure_vector<uint8_t> signature = signer.signature(rng);

// ---- 验签 ----
Botan::PK_Verifier verifier(
    public_key, 
    "EMSA-PSS(SHA-256)",
    Botan::PK_Verifier::UseNonSaltLength::NO,
    32
);

verifier.update(reinterpret_cast<const uint8_t*>(message.data()), 
                message.size());

bool valid = verifier.check_signature(signature.data(), signature.size());
std::cout << "签名" << (valid ? "有效" : "无效") << std::endl;

PSS vs PKCS#1 v1.5:

  • PSS(Probabilistic Signature Scheme):推荐使用,具有可证明的安全性
  • PKCS#1 v1.5:仅用于兼容,存在理论上的安全风险

5.6 完整的混合加密应用

以下是使用Botan实现RSA+AES混合加密的生产级代码:

#include <botan/rsa.h>
#include <botan/pk_encrypt.h>
#include <botan/pkcs8.h>
#include <botan/x509_key.h>
#include <botan/hex.h>
#include <botan/system_rng.h>
#include <botan/aes.h>
#include <botan/mode.h>
#include <botan/data_src.h>
#include <botan/exceptn.h>

#include <iostream>
#include <fstream>
#include <vector>
#include <memory>

class RSAHybridCrypto {
public:
    RSAHybridCrypto() : m_rng(std::make_unique<Botan::System_RNG>()) {
        // 检查RSA支持
        if (!Botan::RSA_PrivateKey::check_key("RSA", *m_rng)) {
            throw std::runtime_error("RSA not supported");
        }
    }

    // 生成并保存密钥对
    void generateAndSaveKeys(int bits = 2048) {
        Botan::RSA_PrivateKey private_key(*m_rng, bits);

        // 保存私钥(AES-256-GCM加密)
        std::string pem_priv = Botan::PKCS8::PEM_encode(
            private_key, *m_rng, "strong-passphrase", "AES-256-GCM");
        std::ofstream("private.pem") << pem_priv;

        // 保存公钥
        std::string pem_pub = Botan::X509::PEM_encode(private_key);
        std::ofstream("public.pem") << pem_pub;
    }

    // 加载密钥
    void loadKeys() {
        // 加载私钥
        std::ifstream priv_file("private.pem");
        std::string priv_pem((std::istreambuf_iterator<char>(priv_file)),
                              std::istreambuf_iterator<char>());
        Botan::DataSource_Memory priv_src(priv_pem);
        m_private_key = Botan::PKCS8::load_key(priv_src, *m_rng, "strong-passphrase");

        // 加载公钥
        std::ifstream pub_file("public.pem");
        std::string pub_pem((std::istreambuf_iterator<char>(pub_file)),
                            std::istreambuf_iterator<char>());
        Botan::DataSource_Memory pub_src(pub_pem);
        m_public_key = Botan::X509::load_key(pub_src);

        // 验证密钥匹配
        if (!m_private_key || !m_public_key) {
            throw std::runtime_error("Failed to load keys");
        }
    }

    // 混合加密
    std::vector<uint8_t> hybridEncrypt(const std::vector<uint8_t>& data) {
        // 1. 生成AES-256密钥和随机IV
        Botan::secure_vector<uint8_t> aes_key(32);
        Botan::secure_vector<uint8_t> iv(16);
        m_rng->randomize(aes_key.data(), aes_key.size());
        m_rng->randomize(iv.data(), iv.size());

        // 2. AES-GCM加密数据
        auto enc = Botan::Cipher_Mode::create_or_throw("AES-256/GCM", 
                                                       Botan::Cipher_Dir::Encryption);
        enc->set_key(aes_key);
        enc->start(iv);

        Botan::secure_vector<uint8_t> encrypted_data(data.begin(), data.end());
        enc->finish(encrypted_data);

        // 3. RSA加密AES密钥(使用OAEP)
        Botan::PK_Encryptor_EME encryptor(*m_public_key, *m_rng, "EME-OAEP(SHA-256)");
        Botan::secure_vector<uint8_t> encrypted_key = 
            encryptor.encrypt(aes_key.data(), aes_key.size(), *m_rng);

        // 4. 组装:RSA加密的密钥(256字节) + IV(16字节) + AES密文
        std::vector<uint8_t> result;
        result.reserve(encrypted_key.size() + iv.size() + encrypted_data.size());
        result.insert(result.end(), encrypted_key.begin(), encrypted_key.end());
        result.insert(result.end(), iv.begin(), iv.end());
        result.insert(result.end(), encrypted_data.begin(), encrypted_data.end());

        return result;
    }

    // 混合解密
    std::vector<uint8_t> hybridDecrypt(const std::vector<uint8_t>& package) {
        size_t key_len = m_private_key->key_length() / 8; // RSA密钥字节数

        // 1. 提取各部分
        std::vector<uint8_t> enc_key(package.begin(), package.begin() + key_len);
        std::vector<uint8_t> iv(package.begin() + key_len, 
                                package.begin() + key_len + 16);
        std::vector<uint8_t> enc_data(package.begin() + key_len + 16, 
                                      package.end());

        // 2. RSA解密AES密钥
        Botan::PK_Decryptor_EME decryptor(*m_private_key, *m_rng, "EME-OAEP(SHA-256)");
        Botan::secure_vector<uint8_t> aes_key = 
            decryptor.decrypt(enc_key.data(), enc_key.size());

        // 3. AES-GCM解密数据
        auto dec = Botan::Cipher_Mode::create_or_throw("AES-256/GCM", 
                                                       Botan::Cipher_Dir::Decryption);
        dec->set_key(aes_key);
        dec->start(iv);

        Botan::secure_vector<uint8_t> decrypted(enc_data.begin(), enc_data.end());
        dec->finish(decrypted);

        return std::vector<uint8_t>(decrypted.begin(), decrypted.end());
    }

private:
    std::unique_ptr<Botan::System_RNG> m_rng;
    std::unique_ptr<Botan::Private_Key> m_private_key;
    std::unique_ptr<Botan::Public_Key> m_public_key;
};

// 使用示例
int main() {
    try {
        RSAHybridCrypto crypto;

        // 生成或加载密钥
        if (!std::ifstream("private.pem").is_open()) {
            crypto.generateAndSaveKeys(2048);
        }
        crypto.loadKeys();

        // 测试数据
        std::string test_message = "This is a large test message that exceeds "
                                   "RSA's direct encryption limit. The hybrid "
                                   "scheme uses AES-GCM for bulk encryption.";
        std::vector<uint8_t> data(test_message.begin(), test_message.end());

        // 加密
        auto encrypted = crypto.hybridEncrypt(data);
        std::cout << "加密后大小: " << encrypted.size() << " 字节" << std::endl;

        // 解密
        auto decrypted = crypto.hybridDecrypt(encrypted);
        std::string recovered(decrypted.begin(), decrypted.end());
        std::cout << "解密结果: " << recovered << std::endl;

    } catch (const Botan::Exception& e) {
        std::cerr << "Botan错误: " << e.what() << std::endl;
        return 1;
    } catch (const std::exception& e) {
        std::cerr << "错误: " << e.what() << std::endl;
        return 1;
    }
    return 0;
}

5.7 Botan的错误处理

Botan有自己的异常体系,所有异常都继承自Botan::Exception。常见异常包括:

  • Botan::Invalid_Argument:参数无效(如密钥长度不合法)
  • Botan::Invalid_Key_Length:密钥长度不正确
  • Botan::Decoding_Error:解码失败(如填充校验失败)
  • Botan::Integrity_Failure:完整性校验失败(如GCM认证失败)

建议在加密操作中捕获这些异常,并根据错误类型决定是否重试或报告错误。

六、RSA的安全性与未来

6.1 当前的安全建议

  • 密钥长度:2015年后强烈建议2048位,敏感应用使用4096位
  • 填充方案:
  • 加密必须使用OAEP(Botan中指定"EME-OAEP(SHA-256)")
  • 签名必须使用PSS(Botan中指定"EMSA-PSS(SHA-256)")
  • 随机数生成:必须使用密码学安全的随机数发生器(Botan的System_RNG)
  • 密钥管理:私钥加密存储(使用AES-256-GCM),定期轮换

6.2 量子计算的威胁

Shor算法在量子计算机上可以在多项式时间内分解大整数,这意味着理论上可以破解RSA。

实际影响取决于:

  • 破解2048位RSA需要约2000万量子比特的量子计算机
  • 目前最先进的量子计算机约为100量子比特量级
  • 专家预计至少还需要10-20年才有可能威胁RSA

应对措施:

  • Post-Quantum Cryptography(PQC):NIST正在标准化抗量子算法
  • 混合方案:在现有RSA基础上叠加PQC算法,实现平滑过渡
  • Botan本身也在积极跟进PQC算法的实现

七、总结

RSA的诞生标志着现代密码学的分水岭。它用优雅的数学构造——欧拉函数、模运算、大整数分解——构建了一个全球性的信任基础设施。从理论推导到实际工程,RSA经历了近半个世纪的安全考验,至今仍是互联网安全的基石。

在C++生态中,Botan以现代、安全的API封装了RSA的复杂性。开发者无需深入理解数论细节,但掌握其核心原理——密钥生成、加密解密、数字签名、混合方案——对于构建安全应用至关重要。Botan的模块化设计和严格的默认安全配置,使得在项目中正确使用RSA变得更加容易。

随着量子时代的逼近,RSA的故事仍在继续,而它启发的密码学思想将继续指引未来的安全技术。无论是使用Botan还是其他加密库,理解RSA背后的数学之美和工程智慧,都是每一位安全开发者不可或缺的修炼。


附:Botan的获取与集成

# 源码下载
git clone https://github.com/randombit/botan.git

# 编译安装(Linux/macOS)
./configure.py --prefix=/usr/local
make -j$(nproc)
sudo make install

# 使用Conan集成(conanfile.txt)
requires = botan/3.12.0

# 使用CMake集成
find_package(Botan REQUIRED)
target_link_libraries(your_app Botan::Botan)
作者

老丹

关注我
其他文章
上一个

AEAD完全指南:从原理到前沿

下一个

RSA密码算法完全解析:从数学原理到数字演算

关于博主

    老丹是一名C/C++后台开发工程师,信奉“无抽象不设计,无性能不生产”。

  • 技术栈:Modern C++、Linux环境编程、多线程/并发、网络编程等。
  • 信条:能用constexpr解决的问题绝不拖到运行时,能靠RAII避免的泄漏绝不写析构。
  • 正在填坑:从解封装到渲染的C++全链路实现,正在驯服FFmpeg与H.264/H.265。
  • 输出原则:这里的每一段代码都经过-Wall -Wextra -Werror -O2的洗礼。

近期文章

  • Linux系统的安全基石:深入理解可插拔认证模块(PAM) 2026年7月27日
  • vsftpd 完全指南:从核心原理到Docker容器化部署 2026年7月27日
  • 互联网的”导航”安全卫士:深入解读DNSSEC 2026年7月27日
  • Ubuntu DNS 配置完全指南 2026年7月27日
  • 在 Ubuntu 中使用 Certbot 的操作指南 2026年7月27日

文章分类

  • C/C++开发 (13)
  • Docker容器 (3)
  • Linux工具包 (10)
  • Linux服务配置 (33)
  • Linux系统 (10)
  • OpenWrt路由 (2)
  • Shell脚本 (3)
  • 安防技术 (4)
  • 数据安全 (30)
  • 网络协议 (17)
  • 计算机理论 (22)
联系我们:📍 地址:中国·广东省深圳市   |   ✉️ 邮箱:support@tanglinux.com   |   💬 QQ:870866607
版权所有:老丹的足迹粤ICP备2026061170号-1       公安备案图标 粤公网安备44030002013274号