一行代码带你实现最简单的国密浏览器

750 阅读2分钟

我正在参加「掘金·启航计划」

——————————————————————————————

源起

会点进来的同学应该对国密不会陌生,这里就不多赘述。现在市面上的浏览器大多都不兼容国密HTTPS,专门做兼容国密的厂商一般都不开源甚至不免费,linux下国密浏览器更是没有,而我的主力机系统是manjaro。所以我萌生了实现一个linux下简单国密浏览器的想法,也有想过做国密正代,更具有普适性,但感觉没浏览器“高级”。

开发方向和准备

现在的国密浏览器基本都是基于chromium进行二开,chromium对于我来说还是太重了,我也没有足够的计算资源来进行编译。事实证明我的决定是正确的,我的电脑编译webkit都需要很久的时间。所以我选择了更为小巧的webkit,或许它还不能被称作一个完整的浏览器,仅仅是一个引擎,但可以进行二次封装,官方就有简单实现MiniBrowser。

主要准备就是webkit源码和利用TASSL和nginx搭建一个国密HTTPS服务器。

国密协议浅析

gm_wireshark.png

fgm.png 从wirshark抓包来看,主要区别就在于TLS版本以及加密套件。TLS原理我在这也不再赘述,从抓包分析来看,普通浏览器无法访问国密站点就主要是这两个地方。一不认识TLS版本,二也没有此国密加密套件。

其实我不是很明白国密HTTPS的设计为什么要新定一个TLS版本,如果仅仅是缺少加密套件,后续兼容国密和非国密会轻松很多。

webkit网络模块浅析

虽说比不上chromium,但webkit的代码量也决不能小看。https主要涉及到网络模块,webkit是跨平台的,它的网络模块也有多种实现,在我linux下默认是借用libsoup库进行网络传输交互。

  • libsoup

我们去下载soup的源码看其原理,发现

static void

soup_connection_event (SoupConnection *conn,

GSocketClientEvent event,

GIOStream *connection)

{

SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
if (!connection && priv->socket)

connection = soup_socket_get_connection (priv->socket);
g_signal_emit (conn, signals[EVENT], 0,

event, connection);

}

看到GIOStream,就有头绪了,对常用lib比较了解的童鞋直接ldd看下依赖就知道了。

  • glib-gio 大致浏览一下gio,应是一个io抽象接口库,直接看其tls的实现
/**
 * g_tls_backend_get_default:
 *
 * Gets the default #GTlsBackend for the system.
 *
 * Returns: (not nullable) (transfer none): a #GTlsBackend, which will be a
 *     dummy object if no TLS backend is available
 *
 * Since: 2.28
 */
GTlsBackend *
g_tls_backend_get_default (void)
{
  if (g_once_init_enter (&tls_backend_default_singleton))
    {
      GTlsBackend *singleton;

      singleton = _g_io_module_get_default (G_TLS_BACKEND_EXTENSION_POINT_NAME,
                                            "GIO_USE_TLS",
                                            NULL);

      g_once_init_leave (&tls_backend_default_singleton, singleton);
    }

  return tls_backend_default_singleton;
}

由于gio对IO高度抽象,代码中不容易体现,主要就是gio会去动态加载指定的lib库实现tls。

……真是一层套一层啊……(┙>∧<)┙へ┻┻

  • glib-networking

观其文件结构,发现tls的实现依赖于两个tls库……

  • openssl/guntls

终于到了最终的tls实现的地方。openssl比较常用,guntls貌似在开发中使用的并不是很多。开源的国密ssl的实现基本都基于openssl进行二开。所以我们也从openssl进行下手。

一行代码实现国密HTTPS

我们引入tassl替代openssl,并参考其国密实现,我们在glib-networking的openssl封装模块中进行修改。

//修改前
priv->ssl_ctx = SSL_CTX_new (SSLv23_client_method ());
//修改后
priv->ssl_ctx = SSL_CTX_new (CNTLS_client_method());

接下来重新编译即可,你看真的只修改了一行……

因为强制进行国密交互,正常的https都将无法通信……

成果

Screenshot_20221012_231205.png

Screenshot_20221012_231400.png