TLS握手流程分析

news2024/11/16 3:20:59

1.SSL/TLS 历史

HTTP 是明文传输的协议,可能受到第三方的攻击,非常不安全。因此才诞生 “HTTPS”。
这个 “S” 表示 SSL/TLS 协议,用公式说明:HTTPS = HTTP + SSL(TLS)
其中 SSL 即安全套接层(Secure Sockets Layer),处于 OSI 七层模型中的会话层。

  • 1996 年,SSL3.0 问世,得到大规模应用(已于 2015 年弃用)。
  • 1999 年,SSL3.1 被标准化,更新为 TLS1.0(传输层安全,Transport Layer Security)。所以有,TLS1.0 = SSL3.1。
  • 现在主流的版本是 TLS1.2(2008 年发布)。
  • 未来,可能是 TLS1.3 的时代(2018 年发布)。

2.TLS握手

2.1 握手概念

  • 首先,什么是握手呢?

    握手 (Handshake):指通信的前置动作,即达成某种约定,比如 TCP 握手是要确定双端的接收、发送能力等;而 TLS 握手则是为了验证身份、交换信息从而生成秘钥,为后续加密通信做准备。具体来说,SSL/TLS 握手就是通过非对称加密,生成对称加密的 session key 的过程。

  • 通过 Wireshark 抓包发现:

    不论客户端和服务端的连接走 HTTP 还是 TLS 协议,所有连接最初都要经过 TCP 三次握手,而 TLS 四次握手是在 TCP 建立连接之后进行的。

    因此,HTTPS 首次通信需要 7 次握手

本节将会解析 TLS 主要的两种握手方式,分别为:RSA 握手、DH 握手。再以 DH 握手为基础继续演进优化,推出更安全、性能更佳的 TLS1.3 版本握手方式。不过在此之前,还需要了解一些基础概念 。

2.2基础概念

1.对称加密

加、解密使用的同一串密钥,常见的对称加密算法:DES,AES 等。

2.非对称加密

加、解密使用不同的密钥,一把作为公开的公钥,另一把作为保密的私钥。公钥加密的信息,只有私钥才能解密。反之,私钥加密的信息,只有公钥才能解密。
常见的非对称加密算法:RSA,ECC 等。

RSA 算法:该算法的命名以三位科学家的姓氏缩写组合得来,在计算机网络世界,一直是最广为使用的 “非对称加密算法”。

ECC 是非对称加密里的 “后起之秀”,它基于 “椭圆曲线离散对数” 的数学难题,使用特定的曲线方程和基点生成公钥和私钥,子算法 ECDHE 用于密钥交换,ECDSA 用于数字签名。

3.混合加密

对称加密 算法中只要持有密钥就可以解密。如果你和网站约定的密钥在传递途中被黑客窃取,那他就可以在之后随意解密收发的数据,通信过程也就没有机密性可言了。

非对称加密算法中,需要应用到复杂的数学运算,虽然保证了安全,但速度很慢,比对称加密算法差了好几个数量级。

所以,TLS 里使用了 “混合加密” 的方式博采众长:「在通信刚开始的时候使用非对称加密算法,解决密钥交换的问题。后续全都使用对称加密进行通信」。

HTTPS常用的密钥交换算法有两种,分别是RSAECDHE算法。

其中,RSA是比较传统的密钥交换算法,它不具备前向安全(指的是长期使用的主密钥泄漏不会导致过去的会话密钥泄漏)的性质,因此现在很少服务器使用它。而ECDHE算法具有前向安全,所以被广泛使用。

4.加密套件列表

加密套件列表一般由客户端发送,供服务器选择。用 openssl 查看列表:

➜ openssl ciphers -v
ECDHE-RSA-AES256-GCM-SHA384  	TLSv1.2 Kx=ECDH     Au=RSA    Enc=AESGCM(256) Mac=AEAD
ECDHE-ECDSA-AES256-GCM-SHA384 	TLSv1.2 Kx=ECDH     Au=ECDSA  Enc=AESGCM(256) Mac=AEAD
AES256-SHA256                     TLSv1.2 Kx=RSA      Au=RSA    Enc=AES(256)   Mac=SHA256
...
... Cipher Suites(17 suites)

在这里插入图片描述

上述加密套件的意思是:

  • TLS 握手过程中,使用 ECDHE 算法生成 premaster key(预主密钥)。
  • 身份验证 (签名) 使用 RSA 算法。
  • 128 位的 AES 算法进行对称加密,在对称加密的过程中使用主流的 GCM 分组模式,可以让算法用固定长度的密钥加密任意长度的明文。
  • 最后是用于数据完整性校验的哈希摘要算法,采用 SHA256 算法。

5.数字证书

证书本身是由权威、受信任的证书颁发机构 (CA) 授予的。

数字证书一般有两个作用:

  • 服务器向客户端证明自己的身份,毕竟秘钥、甚至服务器域名都是可以伪造的。
  • 公钥传给客户端。

2.3 TLS1.2握手

1.通俗解释

整个握手过程通俗地说分为如下五步(真实的过程涉及的细节比这个多):

  • 客户端:给出协议版本号、一个客户端生成的随机数(Client random),以及客户端支持的加密方法

  • 服务端:确认双方使用的加密方法,并给出数字证书、以及一个服务器生成的随机数(Server random)。

  • 客户端:确认数字证书有效,然后生成一个新的随机数(Premaster secret),并使用数字证书中的公钥,加密这个随机数,发给服务端。

  • 服务端:使用自己的私钥,解密后获取客户端发的随机数(即Premaster secret)。

  • 客户端和服务端根据约定的加密方法,使用前面的三个随机数,生成对话密钥(session key),用来加密接下来的整个对话过程。

2.具体介绍

在这里插入图片描述


客户端发送

  • ClientHello:用于初始化会话消息,该消息主要包含如下信息:
    • Version Number: 客户端发送它所支持的最高 SSL/TLS 版本;
    • Randomly Generated Data:一个 32 字节的客户端随机数,该随机数被服务端生成通信用的对称密钥(master secret);
    • Session Identification :session ID 被客户端用于恢复之前的会话
    • Cipher Suite: 客户端发送它所支持的加密套件列表

服务端发送

  • ServerHello,该消息主要包含如下信息:

    • Version Number:服务端发送双方所支持的最高的 SSL/TLS 版本;
    • Randomly Generated Data:一个 32 字节的服务端随机数,被客户端用于生成通信用的对称密钥(master secret);
    • Session Identification:用于会话恢复;
    • Cipher Suite: 服务端发送双发支持的最安全的加密套件。
  • ServerCertificate:服务端下发SSL证书,客户端用该证书验证服务端的身份;

  • ServerKeyExchange【可选】:用来传递双方协商密钥的参数

  • ClientCertificateRequest【可选】:只有当服务端也需要验证客户端身份会用到;

  • ServerHelloDone:告知客户端握手相关的消息发送完毕,等待客户端响应。


客户端发送

  • ClientCertificate:如果服务端发送了ClientCertificateRequest消息,那么客户端会发送该消息给服务端,包含自己的证书信息,供服务端进行客户端身份认证;
  • ClientKeyExchange:根据协商的密钥算法不同,该消息的内容会不同,该消息主要包含密钥协商的参数
  • CertificateVerify:该消息只有在ClientCertificate消息发送时才发送。客户端用自己的私钥签名从开始到现在的所有发送过的消息,然后服务端会用客户端的公钥验证这个签名
  • ChangeCipherSpec:通知对方此消息以后会以之前协商的密钥加密发送数据;【开始使用对称密钥加密消息】
  • Finished:客户端计算生成对称密钥,然后使用该对称密钥加密之前所有收发握手消息的 Hash 值,发送给服务器,服务器将用相同的会话密钥(使用相同方法生成)解密此消息,校验其中的Hash 值。该消息是 SSL 握手协议记录层加密的第一条消息。【之前的握手消息都以明文呈现】

服务端发送

  • ChangeCipherSpec:通知对方此消息以后会以之前协商的密钥加密发送数据;

  • Finished:服务器使用对称密钥加密(生成方式与客户端相同)之前所发送的所有握手消息的hash值,发送给客户端去校验。

    至此 SSL 握手过程结束,双发之后的通信数据都会用双方协商的对称密钥 Session Key 加密传输。

3.Wireshark 抓包分析 SSL/TLS 握手过程

本节使用 wireshark 抓包工具分析一个完整的 HTTPS 通信过程,看看通信过程中双方消息是如何传送的。前面我们说过,根据服务端实现的不同,可能一个 TCP 包中包含多条 SSL/TLS 消息,而不是每条消息单独发送(每条单独发送效率太低)。

下面为 Wireshark 抓取的 https 流量包,展示了整个通信过程:建立 TCP 连接 --> SSL/TLS 握手(ECDHE交换密钥) --> 应用数据加密传输:

在这里插入图片描述
上面是一个实际的 SSL/TLS 握手过程,分为如下 5 步:

  1. 客户端发送 Client Hello 消息给服务端;
  2. 服务端回应 Server Hello 消息;
  3. 服务端同时回应 Server Certificate、Server Key Exchange 和 Server Hello Done 消息;
  4. 客户端发送 Client Key Exchange、Change Cipher Spec 和 Client Finished 消息;
  5. 服务端最后发送 Change Cipher Spec 和 Server Finished 消息。

下面我们分步分析每个阶段的包的内容:

  1. 客户端发送 Client Hello 消息给服务端

    在这里插入图片描述

    可以看出 TLS 协议分为两层:「TLS 记录层、TLS 握手层」,其中 TLS 握手层基于 TLS 记录层。

    另外客户端发送的 Client Hello 消息当中包含的信息也可以看到:

    • Version:客户端支持的 TLS 版本号;
    • Random:客户端生成的 32 字节随机数;
    • Session ID:会话 ID;
    • Cipher Suites:客户端支持的加密套件列表;
    • Compression Methods:客户端支持的压缩算法;
  2. 服务端回应 Server Hello 消息:

    在这里插入图片描述

    Server Hello 包含如下信息:

    • Version:双方支持的 TLS 版本号;
    • Random:服务端生成的 32 字节随机数;
    • Session ID:会话 ID;
    • Cipher Suites:双方协商的加密套件;
    • Compression Methods:压缩算法。
  3. 服务端同时回应 Server Certificate、Server Key Exchange 和 Server Hello Done 消息:

    在这里插入图片描述

    可以看出每个 TLS 记录层是一个消息,服务端同时回复了有 3 个消息:Server Certificate、Server Key Exchange、Server Hello Done。

    服务器通过Server Key Exchange 消息发送椭圆曲线参数,包括选定的曲线类型和曲线参数。可以看出双方密钥协商使用的是 Diffie-Hellman (迪菲) 算法,该消息用于传递 Diffie-Hellman (迪菲) 算法的参数。

  4. 客户端发送 Client Key Exchange、Change Cipher Spec 和 Client Finished 消息:

    在这里插入图片描述

    可以看出客户端同时回复了 3 个消息:Client Key Exchange、Change Cipher Spec 和 Client Finished 消息。Client Key Exchange 的内容为 Diffie-Hellman (迪菲) 算法的参数,用于生成 premaster key,然后和双方之前的随机数结合生成对称密钥。

  5. 服务端最后发送 Change Cipher Spec 和 Server Finished 消息:

    在这里插入图片描述

    服务端最后发送 Change Cipher Spec 和 Server Finished 消息,至此 SSL/TLS 握手完毕,接下来双方会用对称加密的方式加密传输数据。

2.4 RSA握手

在这里插入图片描述

  1. 客户端向服务端发送随机数 client_random,TLS 版本和供筛选的加密套件列表。
  2. 服务端接收到,立即返回 server_random,确认好双方都支持的加密套件
    ,同时下发服务端证书 (证书中附带公钥 Public key certificate)。
  3. 客户端证书验证通过后,生成另一个随机数premaster secret(预主密钥),通过服务器证书公钥加密,传给服务器。
  4. 服务器用私钥解密,获取到premaster secret。
  5. 现在客户端和服务器都有 client_random、server_random、premaster secret了,用确定好的加密方法混合这三个参数,生成主密钥。最后,主密钥结合其他参数(随机数/标识符),通过密钥派生函数(如PRF)生成会话密钥,客户端和服务端使用相同的会话密钥进行通信,即使用对称加密
  • 总结:
    客户端生成两个随机数(client_random和premaster secret),服务器生成一个随机数(server_random)。
  • 注意:
    • 握手中的任何消息均未使用密钥加密,它们都是明文发送的。
    • TLS 握手其实是一个双向认证的过程,客户端和服务器都需要进行摘要认证以及收尾 (发送 Finished 消息,意为后面 HTTP 开始正式加密报文通信了)。

2.5 ECDHE握手

在这里插入图片描述

  • 客户端向服务器发送随机数client random1,TLS版本,支持的加密套件列表;
  • 服务器响应随机数server random1,确认好双方都支持的加密套件,同时下发服务器证书。同时会生成随机数作为私钥 server random2,保留在本地。服务器会在ServerHello消息中选择一个椭圆曲线,并将该参数(曲线类型和曲线参数)发送给客户端。服务器根据DH算法参数和临时生成的私钥,算出公钥,下发给客户端。为了保证不被篡改,同时会生成签名下发给客户端;
  • 客户端验证通过后,生成自己私钥client random2,然后根据服务器公开的DH算法参数,算出公钥,发送给服务端
  • 服务器得到客户端的临时公钥
  • 至此,对方的公钥,自己的私钥,DH算法的公共参数都已经得到,即可算出主密钥。最终的会话密钥,通过client_randomserver_random主密钥一起确定。

下面这张图展示了ServerKeyExchange消息的参数,其中Pubkey是服务器计算出来的公钥(私钥不可见),再加上自己的私钥签名认证(签名算法为RSA):

在这里插入图片描述

下面这张图展示了ClientKeyExchange消息的参数,同样地,Pubkey是客户端计算出来的公钥(私钥不可见)。

在这里插入图片描述

  • 计算预主密钥和主密钥的详细过程

    客户端首先根据服务器的公钥自身的私钥计算出预主密钥Pre-master key,然后再使用 Client Random、Server Random、Pre-master key这三个随机数计算出主密钥 Master Secret,保存在本地。并使用 Change Cipher Spec 消息将通知服务器开始使用对称加密的方式进行通信。

    服务器也是使用同样的方式计算出预主密钥和主密钥。

  • 总结:

    • 客户端生成两个随机数(client_random1和client_random2),服务器生成两个随机数(server_random1和server_random2)。
    • 其中,client_random2 和 server_random2 通过DH算法参数分别生成自己的公钥client_pubkey和server_pubkey。
    • 预主密钥的生成:
      client_pubkey + server_random2 ==> Pre-master key
      server_pubkey + client_random2 ==> Pre-master key
    • 主密钥的生成:
      client_random1 + server_random1 + Pre-master key ==> Master key

2.6 RSA握手和ECDHE握手的差异

  1. 客户端向服务端发送随机数 client_random,TLS 版本和供筛选的加密套件列表。

  2. // RSA
    服务端接收到,立即返回 server_random,确认好双方都支持的加密套件,以及服务端证书 (证书中附带公钥)。
    // DH
    服务端接收到,立即返回 server_random,确认好双方都支持的加密套件,以及服务端证书 (证书中附带公钥)。
    同时服务端利用私钥将 client_random、server_random、server_params(DH算法参数) 签名,生成服务端签名。然后将签名和 server_params 也发送给客户端。

  3. // RSA
    客户端接收,先验证服务端证书。
    若通过,接着使用加密套件的密钥协商算法 ——RSA 算法,生成另一个随机数 premaster secret(预主密钥),并且用证书里的公钥加密,传给服务器
    // DH
    客户端接收,先验证服务端证书和签名。
    若通过,客户端生成自己私钥,然后根据服务器公开的DH算法参数,算出公钥,发送给服务端

  4. // RSA
    服务端用私钥解密这个被加密后的 premaster secret。
    // DH
    现在客户端和服务器都有 client_params、server_params 两个参数(双方的公钥),通过对方的公钥,自己的私钥,DH算法的公共参数都已经得到,即可算出预主密钥。最终的主密钥,通过client_random,server_random和预主密钥一起确定。

  5. 主密钥和其他参数共同计算出会话密钥,作为后续通信的对称密钥。

2.7 TLS1.3握手

1. TLS1.2和TLS1.3握手的区别

  1. 更快的访问速度

    使用 TLS 1.2 需要两次往返( 2-RTT )才能完成握手,然后才能发送请求。
    TLS 1.3 的握手不再支持静态的 RSA 密钥交换,这意味着必须使用带有前向安全的 Diffie-Hellman 进行握手

    使用 TLS 1.3 协议只需要一次往返( 1-RTT )就可以完成握手。如下图所示:
    在这里插入图片描述

  2. 更强的安全性

    TLS 1.3 在之前版本的基础上删除了那些不安全的加密算法,包括:

    • RSA 密钥传输 —— 不支持前向安全性
    • CBC 模式密码 —— 易受 BEAST 和 Lucky 13 攻击
    • RC4 流密码 —— 在 HTTPS 中使用并不安全
    • SHA-1 哈希函数 —— 建议以 SHA-2 取而代之
    • 任意 Diffie-Hellman 组—— CVE-2016-0701 漏洞
    • 输出密码 —— 易受 FREAK 和 LogJam 攻击

2. TLS1.3握手细节

在这里插入图片描述

参考资料

  1. TLS 详解握手流程
  2. Wireshark 抓包理解 HTTPS 协议
  3. HTTPS(三):使用ECDHE加密算法的TLS握手流程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1387007.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【开发篇】四、MAT堆内存分析(Memory Analyzer Tool)

文章目录 1、使用2、报错3、MAT支配树4、MAT内存泄漏的检测原理5、导出运行中系统的内存快照6、补充 1、使用 内存溢出后,分析泄露的思路是: 在OOM前,将整个堆内存保存成一个hprof文件MAT打开hprof文件,MAT自行分析可疑对象 添…

模拟器安装XPosed框架教程

Xposed框架下载(搞不懂就先看完本篇教程再下载) 99%的情况只需要下载里面的XPosed鸭就行了 安卓8及以下XPosed框架 - 多开鸭模拟器安装XPosed框架图文视频教程 关于本站XPosed框架的说明 XPosed框架(即XP框架),由rovo89开发。适用于安卓7以…

Kafka消费流程

Kafka消费流程 消息是如何被消费者消费掉的。其中最核心的有以下内容。 1、多线程安全问题 2、群组协调 3、分区再均衡 1.多线程安全问题 当多个线程访问某个类时,这个类始终都能表现出正确的行为,那么就称这个类是线程安全的。 对于线程安全&…

HNU-算法设计与分析-实验4

算法设计与分析实验4 计科210X 甘晴void 202108010XXX 目录 文章目录 算法设计与分析<br>实验41 回溯算法求解0-1背包问题问题重述想法代码验证算法分析 2 回溯算法实现题5-4运动员最佳配对问题问题重述想法代码验证算法分析 3 分支限界法求解0-1背包问题问题重述想法…

OpenGL Assimp加载各类型模型(.obj、.fbx、.glb、.3ds)

1.简介 本博客以.glb格式为例&#xff0c;加载glb格式的3d模型&#xff0c;网上找了一圈&#xff0c;基本上都是根据OpenGL官方示例&#xff0c;加载.obj格式的3d模型。 下面以.obj和.glb格式的3D模型简单介绍一下。 常见的.obj格式的3D模型如下所示&#xff1a;纹理都已经被…

FineBI实战项目一(23):订单商品分类词云图分析开发

点击新建组件&#xff0c;创建订单商品分类词云图组件。 选择词云&#xff0c;拖拽catName到颜色和文本&#xff0c;拖拽cat到大小。 将组件拖拽到仪表板。 结果如下&#xff1a;

MYSQL的学习——单行函数详解

目录 1. 数值函数 1) 基本函数 2) 角度与弧度互换函数 3) 三角函数 4) 指数与对数函数 5) 进制间的转换 2. 字符串函数 3. 日期和时间函数 1) 获取日期、时间 2) 日期与时间戳的转换 3) 获取月份、星期、星期数、天数等函数 4) 日期的操作函数 5) 时间和秒钟转换的…

商业世界,从2023到2024

作者&#xff5c;潮汐商业评论 编辑&#xff5c;Ray 变化总在发生&#xff0c;你不去迎接进步的变化&#xff0c;就会等到退步的变化。 —— 查理.芒格 2023, 我们似乎总在不断告别。从“一生自由”的大家黄永玉到“智慧”投资家查理.芒格&#xff0c;再到写出《不能承受的生命…

条款24:若所有参数皆需类型转换,请为此采用非成员函数

设计一个表示有理数的类时&#xff0c;允许从整数隐式转换为有理数是有用的&#xff1a; class Rational { public:Rational(int numerator 0, // 该构造函数没有explicit限制;int denominator 1); int numerator() const; int denominator() const; const Rational opera…

如何用GPT制作PPT和写代码?

详情点击链接&#xff1a;如何用GPT制作PPT和写模型代码&#xff1f; 一OpenAI 1.最新大模型GPT-4 Turbo 2.最新发布的高级数据分析&#xff0c;AI画图&#xff0c;图像识别&#xff0c;文档API 3.GPT Store 4.从0到1创建自己的GPT应用 5. 模型Gemini以及大模型Claude2二定…

04.SpringCloud网关-gateway

1.Gateway服务网关 Spring Cloud Gateway 是 Spring Cloud 的一个全新项目&#xff0c;该项目是基于 Spring 5.0&#xff0c;Spring Boot 2.0 和 Project Reactor 等响应式编程和事件流技术开发的网关&#xff0c;它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式…

qt.qpa.plugin: Could not find the Qt platform plugin “windows“ in ““

系统环境&#xff1a;Win10家庭中文版 Qt : 5.12.9 链接了一些64位的第三方库&#xff0c;程序编译完运行后出现 qt.qpa.plugin: Could not find the Qt platform plugin "windows" in "" 弹窗如下&#xff1a; 网上搜了一些都是关于pyQt的&#xff0c…

基于Python+Django,我搭建一个视频点播平台

学习过程中&#xff0c;遇到问题可以咨询作者 功能介绍 平台采用B/S结构&#xff0c;后端采用主流的Python语言进行开发&#xff0c;前端采用主流的Vue.js进行开发。 整个平台包括前台和后台两个部分。 前台功能包括&#xff1a;首页、视频列表页面、视频详情页、用户中心模…

国际版WPS Office 18.6.1

【应用名称】&#xff1a;WPS Office 【适用平台】&#xff1a;#Android 【软件标签】&#xff1a;#WPS 【应用版本】&#xff1a;18.6.1 【应用大小】&#xff1a;160MB 【软件说明】&#xff1a;软件日常更新。WPS Office是使用人数最多的移动办公软件。独有手机阅读模式…

MR-GCN

∘ Φ \circ_Φ ∘Φ​ denotes a convolution Let b l o c k d i a g blockdiag blockdiag(A) be a n1n3-by-n2n3 block diagonal matrix&#xff0c; f o l d fold fold indicate its inverse operator diagonal degree tensor D \mathcal{D} D 作者未提供代码

科教文汇期刊怎么投稿?

《科教文汇》系国家新闻出版署认定的第一批学术期刊&#xff0c;主要刊登教育领域有创新性、学术性和实用性&#xff0c;有较高学术价值的论文。本刊由顾问、名誉社长、主任编委及编委组成学术审读团体&#xff0c;注重教研教改成果的宣传、案例的分析、经验的介绍及学术的交流…

Maven《二》-- Maven的安装与配置(亲测成功版)

目录 &#x1f436;2.1 Maven的安装条件 &#x1f436;2.2 Maven安装步骤 1. 检查本地%JAVA_HOME% 2. 解压maven 3. 配置maven的环境变量 4. 校验maven是否配置成功 5. 配置本地仓库 &#x1f436;2.3 Idea配置本地Maven软件 &#x1f436;2.1 Maven的安装条件 各个工具…

44-js return返回值,全局作用域,局部作用域,隐式作用域,变量的生命周期,delete释放内存

1.return返回值&#xff1a;函数执行后剩下结果就是返回值。 function fn(a,b,c){//return返回值return(abc);// console.log("aaa"); //return之后的值都不在执行了// alert("bbb"); //return之后的值不在执行了}console.log(fn(1,2,3)*10)…

利用Python的csv(CSV)库读取csv文件并取出某个单元格的内容的学习过程

csv库在python3中是自带的。 利用它可以方便的进行csv文件内容的读取。 注意&#xff1a;要以gbk的编码形式打开&#xff0c;因为WPS的csv文件默认是gbk编码&#xff0c;而不是utf-8。 01-读取表头并在打印每一行内容时一并输出表头 表头为第1行&#xff0c;现在要读取并打…

搜维尔科技:【简报】元宇宙数字人赛道,《寒朵鹿》赏析!

寒朵鹿的外观是柔和无攻击性的小鹿拟人样&#xff0c;头上长有一对鹿角及鹿耳&#xff0c;虽然绝大部分雌鹿并不会长角&#xff0c;但由于寒朵鹿是AI的智能机器人&#xff0c;所以为了依照普遍大众对鹿的印象依旧帮她加上了角。 学校&#xff1a; 台北商业大学 选手&#xff1…