网络传输加密及openssl使用样例(客户端服务器)

news/2024/9/18 6:53:16/ 标签: 网络, 服务器, ssl, 密码学, tcp, 安全, 区块链

文章目录

  • 背景
    • 常用加密方式
    • SSL
    • OpenSSL
      • 主要功能
    • 库结构
  • 交互流程
  • 证书生成
    • 生成 RSA 私钥
      • 私钥的主要组成部分
      • 私钥的格式
    • 创建自签名证书:
  • 签发证书
  • 服务器端代码
  • 客户端代码
  • 常见错误
    • 版本问题
    • 证书问题
    • 证书格式

背景

网络传输中为保证数据安全,通常需要加密

常用加密方式

  • 对称加密:
    特点:使用同一个密钥进行加密和解密,速度快,但密钥管理复杂,适合在密钥交换后的大量数据传输。
    常用算法:DES、3DES、AES等。
  • 非对称加密:
    特点:使用一对密钥(公钥和私钥),公钥用于加密,私钥用于解密,或者私钥用于签名,公钥用于验证。这种方式解决了密钥交换的问题,但加密解密速度较慢。
    常用算法:RSA、DSS、ECC等。
  • 单向加密(散列加密、签名):
    特点:只能加密,不能解密,常用于生成数据的摘要或验证数据的完整性。元数据改变签名大概率发生变化
    常用算法:MD5、SHA-1、SHA-256、SHA-512等。

SSL

SSL(Secure Sockets
Layer)是最常用的网络传输加密协议之一,但需要注意的是,随着技术的发展,SSL的继任者TLS(Transport Layer
Security)已经成为更广泛使用的标准。然而,由于历史原因,“SSL”这一术语在日常使用中仍然非常普遍。

SSL/TLS协议通过结合对称加密、非对称加密和数字签名等多种加密技术,为网络通信提供了强大的安全保障。RSA、DSA和ECC等算法在SSL/TLS中扮演着重要的角色,分别用于密钥交换、数字签名等关键过程。

SSL/TLS协议主要包括以下几个关键组成部分:

  • 加密技术:SSL/TLS利用对称加密和非对称加密技术来保护数据的机密性和完整性。
  • 对称加密(如AES)用于加密和解密实际传输的数据,因为它速度快且效率高。
  • 非对称加密(如RSA、DSS、ECC)则用于密钥交换和数字签名,以确保通信双方的身份验证和数据的不可抵赖性。
  • 密钥交换:在SSL/TLS握手过程中,客户端和服务器会协商一个共同的加密密钥(会话密钥),用于后续的数据加密和解密。这个过程中,非对称加密算法(如RSA、ECC)被用来安全地交换密钥信息。
  • 数字签名:数字签名用于验证通信双方的身份和数据的完整性。在SSL/TLS中,服务器会向客户端发送一个包含其公钥和身份信息的证书,该证书由受信任的证书颁发机构(CA)签发,并使用非对称加密算法(如RSA、ECC)进行签名。客户端通过验证证书的签名来确认服务器的身份。

关于RSA、DSS(通常指DSA,即Digital Signature Algorithm)和ECC(Elliptic Curve Cryptography)与SSL/TLS的关系:

  • RSA:是一种非对称加密算法,广泛用于SSL/TLS中的密钥交换和数字签名。RSA算法的安全性基于大数分解的困难性,它使用一对密钥(公钥和私钥)来进行加密和解密操作。
  • DSA:是一种专门用于数字签名的算法,与RSA类似,也是基于非对称加密原理。DSA在SSL/TLS中主要用于验证证书的数字签名,而不是用于密钥交换。
  • ECC:是一种基于椭圆曲线密码学的加密算法,与RSA相比,ECC提供了更高的安全性和更短的密钥长度。ECC在SSL/TLS中的应用越来越广泛,因为它能够在保持相同安全性的同时减少计算量和存储需求。

OpenSSL

OpenSSL 是一个强大的开源加密软件库,它提供了多种加密算法、随机数生成器、安全套接字层(SSLv3/TLS)协议实现等功能。OpenSSL
不仅可以用于开发安全网络应用程序,还可以作为一个命令行工具来处理加密相关的任务。

主要功能

  • 加密算法: 包括对称加密算法(如 AES)、非对称加密算法(如 RSA 和 ECC)、哈希算法(如 SHA-256)等。
  • 随机数生成: 提供了安全的随机数生成器。
  • 安全套接字层 (SSL/TLS): 实现了 SSLv3 和 TLS 协议,用于加密网络通信。
  • X.509 证书管理: 支持 X.509 标准的数字证书的创建、验证和管理。
  • PKCS 标准: 支持 PKCS#7、PKCS#12 等标准。

库结构

  • Crypto Library: 提供基本的加密功能。
  • SSL Library: 基于 Crypto Library 提供 SSL/TLS 协议的支持。
    命令行工具:
    openssl: 提供了丰富的命令行选项,用于执行各种加密操作

版本查看

openssl version -a

交互流程

证书生成

生成 RSA 私钥

openssl genrsa -out server.key 2048

OpenSSL
生成的私钥的数据结构并不是简单的随机数集合,而是遵循特定加密算法和协议规定的复杂数据结构。私钥的格式和内容取决于所使用的加密算法(如RSA、DSA、ECDSA等)和密钥的格式(如PEM、DER等)。

私钥的主要组成部分

对于不同的加密算法,私钥的组成会有所不同,但通常包括以下几个基本部分:

  • 算法标识:指明使用的是哪种加密算法(如RSA、DSA、ECDSA等)。
  • 参数:包括加密算法所需的具体参数。例如,对于RSA算法,这些参数可能包括两个大的质数(p和q)、模数(n = p * q)、公钥和私钥指数(e和d)等。
  • 随机性:私钥的生成通常涉及一定程度的随机性,以确保私钥的不可预测性和安全性。然而,这并不意味着私钥完全由随机数组成,而是随机数用于生成私钥的某些关键部分(如私钥指数d)。

私钥的格式

私钥可以以不同的格式存储,最常见的两种是PEM(Privacy Enhanced Mail)和DER(Distinguished Encoding Rules):

  • PEM格式:以-----BEGIN PRIVATE KEY-----和-----END PRIVATE KEY-----为边界,内部是Base64编码的私钥数据。PEM格式是文本格式,易于阅读和存储,但可能会包含额外的换行符和空格。
  • DER格式:DER是二进制格式,直接存储私钥的二进制数据,没有额外的编码或标记。DER格式更加紧凑,适用于需要最小化大小的场景,如嵌入式系统。

创建自签名证书:

openssl req -new -key server.key -out server.csr
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
在创建证书的过程中,你可能需要输入一些信息,
如国家代码、组织名称等。你可以填写适当的值或直接按回车键使用默认值

在这里插入图片描述

使用openssl req -new -x509 -key server.key -out server.crt -days 365
-nodes命令生成的证书(server.crt)是一个自签名的X.509证书,其数据结构包含了多个关键的信息字段。以下是该证书数据结构中通常包含的内容:

  • 证书版本(Version):
    表明证书遵循的X.509版本,常见的版本有V1、V2和V3。
  • 序列号(Serial Number):
    由证书颁发机构(CA)分配的唯一数字,用于唯一标识该证书。
  • 签名算法(Signature Algorithm):
    指出用于签名证书的算法,如SHA-256 with RSA等。
  • 颁发者(Issuer):
    指出颁发该证书的实体,由于是自签名证书,颁发者和主体(Subject)通常相同。
  • 有效期(Validity Period):
    包括证书的生效日期(Not Before)和失效日期(Not After),本例中有效期为365天。
  • 主体(Subject):
    包含证书持有者的信息,如组织名(O)、组织单位名(OU)、通用名(CN,即- Common Name,通常指域名或服务器名)等。这些信息在openssl req命令执行时会通过交互式输入或配置文件提供。
  • 公钥信息(Public Key Info):
    包含证书的公钥和公钥使用的算法(如RSA)。公钥用于与私钥配对,进行加密/解密和签名/验证操作。
  • 证书签名(Signature):
    使用颁发者的私钥对证书内容的哈希值进行签名,确保证书内容的完整性和真实性。由于是自签名证书,这里的签名是使用server.key中的私钥生成的。
  • 其他可选字段:
    如主题备用名称(Subject Alternative Name,SANs),可以包含除了CN之外的多个域名或IP地址,增强证书的灵活性。
  • 扩展(Extensions):
    X.509 V3证书支持扩展字段,用于包含额外的信息,如证书吊销列表(CRL)的分发点、密钥用途(Key Usage)、扩展密钥用途(Extended Key Usage)等。

签发证书

上述例子由于是自签名证书,其颁发者信息、签名和公钥都是基于server.key中的私钥生成的,因此不需要外部证书颁发机构(CA)的参与。自签名证书通常用于测试和开发环境,而不推荐在生产环境中使用,因为自签名证书无法由客户端自动验证其真实性,可能会导致安全警告。

线上环境需要购买证书 比如使用XX云

服务器端代码

版本 OpenSSL 1.0.2

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>#define PORT 8443
#define BUFFER_SIZE 1024void handle_ssl_connection(SSL *ssl) {char buffer[BUFFER_SIZE];char resp[BUFFER_SIZE+10];int bytes_received;while ((bytes_received = SSL_read(ssl, buffer, BUFFER_SIZE)) > 0) {buffer[bytes_received] = '\0';printf("Received: %s\n", buffer);sprintf(resp,"srv recv %s",buffer);// 响应客户端SSL_write(ssl,resp, strlen(resp) + 1);}if (bytes_received <= 0) {fprintf(stderr, "Error on read: %d\n", bytes_received);}
}int main() {int listen_fd, conn_fd;struct sockaddr_in serv_addr;SSL_CTX *ctx;SSL *ssl;int opt = 1;// 创建 socketif ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {perror("Socket creation failed");exit(EXIT_FAILURE);}// 设置 SO_REUSEADDR 选项if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {perror("Setsockopt failed");exit(EXIT_FAILURE);}// 绑定地址memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);serv_addr.sin_port = htons(PORT);if (bind(listen_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {perror("Bind failed");exit(EXIT_FAILURE);}// 监听连接if (listen(listen_fd, 5) < 0) {perror("Listen failed");exit(EXIT_FAILURE);}// 初始化 SSL 上下文SSL_library_init();ctx = SSL_CTX_new(TLSv1_2_method());if (!ctx) {ERR_print_errors_fp(stderr);exit(EXIT_FAILURE);}// 加载证书和私钥if (SSL_CTX_use_certificate_file(ctx, "server.crt", SSL_FILETYPE_PEM) <= 0) {ERR_print_errors_fp(stderr);exit(EXIT_FAILURE);}if (SSL_CTX_use_PrivateKey_file(ctx, "server.key", SSL_FILETYPE_PEM) <= 0) {ERR_print_errors_fp(stderr);exit(EXIT_FAILURE);}// 验证私钥if (!SSL_CTX_check_private_key(ctx)) {fprintf(stderr, "Private key does not match the certificate public key\n");exit(EXIT_FAILURE);}// 接受客户端连接while (1) {struct sockaddr_in client_addr;socklen_t client_len = sizeof(client_addr);conn_fd = accept(listen_fd,(struct sockaddr *)&client_addr, &client_len);if (conn_fd < 0) {perror("Accept failed");continue;}printf("Accepted client  %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));ssl = SSL_new(ctx);SSL_set_fd(ssl, conn_fd);// 建立 SSL 连接if (SSL_accept(ssl) == -1) {ERR_print_errors_fp(stderr);SSL_free(ssl);close(conn_fd);continue;}// 处理 SSL 连接handle_ssl_connection(ssl);// 清理资源SSL_shutdown(ssl);SSL_free(ssl);close(conn_fd);}SSL_CTX_free(ctx);close(listen_fd);return 0;
}

客户端代码

#include <stdio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>#define RCVBUFSIZE 1024/*
gcc -g -o client client.c -lssl -lcrypto
*/int main() {int sock;struct sockaddr_in serv_addr;char user_input[1024];char buffer[RCVBUFSIZE];int  recv_len =0;SSL_CTX *ctx;SSL *ssl;int res = 0;int i = 0;SSL_library_init();ctx = SSL_CTX_new(TLSv1_2_method());if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {perror("Socket creation failed");exit(EXIT_FAILURE);}memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(8443);inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr);if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {printf("socket connect failed\n");}printf("connect client, socket:%d\n", sock);ssl = SSL_new(ctx);if (ssl == NULL) {printf("socket:%d, SSL_new failed\n", sock);goto sslerror;}SSL_set_fd(ssl, sock);if (SSL_connect(ssl) < 0) {printf("socket:%d, SSL_accept failed\n", sock);goto sslerror;}printf("ssl connected ok\n");for(i+0;i<500;i++){ // testprintf("Enter data to send: ");fgets(user_input,sizeof(user_input)-1, stdin);int result = SSL_write(ssl, user_input, strlen(user_input));if (result <= 0) {int error = SSL_get_error(ssl, result);switch (error) {case SSL_ERROR_ZERO_RETURN:printf("SSL_write error: Connection closed\n");break;case SSL_ERROR_WANT_READ:case SSL_ERROR_WANT_WRITE:printf("SSL_write error: Try again later\n");break;case SSL_ERROR_SYSCALL:perror("SSL_write error");break;default:printf("SSL_write error: %d\n", error);break;}}recv_len = SSL_read(ssl, buffer, sizeof(buffer));printf("Received[%d] %s\n",recv_len,buffer);}sslerror:SSL_shutdown(ssl);close(sock); if (ssl) SSL_free(ssl);return 0;
}

常见错误

版本问题

新旧版本接口API会有不同

证书问题

139851570730928:error:140A90A1:lib(20):func(169):reason(161):ssl_lib.c:1966
  • lib(20) 表示错误发生在 SSL 库中。
  • func(169) 表示错误发生在 SSL 库中的某个特定函数。
  • reason(161) 表示错误的具体原因,通常表示私钥与证书不匹配或私钥格式不正确。
  • ssl_lib.c:1966 表示错误发生的源代码位置
    解决方法
  • 确认私钥格式:
    确保私钥文件 server.key 的格式是 PEM 格式。
    使用 openssl rsa -in server.key -check 来检查私钥文件是否正确。
  • 确认证书和私钥匹配:
    确保证书和私钥是由同一组密钥对生成的。
    使用 openssl x509 -pubkey -in server.crt -noout -text 获取证书中的公钥。
    使用 openssl rsa -in server.key -pubout -outform PEM 获取私钥对应的公钥,并与证书中的公钥进行比较。
  • 重新生成证书和私钥:
    如果私钥文件格式不正确或与证书不匹配,可以尝试重新生成证书和私钥文件。
    使用 OpenSSL 命令行工具重新生成文件。
  • 检查文件路径:
    确认 server.crt 和 server.key 文件的路径是正确的。
    如果文件不在当前目录下,确保提供完整的路径。
  • 设置正确的文件权限:
    确保您的程序有足够的权限读取证书和私钥文件。
    可以尝试更改文件权限以允许程序访问:chmod 600 server.crt server.key

证书格式

SSL_FILETYPE_PEM 是 OpenSSL 中用于处理 PEM (Privacy Enhanced Mail) 格式的常量。PEM
是一种广泛使用的 ASCII 编码格式,用于存储证书、密钥和其他加密数据。除了 PEM 格式之外,OpenSSL 还支持其他几种格式,例如
DER (Distinguished Encoding Rules) 格式。

PEM 格式的特点是使用 Base64 编码,并以特定的文本标记开始和结束。例如,一个 PEM 格式的私钥文件通常看起来像这样:

-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAwUq...
...
-----END RSA PRIVATE KEY-----

PEM 格式的证书文件通常看起来像这样:

-----BEGIN CERTIFICATE-----
MIIEowIBAAKCAQEAwUq...
...
-----END CERTIFICATE-----

DER 格式

DER 格式是一种二进制编码格式,它不包含任何文本标记。这种格式比 PEM 更紧凑,但不是文本可读的。一个 DER
格式的文件通常是一个纯二进制文件,没有额外的文本标记。

在 OpenSSL 中使用不同格式

  • 使用 PEM 格式:
    使用 SSL_FILETYPE_PEM 常量来加载 PEM 格式的证书和密钥文件。
  • 使用 DER 格式:
    使用 SSL_FILETYPE_ASN1 常量来加载 DER 格式的证书和密钥文件。

http://www.ppmy.cn/news/1519219.html

相关文章

php mail函数配置SMTP服务器发邮件的指南!

php mail函数安全性考虑&#xff1f;PHP mail()函数漏洞利用技巧&#xff1f; 在使用PHP进行开发时&#xff0c;发送邮件是一个常见的需求。使用php mail函数配置SMTP服务器发邮件&#xff0c;则是实现这一需求的有效途径。AokSend将详细探讨如何通过php mail函数来配置SMTP服…

【Linux】初识加搭建Linux

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &#x1f4e2;本文由 JohnKi 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f4e2;未来很长&#…

Nepctf2024-Web-NepDouble

一、题目 题目:NepDouble 题目描述&#xff1a;二、WriteUp 1. 源码分析# / app.route(/, methods[GET, POST]) def main():if request.method ! "POST":return Please use POST method to upload files.try:clear_uploads_folder()files request.files.get(tp_fi…

读懂华为V模型:连接业务与IT,用V模型指导服务化设计

今天笔者带来华为的V模型&#xff0c;与读者一起解读下&#xff0c;V模型是如何连接业务与IT&#xff0c;实现IT产品服务化设计的。 华为用V模型连接业务与IT&#xff0c;指导服务化设计&#xff0c;是一种创新的方法论&#xff0c;旨在通过业务与IT的深度融合&#xff0c;提升…

小程序自定义目录结构

精简目录 /my-miniprogram ├── /miniprogram │ ├── /pages │ │ └── /index │ │ ├── index.js │ │ ├── index.json │ │ ├── index.wxml │ │ └── index.wxss │ ├── app.js │ ├── app.json │…

培训第三十六天(docker应用,mysql容器应用,容器核对时间,容器之间的依赖 域名,文件联合系统,制作基础镜像,commit制作镜像)

上午 容器就是在镜像上添加了一个可写层 # 从镜像库中下载mysql镜像&#xff0c;mysql镜像是在基础镜像上加很多层制成的[rootdocker ~]# docker pull mysqlUsing default tag: latestlatest: Pulling from library/mysql6e839ac3722d: Pull complete ad912193ad5f: Pull comp…

网工面试题(安全)

上一篇&#xff1a;网工面试题&#xff08;数通&#xff09; 防火墙 防火墙的应用场景 防火墙&#xff1a;部署在网络出口处/服务器区(数据中心&#xff09;/广域网接入&#xff0c;用于防止外界黑客攻击、保护内网安全硬件。 传统防火墙和下一代防火墙的区别 传统防火墙的功能…

关于vue3.x中最长递增子序列(LIS)

什么是最长递增子序列&#xff1f; 简单来说最长递增子序列就是在一个数组中呈现递增的数据的长度 lettcode原题 示例&#xff1a;我们有一个数组 arr const arr [10, 9, 2, 5, 3, 7, 101, 18]那么他的序列有那些呢&#xff1f; [2][2, 3][2, 5][2, 3, 7][2, 5, 7][2, 3, 1…

常用的加密算法分析

常用的加密算法主要分为对称加密算法&#xff08;DES、3DES、AES&#xff09;、非对称加密算法&#xff08;RSA、DSA、ECC&#xff09;以及散列算法&#xff08;MD5、SHA&#xff09;三大类。 一、对称加密算法 对称加密算法&#xff0c;又称为单密钥加密或共享密钥加密&…

Expected expression after operator

这个错误直译过来就是:运算符号后没有预期的表达式 这个错误通常出现在编程语言中&#xff0c;尤其是在编写C或C等类型语言的时候&#xff0c;它意味着在源代码中遇到了一个操作符&#xff08;比如 , -, *, /, , 等等&#xff09;&#xff0c;但在该操作符后面没有紧跟相应的表…

Spring MVC RESTful API - 修改状态接口示例

前言 在许多应用程序中&#xff0c;更新资源的状态是一项常见的需求。例如&#xff0c;在任务管理系统中&#xff0c;用户可能需要更新任务的状态&#xff0c;如从“待办”变为“完成”。为了实现这一功能&#xff0c;我们可以使用Spring MVC框架结合MyBatis Plus来创建一个简…

《第三十章 性能优化 - 存储优化》

一、引言 在 Android 应用开发中&#xff0c;性能优化是一个持续且重要的工作。存储优化作为性能优化的关键环节之一&#xff0c;对于提高应用的响应速度、节省资源以及提升用户体验具有重要意义。在本章中&#xff0c;我们将重点探讨数据库优化和缓存策略这两个主要知识点。 二…

go+gin+vue入门

后端框架 1、安装go、goland 2、创建空项目 3、下载要用的包&#xff1a;命令行输入go get -u github.com/xxxx 4、安装mysql数据库&#xff0c;使用navicat创建数据库。 5、按照项目框架搭建目录、文件、代码&#xff1a;如router、model… 6、运行测试&#xff0c;go run ma…

JVM面试(一)什么是虚拟机?什么是class文件?

什么是java虚拟机&#xff1f; 如果通俗点来讲&#xff0c;我们在电脑上一行行敲出来的代码&#xff0c;电脑本身是不认识的&#xff0c;最终是要转成电脑可以运行的101001这种字节。 但是这些我们又不可能手动来转换&#xff0c;所以呢&#xff0c;就需要一个工具&#xff0…

学习笔记——后端项目中的相关技术 【随时更新】

文章目录 1. Session 共享1.0 cookie和session的工作流1.1 Cookie范围1.2 为什么要共享&#xff1f;1.3 如何共享存储1.4 session共享实现 2. 缓存的实现2.1 缓存分类2. 2 Redis 缓存实现2.1.1 Redis 的数据结构&#xff08;高频考点&#xff09;2.1.2 redisTemplate 自定义序列…

【Linux】CodeServer:离线插件配置

下载 VS Code 插件 要为 code-server 添加离线插件&#xff0c;首先需要从 Visual Studio Code 插件市场下载所需的插件&#xff1a; 打开浏览器&#xff0c;访问 Visual Studio Code 插件市场。 在搜索栏中输入您需要的插件名称&#xff0c;找到插件后&#xff0c;点击插件页…

“线程池中线程异常后:销毁还是复用?”

目录 一、验证execute提交线程池中 测试 结论 二、验证submit提交线程池中 测试 结论 三、源码解析 查看submit方法的执行逻辑 查看execute方法的执行逻辑 为什么submit方法&#xff0c;没有创建新的线程&#xff0c;而是继续复用原线程&#xff1f; 四、总结 需要说…

经典大语言模型解读(2):生成式预训练的先锋GPT-1

论文地址&#xff1a;Improving Language Understanding by Generative Pre-Training 概述 现实世界中包含了大量的文本语料数据&#xff0c;然而&#xff0c;绝大多数语料都是无标签的。 为了充分利用这些无标签语料库&#xff0c;GPT1.0提出直接利用这些未标记的语料来进行…

VMware Workstation安装及配置CentOS7 Linux操作系统

VMware workstation安装 百度网盘&#xff0c;VMware-workstation-full-17.5.2.exe 安装包&#xff1a; 链接:https://pan.baidu.com/s/1xgbWUlo-hFUbb11MRxIVsw?pwd87bq 提取码: 87bq 检查网络适配器是否正常配置 在VMware Workstation中安装CentOS7 Linux操作系统 下载…

探秘Python字典:解锁数据管理的艺术

引言 字典&#xff08;Dictionary&#xff09;是一种可变容器模型&#xff0c;它可以存储任意类型对象。Python字典使用键-值对&#xff08;key-value pair&#xff09;存储数据&#xff0c;其中键必须是不可变的数据类型如数字、字符串等&#xff0c;而值可以是任何数据类型。…