关于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";
// Dispatcher
TBinaryProtocolFactoryT<TBufferBase>* binaryProtocolFactory = new TBinaryProtocolFactoryT<TBufferBase>();
binaryProtocolFactory->setContainerSizeLimit(0);
binaryProtocolFactory->setStringSizeLimit(0);
std::shared_ptr<TProtocolFactory> protocolFactory;
protocolFactory.reset(binaryProtocolFactory);
// Processors
std::shared_ptr<TestHandler> testHandler(new TestHandler());
std::shared_ptr<TProcessor> testProcessor(new FirstServiceProcessor(testHandler));
// Transport
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));
// Factory
std::shared_ptr<TTransportFactory> transportFactory;
transportFactory = std::make_shared<TBufferedTransportFactory>();
// Server Info
cout << "Starting simple server (buffered/binary) listen on: " << serverSocket->getPort() << endl;
// Server
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(); // wait for a shutdown signal
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)
{
// this is typical for normal cpp generated code
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);
// Connection info
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;
}
|
上述客户端代码和服务端代码,可以在如下两个地址下载: