# Gavin Andresen # 2010-09-03 23:41:13 # https://bitcointalk.org/index.php?topic=978.msg12002#msg12002 Nobody volunteered, but the boost ssl LOOKS like it will make it easy... so I've started playing around with it. @p{par} After much wrestling with the (sucky) OpenSSL and boost::asio::ssl docs, I've got a standalone, dumb, Satoshi-style-c++ https server running (code below). @p{par} Are there any real OpenSSL experts here who can review the code and answer questions like: @p{par} + I understand the temp Diffie-Hellman file contains large prime numbers used to do public key exchange. Everything works just fine if I leave out the call to context.use_tmp_dh_file; what are the security implications? Will it matter for what we'll be doing (securing the JSON-RPC channel from eavesdropping/man-in-the-middle attacks)? @p{par} + I'm following the advice from @p{(link}here@p{link)}, excluding old, low-security ciphers using: @p{brk} SSL_CTX_set_cipher_list(context.impl(), "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH"); @p{brk} Am I correct in assuming that any sane JSON-RPC/HTTP/HTTPS library will support the higher-strength ciphers? Or does Java on a PC do something braindead and support only DES-MD5? (and yeah, I'll make this overridable via a config file param, but I want to get the defaults right) @p{par} + Oh, and a C++ expert question: what magic incantation will turn the boost::asio::ssl::stream into an iostream that understands @p{lt}@p{lt} and @s{gt}@s{gt} ? @p{par} And thumbnail sketch of how I imagine this working with bitcoin: @p{par} + config file setting to turn on ssl/tls rpc ( maybe rpcssl=true ... or should it be rpctls=true ? ) @p{brk} + if turned on, only ssl connections accepted on the rpcport @p{brk} + if turned on, bitcoin binds rpcport to all addresses (not just 127.0.0.1) @p{par} Code: #include @p{lt}boost/asio.hpp@s{gt} @p{brk} #include @p{lt}boost/asio/ssl.hpp@s{gt} @p{brk} #include @p{lt}boost/foreach.hpp@s{gt} @p{brk} #include @p{lt}iostream@s{gt} @p{brk} #include @p{lt}sstream@s{gt} @p{brk} #include @p{lt}string@s{gt} @p{par} using namespace std; @p{brk} using namespace boost; @p{brk} using boost::asio::ip::tcp; @p{par} typedef boost::asio::ssl::stream@p{lt}boost::asio::ip::tcp::socket@s{gt} ssl_stream; @p{par} string HTTPReply(int, const string&); @p{par} int main() @p{brk} { @p{brk} // Bind to loopback 127.0.0.1 so the socket can only be accessed locally @p{brk} boost::asio::io_service io_service; @p{brk} tcp::endpoint endpoint(boost::asio::ip::address_v4::loopback(), 1111); @p{brk} tcp::acceptor acceptor(io_service, endpoint); @p{par} boost::asio::ssl::context context(io_service, boost::asio::ssl::context::sslv23); @p{brk} context.set_options( @p{brk} boost::asio::ssl::context::default_workarounds @p{brk} | boost::asio::ssl::context::no_sslv2); @p{brk} context.use_certificate_chain_file("server.cert"); @p{brk} context.use_private_key_file("server.pem", boost::asio::ssl::context::pem); @p{brk} context.use_tmp_dh_file("dh512.pem"); @p{brk} SSL_CTX_set_cipher_list(context.impl(), "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH"); @p{par} for (;;) @p{brk} { @p{brk} // Accept connection @p{brk} ssl_stream stream(io_service, context); @p{brk} tcp::endpoint peer_endpoint; @p{brk} acceptor.accept(stream.lowest_layer(), peer_endpoint); @p{brk} boost::system::error_code ec; @p{brk} stream.handshake(boost::asio::ssl::stream_base::server, ec); @p{par} if (!ec) { @p{brk} boost::asio::write(stream, boost::asio::buffer(HTTPReply(200, "Okely-Dokely\n"))); @p{brk} } @p{brk} } @p{brk} } @p{par} string HTTPReply(int nStatus, const string& strMsg) @p{brk} { @p{brk} if (nStatus == 401) @p{brk} return "HTTP/1.0 401 Authorization Required\r\n" @p{brk} "Server: HTTPd/1.0\r\n" @p{brk} "Date: Sat, 08 Jul 2006 12:04:08 GMT\r\n" @p{brk} "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n" @p{brk} "Content-Type: text/html\r\n" @p{brk} "Content-Length: 311\r\n" @p{brk} "\r\n" @p{brk} "@p{lt}!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n" @p{brk} "\"@s{(link)}\"@s{gt}\r\n" @p{brk} "@p{lt}HTML@s{gt}\r\n" @p{brk} "@p{lt}HEAD@s{gt}\r\n" @p{brk} "@p{lt}TITLE@s{gt}Error@p{lt}/TITLE@s{gt}\r\n" @p{brk} "@p{lt}META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'@s{gt}\r\n" @p{brk} "@p{lt}/HEAD@s{gt}\r\n" @p{brk} "@p{lt}BODY@s{gt}@p{lt}H1@s{gt}401 Unauthorized.@p{lt}/H1@s{gt}@p{lt}/BODY@s{gt}\r\n" @p{brk} "@p{lt}/HTML@s{gt}\r\n"; @p{brk} string strStatus; @p{brk} if (nStatus == 200) strStatus = "OK"; @p{brk} else if (nStatus == 400) strStatus = "Bad Request"; @p{brk} else if (nStatus == 404) strStatus = "Not Found"; @p{brk} else if (nStatus == 500) strStatus = "Internal Server Error"; @p{brk} ostringstream s; @p{brk} s @p{lt}@p{lt} "HTTP/1.1 " @p{lt}@p{lt} nStatus @p{lt}@p{lt} " " @p{lt}@p{lt} strStatus @p{lt}@p{lt} "\r\n" @p{brk} @p{lt}@p{lt} "Connection: close\r\n" @p{brk} @p{lt}@p{lt} "Content-Length: " @p{lt}@p{lt} strMsg.size() @p{lt}@p{lt} "\r\n" @p{brk} @p{lt}@p{lt} "Content-Type: application/json\r\n" @p{brk} @p{lt}@p{lt} "Date: Sat, 09 Jul 2009 12:04:08 GMT\r\n" @p{brk} @p{lt}@p{lt} "Server: json-rpc/1.0\r\n" @p{brk} @p{lt}@p{lt} "\r\n" @p{brk} @p{lt}@p{lt} strMsg; @p{brk} return s.str(); @p{brk} } @p{brk}