Local HTTPS server on Android with client authentication

I need to create a key distribution server, which should be just a local server that receives AES keys from a DRM provider through its own API and returns them to connected clients. The client is either my own media player, based on the FFmpeg library, or on my own Android media player.

I experimented on Windows with the Boost ASIO library and OpenSSL - there is an example of how to create a simple HTTPS server and client. In my case, I should only allow access to the server for dedicated applications / clients, so I need client authentication. There are a few things that I don’t understand, since I have never been an expert in this matter.

I know that an HTTPS server must request authentication from the client, the client must send its certificate, and then the server picks up and verifies some data from the certificate. Questions:

  • What certificate do I need for the client, how to create it?
  • What certificate do I need for the server, how to create it?
  • Where can I store a client certificate so that it is available to clients (FFmpeg, Android MediaPlayer)? It should not be available for any other application.
  • Where can I store the server certificate?

Everything that I write about execution is done at its own level, that is, it is implemented by the shared Linux libraries. This is why I think this is more of a question for Linux gurus than for regular Android developers.

- - , - - ? !

!

+5
1

, -, . . , , . , . . , username/password, , . , .

, SSL-:

void SSLSocket::Connect(SSLSocket* psSLS, const string& serverPath, string& port)
{
   // Connects to the server.
   // serverPath - specifies the path to the server.  Can be either an ip address or url.
   // port - port server is listening on.
   //
   try
   {
      boost::shared_ptr<boost::asio::io_service> IOServ(new boost::asio::io_service);
      IOService = IOServ; // Defined the same way in the class
      Locking CodeLock(SocketLock); // Single thread the code.
      // If the user has tried to connect before, then make sure everything is clean before trying to do so again.
      if (pSocket)
      {
         delete pSocket;
         pSocket = 0;
      }                                                                                                  
      // Create the resolver and query objects to resolve the host name in serverPath to an ip address.
      boost::asio::ip::tcp::resolver resolver(*IOService);
      boost::asio::ip::tcp::resolver::query query(serverPath, port);
      boost::asio::ip::tcp::resolver::iterator EndpointIterator = resolver.resolve(query);
      // Set up an SSL context.
      boost::asio::ssl::context ctx(*IOService, boost::asio::ssl::context::tlsv1_client);
      // Specify to not verify the server certificiate right now.
      ctx.set_verify_mode(boost::asio::ssl::context::verify_none);
      // Init the socket object used to initially communicate with the server.
      pSocket = new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(*IOService, ctx);
      //
      // The thread we are on now, is most likely the user interface thread.  Create a thread to handle all incoming and outgoing socket work messages.
      if (!RcvThreadCreated)
      {
         WorkerThreads.create_thread(boost::bind(&SSLSocket::RcvWorkerThread, this));
         RcvThreadCreated = true;
         WorkerThreads.create_thread(boost::bind(&SSLSocket::SendWorkerThread, this));
      }
      // Try to connect to the server.  Note - add timeout logic at some point.
      // This is an async method and will return right away.  When it completes, the
      // SSLSocket::HandleConnect method will be called and will contain error info to
      // identify if a successful connection was made or not.
      boost::asio::async_connect(pSocket->lowest_layer(), EndpointIterator,
         boost::bind(&SSLSocket::HandleConnect, this, boost::asio::placeholders::error));
}

void SSLSocket::HandleConnect(const boost::system::error_code& error)
{
   // This method is called asynchronously when the server has responded to the connect request.
   std::stringstream ss;
   try
   {
      if (!error)
      {
         pSocket->async_handshake(boost::asio::ssl::stream_base::client,
            boost::bind(&SSLSocket::HandleHandshake, this, boost::asio::placeholders::error));
         ss << "SSLSocket::HandleConnect: From worker thread " << Logger::NumberToString(boost::this_thread::get_id()) << ".\n";
         Log.LogString(ss.str(), LogInfo);
      }
      else
      {
         // Log an error.  This worker thread should exit gracefully after this.
         ss << "SSLSocket::HandleConnect: connect failed to " << sClientIp << " : " << uiClientPort << ".  Error: " << error.message() + ".\n";
         Log.LogString(ss.str(), LogError);
         Stop();
      }
   }
   catch (std::exception& e)
   {
      stringstream ss;
      ss << "SSLSocket::InitAsynchIO: threw an error - " << e.what() << ".\n";
      Log.LogString(ss.str(), LogError);
      Stop();
   }
}

void SSLSocket::HandleHandshake(const boost::system::error_code& error)
{
   // This method is called asynchronously when the server has responded to the handshake request.
   std::stringstream ss;
   try
   {
      if (!error)
      {
         // Try to send the first message that the server is expecting.  This msg tells the server we want to connect.
         // The first 4 bytes specifies the msg length after the first 4 bytes.  The next 2 bytes specifies the msg type.
         // The next 4 bytes specifies the source code.  The next 13 bytes specifies the msg "AttackPoker".
         // The next 2 bytes specifies the locale length.  The last 2 bytes specifies the locale - en for English.
         //
         unsigned char Msg[27] = {0x17, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x41,
            0x74, 0x74, 0x61, 0x63, 0x6b, 0x50, 0x6f, 0x6b, 0x65, 0x72, 0x02, 0x00, 0x65, 0x6e};
         boost::system::error_code Err;

         sClientIp = pSocket->lowest_layer().remote_endpoint().address().to_string();
         uiClientPort = pSocket->lowest_layer().remote_endpoint().port();
         ReqAlive = true;
         // boost::asio::async_write(*pSocket, boost::asio::buffer(Msg), boost::bind(&SSLSocket::HandleFirstWrite, this,
         //   boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
         int Count = boost::asio::write(*pSocket, boost::asio::buffer(Msg), boost::asio::transfer_exactly(27), Err);
         if (Err)
         {
            ss << "SSLSocket::HandleHandshake: write failed - " << error.message() << ".\n";
            Log.LogString(ss.str(), LogInfo);
         }
         HandleFirstWrite(Err, Count);
         // boost::asio::async_write(pSocket, boost::asio::buffer(Msg, 27), boost::bind(&SSLSocket::HandleWrite, this,
         // boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
         ss.str("");
         ss << "SSLSocket::HandleHandshake: From worker thread " << boost::this_thread::get_id() << ".\n";
      }
      else
      {
         ss << "SSLSocket::HandleHandshake: failed - " << error.message() << ".\n";
         IOService->stop();
      }
      Log.LogString(ss.str(), LogInfo);
   }
   catch (std::exception& e)
   {
      stringstream ss;
      ss << "SSLSocket::HandleHandshake: threw an error - " << e.what() << ".\n";
      Log.LogString(ss.str(), LogError);
      Stop();
   }
}

void SSLSocket::HandleFirstWrite(const boost::system::error_code& error, size_t bytesTransferred)
{
   // This method is called after a msg has been written out to the socket.
   std::stringstream ss;
   try
   {
      if (!error)
      {
         // boost::asio::async_read(pSocket, boost::asio::buffer(reply_, bytesTransferred), boost::bind(&SSLSocket::handle_read,
         //   this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
         // boost::asio::async_read(pSocket, boost::asio::buffer(reply_, 84), boost::bind(&SSLSocket::handle_read,
         //   this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
         // Locking CodeLock(ReadLock); // Single thread the code.
         // Signal the other threads that msgs are now ready to be sent and received.
         // boost::asio::async_read(pSocket, boost::asio::buffer(pRepBuf), boost::asio::transfer_exactly(4), boost::bind(&SSLSocket::HandleRead,
         //  this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
         //
         // Notify the UI that we are now connected. - TBD
         // Get the 1st 4 bytes of the next msg, which is always the length of the that msg.
         pDataBuf = BufMang.GetPtr(MsgLenBytes);

         // int i1=1,i2=2,i3=3,i4=4,i5=5,i6=6,i7=7,i8=8,i9=9;
         //   (boost::bind(&nine_arguments,_9,_2,_1,_6,_3,_8,_4,_5,_7))
         //     (i1,i2,i3,i4,i5,i6,i7,i8,i9);

         // boost::asio::read(*pSocket, boost::asio::buffer(pReqBuf, MsgLenBytes), boost::asio::transfer_exactly(MsgLenBytes), Err);
         // boost::asio::async_read(pSocket, boost::asio::buffer(pReqBuf, MsgLenBytes), boost::bind(&SSLSocket::HandleRead, _1,_2,_3))
         //   (this, pReqBuf, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred);
         //   boost::asio::async_read(*pSocket, boost::asio::buffer(reply_), boost::asio::transfer_exactly(ByteCount), boost::bind(&Client::handle_read,
         //      this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
         // boost::asio::async_write(*pSocket, boost::asio::buffer(pDataBuf, MsgLenBytes), boost::bind(&SSLSocket::HandleWrite, this,
         //    boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));

         HandShakeReady = true;
         Locking CodeLock(SocketLock); // Single thread the code.
         boost::asio::async_read(*pSocket, boost::asio::buffer(pDataBuf, MsgLenBytes), boost::bind(&SSLSocket::HandleRead, this,
            boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
      }
      else
      {
         ss << "SSLSocket::HandleFirstWrite: failed - " << error.message() << ".\n";
         Log.LogString(ss.str(), LogError);
         Stop();
      }
   }
   catch (std::exception& e)
   {
      stringstream ss;
      ss << "SSLSocket::HandleFirstWrite: threw an error - " << e.what() << ".\n";
      Log.LogString(ss.str(), LogError);
      Stop();
   }
}
+2

All Articles