使用 BoringSSL 生成服务端和客户端证书签发根证书的流程
BoringSSL 是 OpenSSL 的一个分支,专为满足 Google 的需求而设计。与 OpenSSL 不同,BoringSSL 并未提供完整的命令行工具来生成证书和私钥。因此,要使用 BoringSSL 生成自签名的根证书以及服务端和客户端证书,需要通过编程的方式,使用 BoringSSL 的 API 来完成。这篇文章将介绍如何使用 BoringSSL 的 API,在 C++ 中编写代码生成根证书,并为服务端和客户端签发证书。
前提条件
- 安装 BoringSSL 库
- 安装 C++ 编译器(支持 C++11 及以上)
- 安装 CMake 构建工具
1. 编写证书生成代码
由于 BoringSSL 没有提供命令行工具,我们需要编写 C++ 代码来生成证书。以下是使用 BoringSSL API 生成自签名根证书、服务端证书和客户端证书的步骤。
1.1 生成自签名的根证书(CA)
首先,编写代码生成一个自签名的根证书。
#include <openssl/base.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <iostream>
#include <memory>
EVP_PKEY* generate_key() {
EVP_PKEY* pkey = EVP_PKEY_new();
RSA* rsa = RSA_new();
BIGNUM* e = BN_new();
BN_set_word(e, RSA_F4);
RSA_generate_key_ex(rsa, 2048, e, nullptr);
EVP_PKEY_assign_RSA(pkey, rsa);
BN_free(e);
// RSA 结构已被 EVP_PKEY 接管,无需再释放
return pkey;
}
X509* generate_certificate(EVP_PKEY* pkey, int days, const std::string& common_name) {
X509* x509 = X509_new();
ASN1_INTEGER_set(X509_get_serialNumber(x509), 1);
X509_gmtime_adj(X509_get_notBefore(x509), 0);
X509_gmtime_adj(X509_get_notAfter(x509), days * 24 * 3600);
X509_set_pubkey(x509, pkey);
X509_NAME* name = X509_get_subject_name(x509);
// 设置证书的主题信息
X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
(unsigned char*)"US", -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "ST", MBSTRING_ASC,
(unsigned char*)"California", -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC,
(unsigned char*)"MyCompany", -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
(unsigned char*)common_name.c_str(), -1, -1, 0);
// 设置颁发者名称(自签名证书,颁发者即自己)
X509_set_issuer_name(x509, name);
// 使用私钥签名证书
X509_sign(x509, pkey, EVP_sha256());
return x509;
}
void save_key(EVP_PKEY* pkey, const std::string& filename) {
FILE* pkey_file = fopen(filename.c_str(), "wb");
PEM_write_PrivateKey(pkey_file, pkey, nullptr, nullptr, 0, nullptr, nullptr);
fclose(pkey_file);
}
void save_cert(X509* x509, const std::string& filename) {
FILE* cert_file = fopen(filename.c_str(), "wb");
PEM_write_X509(cert_file, x509);
fclose(cert_file);
}
int main() {
// 生成 CA 私钥
EVP_PKEY* ca_pkey = generate_key();
// 生成自签名的 CA 证书
X509* ca_cert = generate_certificate(ca_pkey, 3650, "MyRootCA");
// 保存 CA 私钥和证书
save_key(ca_pkey, "ca.key");
save_cert(ca_cert, "ca.crt");
// 释放资源
EVP_PKEY_free(ca_pkey);
X509_free(ca_cert);
std::cout << "CA key and certificate generated.\n";
return 0;
}
上述代码生成了一个自签名的根证书(CA),并将私钥和证书分别保存为 ca.key
和 ca.crt
。
1.2 使用 CA 签发服务端证书
接下来,使用 CA 的私钥和证书,为服务端生成证书。
// 在 main 函数中添加以下代码
// 生成服务端私钥
EVP_PKEY* server_pkey = generate_key();
// 生成服务端证书请求(CSR)
X509_REQ* req = X509_REQ_new();
X509_REQ_set_version(req, 1);
X509_NAME* name = X509_NAME_new();
X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
(unsigned char*)"US", -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "ST", MBSTRING_ASC,
(unsigned char*)"California", -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC,
(unsigned char*)"MyCompany", -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
(unsigned char*)"server.example.com", -1, -1, 0);
X509_REQ_set_subject_name(req, name);
X509_REQ_set_pubkey(req, server_pkey);
X509_REQ_sign(req, server_pkey, EVP_sha256());
// 使用 CA 签发服务端证书
X509* server_cert = X509_new();
ASN1_INTEGER_set(X509_get_serialNumber(server_cert), 2);
X509_gmtime_adj(X509_get_notBefore(server_cert), 0);
X509_gmtime_adj(X509_get_notAfter(server_cert), 365 * 24 * 3600);
X509_set_issuer_name(server_cert, X509_get_subject_name(ca_cert)); // 颁发者为 CA
X509_set_subject_name(server_cert, X509_REQ_get_subject_name(req));
X509_set_pubkey(server_cert, server_pkey);
// 签名证书
X509_sign(server_cert, ca_pkey, EVP_sha256());
// 保存服务端私钥和证书
save_key(server_pkey, "server.key");
save_cert(server_cert, "server.crt");
// 释放资源
EVP_PKEY_free(server_pkey);
X509_REQ_free(req);
X509_free(server_cert);
X509_NAME_free(name);
std::cout << "Server key and certificate generated.\n";
1.3 使用 CA 签发客户端证书
类似地,为客户端生成证书:
// 生成客户端私钥
EVP_PKEY* client_pkey = generate_key();
// 生成客户端证书请求(CSR)
X509_REQ* client_req = X509_REQ_new();
X509_REQ_set_version(client_req, 1);
X509_NAME* client_name = X509_NAME_new();
X509_NAME_add_entry_by_txt(client_name, "C", MBSTRING_ASC,
(unsigned char*)"US", -1, -1, 0);
X509_NAME_add_entry_by_txt(client_name, "ST", MBSTRING_ASC,
(unsigned char*)"California", -1, -1, 0);
X509_NAME_add_entry_by_txt(client_name, "O", MBSTRING_ASC,
(unsigned char*)"MyCompany", -1, -1, 0);
X509_NAME_add_entry_by_txt(client_name, "CN", MBSTRING_ASC,
(unsigned char*)"client.example.com", -1, -1, 0);
X509_REQ_set_subject_name(client_req, client_name);
X509_REQ_set_pubkey(client_req, client_pkey);
X509_REQ_sign(client_req, client_pkey, EVP_sha256());
// 使用 CA 签发客户端证书
X509* client_cert = X509_new();
ASN1_INTEGER_set(X509_get_serialNumber(client_cert), 3);
X509_gmtime_adj(X509_get_notBefore(client_cert), 0);
X509_gmtime_adj(X509_get_notAfter(client_cert), 365 * 24 * 3600);
X509_set_issuer_name(client_cert, X509_get_subject_name(ca_cert)); // 颁发者为 CA
X509_set_subject_name(client_cert, X509_REQ_get_subject_name(client_req));
X509_set_pubkey(client_cert, client_pkey);
// 签名证书
X509_sign(client_cert, ca_pkey, EVP_sha256());
// 保存客户端私钥和证书
save_key(client_pkey, "client.key");
save_cert(client_cert, "client.crt");
// 释放资源
EVP_PKEY_free(client_pkey);
X509_REQ_free(client_req);
X509_free(client_cert);
X509_NAME_free(client_name);
std::cout << "Client key and certificate generated.\n";
1.4 完整的代码
将以上所有代码整合到一个 generate_certs.cpp
文件中,确保所有头文件和函数都包含在内。
2. 编译和运行代码
2.1 配置 CMakeLists.txt
创建一个 CMakeLists.txt
文件,用于编译上述代码:
cmake_minimum_required(VERSION 3.10)
project(BoringSSL_Cert_Generator)
set(CMAKE_CXX_STANDARD 11)
add_executable(generate_certs generate_certs.cpp)
# 设置 BoringSSL 的路径
set(BORINGSSL_ROOT_DIR "/path/to/boringssl") # 修改为实际的 BoringSSL 路径
include_directories(${BORINGSSL_ROOT_DIR}/include)
target_link_libraries(generate_certs
${BORINGSSL_ROOT_DIR}/build/ssl/libssl.a
${BORINGSSL_ROOT_DIR}/build/crypto/libcrypto.a
Threads::Threads)
注意,需要将 BORINGSSL_ROOT_DIR
修改为你 BoringSSL 的实际路径。
2.2 编译代码
在终端中运行以下命令:
mkdir build
cd build
cmake ..
make
2.3 运行程序
编译成功后,运行程序:
./generate_certs
程序将生成以下文件:
ca.key
:CA 私钥
ca.crt
:CA 证书
server.key
:服务端私钥
server.crt
:服务端证书
client.key
:客户端私钥
client.crt
:客户端证书
3. 使用生成的证书
生成的证书和私钥可以用于配置 gRPC 或其他需要 SSL/TLS 证书的服务。
4. 注意事项
- 由于 BoringSSL 的 API 变化较快,请确保使用的版本支持上述代码。
- BoringSSL 不提供命令行工具,因此需要通过编程方式生成证书。
- 以上示例代码简化了很多错误处理,建议在生产环境中添加必要的错误检查。
5. 总结
虽然 BoringSSL 没有 OpenSSL 那样的命令行工具,但通过使用 BoringSSL 的 API,我们仍然可以生成自签名的根证书,并为服务端和客户端签发证书。通过编写 C++ 代码,我们可以更灵活地控制证书的生成过程。