git clone https://github.com/red-keys/grpc.git (支持NTLS功能的在grpc-tongsuo分支)
mkdir -p cmake/build
rm -rf /home/red/grpc/cmake/build/*
设置环境变量
确保使用铜锁:
bash
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
export PKG_CONFIG_PATH=/usr/local/tongsuo/lib64/pkgconfig:$PKG_CONFIG_PATH
export LD_LIBRARY_PATH=/usr/local/tongsuo/lib64:$LD_LIBRARY_PATH
export LIBRARY_PATH=/usr/local/tongsuo/lib64:$LIBRARY_PATH
注意:不止编译grpc的时候需要设置环境变量,编译grpc服务端客户端的时候也要设置环境变量。不然会报错:
text
ld: /usr/local/lib/libgrpc.so: undefined reference to `SSL_CTX_use_enc_certificate@OPENSSL_3.0.0`
原因是用 ldd /usr/local/lib/libgrpc.so | grep ssl 发现 libssl.so.3 => /lib/x86_64-linux-gnu/libssl.so.3,没有链接到 /usr/local/tongsuo/lib64/libssl.so.3。
CMake 配置修改
给 CMakeLists.txt 添加:
cmake
if(OPENSSL_IS_TONGSUO)
add_definitions(-DOPENSSL_IS_TONGSUO)
endif()
然后给 cmake 加 -DOPENSSL_IS_TONGSUO=ON 编译选项,代码里面所有的
其他有用选项:
-DCMAKE_BUILD_TYPE=Debug:编译出带调试信息的 grpc 动态库文件,用于 GDB 调试
-DgRPC_SSL_PROVIDER=package:获取本地的 openssl 而不是远程下载源码再编译额外的 openssl
-DCMAKE_CXX_STANDARD=17:避免 C++ 17 特性编译报错
编译 gRPC
bash
cmake \
-DOPENSSL_IS_TONGSUO=ON \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_CXX_STANDARD=17 \
-DBUILD_SHARED_LIBS=ON \
-DgRPC_SSL_PROVIDER=package \
-DOPENSSL_CRYPTO_LIBRARY=/usr/local/tongsuo/lib64/libcrypto.so \
-DOPENSSL_SSL_LIBRARY=/usr/local/tongsuo/lib64/libssl.so \
-DOPENSSL_INCLUDE_DIR=/usr/local/tongsuo/include \
-DOPENSSL_ROOT_DIR=/usr/local/tongsuo \
../..
make -j$(nproc)
make install
apt clean
apt update
apt install -y libssl-dev libre2-dev
验证安 装
bash
pkg-config --modversion grpc++
protoc --version
pkg-config --modversion openssl
关键代码结构
C++ 服务端凭据结构
定义在 include/grpcpp/security/server_credentials.h:
cpp
struct PemKeyCertPair { }
关键转换函数在 src/cpp/server/secure_server_credentials.cc:
cpp
std::shared_ptr<ServerCredentials> SslServerCredentials(
C++ 客户端凭据结构
定 义在 include/grpcpp/security/credentials.h:
cpp
struct SslCredentialsOptions { }
关键转换函数在 src/cpp/client/secure_credentials.cc:
cpp
std::shared_ptr<ChannelCredentials> SslCredentials(
C API 结构
定义在 include/grpc/credentials.h:
cpp
typedef struct {
const char* private_key;
const char* cert_chain;
} grpc_ssl_pem_key_cert_pair;
关键转换函数在 src/core/credentials/transport/ssl/ssl_credentials.cc:
cpp
tsi_ssl_pem_key_cert_pair* grpc_convert_grpc_to_tsi_cert_pairs(
内存管理 - 对应的销毁函数在 src/core/credentials/transport/tls/ssl_utils.cc:
cpp
void grpc_tsi_ssl_pem_key_cert_pairs_destroy
TSI 层
定义在 src/core/tsi/ssl_transport_security.h:
cpp
struct tsi_ssl_pem_key_cert_pair {
const char* private_key;
const char* cert_chain;
};
生成证书脚本
创建 make_gm_certs.sh:
bash
WORKDIR="./ntls_certs"
mkdir -p $WORKDIR && cd $WORKDIR || exit 1
/usr/local/tongsuo/bin/openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:sm2 -out ca.key
/usr/local/tongsuo/bin/openssl req -x509 -new -key ca.key -out ca.crt -subj "/C=CN/ST=ZJ/L=HZ/O=NTLS_CA/CN=Root CA" -sigopt sm2_id:1234567812345678
echo "====== 生成服务器证书 ======"
/usr/local/tongsuo/bin/openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:sm2 -out sign_server.key
/usr/local/tongsuo/bin/openssl req -new -key sign_server.key -out server_sign.csr \
-subj "/C=CN/ST=ZJ/L=HZ/O=Server/CN=server.example.com"
/usr/local/tongsuo/bin/openssl x509 -req -in server_sign.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out sign_server.crt -days 365 \
-extfile <(echo -e "basicConstraints=critical,CA:FALSE\nkeyUsage=Digital Signature\nextendedKeyUsage=serverAuth") \
-sigopt sm2_id:1234567812345678
/usr/local/tongsuo/bin/openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:sm2 -out enc_server.key
/usr/local/tongsuo/bin/openssl req -new -key enc_server.key -out server_enc.csr \
-subj "/C=CN/ST=ZJ/L=HZ/O=Server/CN=server.example.com"
/usr/local/tongsuo/bin/openssl x509 -req -in server_enc.csr \
-CA ca.crt -CAkey ca.key \
-out enc_server.crt -days 365 \
-extfile <(echo -e "basicConstraints=critical,CA:FALSE\nkeyUsage=Key Encipherment, Data Encipherment, Key Agreement\nextendedKeyUsage=serverAuth") \
-sigopt sm2_id:1234567812345678
echo "====== 生成客户端证书 ======"
/usr/local/tongsuo/bin/openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:sm2 -out sign_client.key
/usr/local/tongsuo/bin/openssl req -new -key sign_client.key -out client_sign.csr \
-subj "/C=CN/ST=ZJ/L=HZ/O=Client/CN=client.example.com"
/usr/local/tongsuo/bin/openssl x509 -req -in client_sign.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out sign_client.crt -days 365 \
-extfile <(echo -e "basicConstraints=critical,CA:FALSE\nkeyUsage=Digital Signature\nextendedKeyUsage=clientAuth") \
-sigopt sm2_id:1234567812345678
/usr/local/tongsuo/bin/openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:sm2 -out enc_client.key
/usr/local/tongsuo/bin/openssl req -new -key enc_client.key -out client_enc.csr \
-subj "/C=CN/ST=ZJ/L=HZ/O=Client/CN=client.example.com"
/usr/local/tongsuo/bin/openssl x509 -req -in client_enc.csr \
-CA ca.crt -CAkey ca.key \
-out enc_client.crt -days 365 \
-extfile <(echo -e "basicConstraints=critical,CA:FALSE\nkeyUsage=Key Encipherment, Data Encipherment, Key Agreement\nextendedKeyUsage=clientAuth") \
-sigopt sm2_id:1234567812345678
echo "证书生成完成,目录:$WORKDIR"
编译脚本
创建 make_server_client.sh:
bash
protoc --grpc_out=. --plugin=protoc-gen-grpc=/usr/local/bin/grpc_cpp_plugin echo.proto
protoc --cpp_out=. echo.proto
g++ -std=c++17 -c helloworld.pb.cc helloworld.grpc.pb.cc
g++ -std=c++17 -c greeter_server_mtls.cc
g++ -std=c++17 -c greeter_client_mtls.cc
g++ -std=c++17 -o server helloworld.pb.o helloworld.grpc.pb.o greeter_server_mtls.o \
$(pkg-config --cflags --libs grpc++ protobuf)
g++ -std=c++17 -o client helloworld.pb.o helloworld.grpc.pb.o greeter_client_mtls.o \
$(pkg-config --cflags --libs grpc++ protobuf)
编译完之后分别执行 ./server 和 ./client 即可。
Proto 文件
创建 helloworld.proto:
protobuf
syntax = "proto3";
package helloworld;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}