关于Thrift和OpenSSL的安全通信,上篇我们描述了数字证书的生成方法,本文在此基础上编写单向验证的测试代码。
- 01#编译OpenSSL库;
- 02#编译Boost库;
- 03#编译zlib库;
- 04#编译libevent库;
- 05#编译Thrift库;
- 06#生成客户端和服务端通信所用的数字证书;
- 07#编写基于Linux系统的测试代码(单向验证:客户端验证服务端);
- 08#编写基于Windows系统的测试代码(单向验证:客户端验证服务端);
- 09#编写基于Linux系统的测试代码(双向验证:客户端验证服务端+服务端验证客户端);
- 10#编写基于Windows系统的测试代码(双向验证:客户端验证服务端+服务端验证客户端);
- 11#自定义数字证书的验证策略;
数字证书单向验证原理简介
提到数字证书的验证,一般指的是:
- CA机构使用其根公钥证书(ca.crt)签发服务端公钥证书(server.crt);
- 服务端程序内置服务端私钥证书(server.key)和服务端公钥证书(server.crt);
- 客户端程序内置根公钥证书(ca.crt);
- 客户端程序启动后,向服务端请求证书,然后服务端将server.crt发送给客户端;
- 客户端获得服务端公钥证书(server.crt)后,使用根公钥证书(ca.crt)验证server.crt的合法性;
- 验证机制如果发现server.crt是ca.crt签发的,则继续SSL握手操作,如对称密钥交换等步骤;
- 验证机制如果发现server.crt非ca.crt签发的,则终止SSL握手操作,终止本次通信;
上述步骤即是我们常见的单向验证,即客户端验证服务端的合法性,如果服务端是合法的,则进行SSL安全通信,否则就终止通信。
如果验证机制通过了server.crt的合法性校验,那服务端私钥证书server.key扮演了什么角色?
- 我们知道SSL考虑到加密性能,在进行真实数据通信时,最终还得采用对称加密。
- 客户端生成对称加密密钥(random_key),再由服务端公钥证书(server.crt)进行非对称加密后得到密文,然后将此密文发给服务端。
- 服务端获得密文后,由服务端私钥证书(server.key)进行解密,得到对称加密密钥的明文(random_key)。
- 后续的通信,双方均使用该密钥(random_key)进行加解密。
服务端程序
以下是SSL通信所用的服务端测试程序,从代码看其用到了server.crt和server.key两个证书。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| #include <stdint.h> #include <inttypes.h> #include <signal.h> #include <iostream> #include <stdexcept> #include <sstream>
#include <thrift/concurrency/ThreadFactory.h> #include <thrift/concurrency/ThreadManager.h> #include <thrift/transport/TSSLServerSocket.h> #include <thrift/transport/TSSLSocket.h> #include <thrift/protocol/TBinaryProtocol.h> #include <thrift/server/TSimpleServer.h> #include "FirstService.h"
using namespace std; using namespace apache::thrift::protocol; using namespace apache::thrift::transport; using namespace apache::thrift::server; using namespace thrift::test;
apache::thrift::concurrency::Monitor gMonitor; void signal_handler(int signum) { if (signum == SIGINT) { gMonitor.notifyAll(); } }
class TestHandler : public FirstServiceIf { public: TestHandler() = default;
void testString(string& out, const string& thing) override { printf("testString(\"%s\")\n", thing.c_str()); out = thing; } };
int main(int argc, char** argv) { std::string sServerCRT = "cert.v3/server.crt"; std::string sServerKEY = "cert.v3/server.key";
TBinaryProtocolFactoryT<TBufferBase>* binaryProtocolFactory = new TBinaryProtocolFactoryT<TBufferBase>(); binaryProtocolFactory->setContainerSizeLimit(0); binaryProtocolFactory->setStringSizeLimit(0);
std::shared_ptr<TProtocolFactory> protocolFactory; protocolFactory.reset(binaryProtocolFactory);
std::shared_ptr<TestHandler> testHandler(new TestHandler()); std::shared_ptr<TProcessor> testProcessor(new FirstServiceProcessor(testHandler));
std::shared_ptr<TSSLSocketFactory> sslSocketFactory; sslSocketFactory = std::shared_ptr<TSSLSocketFactory>(new TSSLSocketFactory()); sslSocketFactory->loadCertificate(sServerCRT.c_str()); sslSocketFactory->loadPrivateKey(sServerKEY.c_str()); sslSocketFactory->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
std::shared_ptr<TServerSocket> serverSocket; serverSocket = std::shared_ptr<TServerSocket>(new TSSLServerSocket(9090, sslSocketFactory));
std::shared_ptr<TTransportFactory> transportFactory; transportFactory = std::make_shared<TBufferedTransportFactory>();
cout << "Starting simple server (buffered/binary) listen on: " << serverSocket->getPort() << endl;
std::shared_ptr<apache::thrift::server::TServer> server; server.reset(new TSimpleServer(testProcessor, serverSocket, transportFactory, protocolFactory));
if (server.get() != nullptr) { apache::thrift::concurrency::ThreadFactory factory; factory.setDetached(false); std::shared_ptr<apache::thrift::concurrency::Runnable> serverThreadRunner(server); std::shared_ptr<apache::thrift::concurrency::Thread> thread = factory.newThread(serverThreadRunner);
signal(SIGINT, signal_handler);
thread->start(); gMonitor.waitForever();
signal(SIGINT, SIG_DFL);
server->stop(); thread->join(); server.reset(); }
cout << "done." << endl;
return 0; }
|
客户端程序
以下是SSL通信所用的客户端测试程序,从代码看其用到了ca.crt一个证书。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
| #include <iostream> #include <sstream> #include <thrift/protocol/TBinaryProtocol.h> #include <thrift/transport/TSSLSocket.h> #include "FirstService.h"
using namespace apache::thrift::protocol; using namespace apache::thrift::transport; using namespace thrift::test;
template<typename Proto> class TPedanticProtocol : public Proto { public: TPedanticProtocol(std::shared_ptr<TTransport>& transport) : Proto(transport), m_last_seqid((std::numeric_limits<int32_t>::max)() - 10) {
}
virtual uint32_t writeMessageBegin_virt(const std::string& name, const TMessageType messageType, const int32_t in_seqid) override { int32_t seqid = in_seqid; if (!seqid) { seqid = ++m_last_seqid; }
return Proto::writeMessageBegin_virt(name, messageType, seqid); }
virtual uint32_t readMessageBegin_virt(std::string& name, TMessageType& messageType, int32_t& seqid) override { uint32_t result = Proto::readMessageBegin_virt(name, messageType, seqid); if (seqid != m_last_seqid) { std::stringstream ss; ss << "ERROR: send request with seqid " << m_last_seqid << " and got reply with seqid " << seqid; throw std::logic_error(ss.str()); }
return result; }
private: int32_t m_last_seqid; };
int main(int argc, char** argv) { std::string sCACertPath = "cert.v3/ca.crt"; std::string sServerHost = "127.0.0.1";
std::shared_ptr<TSSLSocketFactory> factory; factory = std::shared_ptr<TSSLSocketFactory>(new TSSLSocketFactory()); factory->loadTrustedCertificates(sCACertPath.c_str()); factory->authenticate(true); factory->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
std::shared_ptr<TSocket> socket; socket = factory->createSocket(sServerHost, 9090);
std::shared_ptr<TTransport> transport; transport = std::make_shared<TBufferedTransport>(socket);
typedef TPedanticProtocol<TBinaryProtocol> TPedanticBinaryProtocol; std::shared_ptr<TProtocol> protocol; protocol = std::make_shared<TPedanticBinaryProtocol>(transport);
std::cout << "Connecting (buffered/binary" << ") to: " << sServerHost << ":" << socket->getPort() << std::endl;
FirstServiceClient testClient(protocol);
try { transport->open(); std::string s; testClient.testString(s, "Test"); std::cout << "testString(\"Test\")" << " = " << s << std::endl; if (s != "Test") { std::cout << "error" << std::endl; } } catch (TTransportException& ex) { std::cout << "Connect failed: " << ex.what() << std::endl;
return 0; }
transport->close();
return 0; }
|
上述客户端代码和服务端代码,可以在如下两个地址下载: