生成自签名证书搭建HTTPS代理服务器遇证书信任错误排查
问题分析与修复方案
你的代码存在几个核心错误,导致浏览器无法识别证书由信任的CA签发,具体问题和修复方式如下:
1. 错误复用CA的密钥对
你直接将CA的公钥绑定到域名证书,还把CA私钥作为域名证书的私钥使用:
X509_set_pubkey(cert.get(), X509_get_pubkey(ca_cert)); // ... SSL_use_PrivateKey(ssl, ca_pkey);
每个域名证书需要独立的密钥对,CA私钥仅用于签名域名证书,不能作为域名证书的私钥。
2. 证书序列号与CA重复
X509_set_serialNumber(cert.get(), X509_get_serialNumber(ca_cert));
X.509标准要求每个证书的序列号必须唯一,和CA证书复用序列号会被浏览器判定为无效。
3. 颁发者名称设置错误
X509_set_issuer_name(cert.get(), name);
你把颁发者设为当前域名证书的主体名称,导致证书变成自签名状态。正确做法是将颁发者设置为CA证书的主体名称,这样浏览器才能验证证书链到已安装的信任CA。
修复后的代码示例
static bool generateX509(const std::shared_ptr<CA>& ca, SSL *ssl, const char * domain) { auto ca_cert = ca->ca_crt.get(); auto ca_pkey = ca->ca_key.get(); // 生成域名证书专属密钥对 std::unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)> domain_pkey{EVP_PKEY_new(), EVP_PKEY_free}; std::unique_ptr<RSA, void (*)(RSA *)> rsa{RSA_generate_key(2048, RSA_F4, nullptr, nullptr), RSA_free}; if (!rsa || !EVP_PKEY_assign_RSA(domain_pkey.get(), rsa.release())) { return false; } // 初始化域名证书 std::unique_ptr<X509, void (*)(X509 *)> cert{X509_new(), X509_free}; // 继承CA的有效期 X509_set_notBefore(cert.get(), X509_get_notBefore(ca_cert)); X509_set_notAfter(cert.get(), X509_get_notAfter(ca_cert)); // 生成唯一序列号(示例用时间戳+随机数,实际可改用加密安全随机数) ASN1_INTEGER *serial = ASN1_INTEGER_new(); ASN1_INTEGER_set(serial, time(nullptr) + rand()); X509_set_serialNumber(cert.get(), serial); ASN1_INTEGER_free(serial); // 绑定域名证书自己的公钥 X509_set_pubkey(cert.get(), domain_pkey.get()); X509_set_version(cert.get(), 2); const unsigned char country[] = "RU"; const unsigned char company[] = "MyCompany, PLC"; // 设置域名证书的主体信息 X509_name_st* subject_name = X509_get_subject_name(cert.get()); X509_NAME_add_entry_by_txt(subject_name, "C", MBSTRING_ASC, country, -1, -1, 0); X509_NAME_add_entry_by_txt(subject_name, "O", MBSTRING_ASC, company, -1, -1, 0); X509_NAME_add_entry_by_txt(subject_name, "CN", MBSTRING_ASC, (const unsigned char *)domain, -1, -1, 0); // 设置颁发者为CA的主体信息 X509_set_issuer_name(cert.get(), X509_get_subject_name(ca_cert)); // 添加SAN扩展(兼容现代浏览器要求) X509_EXTENSION *Extension; char Buffer[512]; sprintf(Buffer, "DNS:%s", domain); Extension = X509V3_EXT_conf_nid(nullptr, nullptr, NID_subject_alt_name, Buffer); if (Extension) { X509_add_ext(cert.get(), Extension, -1); X509_EXTENSION_free(Extension); } // 用CA私钥签名域名证书 X509_sign(cert.get(), ca_pkey, EVP_sha256()); // 绑定域名证书和它自己的私钥到SSL SSL_use_certificate(ssl, cert.get()); SSL_use_PrivateKey(ssl, domain_pkey.get()); // 验证证书与私钥匹配 if (!SSL_check_private_key(ssl)) { return false; } return true; }
额外注意事项
- 确保CA证书已正确安装到系统的受信任根证书颁发机构存储中,不同系统/浏览器的安装路径需对应操作。
- 序列号生成可改用加密安全的随机数(如
RAND_bytes),避免冲突风险。 - 密钥长度建议使用2048位以上,符合现代安全标准。
内容的提问来源于stack exchange,提问作者Nina
相关产品推荐
相关产品推荐

