# 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}