当前位置: 首页 > news >正文

国密https访问

前言

现在的SSL的加密算法实际上主要是国际算法,包括JDK,Go等语言也仅支持国际算法加密(毕竟是国外开源项目),hash。随着国密算法的普及,比如openssl就支持国密了,还要新版本的Linux内核也开始支持,以openssl为例:

那么如果需要国密证书,或者访问国密https的时候就需要特定的sslsocket的握手算法

传输层密码协议(TLCP)

TLCP是中国基于TLS 1.1和1.2协议定制而成的协议,对应的中国国家标准为:

  • GB/T 38636-2020:传输层密码协议

该协议与TLS协议的最重大区别,就是要求通信端提供两个证书:认证证书和加密证书。其中认证证书与TLS协议使用的证书功能类似,用于对通信端的身份进行验证。而加密证书则为TLCP协议独有,它只会用于密钥交换。

开源套件

常用的国密开源套件有BouncyCastle、Kona,BouncyCastle时间比较久远了,用的比较多,比如OOM的坑,Kona是腾讯开源的国密套件,开始基于BouncyCastle,后面重写了,分别使用者两个套件试试沃通搭建的SM2SSL证书测试网站欢迎访问沃通基于国密算法的https加密解决方案演示网站icon-default.png?t=N7T8https://sm2test.ovssl.cn/

和 中国银行官网icon-default.png?t=N7T8https://ebssec.boc.cn/

其中沃通是支持国密和国际算法的无缝切换的,因为有双证书

 

BouncyCastle

https://www.bouncycastle.org/java.html,github使用MIT license

 

截止当前的日期

 

不支持国密源码分析

bouncycastle的ssl支持org.bouncycastle.jsse.provider.BouncyCastleJsseProvider

        <dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk18on</artifactId><version>1.76</version></dependency><dependency><groupId>org.bouncycastle</groupId><artifactId>bctls-jdk18on</artifactId><version>1.76</version></dependency>

定义了protocol

 

都是标准的,而这些支持的加密套件呢,实际上国密SSL(中国银行官网)使用的是TLSV1.1变化版本,这里实际执行的类是org.bouncycastle.jsse.provider.ProvSSLContextSpi的

createSupportedCipherSuiteMap

定义了使用的加密套件,然而国密的套件实际上有定义

org.bouncycastle.tls.CipherSuite

RFC 8998

RFC 8998规范将国密算法要素应用到了TLS 1.3协议中。KonaSSL实现了该规范定义的椭圆曲线curveSM2(41),签名机制sm2sig_sm3(0x0708)和密码套件TLS_SM4_GCB_SM3(0x00C6)。

所以,虽然支持RFC 8998标准,但是tls不支持国密通信,因为没有加入加密套件,也没有定义协议版本

笔者自己加了一个,不过发现修改的地方不少,国密ssl版本号定义0x0101,跟主流的ssl差别很大 

Kona

企鹅开源的,在1.0.5版本不再基于BouncyCastle

腾讯Kona国密套件使用的许可协议是GNU GPL v2.0 license with Classpath Exception,这也正是OpenJDK使用的许可协议。

腾讯Kona国密套件包含四个Java Security Provider

  • KonaCrypto,它是一个Java Cryptography Extension(JCE)实现,遵循标准的Java Cryptography Architecture(JCA)框架实现了国密基础算法SM2,SM3和SM4。
  • KonaPKIX,它实现了国密证书的解析及其证书链验证,并可加载和创建包含国密证书的密钥库(Key Store)文件。
  • KonaSSL,它实现了中国的传输层密码协议(TLCP),并遵循RFC 8998规范将国密基础算法应用到了TLS 1.3协议中。
  • Kona,它没有直接实现任何功能,而是将KonaCrypto,KonaPKIX和KonaSSL中的特性进行了简单的封装,以方便用户仅使用这一个Provider就能够调用上述三个Provider中的全部功能。一般地,建议使用这个Provider。

--来源于腾讯Kona国密套件:从基础算法到安全协议-腾讯云开发者社区-腾讯云

socket支持

以kona为例,实际上BouncyCastle也有TLS模块,但是貌似没做国密的协议簇,只能使用国际标准,这里使用不知道初始原作者是谁的gmssl的demo代码,引入pom

        <dependency><groupId>com.tencent.kona</groupId><artifactId>kona-provider</artifactId><version>${kona.version}</version></dependency><dependency><groupId>com.tencent.kona</groupId><artifactId>kona-crypto</artifactId><version>${kona.version}</version></dependency><dependency><groupId>com.tencent.kona</groupId><artifactId>kona-ssl</artifactId><version>${kona.version}</version></dependency><dependency><groupId>com.tencent.kona</groupId><artifactId>kona-pkix</artifactId><version>${kona.version}</version></dependency>

然后通过socket方式支持国密https,以中国银行官网为测试

package org.example;import com.tencent.kona.KonaProvider;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider;import java.net.*;
import java.io.*;
import java.security.*;import javax.net.*;
import javax.net.ssl.*;public class SocketGet {public static void main(String[] args) {SocketFactory fact = null;SSLSocket socket = null;//        String addr = "sm2test.ovssl.cn";String addr = "ebssec.boc.cn";int port = 443;String uri = "/";try {if (args.length > 0) {addr = args[0];port = Integer.parseInt(args[1]);uri = args[2];}System.out.println("\r\naddr=" + addr);System.out.println("port=" + port);System.out.println("uri=" + uri);// 加载国密提供者
//            Security.insertProviderAt(new BouncyCastleProvider(), 1);
//            Security.insertProviderAt(new BouncyCastleJsseProvider(), 2);Security.insertProviderAt(new KonaProvider(), 1);fact = createSocketFactory(null, null);socket = (SSLSocket) fact.createSocket();socket.setTcpNoDelay(true);System.out.println("\r\nGM SSL connecting...");socket.connect(new InetSocketAddress(addr, port), 5000);socket.setTcpNoDelay(true);socket.startHandshake();System.out.println("Connected!\n");DataInputStream in = new DataInputStream(socket.getInputStream());OutputStream out = socket.getOutputStream();String s = "GET " + uri + " HTTP/1.1\r\n";s += "Accept: */*\r\n";s += "User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)\r\n";s += "Host: " + addr + (port == 443 ? "" : ":" + port) + "\r\n";s += "Connection: Close\r\n";s += "\r\n";out.write(s.getBytes());out.flush();// 读取HTTP头while (true) {byte[] lineBuffer = ReadLine.read(in);if (lineBuffer == null || lineBuffer.length == 0) {System.out.println();break;}String line = new String(lineBuffer);System.out.println(line);}// 读取HTTP内容{byte[] buf = new byte[1024];while (true) {int len = in.read(buf);if (len == -1) {break;}System.out.println(new String(buf, 0, len));}}in.close();out.close();} catch (Exception e) {e.printStackTrace();} finally {try {socket.close();} catch (Exception e) {}}}private static SSLSocketFactory createSocketFactory(KeyStore kepair, char[] pwd) throws Exception {X509TrustManager[] trust = {new MyTrustAllManager()};KeyManager[] kms = null;if (kepair != null) {KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");kmf.init(kepair, pwd);kms = kmf.getKeyManagers();}// 使用国密SSL
//        String protocol = "TLSV1.1";String protocol = "TLCPv1.1";String provider = KonaProvider.NAME;
//        String provider = BouncyCastleJsseProvider.PROVIDER_NAME;SSLContext ctx = SSLContext.getInstance(protocol, provider);java.security.SecureRandom secureRandom = new java.security.SecureRandom();ctx.init(kms, trust, secureRandom);SSLSocketFactory factory = ctx.getSocketFactory();return factory;}}package org.example;import java.io.*;
import java.net.SocketException;class ReadLine {public static final byte[] CRLF = {'\r', '\n'};public static final byte CR = '\r';public static final byte LF = '\n';private static final int LINE_MAX_SIZE = 16384;public static byte[] read(DataInputStream in) throws IOException, SocketException {ByteArrayOutputStream baos = new ByteArrayOutputStream();DataOutputStream s = new DataOutputStream(baos);boolean previousIsCR = false;int len = 0;byte b = 0;try {b = in.readByte();len++;} catch (EOFException e) {return new byte[0];}while (true) {if (b == LF) {if (previousIsCR) {s.flush();byte[] rs = baos.toByteArray();s.close();return rs;} else {s.flush();byte[] rs = baos.toByteArray();s.close();return rs;}} else if (b == CR) {if (previousIsCR) {s.writeByte(CR);}previousIsCR = true;} else {if (previousIsCR) {s.writeByte(CR);}previousIsCR = false;s.write(b);}if (len > LINE_MAX_SIZE) {s.close();throw new IOException("Reach line size limit");}try {b = in.readByte();len++;} catch (EOFException e) {s.flush();byte[] rs = baos.toByteArray();s.close();return rs;}}}
}package org.example;import javax.net.ssl.X509TrustManager;
import java.security.cert.X509Certificate;class MyTrustAllManager implements X509TrustManager
{private X509Certificate[] issuers;public MyTrustAllManager(){this.issuers = new X509Certificate[0];}public X509Certificate[] getAcceptedIssuers(){return issuers ;}public void checkClientTrusted(X509Certificate[] chain, String authType){}public void checkServerTrusted(X509Certificate[] chain, String authType){}
}

核心步骤

实际上就是2步:

1. 注册国密

Security.insertProviderAt(new KonaProvider(), 1);

2. 使用国密sslcontext

SSLContext ctx = SSLContext.getInstance(protocol, provider);

实际上这步是核心,BouncyCastle不支持国密就是因为国密protocol在BouncyCastle没有定义

访问后如下:

addr=ebssec.boc.cn
port=443
uri=/GM SSL connecting...
Connected!HTTP/1.1 200 OK
Date: Sat, 21 Oct 2023 11:55:15 GMT
Last-Modified: Sat, 27 Jun 2015 16:48:38 GMT
Accept-Ranges: bytes
Content-Length: 156
Cache-Control: max-age=300
Expires: Sat, 21 Oct 2023 12:00:15 GMT
Vary: Accept-Encoding,User-Agent
Content-Type: text/html
Connection:close<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0;url=/boc15/login.html"><meta name="renderer" content="ie-stand"></head><body></body></html>

简单源码分析

协议支持,通过注册provider

 

然后com.tencent.kona.sun.security.ssl.CipherSuite定义了TLCP的加密套件,所以可以匹配国密,当然BouncyCastle的tls也可以在上面支持,不过这个就跟kona区别不大,毕竟开始kona也是基于BouncyCastle开发的

有了加密套件,那么按照jce和jsse的模式进行ssl握手,然后加密传输数据。

httpclient

实际上就是那2行代码,再核心一点就是国密算法和加密套件的注册和使用 

public class HttpClientDemo {public static void main(String[] args) throws IOException, NoSuchAlgorithmException, NoSuchProviderException, KeyManagementException {Security.insertProviderAt(new KonaProvider(), 1);SSLContext sslContext = SSLContext.getInstance("TLCPv1.1", KonaProvider.NAME);sslContext.init(null, new TrustManager[] {new MyTrustAllManager()}, new SecureRandom());CloseableHttpClient client = HttpClients.custom().setSSLContext(sslContext).build();HttpGet get = new HttpGet("https://ebssec.boc.cn");CloseableHttpResponse response = client.execute(get);System.out.println(response.getStatusLine().toString());Header[] headers = response.getAllHeaders();for(Header header : headers ){System.out.println(header.getName() +" : "+header.getName());}HttpEntity e = response.getEntity();System.out.println(EntityUtils.toString(e,"UTF-8"));}
}

运行后,结果如下

HTTP/1.1 200 OK
Date : Date
Last-Modified : Last-Modified
Accept-Ranges : Accept-Ranges
Cache-Control : Cache-Control
Expires : Expires
Vary : Vary
Keep-Alive : Keep-Alive
Connection : Connection
Content-Type : Content-Type
<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0;url=/boc15/login.html"><meta name="renderer" content="ie-stand"></head><body></body></html>

 相当于把刚刚,那一系列的代码用Apache的jar实现了,开源方便👍🏻

总结

国密SSL实际上就是TLS1.1和TLS1.2的结合体,一般而言使用TLS1.1即可,协议版本定义0x0101,而且ssl握手需要双证书认证:握手中发送签名证书、加密证书,一般而言签名证书先发,后发加密证书。签名证书仅用于验证身份,私钥自己生成,由CA机构签发;加密证书用于数据加密,CA机构生成并持有私钥。国密SSL实际上跟普通SSL区别不大,核心是使用国密算法密码套件。

相关文章:

国密https访问

前言 现在的SSL的加密算法实际上主要是国际算法&#xff0c;包括JDK&#xff0c;Go等语言也仅支持国际算法加密&#xff08;毕竟是国外开源项目&#xff09;&#xff0c;hash。随着国密算法的普及&#xff0c;比如openssl就支持国密了&#xff0c;还要新版本的Linux内核也开始…...

31二叉树-递归遍历二叉树

目录 LeetCode之路——145. 二叉树的后序遍历 分析 LeetCode之路——94. 二叉树的中序遍历 分析 LeetCode之路——145. 二叉树的后序遍历 给你一棵二叉树的根节点 root &#xff0c;返回其节点值的 后序遍历 。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出…...

【【萌新的FPGA学习之管脚设定xdc文件】】

萌新的FPGA学习之管脚设定xdc文件 xdc文件可以自己设置 也可以匹配 我们根据正点原子的流水灯管脚设定 主要讲述一下 各个英文设计是什么意思 Name&#xff1a;工程中顶层端口的名称。 Direction&#xff1a;说明管脚是输入还是输出。 Neg Diff Pair&#xff1a;负差分对&…...

tomcat---动静分离

访问静态和动态页面分开 实现动态的静态页面负载均衡 实验一 准备阶段&#xff1a;三台虚拟机 nginx代理服务器 &#xff1a;20.0.0.40 tomcat1 &#xff1a;20.0.0.50 tomcat2&#xff1a;20.0.0.51 配置关闭虚拟机防火墙和安全机制 systemctl stop firewalld setenf…...

Spring MVC(一)【什么是Spring MVC】

重点 Spring&#xff1a;IOC 和 AOP 。 Spring MVC &#xff1a;Spring MVC 的执行流程。 SSM 框架的整合&#xff01; Spring 和 Mybatis 我们不建议使用太多注解&#xff0c;Spring MVC 建议全部使用注解开发&#xff01; 1、MVC 回顾 1.1、什么是MVC MVC是模型(Model)…...

.npmrc 使用详解

配置.npmrc之后需要&#xff1a; 清理项目目录中的 node _modules 目录(package-lock.json,umi)。清理 node cache: npm cache clear --force&#xff1b;{ 此步骤必须&#xff0c;主要是大家的电脑经过多年使用后&#xff0c;npm 配置比较混乱&#xff0c;为了避免或者减少配…...

3D视觉硬件技术

目前市面上主流的3D光学视觉方案有三种&#xff1a; 双目立体视觉法&#xff08;Stereo Vision&#xff0c;在下文称双目法&#xff09;&#xff0c;结构光法&#xff08;Structured Light&#xff0c;在下文称结构光&#xff09;以及飞行时间法(Time of Flight, ToF在下文称T…...

【使用OpenCV进行目标分割与计数的代码实例详解】

文章目录 概要实例一&#xff1a;硬币分割计数实例二&#xff1a;玉米粒分割计数 概要 在当今数字图像处理领域&#xff0c;图像分割技术是一项至关重要的任务。图像分割旨在将图像中的不同目标或区域准确地分开&#xff0c;为计算机视觉、图像识别和机器学习等领域提供了坚实…...

npm ERR! exited with error code: 128

1.遇到的问题 报错信息&#xff1a;npm ERR! E:\tools\Gitt\Git\cmd\git.EXE ls-remote -h -t https://github.com/nhn/raphael.git npm ERR! npm ERR! fatal: unable to access https://github.com/nhn/raphael.git/: OpenSSL SSL_read: Connection was reset, errno 10054 …...

Spark---数据输出

1. 输出为Python对象 collect算子&#xff1a;将RDD各个分区内的数据&#xff0c;统一收集到Driver中&#xff0c;形成一个List对象 reduce算子&#xff1a;对RDD数据集按照传入的逻辑进行聚合 take算子&#xff1a;取RDD的前N个元素&#xff0c;组合成list返回给你 count…...

虹科干货 | Redis Enterprise 自动分层技术:大数据集高性能解决方案

文章来源&#xff1a;虹科云科技 阅读原文&#xff1a;https://mp.weixin.qq.com/s/5ik-WLHwEmPn42f1FissQw 越来越多的应用程序依赖于庞大的数据集合&#xff0c;而这些应用程序必须快速响应。借助自动分层&#xff0c;Redis Enterprise 7.2 帮助开发人员轻松创建超快的应用程…...

信息系统项目管理师第四版学习笔记——组织通用治理

组织战略 组织战略是组织高质量发展的总体谋略&#xff0c;是组织相关干系方就其发展达成一致认识的重要基础。组织战略是指组织针对其发展进行的全局性、长远性、纲领性目标的策划和选择。 战略目标是组织在一定的战略期内总体发展的总水平和总任务。它决定了组织在该战略期…...

安装zip扩展(PHP)

记录一次 安装zip扩展的最优方案 &#xff08;备注 网上以及Ai提供的很乱不能很快解决&#xff09; 首先搜索zip包 yum search zip选择自己合适的php版本 比如我的php是7.4.33的 我就用php74-php-pecl-zip 如果没有的话 先添加软件源 sudo yum install epel-release sudo yu…...

深度学习YOLOv4环境配置

软件安装 1、什么是CUDA CUDA(ComputeUnified Device Architecture)&#xff0c;是显卡厂商NVIDIA推出的运算平台。 CUDA是一种由NVIDIA推出的通用并行计算架构&#xff0c;该架构使GPU能够解决复杂的计算问题。 CUDA下载地址为CUDA Toolkit Archive | NVIDIA Developer 版…...

从0到1:云计算工程师入门指南

华为职业认证覆盖ICT全领域&#xff0c;致力于提供领先的人才培养体系和认证标准培养数字化时代的新型ICT人才&#xff0c;构建良性的ICT人才生态。 华为推荐职业认证进阶路线 根据ICT从业者的学习和进阶需求华为职业认证分为工程师级别、高级工程师级别和专家级别三个认证等…...

【微信小程序】6天精准入门(第3天:小程序flex布局、轮播图组件及mock运用以及综合案例)附源码

一、flex布局 布局的传统解决方案&#xff0c;基于[盒状模型]&#xff0c;依赖display属性 position属性 float属性 1、什么是flex布局&#xff1f; Flex是Flexible Box的缩写&#xff0c;意为”弹性布局”&#xff0c;用来为盒状模型提供最大的灵活性。任何一个容器都可以…...

Hadoop3教程(二十五):Yarn的多队列调度器使用案例

文章目录 &#xff08;136&#xff09;生产环境多队列创建&好处&#xff08;137&#xff09;容量调度器多队列提交案例如何创建多个队列如何向指定队列提交任务 &#xff08;138&#xff09;容量调度器任务优先级&#xff08;139&#xff09;公平调度器案例参考文献 &#…...

【SA8295P 源码分析 (四)】27 - QNX Ethernet MAC 驱动 之 emac_tx_thread_handler 数据发送线程 源码分析

【SA8295P 源码分析】27 - QNX Ethernet MAC 驱动 之 emac_tx_thread_handler 数据发送线程 源码分析 系列文章汇总见:《【SA8295P 源码分析 (四)】网络模块 文章链接汇总 - 持续更新中》 本文链接:《【SA8295P 源码分析 (四)】27 - QNX Ethernet MAC 驱动 之 emac_tx_thread…...

思维模型 上瘾模型(hook model)

本系列文章 主要是 分享 思维模型&#xff0c;涉及各个领域&#xff0c;重在提升认知。你到底是怎么上瘾&#xff08;游戏/抖音&#xff09;的&#xff1f;我们该如何“积极的上瘾”&#xff1f;让我们来一切揭晓这背后的秘密。 1 上瘾模型的应用 1.1上瘾模型的积极应用 1 学…...

中文编程开发语言工具编程实际案例:美发店会员管理系统软件编程实例

中文编程开发语言工具编程实际案例&#xff1a;美发店会员管理系统软件编程实例 中文编程开发语言工具编程实际案例&#xff1a;美发店会员管理系统软件编程实例。 软件功能&#xff1a; 1、系统设置&#xff1a;参数设定&#xff0c;账号及权限设置&#xff0c;系统初始化&a…...

浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)

✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义&#xff08;Task Definition&…...

【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15

缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下&#xff1a; struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件

今天呢&#xff0c;博主的学习进度也是步入了Java Mybatis 框架&#xff0c;目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学&#xff0c;希望能对大家有所帮助&#xff0c;也特别欢迎大家指点不足之处&#xff0c;小生很乐意接受正确的建议&…...

【算法训练营Day07】字符串part1

文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接&#xff1a;344. 反转字符串 双指针法&#xff0c;两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案

一、TRS收益互换的本质与业务逻辑 &#xff08;一&#xff09;概念解析 TRS&#xff08;Total Return Swap&#xff09;收益互换是一种金融衍生工具&#xff0c;指交易双方约定在未来一定期限内&#xff0c;基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…...

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#xff0c;开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发&#xff0c;但背后往往隐藏着系统资源调度不当…...

C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...