| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 网络协议 -> HTTPS的理解(证书、认证方式、TLS握手) -> 正文阅读 |
|
[网络协议]HTTPS的理解(证书、认证方式、TLS握手) |
文章目录Https基本介绍HTTP 存在的问题 没有加密,无法保证通信内容不被窃听。 没有报文完整性验证,无法确保通信内容在传输中不被改变。 没有身份鉴别,无法让通信双方确认对方身份。 HTTP over SSL,在 HTTP 传输上增加了传输层安全性(TLS)或安全套接字层(SSL),通过信息加密、数据完整性校验、身份鉴别为 HTTP 事务提供安全保证。SSL 会对数据进行加密并把加密数据送往 TCP 套接字,在接收方,SSL 读取 TCP 套接字的数据并解密,把数据交给应用层。 HTTPS 采用混合加密机制,使用非对称加密传输对称密钥保证传输安全,使用对称加密保证通信效率。每一个 当我们申请域名证书,SSL会有一个pem证书和key私钥,pem证书包含数字签名和公钥。 证书文件证书相关文件有多种格式,常见格式:
实际上,上述文件的扩展名可以随意命名。只是为了容易理解文件的功能而选择大家都认识的命名方式。但是,上述文件是有格式的,只能是
证书文件:
私钥文件:
请求文件:
证书种类证书分为根证书、服务器证书、客户端证书。根证书文件( 客户端会有一个信任库,里面保存了该客户端信任的CA(证书签发机构)的证书,如果收到的证书签发机构不在信任库中,则客户端会提示用户证书不可信。 若客户端是浏览器,各个浏览器都会内置一些可信任的证书签发机构列表,在浏览器的设置中可以看到。 证书内容一个数字证书通常包含了:
服务器获取证书步骤和客户端验证步骤服务器获取证书:
由于 客户端验证证书:
证书链但事实上,证书的验证过程中还存在一个证书信任链的问题,因为我们向 CA 申请的证书一般不是根证书签发的,而是由中间证书签发的,比如百度的证书,从下图你可以看到,证书的层级有三级: 对于这种三级层级关系的证书的验证过程如下:
在这三个步骤中,最开始客户端只信任根证书 GlobalSign Root CA 证书的,然后 “GlobalSign Root CA” 证书信任 “GlobalSign Organization Validation CA - SHA256 - G2” 证书,而 “GlobalSign Organization Validation CA - SHA256 - G2” 证书又信任 baidu.com 证书,于是客户端也信任 baidu.com 证书。 这样的一层层地验证就构成了一条信任链路,整个证书信任链验证流程如下图所示: 最后一个问题,为什么需要证书链这么麻烦的流程?Root CA 为什么不直接颁发证书,而是要搞那么多中间层级呢? 这是为了确保根证书的绝对安全性,将根证书隔离地越严格越好,不然根证书如果失守了,那么整个信任链都会有问题。 自签名证书我们的网页要想使用HTTPS访问,就必须向证书机构去申请签发,并放在Nginx服务器下。但如果只是企业内部使用,不是给公众使用,也可以自行颁发自签名证书,比如K8S集群中就可以自行签发证书完成HTTPS双向认证。 一下以openssl来进行创建证书相关文件。 生成根证书生成这一些列证书之前,我们需要先生成一个CA根证书,然后由这个CA根证书颁发服务器公钥证书和客户端公钥证书。为了验证根证书颁发与验证客户端证书这个逻辑,我们使用根证书生成两套不同的客户端证书,然后同时用两个客户端证书来发送请求,看服务器端是否都能识别。
在创建证书请求文件的时候需要注意三点,下面生成服务器请求文件和客户端请求文件均要注意这三点:
经过上面三个命令行,我们最终可以得到一个签名有效期为10年的根证书root.crt,后面我们可以用这个根证书去颁发服务器证书和客户端证书。 生成自签名服务器端证书
经过上面的三个命令,我们得到: server.key:服务器端的秘钥文件 server.crt:有效期十年的服务器端公钥证书,使用根证书和服务器端私钥文件一起生成 生成自签名客户端证书
重复使用上面的三个命令,我们得到两套客户端证书: client.key / client2.key: 客户端的私钥文件 client.crt / client2.key: 有效期十年的客户端证书,使用根证书和客户端私钥一起生成 client.p12/client2.p12:客户端p12格式,这个证书文件包含客户端的公钥和私钥,主要用来给浏览器访问使用。 Java API调用由于使用的是自签名证书,使用ApacheHttpClient去调用的话,需要将服务器证书加入可信任证书库中,才能成功调用,也可以在代码中简单忽略证书。
将服务器端公钥证书设置为可信证书后,使用以下代码可以直接发起带客户端证书的HTTPS请求:
cfssl的使用除了openssl之外,还有一种cfssl也可以达到自签名证书的效果。
认证方式和TLS握手(※)https有两种认证方式:单向验证和双向验证,而使用哪一种,是由服务器决定的。 单向认证单向验证是指通信双方中一方验证另一方是否合法。通常是指客户端验证服务器。
HTTPS 流程(TLS4次握手):
① 客户发送它支持的密码套件列表、一个不重数、SSL/TLS协议版本列表。不重数就是在协议的生存期只使用一次的数,用于防止重放攻击(端点鉴别),每个 TCP 会话使用不同的不重数,可以使加密密钥不同,重放记录无法通过完整性检查。 ② 服务器将SSL/TLS版本、一个不重数、密码套件列表中的一个密码套件、证书(证书包含公钥)发送给客户。密码套件基本的形式是「密钥交换算法(RSA) + 签名算法(RSA) + 对称加密算法(AES) + 摘要算法(SHA384)」 ③ 客户通过 CA(
服务器和客户端使用相同的密钥导出函数,独立的根据PMS和两个不重数中计算出主密钥MS。MS被切分为一个加密密钥和一个MAC密钥。当选择的对称密钥使用CBC时,两个初始向量也从MS获得,分别用于客户端和发送端。对每个记录附加一个MAC用于完整性检查,然后加密“记录+MAC”。MAC是数据+MAC密钥+当前序号的散列。序号是保证记录数据流不被第三方扰乱,接收方通过发送方的序号,通过在MAC的计算中包括适当的序号,验证记录的数据完整性。 然后客户端把之前发送的数据+数据的摘要(报文鉴别码Mac)进行加密,发送给服务器做一个验证,验证加密通信是否可用和之前握手信息是否有被中途篡改过。 ④服务器用私钥得到PMS,和客户端一样从MS中获取加密密钥和Mac密钥,也将之前发送的数据+数据的摘要(报文鉴别码Mac)进行加密,发送给客户端做一个验证,验证加密通信是否可用和之前握手信息是否有被中途篡改过。 应用: 我们平时使用 PC 上网时使用的就是单向验证的方式。即,我们验证我们要访问的网站的合法性。PC 中的浏览器(火狐、IE、chrome等)已经包含了很多 CA 的根证书(
双向认证单向验证过程中,客户端会验证自己访问的服务器,服务器对来访的客户端身份不做任何限制。如果服务器需要限制客户端的身份,则可以选择开启服务端验证,这就是双向验证。从这个过程中我们不难发现,使用单向验证还是双向验证,是服务器决定的。 一般而言,我们的服务器都是对所有客户端开放的,所以服务器默认都是使用单向验证。如果你使用的是Tomcat服务器,在配置文件server.xml中,配置Connector节点的clientAuth属性即可。若为true,则使用双向验证,若为false,则使用单向验证。如果你的服务,只允许特定的客户端访问,那就需要使用双向验证了。 双向验证是指通信双方需要互相验证对方是否合法。服务器验证客户端,客户端验证服务器。
HTTPS流程(TLS4次握手): 双向验证基本过程与单向验证相同,不同在于:
所以,**在双向验证中,客户端需要用到密钥库,保存自己的私钥和证书,并且证书需要提前发给服务器,由服务器放到它的信任库中。**这一点至关重要,因为之前没想通这一点,导致我看K8S的kubeconfig的操作一脸懵逼。 应用: 双向验证通常用于支付系统中,比如支付宝。我们在使用支付宝时必须下载数字证书,该证书就是支付宝颁发给针对我们这台机器的证书,我们只能使用这台机器访问支付宝。如果换了机器,那么需要重新申请证书。 RSA握手具体分析TLS 第一次握手客户端首先会发一个「Client Hello」消息,字面意思我们也能理解到,这是跟服务器「打招呼」。 消息里面有客户端使用的 TLS 版本号、支持的密码套件列表,以及生成的随机数(Client Random),这个随机数会被服务端保留,它是生成主密钥MS的材料之一。 TLS 第二次握手当服务端收到客户端的「Client Hello」消息后,会确认 TLS 版本号是否支持,和从密码套件列表中选择一个密码套件,以及生成随机数(Server Random)。 接着,返回「Server Hello」消息,消息里面有服务器确认的 TLS 版本号,也给出了随机数(Server Random),然后从客户端的密码套件列表选择了一个合适的密码套件。 可以看到,服务端选择的密码套件是 “Cipher Suite: TLS_RSA_WITH_AES_128_GCM_SHA256”。 这个密码套件是有固定格式和规范的。基本的形式是「密钥交换算法 + 签名算法 + 对称加密算法 + 摘要算法」, 一般 WITH 单词前面有两个单词,第一个单词是约定密钥交换的算法,第二个单词是约定证书的验证算法。比如刚才的密码套件的意思就是:
就前面这两个客户端和服务端相互「打招呼」的过程,客户端和服务端就已确认了 TLS 版本和使用的密码套件,而且你可能发现客户端和服务端都会各自生成一个随机数,并且还会把随机数传递给对方。 那这个随机数有啥用呢?其实这两个随机数是后续作为生成「会话密钥」的条件,所谓的会话密钥就是数据传输时,所使用的对称加密密钥。 然后,服务端为了证明自己的身份,会发送「Server Certificate」给客户端,这个消息里含有数字证书。 随后,服务端发了「Server Hello Done」消息,目的是告诉客户端,我已经把该给你的东西都给你了,本次打招呼完毕。 TLS 第三次握手客户通过 CA(
成功后提取证书中的公钥,生成一个前主密钥 PMS 并使用公钥加密并通过「Change Cipher Key Exchange」消息传给发送给服务器。 客户端和服务端双方都共享了三个随机数,分别是 Client Random、Server Random、pre-master。于是,双方根据已经得到的三个随机数,生成会话密钥/主密钥(Master Secret),MS被切分为两个加密密钥和两个MAC密钥。当选择的对称密钥使用CBC时,两个初始向量也从MS获得,分别用于客户端和发送端。对每个记录附加一个MAC用于完整性检查,然后加密“记录+MAC”。MAC是数据+MAC密钥+当前序号的散列。序号是保证记录数据流不被第三方扰乱,接收方通过发送方的序号,通过在MAC的计算中包括适当的序号,验证记录的数据完整性。 生成完会话密钥后,然后客户端发一个「Change Cipher Spec」,告诉服务端开始使用加密方式发送消息。 然后,客户端再发一个「Encrypted Handshake Message(Finishd)」消息,把之前所有发送的数据做个摘要,再加密一下,让服务器做个验证,验证加密通信是否可用和之前握手信息是否有被中途篡改过。 可以发现,「Change Cipher Spec」之前传输的 TLS 握手数据都是明文,之后都是对称密钥加密的密文。 TLS 第四次握手服务器也是同样的操作,发「Change Cipher Spec」和「Encrypted Handshake Message」消息,如果双方都验证加密和解密没问题,那么握手正式完成。 最后,就用「会话密钥」加解密 HTTP 请求和响应了。 RSA 算法的缺陷使用 RSA 密钥协商算法的最大问题是不支持前向保密。因为客户端传递的PMS(用于生成对称加密密钥的条件之一)给服务端时使用的是公钥加密的,服务端收到到后,会用私钥解密得到随机数。所以一旦服务端的私钥泄漏了,过去被第三方截获的所有 TLS 通讯密文都会被破解。 为了解决这一问题,于是就有了 DH 密钥协商算法,这里简单介绍它的工作流程。 客户端和服务端各自会生成随机数,并以此作为私钥,然后根据公开的 DH 计算公示算出各自的公钥,通过 TLS 握手双方交换各自的公钥,这样双方都有自己的私钥和对方的公钥,然后双方根据各自持有的材料算出一个随机数,这个随机数的值双方都是一样的,这就可以作为后续对称加密时使用的密钥。 DH 密钥交换过程中,即使第三方截获了 TLS 握手阶段传递的公钥,在不知道的私钥的情况下,也是无法计算出密钥的,而且每一次对称加密密钥都是实时生成的,实现前向保密。 但因为 DH 算法的计算效率问题,后面出现了 ECDHE 密钥协商算法,我们现在大多数网站使用的正是 ECDHE 密钥协商算法。 ECDHE握手具体分析
我用 Wireshark 工具抓了用 ECDHE 密钥协商算法的 TSL 握手过程,可以看到是四次握手: 使用了 ECDHE,在 TLS 第四次握手前,客户端就已经发送了加密的 HTTP 数据,而对于 RSA 握手过程,必须要完成 TLS 四次握手,才能传输应用数据。 所以,ECDHE 相比 RSA 握手过程省去了一个消息往返的时间,这个有点「抢跑」的意思,它被称为是「TLS False Start」,跟「TCP Fast Open」有点像,都是在还没连接完全建立前,就发送了应用数据,这样便提高了传输的效率。 接下来,分析每一个 ECDHE 握手过程。 TLS 第一次握手客户端首先会发一个「Client Hello」消息,消息里面有客户端使用的 TLS 版本号、支持的密码套件列表,以及生成的随机数(Client Random)。 TLS 第二次握手服务端收到客户端的「打招呼」,同样也要回礼,会返回「Server Hello」消息,消息面有服务器确认的 TLS 版本号,也给出了一个随机数(Server Random),然后从客户端的密码套件列表选择了一个合适的密码套件。 不过,这次选择的密码套件就和 RSA 不一样了,我们来分析一下这次的密码套件的意思。 「 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384」
接着,服务端为了证明自己的身份,发送「Certificate」消息,会把证书也发给客户端。 这一步就和 RSA 握手过程有很大到区别了,因为服务端选择了 ECDHE 密钥协商算法,所以会在发送完证书后,发送「Server Key Exchange」消息。 这个过程服务器做了三件事:
随后,就是「Server Hello Done」消息,服务端跟客户端表明:“这些就是我提供的信息,打招呼完毕”。 至此,TLS 两次握手就已经完成了,目前客户端和服务端通过明文共享了这几个信息:Client Random、Server Random 、使用的椭圆曲线、椭圆曲线基点 G、服务端椭圆曲线的公钥,这几个信息很重要,是后续生成会话密钥的材料。 TLS 第三次握手客户端收到了服务端的证书后,自然要校验证书是否合法,如果证书合法,那么服务端到身份就是没问题的。校验证书到过程,会走证书链逐级验证,确认证书的真实性,再用证书的公钥验证签名,这样就能确认服务端的身份了,确认无误后,就可以继续往下走。 客户端会生成一个随机数作为客户端椭圆曲线的私钥,然后再根据服务端前面给的信息,生成客户端的椭圆曲线公钥,然后用「Client Key Exchange」消息发给服务端。 至此,双方都有对方的椭圆曲线公钥、自己的椭圆曲线私钥、椭圆曲线基点 G。于是,双方都就计算出点(x,y),其中 x 坐标值双方都是一样的,前面说 ECDHE 算法时候,x是一个共享密钥,但实际应用中,x 还不是最终的会话密钥。 还记得 TLS 握手阶段,客户端和服务端都会生成了一个随机数传递给对方吗? 最终的会话密钥,就是用「客户端随机数 + 服务端随机数 + x(ECDHE 算法算出的共享密钥) 」三个材料生成的。 之所以这么麻烦,是因为 TLS 设计者不信任客户端或服务器「伪随机数」的可靠性,为了保证真正的完全随机,把三个不可靠的随机数混合起来,那么「随机」的程度就非常高了,足够让黑客计算出最终的会话密钥,安全性更高。 算好会话密钥后,客户端会发一个「Change Cipher Spec」消息,告诉服务端后续改用对称算法加密通信。 接着,客户端会发「Encrypted Handshake Message」消息,把之前发送的数据做一个摘要,再用对称密钥加密一下,让服务端做个验证,验证下本次生成的对称密钥是否可以正常使用。 TLS 第四次握手最后,服务端也会有一个同样的操作,发「Change Cipher Spec」和「Encrypted Handshake Message」消息,如果双方都验证加密和解密没问题,那么握手正式完成。于是,就可以正常收发加密的 HTTP 请求和响应了。 RSA和ECGHE握手的区别
TLS 和 TCP 能同时握手前面说了,HTTPS 建立连接的过程,先进行 TCP 三次握手,再进行 TLS 四次握手。 但「HTTPS 中的 TLS 握手过程可以同时进行三次握手」,这个场景是可能存在的,但是一定要有前提条件。
也就是说如果客户端和服务端都开启了 TCP Fast Open 功能,且 TLS 版本是 1.3,且客户端和服务端已经完成过一次通信,那么HTTPS 中的 TLS 握手过程可以同时进行三次握手。 TCP Fast Open常规的情况下,如果要使用 TCP 传输协议进行通信,则客户端和服务端通信之前,先要经过 TCP 三次握手后,建立完可靠的 TCP 连接后,客户端才能将数据发送给服务端。 其中,TCP 的第一次和第二次握手是不能够携带数据的,而 TCP 的第三次握手是可以携带数据的,因为这时候客户端的 TCP 连接状态已经是 ESTABLISHED,表明客户端这一方已经完成了 TCP 连接建立。 就算客户端携带数据的第三次握手在网络中丢失了,客户端在一定时间内没有收到服务端对该数据的应答报文,就会触发超时重传机制,然后客户端重传该携带数据的第三次握手的报文,直到重传次数达到系统的阈值,客户端就会销毁该 TCP 连接。 说完常规的 TCP 连接后,我们再来看看 TCP Fast Open。 TCP Fast Open 是为了绕过 TCP 三次握手发送数据,在 Linux 3.7 内核版本之后,提供了 TCP Fast Open 功能,这个功能可以减少 TCP 连接建立的时延。 要使用 TCP Fast Open 功能,客户端和服务端都要同时支持才会生效,而且开启了 TCP Fast Open 功能,想要绕过 TCP 三次握手发送数据,得建立第二次以后的通信过程。 在客户端首次建立连接时的过程,如下图: 具体介绍:
所以,第一次客户端和服务端通信的时候,还是需要正常的三次握手流程。随后,客户端就有了 Cookie 这个东西,它可以用来向服务器 TCP 证明先前与客户端 IP 地址的三向握手已成功完成。 对于客户端与服务端的后续通信,客户端可以在第一次握手的时候携带应用数据,从而达到绕过三次握手发送数据的效果,整个过程如下图: 我详细介绍下这个过程:
所以,如果客户端和服务端同时支持 TCP Fast Open 功能,那么在完成首次通信过程后,后续客户端与服务端 的通信则可以绕过三次握手发送数据,这就减少了握手带来的 1 个 RTT 的时间消耗。 TLSv1.3在最开始的时候,我也提到 TLSv1.3 握手过程只需 1-RTT 的时间,它到整个握手过程,如下图: TCP 连接的第三次握手是可以携带数据的,如果客户端在第三次握手发送了 TLSv1.3 第一次握手数据,是不是就表示「HTTPS 中的 TLS 握手过程可以同时进行三次握手」?。 不是的,因为服务端只有在收到客户端的 TCP 的第三次握手后,才能和客户端进行后续 TLSv1.3 握手。 TLSv1.3 还有个更厉害到地方在于会话恢复机制,在重连 TLvS1.3 只需要 0-RTT,用“pre_shared_key”和“early_data”扩展,在 TCP 连接后立即就建立安全连接发送加密消息,过程如下图: TCP Fast Open + TLSv1.3在前面我们知道,客户端和服务端同时支持 TCP Fast Open 功能的情况下,在第二次以后到通信过程中,客户端可以绕过三次握手直接发送数据,而且服务端也不需要等收到第三次握手后才发送数据。 如果 HTTPS 的 TLS 版本是 1.3,那么 TLS 过程只需要 1-RTT。 因此如果「TCP Fast Open + TLSv1.3」情况下,在第二次以后的通信过程中,TLS 和 TCP 的握手过程是可以同时进行的。 如果基于 TCP Fast Open 场景下的 TLSv1.3 0-RTT 会话恢复过程,不仅 TLS 和 TCP 的握手过程是可以同时进行的,而且 HTTP 请求也可以在这期间内一同完成。 TLS和SSL的区别最新版本的 TLS 是 IETF(Internet Engineering Task Force,Internet 工程任务组)制定的一种新的协议,它建立在 TLS 的主要目标是使 SSL 更安全,并使协议的规范更精确和完善。TLS 在 SSL v3.0 的基础上,提供了一下增强内容:
主要区别如下:
HTTPS 一定安全可靠吗?这个问题的场景是这样的:客户端通过浏览器向服务端发起 HTTPS 请求时,被「假基站」转发到了一个「中间人服务器」,于是客户端是和「中间人服务器」完成了 TLS 握手,然后这个「中间人服务器」再与真正的服务端完成 TLS 握手。那么这个「中间人服务器」就获取了全部数据。 具体过程如下:
从客户端的角度看,其实并不知道网络中存在中间人服务器这个角色。 那么中间人就可以解开浏览器发起的 HTTPS 请求里的数据,也可以解开服务端响应给浏览器的 HTTPS 响应数据。相当于,中间人能够 “偷看” 浏览器与服务端之间的 HTTPS 请求和响应的数据。 但是要发生这种场景是有前提的,前提是用户点击接受了中间人服务器的证书。 中间人服务器与客户端在 TLS 握手过程中,实际上发送了自己伪造的证书给浏览器,而这个伪造的证书是能被浏览器(客户端)识别出是非法的,于是就会提醒用户该证书存在问题。 如果用户执意点击「继续浏览此网站」,相当于用户接受了中间人伪造的证书,那么后续整个 HTTPS 通信都能被中间人监听了。 所以,这其实并不能说 HTTPS 不够安全,毕竟浏览器都已经提示证书有问题了,如果用户坚决要访问,那不能怪 HTTPS ,得怪自己手贱。 抓包工具
抓包工具 Fiddler 之所以可以明文看到 HTTPS 数据,工作原理与中间人一致的。 对于 HTTPS 连接来说,中间人要满足以下两点,才能实现真正的明文代理:
中间人要拿到私钥只能通过如下方式:
不用解释,抓包工具只能使用第三种方式取得中间人的身份。 使用抓包工具进行 HTTPS 抓包的时候,需要在客户端安装 Fiddler 的根证书,这里实际上起认证中心(CA)的作用。 Fiddler 能够抓包的关键是客户端会往系统受信任的根证书列表中导入 Fiddler 生成的证书,而这个证书会被浏览器信任,也就是 Fiddler 给自己创建了一个认证中心 CA。 客户端拿着中间人签发的证书去中间人自己的 CA 去认证,当然认为这个证书是有效的。
我们要保证自己电脑的安全,不要被病毒乘虚而入,而且也不要点击任何证书非法的网站,这样 HTTPS 数据就不会被中间人截取到了。 当然,我们还可以通过 HTTPS 双向认证来避免这种问题。一般我们的 HTTPS 是单向认证,客户端只会验证了服务端的身份,但是服务端并不会验证客户端的身份。如果用了双向认证方式,不仅客户端会验证服务端的身份,而且服务端也会验证客户端的身份。服务端一旦验证到请求自己的客户端为不可信任的,服务端就拒绝继续通信,客户端如果发现服务端为不可信任的,那么也中止通信。
|
|
网络协议 最新文章 |
使用Easyswoole 搭建简单的Websoket服务 |
常见的数据通信方式有哪些? |
Openssl 1024bit RSA算法---公私钥获取和处 |
HTTPS协议的密钥交换流程 |
《小白WEB安全入门》03. 漏洞篇 |
HttpRunner4.x 安装与使用 |
2021-07-04 |
手写RPC学习笔记 |
K8S高可用版本部署 |
mySQL计算IP地址范围 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/25 21:32:12- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |