将QSslSocket与OpenSSL服务器连接

时间:2016-09-03 00:04:41

标签: c++ qt sockets ssl

我正在尝试在我的Qt应用程序(使用QSslSockets)和运行SSL套接字的c ++服务器(使用openssl)之间连接SSL套接字。

服务器代码:

int create_socket(int port)
{
   int s;
   struct sockaddr_in addr;

   addr.sin_family = AF_INET;
   addr.sin_port = htons(port);
   addr.sin_addr.s_addr = htonl(INADDR_ANY);

   s = socket(AF_INET, SOCK_STREAM, 0);
   if (s < 0) {
      perror("Unable to create socket");
      exit(EXIT_FAILURE);
   }

   if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
      perror("Unable to bind");
      exit(EXIT_FAILURE);
   }

   if (listen(s, 1) < 0) {
      perror("Unable to listen");
      exit(EXIT_FAILURE);
   }
   return s;
}

void init_openssl()
{
   SSL_load_error_strings();
   OpenSSL_add_ssl_algorithms();
}

void cleanup_openssl()
{
   EVP_cleanup();
}

 SSL_CTX *create_context()
{
   const SSL_METHOD *method;
   SSL_CTX *ctx;
   method = SSLv23_server_method();
   ctx = SSL_CTX_new(method);
   if (!ctx) {
      perror("Unable to create SSL context");
      ERR_print_errors_fp(stderr);
      exit(EXIT_FAILURE);
   }
   return ctx;
}

void configure_context(SSL_CTX *ctx)
{    
   if(SSL_CTX_use_certificate_file(ctx, "/root/myCA/server_crt.pem", SSL_FILETYPE_PEM) > 0)
   {
      std::cout<<"Cert found"<<std::endl;
   }
   if (SSL_CTX_use_PrivateKey_file(ctx, "/root/myCA/server_key.pem", SSL_FILETYPE_PEM) > 0 ) {
      std::cout<<"Key found"<<std::endl;
   }
   if(SSL_CTX_check_private_key(ctx) > 0)
   {
      std::cout<<"Key valid"<<std::endl;
   }
}

int main(int argc, char **argv)
{
   int sock;
   SSL_CTX *ctx;

   init_openssl();
   ctx = create_context();

   configure_context(ctx);
   sock = create_socket(3000);

   while(1) {
       struct sockaddr_in addr;
       uint len = sizeof(addr);
       SSL *ssl;
       const char reply[] = "test\n";

      int client = accept(sock, (struct sockaddr*)&addr, &len);
      if (client > 0) {
          std::cout<<"Client accepted..."<<std::endl;
      }
      else
      {
          perror("Unable to accept");
          exit(EXIT_FAILURE);
      }

      ssl = SSL_new(ctx);
      SSL_set_fd(ssl, client);

      if (SSL_accept(ssl) <= 0) {
          ERR_print_errors_fp(stderr);
      }
      else {
          SSL_write(ssl, reply, strlen(reply));
      }

      SSL_free(ssl);
      close(client);
    }
  close(sock);
  SSL_CTX_free(ctx);
  cleanup_openssl();

}

Qt客户代码:

 SSLSOCKET::SSLSOCKET(QObject *parent):QObject(parent)
 {
   connect(&client,SIGNAL(encrypted()),this,SLOT(ConnectionEstablished()));
   connect(&client,SIGNAL(sslErrors(const QList<QSslError>&)),this,SLOT(ErrorOccured(const QList<QSslError> &)));
   QList<QSslCertificate>
   trusted_ca=QSslCertificate::fromPath("/Users/test/Desktop/server_crt.pem");
   if(trusted_ca.empty())
   {
      qDebug()<<"Error not trusted Ca.";
   }
   client.setCaCertificates(trusted_ca);
   client.connectToHostEncrypted(*my ip address*,3000);
 }

 void SSLSOCKET::ErrorOccured(const QList<QSslError> &error)
 {
    qDebug()<<"ERROR HERE----:";
    qDebug()<<error;
 }

 void SSLSOCKET::ConnectionEstablished()
 {
    qDebug()<<"CONNECTION WORKED------:";
    if(!client.waitForEncrypted())
    {
       qDebug()<<client.errorString();
    }
    else
    {
       qDebug()<<"Encrypted Connection Established...";
    }
  }

我可以看到客户端和服务器之间的连接,但是client.waitForEncrypted()显示“未知错误”..

有什么想法吗?

1 个答案:

答案 0 :(得分:2)

您正在waitForEncrypted()处理程序中调用connected()。客户端尚未实际启动SSL握手,因此您无法等待它。根据{{​​3}}文档:

  

使用hostname作为port,在mode上启动与设备OpenMode的加密连接。这相当于调用connectToHost()建立连接,然后调用startClientEncryption()protocol参数可用于指定要使用的网络协议(例如,IPv4或IPv6)。

     

QSslSocket首先进入HostLookupState。然后,在输入事件循环或其中一个waitFor...()函数后,它会输入ConnectingState发出connected(),然后启动SSL客户端握手。在每次状态更改时,QSslSocket都会发出信号stateChanged()

如果您想像这样处理connected(),则必须使用connectToHostEncrypted()代替connectToHostEncrypted(),然后分别致电connectToHost()

SSLSOCKET::SSLSOCKET(QObject *parent):QObject(parent)
{
    connect(&client, SIGNAL(connected()), this, SLOT(ConnectionEstablished()));
    ...
    client.connectToHost(*my ip address*, 3000);
}

void SSLSOCKET::ConnectionEstablished()
{
    client.startClientEncryption();
    if (!client.waitForEncrypted())
        ...
}

编辑:抓一下。我以为你正在处理connected()信号,但我现在看到你正在处理encrypted()信号。在那种情况下:

  

如果SSL握手成功,QSslSocket会发出encrypted()

所以你根本不需要使用waitForEncrypted()

void SSLSOCKET::ConnectionEstablished()
{
    qDebug()<<"CONNECTION WORKED------:";
    qDebug()<<"Encrypted Connection Established...";
}

即使您确实调用了它,它也应该返回true:

  

等待套接字完成SSL握手并发出encrypted()msecs毫秒,以先发生者为准。 如果已发出encrypted(),则此函数返回true ;否则(例如,套接字断开连接,或SSL握手失败),返回false。

因此,除非在握手完成后断开套接字,或者在加密状态实际发生变化之前发出encrypted(),或者发生其他一些无法预料的错误,否则我看不出任何理由waitForEncrypted()encrypted()处理程序内返回false。