比赛公告

  1. 比赛结束后2小时内,所有参赛队伍都需要上传提交每道赛题详细的解题报告(WriteUp),经组委会审核后,确定各参赛队最终得分和排名。逾期提交或不提交视为放弃本次比赛排名。

  2. 题目答案的格式请参照题目描述要求的格式,不需要额外再加flag{};加密附件的密码也在对应题目上线后第一个小题描述里。目前server_save对应SS.zip 。还没上线的题目自然不会发密码。

  3. 比赛过程中裁判组将会使用3883尾号的电话对参赛队员做随机反作弊抽查。(手机号后续若更新,请留意比赛公告)为了保证比赛的公平性,比赛全程开启人脸识别与电话质询的工作,严格执行竞赛纪律,请各参赛队伍严格遵守。

  4. fungame题目访问方式都是浏览器访问;新上线fungame题目;IncidentResponse_3 题目增加了一个描述,每个队伍该题答题次数加1。

  5. 【数据分析部分场景加密附件,密码比赛当天给出】IR.zip:https://pan.baidu.com/s/1c4q7KwsRxTYO5SEkm2gqJg ;BF.zip:https://pan.baidu.com/s/1RtBnsuT7eiTkEPofjf0htQ ;SS.zip:https://pan.baidu.com/s/1hA91h4Z4zJYoOCRfgTftNQ ;提取码都是GAME(在官方Q群也将共享)

  6. 理论题答题时间:2023年8月26日10:00—10:20 实操题答题时间:2023年8月26日10:20—18:00

  7. 数据分析题:每道题目内置若干个小问,附件的下载在第一个小问的描述内容中。每个小问限制答题次数为5次。

  8. 实操题每道小题前三血的队伍奖励该小题动态分值的5%、3%、1%。

  9. 所有参赛选手在比赛开始至比赛结束,需进行屏幕录制操作,并将原视频文件进行保存1周以上,以备裁判组进行抽查,录屏文件要能够清晰查看解题思路,严禁后期处理,录屏如果出现中断的情况,请在恢复后继续录屏操作,并在文件名称上进行备注中断时间。文件命名方式:战队名称+队员姓名+备注(中断或者重录说明).mp4 ,录屏软件无要求,如未能提供录屏软件文件将判定为比赛成绩无效

安全知识(20题)

单选(10题)

  1. 题目内容: 位置无关可执行文件(Position-Independent Executable, PIE)通过将程序编译为位置无关代码使程序可以被加载到任意位置,但由于PIE的技术缺陷导致程序不会随机化地址的低__位(二进制位)

    • 12
    • 4
    • 8
    • 16
  2. 题目内容: 为防止办公用计算机上的数据丢失或遭破坏,用户应主要做好( )措施。

    • 购置防病毒、防入侵等软件,提升计算机安全防护能力
    • 对计算机上的数据进行加密保护
    • 合理设置计算机登录密码并定期更改
    • 对计算机上的重要数据进行备份
  3. 题目内容: 以下哪个不是PHP中常见的session默认存放路径:

    • /var/log/sess_PHPSESSID
    • /tmp/sessions/sess_PHPSESSID
    • /var/lib/php/sess_PHPSESSID
    • /tmp/sess_PHPSESSID
  4. 题目内容: 该用户希望U盘中的所有可执行程序均无法运行。那么,其最合适的安全规则是()。

    • Internet区域规则
    • 证书规则
    • 路径规则
    • 散列规则
  5. 题目内容: 用户如发现有人在微信上恶意造谣、诽谤或中伤自己,并已引起了很多人的关注,给自己造成了严重影响。该用户应该采取( )措施。

    • 在微信上向对方讲道理,争取对方悔改道歉并撤回以往言论
    • 向公安机关报案,注意保存有关证据,适时向其他人解释有关事实以消除影响
    • 在微信上向对方还击,以牙还牙
    • 联系微信运营单位,注销对方微信账户
  6. 题目内容: 开启SSP安全机制的程序为保护栈会在函数返回前检查Canary是否被篡改,将从栈上取出的Canary与TLS中的Canary进行异或比较,若不相等则转到__函数中,程序终止

    • _dl_setup_stack_chk_guard()
    • __stack_chk_fail()__
    • libc_start_main()
    • security_init()
  7. 题目内容: 做好信息安全应急管理工作的重要前提是()

    • 事件分类
    • 事件响应
    • 事件报告
    • 事件分级
  8. 题目内容: RAID技术能够使得多硬盘读取速度加快及提供一定的容错性,以下读写性能最优且有数 据安全保障的RAID方式是_____

    • RAID 0+1
    • RAID 5
    • RAID 1
    • RAID 0
  9. 题目内容: 信息系统废弃阶段的信息安全管理形式是()

    • 组织信息系统废弃验收
    • 组织建设方案安全评审
    • 开展信息安全风险评估
    • 组织信息系统安全整改
  10. 题目内容: stm32的处理器架构是():

    • ARMv8
    • ARMv7-A&R
    • ARMv6-M
    • ARMv7-M

多选(10题)

  1. 题目内容: 以下,供应链安全可能存在的安全风险包含哪些。

    • 产品和服务的安全性、开放性、透明性,来源的多样性,供应渠道的可靠性以及因为政治、外交、贸易等因素导致供应中断的风险
    • 存在安全隐患的终端接入企业内网,容易把外部的病毒、木马、蠕虫等风险扩散到企业其他终端,给企业的网络安全以及机密信息带来安全隐患
    • 重要数据或大量个人信息被国外政府影响、控制、恶意利用的风险
    • 供应中断对企业业务的连续性造成危害
  2. 题目内容: 网络与数据安全岗位的特点有哪些?

    • 全局认知,推动创建安全的数字化环境。
    • 动态理念,注意安全隐患时刻发生变化。
    • 创新精神,直面技术手段迭代更新的挑战。
    • 风险思维,明白人员是供应链的重要环节。
  3. 题目内容: ()启动进程类的函数,需要严格检查其参数

    • CreateProcess
    • ShellExecute
    • WinExec
    • system
  4. 题目内容: 《数据安全法》中规定了数据处理者的职责有哪些?

    • 定期风险评估
    • 留存审核交易记录
    • 加强风险监测
    • 建立数据安全管理制度
  5. 题目内容: 不要在()中暴露会话标识符。会话标识符应当只出现在 HTTP cookie 头信息中

    • cookie
    • URL
    • 日志
    • 错误信息
  6. 题目内容: 网络与信息系统有哪些阶段?

    • 规划
    • 建设
    • 完善
    • 使用
  7. 题目内容: 事件发生后,涉事单位和相关部门在先期处置过程中,采取的记录手段有()

    • 文件备份
    • 手工记录
    • 删除日志
    • 截屏
  8. 题目内容: C语言编程时不得直接使用无长度限制的字符拷贝函数,例如(),如果环境允许,应当使用其安全版本替代,或者使用n版本函数。

    • sprintf
    • wcscpy
    • strcat
    • strcpy_s
  9. 题目内容: 数字政府的建设与政务数据的运用相关的配套工作有。

    • 强化政务数据共享统筹协调,建立权威高效的共享协调机制
    • 完善政务数据共享安全保障,保障数据共享过程安全可靠
    • 提升政务数据共享平台支撑能力,完善政务数据共享服务体系
    • 建立政务数据高效供需对接机制,建立健全数据全生命周期质量管理体系
  10. 题目内容: 营造良好的数字生态,离不开人与技术的深度协作。针对内部操作人员有哪些制度。

    • 技术监督
    • 安全与备份管理
    • 技术定期提升
    • 考核评价

数据分析(40题)

hard_web

附件下载 提取码(GAME)备用下载

本地文件

hard_web_1

题目内容:服务器开放了哪些端口,请按照端口大小顺序提交答案,并以英文逗号隔开(如服务器开放了80 81 82 83端口,则答案为80,81,82,83)

hard_web_2

题目内容:服务器中根目录下的flag值是多少?

hard_web_3

题目内容:该webshell的连接密码是多少?

sevrer save

本题附件见于平台公告的SS.zip,解压密码为c77ad47ba4c85fae66f08ec12e0085dd

sevrer save_1

题目内容:黑客是使用什么漏洞来拿下root权限的。格式为:CVE-2020-114514

sevrer save_2

题目内容:黑客反弹shell的ip和端口是什么,格式为:10.0.0.1:4444

sevrer save_3

题目内容:黑客的病毒名称是什么? 格式为:filename

sevrer save_4

题目内容:黑客的病毒运行后创建了什么用户?请将回答用户名与密码:username:password

sevrer save_5

题目内容:服务器在被入侵时外网ip是多少? 格式为:10.10.0.1

sevrer save_6

题目内容:病毒运行后释放了什么文件?格式:文件1,文件2

sevrer save_7

题目内容:矿池地址是什么? 格式:domain:1234

sevrer save_8

题目内容:黑客的钱包地址是多少?格式:xx:xxxxxxxx

Wireshark1

附件下载 提取码(GAME)备用下载

本地文件

Wireshark1_1

题目内容:被入侵主机的IP是?

Wireshark1_2

题目内容:被入侵主机的口令是?

Wireshark1_3

题目内容:用户目录下第二个文件夹的名称是?

Wireshark1_4

题目内容:/etc/passwd中倒数第二个用户的用户名是?

IncidentResponse

本题附件见于平台公告的IR.zip,解压密码为f0b1ba11478343f404666c355919de3f

你是公司的一名安全运营工程师,今日接到外部监管部门通报,你公司网络出口存在请求挖矿域名的行为。需要立即整改。经过与网络组配合,你们定位到了请求挖矿域名的内网IP是10.221.36.21。查询CMDB后得知该IP运行了公司的工时系统。(虚拟机账号密码为:root/IncidentResponsePasswd)

IncidentResponse_1

题目内容:挖矿程序所在路径是?(答案中如有空格均需去除,如有大写均需变为小写,使用echo -n 'strings'|md5sum|cut -d ' ' -f1获取md5值作为答案)

IncidentResponse_2

题目内容:挖矿程序连接的矿池域名是?(答案中如有空格均需去除,如有大写均需变为小写,使用echo -n 'strings'|md5sum|cut -d ' ' -f1获取md5值作为答案)

IncidentResponse_3

题目内容:攻击者入侵服务器的利用的方法是?(答案中如有空格均需去除,如有大写均需变为小写,使用echo -n 'strings'|md5sum|cut -d ' ' -f1获取md5值作为答案)

题目提示:答案md5值前两位为3e

IncidentResponse_4

题目内容:攻击者的IP是?(答案中如有空格均需去除,如有大写均需变为小写,使用echo -n 'strings'|md5sum|cut -d ' ' -f1获取md5值作为答案)

IncidentResponse_5

题目内容:攻击者发起攻击时使用的User-Agent是?(答案中如有空格均需去除,如有大写均需变为小写,使用echo -n 'strings'|md5sum|cut -d ' ' -f1获取md5值作为答案)

IncidentResponse_6

题目内容:攻击者使用了两种权限维持手段,相应的配置文件路径是?(md5加密后以a开头)(答案中如有空格均需去除,如有大写均需变为小写,使用echo -n 'strings'|md5sum|cut -d ' ' -f1获取md5值作为答案)

IncidentResponse_7

题目内容:攻击者使用了两种权限维持手段,相应的配置文件路径是?(md5加密后以b开头)(答案中如有空格均需去除,如有大写均需变为小写,使用echo -n 'strings'|md5sum|cut -d ' ' -f1获取md5值作为答案)

SmallSword

附件下载 提取码(GAME)备用下载

本地文件

SmallSword_1

题目内容:连接蚁剑的正确密码是______________?(答案示例:123asd)

SmallSword_2

题目内容:攻击者留存的值是______________?(答案示例:d1c3f0d3-68bb-4d85-a337-fb97cf99ee2e)

SmallSword_3

题目内容:攻击者下载到的flag是______________?(答案示例:flag3{uuid})

ez_web

附件下载 提取码(GAME)备用下载

本地文件

ez_web_1

题目内容:服务器自带的后门文件名是什么?(含文件后缀)

ez_web_2

题目内容:服务器的内网IP是多少?

ez_web_3

题目内容:攻击者往服务器中写入的key是什么?

baby_forensics

本题附件见于平台公告的BF.zip,解压密码为4cf611fce4a2fec305e54c2766b7c860

baby_forensics_1

题目内容:磁盘中的key是多少?

baby_forensics_2

题目内容:电脑中正在运行的计算器的运行结果是多少?

baby_forensics_3

题目内容:该内存文件中存在的flag值是多少?

tcpdump

附件下载 提取码(GAME)备用下载

本地文件

tcpdump_1

题目内容:攻击者通过暴力破解进入了某Wiki 文档,请给出登录的用户名与密码,以:拼接,比如admin:admin

tcpdump_2

题目内容:攻击者发现软件存在越权漏洞,请给出攻击者越权使用的cookie的内容的md5值。(32位小写)

tcpdump_3

题目内容:攻击使用jdbc漏洞读取了应用配置文件,给出配置中的数据库账号密码,以:拼接,比如root:123456

tcpdump_4

题目内容:攻击者又使用了CVE漏洞攻击应用,执行系统命令,请给出此CVE编号以及远程EXP的文件名,使用:拼接,比如CVE-2020-19817:exp.so

tcpdump_5

题目内容:给出攻击者获取系统权限后,下载的工具的名称,比如nmap

hacked

附件下载 提取码(GAME)备用下载

本地文件

hacked_1

题目内容:admIn用户的密码是什么?

hacked_2

题目内容:app.config['SECRET_KEY']值为多少?

hacked_3

题目内容:flask网站由哪个用户启动?

hacked_4

题目内容:攻击者写入的内存马的路由名叫什么?(答案里不需要加/)

Web(1题)

fungame

题目内容:近日,网管小李被通知说自己管理的网站存在漏洞,请你帮助小李找到存在漏洞的位置,并且将漏洞修复(敏感信息泄露也算哦)。修复漏洞后,等待一分钟左右,/flag就会变为可读权限。可以借用 /index.php?r=debug/default/index 的web接口的debug进行分析。

需要平台下发赛题

为什么网线布线长度不能超过100米呢?随手百度了下,原本的预期是一堆看不懂的涉及物理的通信原理计算。实际百度结果说是因为CSMA/CD带来的争用期问题,参考如下:

《局域网与互联网原理及应用》

3.3.1 协议参数

时隙(time slot)是一个应用程序得知消息的正确传输所需的时间。这段时间的最小长 度等于线路上消息传播的最大时长的两倍。这就证实了我们之前所见的布线约束。

​ 考虑两个最远的站并且加上在信号传送一个来回时所能引入的所有延迟。计算的结果 显示最大传播时长为 44.99 μs。对于一个经典的以太网而言,其标准给出的值要稍微高一 些。时隙的时长等于发射 512 个位的时长——以 10 Mb/s 的话是 51.2 μs。

​ 帧的发射时长总是大于或等于时隙。对于一个 10 Mb/s 的网络而言,51.2 μs 与 64 字节 帧的发射时长相关。如果分组很小,那么会引入一些填充(padding)位以便达到这一尺寸。 这一最小时长的引入,是为了让所有的站在传输之后处于相同的状态。

​ 图 3.7 展示了发射时长比传播延迟小的协议。站 S1 发送消息 M1,而站 S7 同时发送消 息 M2。由于发射时长比传播时长小,这些站在发射期间没有时间来发现冲突。它们将传 送一个正确的传送给上一层。我们也可看到:

  • 站 S2 已经正确接收帧 M1,但没有正确接收帧 M2;

  • 站 S6 已经正确接收帧 M2,但没有正确接收帧 M1;

  • 站 S3、S4 和 S5 没有正确接收到任何帧。

图 3.7 发射时长比时隙短

​ 最大帧尺寸为 1518 字节(1500 数据字节,14 个头字节和 4 字节 CRC),以避免一个站独 占信道。这一尺寸的固定值是任意设定的,不过更大的尺寸将增加冲突风险,如图 3.6 所示。

​ 当一个站侦测到冲突时,它不会立即中断传输。而是发送干扰(jamming)数据来让其他站侦测到冲突。干扰数据的大小是 32 位。冲突帧的发射可持续不到一个时隙的时间。 如果不遵守布线规则,冲突会在 51.2 μs 之后被侦测到。在网络中,这种不正确的行为称为迟冲突(late collision)

遂接着百度什么是CSMA/CD,但奇怪的是,好多地方说现代以太网都是全双工模式基本用不到CSMA/CD了。

Computer Networking A Top-Down Approach SEVENTH EDITION

6.4.2 Ethernet

Let’s conclude our discussion of Ethernet technology by posing a question that may have begun troubling you. In the days of bus topologies and hub-based star topologies, Ethernet was clearly a broadcast link (as defined in Section 6.3) in which frame collisions occurred when nodes transmitted at the same time. To deal with these collisions, the Ethernet standard included the CSMA/CD protocol, which is particularly effective for a wired broadcast LAN spanning a small geographical region. But if the prevalent use of Ethernet today is a switch-based star topology, using store-and-forward packet switching, is there really a need anymore for an Ethernet MAC protocol? As we’ll see shortly, a switch coordinates its transmissions and never forwards more than one frame onto the same interface at any time. Furthermore, modern switches are full-duplex, so that a switch and a node can each send frames to each other at the same time without interference. In other words, in a switch-based Ethernet LAN there are no collisions and, therefore, there is no need for a MAC protocol!

关于这个话题,知乎上有一个回答比较有参考价值,由于不允许自由转载就不贴内容了。

全双工的以太网还有必要使用CSMA/CD协议吗? - Wi-Fi研习者的回答 - 知乎 https://www.zhihu.com/question/480967242/answer/2071451945

其实和自顶向下提到的一致。两个原因:

  • As we’ll see shortly, a switch coordinates its transmissions and never forwards more than one frame onto the same interface at any time
  • Furthermore, modern switches are full-duplex

既然现代的以太网用不到CSMA/CD,那还有这个长度限制吗?老传统,先问是不是再问为什么。

是不是

对于不同的传输速率与传输介质,IEEE 802.3 都作了相应规定。在传输介质是双绞线时大部分情况下100米这个说法是正确的,并且这里的100米指的是一个网段的长度,网段的定义可以参考 Wikipedia: Network segment

这里摘几个例子:

  • 100BASE-T4

    IEEE Std 802.3-2018, IEEE Standard for Ethernet

    23. Physical Coding Sublayer (PCS), Physical Medium Attachment (PMA) sublayer and baseband medium, type 100BASE-T4 23.1.2 Objectives

    The following are the objectives of 100BASE-T4:

    1. To support the CSMA/CD MAC in the half duplex mode of operation.
    2. To support the 100BASE-T MII, Repeater, and optional Auto-Negotiation.
    3. To provide 100 Mb/s data rate at the MII.
    4. To provide for operating over twisted pairs of Category 3, 4, or 5 cable, installed as horizontal runs in accordance with ISO/IEC 11801: 1995, as specified in 23.6, at distances up to 100 m (328 ft).
    5. To allow for a nominal network extent of 200 m, including:
      1) Unshielded twisted-pair links of 100 m.
      2) Two-repeater networks of approximately a 200 m span.
    6. To provide a communication channel with a mean ternary symbol error ratio, at the PMA service interface, of less than one part in \(10^8\).
  • 1000BASE0T

    IEEE Std 802.3-2018, IEEE Standard for Ethernet

    40. Physical Coding Sublayer (PCS), Physical Medium Attachment (PMA) sublayer and baseband medium, type 1000BASE-T 40.1.1 Objectives

    The following are the objectives of 1000BASE-T:

    1. Support the CSMA/CD MAC
    2. Comply with the specifications for the GMII (Clause 35)
    3. Support the 1000 Mb/s repeater (Clause 41)
    4. Provide line transmission that supports full and half duplex operation
    5. Meet or exceed FCC Class A/CISPR or better operation
    6. Support operation over 100 meters of copper balanced cabling as defined in 40.7
    7. Bit Error Ratio of less than or equal to 10-10
    8. Support Auto-Negotiation (Clause 28)

为什么

为什么可以用一句话概括,这是标准制定者兼顾传输效率与工程考量作出的限定。厂商依据标准制造设备,施工者依据标准布线,设备才能以理想情况运作起来。

简单来说,对于不共享传输介质采用交换机构成星形拓扑的现代以太网,主要考量因素为:

  1. 物理限制:码间串扰、信号衰减
  2. 工程考量:向后兼容、布线施工

对于传统以太网,因为采用了CSMA/CD机制,则多了一个因素就是帧时隙(Inter Frame Slot,IFS)。详细的内容这里摘一篇文章供参考(非权威):

知乎专栏 数通网络实践

02数据在网络中的传输 - hai niu的文章 - 知乎 https://zhuanlan.zhihu.com/p/101336720

9.2 帧间隙与帧时隙

计算机设备在发送数据帧时并不是连续的,即每发送完一个帧需要等待一个时间才可以发送下一个帧,其目的是为了让接收者能够处理得过来而不至于拥塞。这个等待的时间间隔我们把它叫做帧间隔帧间隙(Inter Frame Gap,IFG),它表示一个站点连续发送数据时,帧与帧之间的空闲时间或间隔时长,但是其量纲却不是时间(time),而是比特(bit)。一般情况下,以太网缺省帧间隔是96Bits,它指的是96位的数据通过线路的时间。此数值在华为等厂家的交换机上可以修改,但是建议保持默认。设置帧间隔有两个目的:1)防止接收端处理不过来而丢失数据;2)防止冲突。

帧时隙(Inter Frame Slot,IFS)也叫帧时槽,用来表示每一个数据帧占用的最小时间长度。以太网的帧时隙是512 Bits,它是由46 Bytes的最小帧数据长度 + 18 Bytes的帧头信息计算得来。在10/100Mbps链路上,如果512位Bits时间仍然没有收到,则认为线路是空闲的,而小于512 Bits的帧则认为是残帧。

一个完整的以太网数据帧是672 Bites,即在有效数据帧的基础上再加上64 Bits的前导符和帧起始定界符 + 96 Bits的帧间隙共160 Bits。如果是计算接口的实际数据流量,前导和帧间隙等的开销也要计算在内,在计算交换机的转发性能时要注意不要漏算了。

定义帧时隙是为了共享通信线路从而提高其利用率,此机制是通过CSMA/CD(Carrier Sense Multiple Access/Collision Detection,带冲突检测的载波侦听多路访问)实现。在10Mbps以太网中,512Bits数据传输的时间是51.2us,5-4-3原则最大传输距离是2500m,而根据电磁波的传输角度来看,512Bits在10Mbps以太网最大可传输2800m。在100Mbps以太网中,512Bits的数据传输完需要5.12us,电磁波在这个时间可传输约200m。

在802.11无线网络中,因为隐蔽站问题(Hidden Station Problem)和暴露站问题(Exposed Station Problem),无法做到有效的冲突检测,取而代之采用的是CSMA/CA(Carrier Sense Multiple Access/Collision Avoidance,带冲突避免的载波侦听多路访问)。CSMA/CA实际上是一种半双工的通信方式,这让无线网络的通信效率大大降低。

9.3 影响以太网传输距离的因素

第一影响以太网传输距离的因素是基于帧间隔和帧时隙基础上的CSMA/CD,其实就是一种介质访问控制。当然通信也离不开物理定律限制的,比如10Gbps以上的以太网都是全双工链路上,全双工链路虽然没有冲突检测,但即使是使用较高等级的线缆,通信距离也会受到限制,如40GBase-T(802.3bq-2016)使用Cat-8等级双绞线,最远通信距离也只有33m。

这时限制通信距离的主要因素是超高频信号的远距离传输和检测,有一个专门的词汇描述这个问题:码间串扰(ISI, InterSymbol Interference)。在信道带宽不变的情况下想提升信息吞吐量只能增加信号频率,而频率的增加意味着单位时间内脉冲数量的增加,如果脉冲时域不变,数量的增加就会导致相邻脉冲的之间相互干扰,信号受干扰后就影响检出,而缩短脉冲时域,会因为信号出现的时间太短而检出困难。距离越长表现得越明显。

另外,最远通信距离的定义还考虑了现实因素,早期的以太网也有比较远的通信距离(如10Base5,1Base5,10Base2等),但是为了满足大多数应用场景的通信距离需求,同时又要兼顾线路的使用效率,最后才普遍采用了100m的最远通信距离,可以认为是工程技术艺术化处理的结果吧!

在一些通讯距离较远,而又无法添加中继设备的场景中,我们还需要用到另外一个技术,叫长距离以太网(Long Reach Ethernet,LRE),它除了将物理信号增强和提高信号检测的能力外,最重要的一点就是修改了帧间隙。因为帧间隙的修改,LRE设备都需要成对使用,否则时隙不一致就会导致冲突的产生。

归根到底你会发现,其实最根本的影响因素原来是人们的需要。只有能满足需求的技术才是有用的技术,只有有用的技术才有价值,只有能够不停满足需要的技术才能生生不息。由此可见以太网及IEEE、IETF等的强大之处。

引言

为什么会有这种疑惑呢?因为在学习计算机网络基础的时候总会看到相关的网络设备介绍,但似乎搜不到网桥这种设备有零售。同时网桥这种字眼在一些Linux操作系统中又很常见,比如OpenWrt。遂使用中文搜索,点进了知乎的一个提问越看越迷糊,包括常见的问答社区 Super User 中的一篇帖子也有回答错误连篇。以至于前期甚至混淆了虚拟机软件 VirtualBox 中提供的网络适配器类型桥接适配器(Bridged Adapter)与网桥的概念,遂决定多查找资料搞清楚网桥到底是个什么玩意。

定义

什么是网桥?可以用 TCP/IP Illustrated,Volume 1 The Protocols 中的一句话概括:

TCP/IP Illustrated,Volume 1 The Protocols

3.4 Bridges and Switches

The IEEE 802.1d standard specifies the operation of bridges, and thus switches, which are essentially high-performance bridges.

IEEE 802.1d 标准规定了网桥的操作,交换机本质上是高性能的网桥。

翻一翻 IEEE 802.1d 标准的文档,网桥被定义为一个网络桥接设备。符合其定义并具有相应功能的都可以被称为网桥,该标准定义的网桥要求至少拥有两个端口,而交换机则是一种多端口网桥(Multiport Bridge)设备。虽然在好几个地方看到说网桥和交换机可以作为同义替换的术语,但主观感受是交换机这个名字已经近乎替代了网桥。

IEEE Std 802.1D-2004, IEEE Standard for Local and metropolitan area networks: Media Access Control (MAC) Bridges

7.1 Bridge operation

The principal elements of Bridge operation are as follows:

  1. Relay and filtering of frames.
  2. Maintenance of the information required to make frame filtering and relaying decisions.
  3. Management of the above.

发展历史

如果说作为实体设备,网桥和交换机是一种东西,为什么还要给他再起个名字呢?这得从他的发展历史说起,参照维基百科贴的内容,在80年代现代意义上的MAC网桥被发明之后出现了多端口网桥,到了90年代 Kalpana, Inc. 给自家采用了直通交换技术(cut-through forwarding)的次世代网桥起了个名字 EtherSwitch ,因为该技术相较于传统的存储转发技术(Store and forward)引入的延迟更低,之后随着产品推广交换机这个名字就逐渐被广泛使用了。

Wikipedia: Network switch

The first MAC Bridge[3][4][5] was invented[6] in 1983 by Mark Kempf, an engineer in the Networking Advanced Development group of Digital Equipment Corporation. The first 2 port Bridge product (LANBridge 100) was introduced by that company shortly after. The company subsequently produced multi-port switches for both Ethernet and FDDI such as GigaSwitch. Digital decided to license its MAC Bridge patent in a royalty-free, non-discriminatory basis that allowed IEEE standardization. This permitted a number of other companies to produce multi-port switches, including Kalpana.[7]

Wikipedia: Kalpana,_Inc.

In 1989 and 1990, Kalpana introduced the first multiport Ethernet switch, its seven-port EtherSwitch.[5] The invention of Ethernet switching made Ethernet networks faster, cheaper, and easier to manage. Multi-port network switches became common, gradually replacing Ethernet hubs for almost all applications, and enabled an easy transition to 100-megabit Fast Ethernet and later Gigabit Ethernet.[6][7]

杂记

搜索的过程中还看到了一些有意义的资料,简单记录下

  1. 关于 Linux Bridge

  2. 关于 VirtualBox 的各种网络适配器

  3. 关于各种网络设备

    • IEEE 802.3 中关于 Repeater 的描述

      IEEE Std 802.3-2018, IEEE Standard for Ethernet

      29.1.2 Repeater usage

      Repeaters are the means used to connect segments of a network medium together into a single collision domain. Different signaling systems (i.e., 100BASE-T2, 100BASE-T4, 100BASE-TX, 100BASE-FX) can be joined into a common collision domain using repeaters. Bridges can also be used to connect different signaling systems; however, if a bridge is so used, each system connected to the bridge will be a separate collision domain.

    • Data Communications and Networking 中关于 Hub 的描述

      Data Communications and Networking with TCP/IP Protocol Suite

      6.1.1 Hubs

      A hub is a device that operates only in the physical layer. Signals that carry information within a network can travel a fixed distance before attenuation endangers the integrity of the data. A repeater receives a signal and, before itbecomes too weak or corrupted, regenerates and retimes the original bit pattern. The repeater then sends the refreshed signal. In the past, when Ethernet LANs were using bus topology, a repeater was used to connect two segments of a LAN to overcome the length restriction of the coaxial cable. Today, however, Ethernet LANs use star topology. In a star topology, a repeater is a multiport device, often called a hub, that can be used to serve as the connecting point and at the same time function as a repeater.

      注:上面的描述最后一句指的应该是在星形拓扑的以太网中中继设备(repeater)通常为多口,被称为 hub 。实际上对于 hub 来说,其物理拓扑为星形但逻辑拓扑仍为总线型,参考如下。

      《计算机网络(第7版)》

      3.3.3 使用集线器的星形拓扑

      集线器的一些特点如下:

      (1)从表面上看,使用集线器的局域网在物理上是一个星形网,但由于集线器使用电子器件来模拟实际电缆线的工作,因此整个系统仍像一个传统以太网那样运行。也就是说,使用这个集线器的以太网在逻辑上仍是一个总线网,各站共享逻辑上的总线,使用的还是 CSMA/CD 协议(更具体些说,是各站中的适配器执行 CSMA/CD 协议)。网络中的各站必须竞争对传输媒体的控制,并且在同一时刻至多只允许一个站发送数据

    • Network Devices (Hub, Repeater, Bridge, Switch, Router, Gateways and Brouter)

这个话题的由头是在HCIE-Datacom试听课程中老师提到了以太网集线器(Ethernet Hub)可以工作在10BASE-T以太网中,而其中的数字10指的是带宽(Bandwidth)也就是数据传输速率为10 Mbit/s,也可以写作10 Mbps。这两者的M前缀都是mega的符号表示,mega等同于\(10^6\)。众所周知1 Byte = 8 bits,所以可以得出10 Mbit/s = 10/8 MB/s = 1.25 MB/s

以上的推论看起来似乎没什么问题,但转念一想不对啊,我的概念里不应该是1 MB = 1024 KB = 1024*1024 B,这乘数\(10^6\)\(2^{20}\)都不一样怎么能直接除8呢?于是转而Google之搞清楚了其中的细节对错:

  1. 老师是对的我是错的
  2. 历史原因这两单位容易被不规范使用(混用、误用)
  3. 1 MB = 1000 KB = 1000*1000 B
  4. 1 MiB = 1024 KiB = 1024*1024 B

MB(megabyte)

megabyte是信息计量单位byte的倍数单位,多用于信息技术领域表示数据量1。其前缀mega国际单位制(International System of Units,SI)规定的单位前缀,符号为M。因其换算关系为10的幂次,又被称为十进制前缀。

国际单位制由国际计量局(Bureau international des poids et mesures,BIPM)制定,查阅其官方网站和标准文档都有相关的描述

Decimal multiples and submultiples of SI units can be written using the SI prefixes listed below:

Name Symbol Multiplying factor
quetta Q \(10^{30}\)
ronna R \(10^{27}\)
yotta Y \(10^{24}\)
zetta Z \(10^{21}\)
exa E \(10^{18}\)
peta P \(10^{15}\)
tera T \(10^{12}\)
giga G \(10^{9}\)
mega M \(10^{6}\)
kilo k \(10^{3}\)
hecto h \(10^{2}\)
deca da \(10^{1}\)
deci d \(10^{-1}\)
centi c \(10^{-2}\)
milli m \(10^{-3}\)
micro µ \(10^{-6}\)
nano n \(10^{-9}\)
pico p \(10^{-12}\)
femto f \(10^{-15}\)
atto a \(10^{-18}\)
zepto z \(10^{-21}\)
yocto y \(10^{-24}\)
ronto r \(10^{-27}\)
quecto q \(10^{-30}\)

MiB(mebibyte)

mebibyte同样是信息计量单位byte的倍数单位,其前缀mebi国际电工委员会(International Electrotechnical Commission,IEC)规定的单位前缀,符号为Mi,由国际单位制(International System of Units,SI)的十进制单位前缀mega衍生而来,词源为megabinary。因其换算关系为2的幂次,又被称为二进制前缀。

该单位在 IEC 60027-2 标准中描述,该标准已被各国际组织广泛接受2,目前最新的版本是 IEC 60027-2:2019 。IEC 最早在 IEC 60027-2:1972/AMD2:1999 作出了明确定义并与国际单位制的十进制前缀区分。没有找到历史版本的文档可以下载,贴一个博客上看到的图3。另外该博主是在实际应用中发现的该问题,比较具有参考价值。

image-202307192235190

Mbps(megabit per second)

megabit per second与前两者不同,用于衡量数据传输速率,是bit per second的倍数单位。Mbps是其符号Mbit/sMb/s的常见缩写,其前缀mega同样是国际单位制(International System of Units,SI)规定的单位前缀,符号为Mbit per second的符号是bit/s,是bits的导出单位,同样具有二进制前缀的版本mebibit per second,符号Mibit/s

以下是摘自维基的表格:

Name Symbol Multiple Multiple
bit per second bit/s 1 1
Metric prefixes (SI)
kilobit per second kbit/s \(10^{3}\) \(1000^{1}\)
megabit per second Mbit/s \(10^{6}\) \(1000^{2}\)
gigabit per second Gbit/s \(10^{9}\) \(1000^{3}\)
terabit per second Tbit/s \(10^{12}\) \(1000^{4}\)
Binary prefixes (IEC 80000-13)
kibibit per second Kibit/s \(2^{10}\) \(1024^{1}\)
mebibit per second Mibit/s \(2^{20}\) \(1024^{2}\)
gibibit per second Gibit/s \(2^{30}\) \(1024^{3}\)
tebibit per second Tibit/s \(2^{40}\) \(1024^{4}\)

实际验证

以上是标准制定的理想情况,实际上由于标准制定晚于实际应用,这是一个历史遗留问题,有许多场合存在单位的错用混用情况。为了防止大家错用单位,其实 SI 标准文档里在其单位前缀的描述部分还作了相应提示:

The SI prefixes refer strictly to powers of 10. They should not be used to indicate powers of 2 (for example, one kilobit represents 1000 bits and not 1024 bits).

有趣的是大部分人印象中的1 MB = 1024 KB = 1024*1024 B并不只是来源于教科书,还来源于生活日常使用各种软件。例如微软的Windows操作系统,同一个文件拷贝到macOS操作系统后变大、1TB容量硬盘显示只有930GB容量,就是因为Windows使用的是十进制前缀的符号TBGBMBKB,换算关系则是用的二进制前缀的 \(2^{40}\)\(2^{30}\)\(2^{20}\)\(2^{10}\)。而硬盘厂商和macOS则规范使用了单位与换算关系,所以一直都是巨硬错了,而不是硬盘厂商或苹果的macOS。(虽然微软已经在修正这个历史遗留问题了

有哪些事实没有一定计算机知识的人不会相信? - Qi K的回答 - 知乎 https://www.zhihu.com/question/288115796/answer/523089229

非常意外地发现很多软粉在评论区疯狂地为微软洗地???

如果你要教育我说微软这样改不符合用户习惯,有兼容性问题等等等等,我建议你免开尊口,因为微软已经在改了。我举一个例子:

StrFormatByteSizeA function (shlwapi.h) - Win32 apps

这是一个名叫StrFormatByteSizeA的Win32 API函数。它的作用是将字节数转换成以KB、MB结尾的字符串。这个函数的文档里有这么一句话:

In Windows 10, size is reported in base 10 rather than base 2. For example, 1 KB is 1000 bytes rather than 1024.

从Windows 10开始,这个函数返回的不再按照1024进位的方式转换,而是采用国际单位制(SI)的规则,返回1000进位转换后的结果。

说白了,微软并不认为一个小小的转换、一个简单的字符串会有什么兼容性问题,API这种最不应该改的东西说改就改了,其余的还不改无非就是懒得动弹,或者傲娇而已。

博主顺道在macOS Monterey 12.6.6系统做了下验证:

首先查看man-pages描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ man dd

bs=n Set both input and output block size to n bytes, superseding the
ibs and obs operands. If no conversion values other than noerror,
notrunc or sync are specified, then each input block is copied to
the output as a single block without any aggregation of short
blocks.

Where sizes or speed are specified, a decimal, octal, or hexadecimal number
of bytes is expected. If the number ends with a “b”, “k”, “m”, “g”, “t”,
“p”, or “w”, the number is multiplied by 512, 1024 (1K), 1048576 (1M),
1073741824 (1G), 1099511627776 (1T), 1125899906842624 (1P) or the number of
bytes in an integer, respectively. Two or more numbers may be separated by
an “x” to indicate a product.

$ man ls

-h When used with the -l option, use unit suffixes: Byte, Kilobyte,
Megabyte, Gigabyte, Terabyte and Petabyte in order to reduce the
number of digits to four or fewer using base 2 for sizes. This
option is not defined in IEEE Std 1003.1-2008 (“POSIX.1”).

生成相应字节数的文件后分别在终端和访达查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ dd if=/dev/zero of=1KiB.file bs=1k count=1
1+0 records in
1+0 records out
1024 bytes transferred in 0.000066 secs (15515152 bytes/sec)
$ dd if=/dev/zero of=1MiB.file bs=1m count=1
1+0 records in
1+0 records out
1048576 bytes transferred in 0.000274 secs (3826919708 bytes/sec)
$ dd if=/dev/zero of=1GiB.file bs=1g count=1
1+0 records in
1+0 records out
1073741824 bytes transferred in 0.747626 secs (1436201823 bytes/sec)
$ ls -lS
total 2099208
-rw-r--r-- 1 user group 1073741824 7 12 22:48 1GiB.file
-rw-r--r-- 1 user group 1048576 7 12 22:48 1MiB.file
-rw-r--r-- 1 user group 1024 7 12 22:48 1KiB.file
$ ls -lhS
total 2099208
-rw-r--r-- 1 user group 1.0G 7 12 22:48 1GiB.file
-rw-r--r-- 1 user group 1.0M 7 12 22:48 1MiB.file
-rw-r--r-- 1 user group 1.0K 7 12 22:48 1KiB.file

访达似乎对于小文件不愿意显示小数点后面的数字 image-20230712230405095

另外群晖的DSM操作系统里1TB硬盘和Windows一样也显示为930GB(\(10^9/2^{30}*10^3\))

image-202307201240320

说到这其实并没有完全的对与错,只需要在一些严肃的场合明确区分就没有问题了(o^^o)


  1. Wikimedia Foundation (2023) Megabyte, Wikipedia. Available at: https://en.wikipedia.org/wiki/Megabyte (Accessed: 18 July 2023).↩︎

  2. Wikimedia Foundation (2023) Megabyte, Wikipedia. Available at: https://en.wikipedia.org/wiki/Megabyte (Accessed: 18 July 2023).↩︎

  3. Wang, W. (2022) 1K bit = 1024 bit?, 17哥. Available at: https://wangwei1237.github.io/2022/06/19/Does-1Kbit-equal-1024bit/ (Accessed: 19 July 2023).↩︎

转载信息:面试官:说一下你常用的加密算法(https://developer.aliyun.com/article/769289)
原文作者:Java旅途(https://developer.aliyun.com/profile/expert/bqscd3m22yfak)
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。
发表日期:2020年07月31日

转载注

实际上不可逆加密算法可逆加密算法这样的分类方式并不常见,文中所述的不可逆加密算法被称为密码散列函数(Cryptographic hash function),而 HMAC(hash-based message authentication code) 是一种消息认证码(Message authentication code) 需要使用密码散列函数(Cryptographic hash function)和自定义的密钥(secret key)计算得出。


加密算法我们整体可以分为:可逆加密和不可逆加密,可逆加密又可以分为:对称加密和非对称加密。

不可逆加密

常见的不可逆加密算法有MD5HMACSHA1SHA-224SHA-256SHA-384,和SHA-512,其中SHA-224SHA-256SHA-384,和SHA-512我们可以统称为SHA2加密算法,SHA加密算法的安全性要比MD5更高,而SHA2加密算法比SHA1的要高。其中SHA后面的数字表示的是加密后的字符串长度,SHA1默认会产生一个160位的信息摘要。

不可逆加密算法最大的特点就是密钥,但是HMAC是需要密钥的【手动狗头】。

由于这些加密都是不可逆的,因此比较常用的场景就是用户密码加密,其验证过程就是通过比较两个加密后的字符串是否一样来确认身份的。网上也有很多自称是可以破解MD5密码的网站,其原理也是一样,就是有一个巨大的资源库,存放了许多字符串及对应的MD5加密后的字符串,通过你输入的MD5加密串来进行比较,如果过你的密码复杂度比较低,还是有很大机率验证出来的。

MD5

MD5信息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。

MD5算法有以下特点:

1、压缩性:无论数据长度是多少,计算出来的MD5值长度相同

2、容易计算性:由原数据容易计算出MD5

3、抗修改性:即便修改一个字节,计算出来的MD5值也会巨大差异

4、抗碰撞性:知道数据和MD5值,很小概率找到相同MD5值相同的原数据。

1
2
3
4
5
6
7
8
9
10
public static String md5(String text) {
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
byte[] bytes = messageDigest.digest(text.getBytes());
return Hex.encodeHexString(bytes);
}

SHA系列

安全散列算法(英语:Secure Hash Algorithm,缩写为SHA)是一个密码散列函数家族,是FIPS所认证的安全散列算法。能计算出一个数字消息所对应到的,长度固定的字符串(又称消息摘要)的算法。且若输入的消息不同,它们对应到不同字符串的机率很高。

2005年8月17日的CRYPTO会议尾声中王小云、姚期智、姚储枫再度发表更有效率的SHA-1攻击法,能在2的63次方个计算复杂度内找到碰撞。

也就是说SHA-1加密算法有碰撞的可能性,虽然很小。

1
2
3
4
5
6
7
8
9
10
public static String sha256(String text) {
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
byte[] bytes = messageDigest.digest(text.getBytes());
return Hex.encodeHexString(bytes);
}

HMAC系列

HMAC是密钥相关的哈希运算消息认证码(Hash-based Message Authentication Code)的缩写,由H.Krawezyk,M.Bellare,R.Canetti于1996年提出的一种基于Hash函数和密钥进行消息认证的方法,并于1997年作为RFC2104被公布,并在IPSec和其他网络协议(如SSL)中得以广泛应用,现在已经成为事实上的Internet安全标准。它可以与任何迭代散列函数捆绑使用。

HMAC算法更像是一种加密算法,它引入了密钥,其安全性已经不完全依赖于所使用的Hash算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static String hmacSha256(String text, SecretKeySpec sk) {
Mac mac = null;
try {
mac = Mac.getInstance("HmacSHA256");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
try {
mac.init(sk);
} catch (InvalidKeyException e) {
e.printStackTrace();
}
byte[] rawHmac = mac.doFinal(text.getBytes());
return new String(Base64.encodeBase64(rawHmac));
}

如果要使用不可逆加密,推荐使用SHA256、SHA384、SHA512以及HMAC-SHA256、HMAC-SHA384、HMAC-SHA512这几种算法。

对称加密算法

对称加密算法是应用比较早的算法,在数据加密和解密的时用的都是同一个密钥,这就造成了密钥管理困难的问题。常见的对称加密算法有DES3DESAES128AES192AES256 (默认安装的 JDK 尚不支持 AES256,需要安装对应的 jce 补丁进行升级 jce1.7jce1.8)。其中AES后面的数字代表的是密钥长度。对称加密算法的安全性相对较低,比较适用的场景就是内网环境中的加解密。

DES

DES是对称加密算法领域中的典型算法,其密钥默认长度为56位。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 加密
public static String encrypt(byte[] dataSource, String password){
try {
SecureRandom random = new SecureRandom();
DESKeySpec desKeySpec = new DESKeySpec(password.getBytes());
//创建一个密匙工厂,然后用它把DESKeySpec转换成
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES");
SecretKey secretKey = secretKeyFactory.generateSecret(desKeySpec);
//Cipher对象实际完成加密操作
Cipher cipher = Cipher.getInstance("DES");
//用密匙初始化Cipher对象
cipher.init(Cipher.ENCRYPT_MODE, secretKey, random);
//正式执行加密操作
return Base64.encodeBase64String(cipher.doFinal(dataSource));
} catch (Throwable e) {
e.printStackTrace();
} return null;
}
// 解密
public static String decrypt(String src, String password) throws Exception{
// DES算法要求有一个可信任的随机数源
SecureRandom random = new SecureRandom();
// 创建一个DESKeySpec对象
DESKeySpec desKeySpec = new DESKeySpec(password.getBytes());
// 创建一个密匙工厂
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
// 将DESKeySpec对象转换成SecretKey对象
SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
// Cipher对象实际完成解密操作
Cipher cipher = Cipher.getInstance("DES");
// 用密匙初始化Cipher对象
cipher.init(Cipher.DECRYPT_MODE, secretKey, random);
// 真正开始解密操作
return new String(cipher.doFinal(Base64.decodeBase64(src)));
}

3DES

3DES(即Triple DES)是DESAES过渡的加密算法,它使用3条56位的密钥对数据进行三次加密。是DES的一个更安全的变形。它以DES为基本模块,通过组合分组方法设计出分组加密算法。比起最初的DES3DES更为安全。密钥长度默认为168位,还可以选择128位。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public static String encryptThreeDESECB(String src, String key) {
try{
DESedeKeySpec dks = new DESedeKeySpec(key.getBytes("UTF-8"));
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
SecretKey securekey = keyFactory.generateSecret(dks);

Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, securekey);
byte[] b = cipher.doFinal(src.getBytes("UTF-8"));

String ss = new String(Base64.encodeBase64(b));
ss = ss.replaceAll("\\+", "-");
ss = ss.replaceAll("/", "_");
return ss;
} catch(Exception ex){
ex.printStackTrace();
return src;
}
}

public static String decryptThreeDESECB(String src, String key) {
try{
src = src.replaceAll("-", "+");
src = src.replaceAll("_", "/");
byte[] bytesrc = Base64.decodeBase64(src.getBytes("UTF-8"));
// --解密的key
DESedeKeySpec dks = new DESedeKeySpec(key.getBytes("UTF-8"));
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
SecretKey securekey = keyFactory.generateSecret(dks);

// --Chipher对象解密
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, securekey);
byte[] retByte = cipher.doFinal(bytesrc);

return new String(retByte, "UTF-8");
} catch(Exception ex){
ex.printStackTrace();
return src;
}
}

AES

AES 高级数据加密标准,能够有效抵御已知的针对DES算法的所有攻击,默认密钥长度为128位,还可以供选择192位,256位。这里顺便提一句这个位指的是bit。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
private static final String defaultCharset = "UTF-8";
private static final String KEY_AES = "AES";
private static final String KEY_MD5 = "MD5";
private static MessageDigest md5Digest;
static {
try {
md5Digest = MessageDigest.getInstance(KEY_MD5);
} catch (NoSuchAlgorithmException e) {

}
}
/**
* 加密
*/
public static String encrypt(String data, String key) {
return doAES(data, key, Cipher.ENCRYPT_MODE);
}
/**
* 解密
*/
public static String decrypt(String data, String key) {
return doAES(data, key, Cipher.DECRYPT_MODE);
}


/**
* 加解密
*/
private static String doAES(String data, String key, int mode) {
try {
boolean encrypt = mode == Cipher.ENCRYPT_MODE;
byte[] content;
if (encrypt) {
content = data.getBytes(defaultCharset);
} else {
content = Base64.decodeBase64(data.getBytes());
}
SecretKeySpec keySpec = new SecretKeySpec(md5Digest.digest(key.getBytes(defaultCharset))
, KEY_AES);
Cipher cipher = Cipher.getInstance(KEY_AES);// 创建密码器
cipher.init(mode, keySpec);// 初始化
byte[] result = cipher.doFinal(content);
if (encrypt) {
return new String(Base64.encodeBase64(result));
} else {
return new String(result, defaultCharset);
}
} catch (Exception e) {
}
return null;
}

推荐使用对称加密算法有:AES128AES192AES256

非对称加密算法

非对称加密算法有两个密钥,这两个密钥完全不同但又完全匹配。只有使用匹配的一对公钥和私钥,才能完成对明文的加密和解密过程。常见的非对称加密有RSASM2等。

RSA

RSA密钥至少为500位长,一般推荐使用1024位。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//非对称密钥算法
public static final String KEY_ALGORITHM = "RSA";

/**
* 密钥长度,DH算法的默认密钥长度是1024
* 密钥长度必须是64的倍数,在512到65536位之间
*/
private static final int KEY_SIZE = 1024;
//公钥
private static final String PUBLIC_KEY = "RSAPublicKey";
//私钥
private static final String PRIVATE_KEY = "RSAPrivateKey";
/**
* 初始化密钥对
*
* @return Map 甲方密钥的Map
*/
public static Map<String, Object> initKey() throws Exception {
//实例化密钥生成器
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
//初始化密钥生成器
keyPairGenerator.initialize(KEY_SIZE);
//生成密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
//甲方公钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
//甲方私钥
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
//将密钥存储在map中
Map<String, Object> keyMap = new HashMap<String, Object>();
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 私钥加密
*
* @param data 待加密数据
* @param key 密钥
* @return byte[] 加密数据
*/
public static byte[] encryptByPrivateKey(byte[] data, byte[] key) throws Exception {

//取得私钥
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
//生成私钥
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
//数据加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return cipher.doFinal(data);
}

/**
* 公钥加密
*
* @param data 待加密数据
* @param key 密钥
* @return byte[] 加密数据
*/
public static byte[] encryptByPublicKey(byte[] data, byte[] key) throws Exception {

//实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
//初始化公钥
//密钥材料转换
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
//产生公钥
PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
//数据加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
return cipher.doFinal(data);
}

/**
* 私钥解密
*
* @param data 待解密数据
* @param key 密钥
* @return byte[] 解密数据
*/
public static byte[] decryptByPrivateKey(byte[] data, byte[] key) throws Exception {
//取得私钥
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
//生成私钥
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
//数据解密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}

/**
* 公钥解密
*
* @param data 待解密数据
* @param key 密钥
* @return byte[] 解密数据
*/
public static byte[] decryptByPublicKey(byte[] data, byte[] key) throws Exception {

//实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
//初始化公钥
//密钥材料转换
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
//产生公钥
PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
//数据解密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, pubKey);
return cipher.doFinal(data);
}

/**
* 取得私钥
*
* @param keyMap 密钥map
* @return byte[] 私钥
*/
public static byte[] getPrivateKey(Map<String, Object> keyMap) {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return key.getEncoded();
}

/**
* 取得公钥
*
* @param keyMap 密钥map
* @return byte[] 公钥
*/
public static byte[] getPublicKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return key.getEncoded();
}

加密盐

加密盐也是比较常听到的一个概念,盐就是一个随机字符串用来和我们的加密串拼接后进行加密。加盐主要是为了提供加密字符串的安全性。假如有一个加盐后的加密串,黑客通过一定手段这个加密串,他拿到的明文,并不是我们加密前的字符串,而是加密前的字符串和盐组合的字符串,这样相对来说又增加了字符串的安全性。

文中的一些算法来源于网络,可直接复制使用

比较推荐的几个加密算法有:

  • 不可逆加密:SHA256SHA384SHA512以及HMAC-SHA256HMAC-SHA384HMAC-SHA512
  • 对称加密算法:AES3DES
  • 非对称加密算法:RSA
img

Hexo 是一个快速、简洁且高效的博客框架,只需一条指令即可部署到 GitHub Pages, Heroku 或其他平台1

如果没有什么特殊的需求,将生成的静态网页托管到 GitHub Pages 是一个不错的选择。目前 Hexo 官方提供了2种方式一键部署到 GitHub Pages:

这两种方式对应了不同的使用场景,第一种方式适合博客源代码没有托管在 GitHub 的场景,第二种相反。博主选择了将博客源代码托管到了 GitHub,所以很自然的选择了第二种方式实现一键部署的需求,接下来具体看看如何利用 CI/CD 工具实现远程一键部署 Hexo 博客到 GitHub Pages。

一键部署博客

博主在几年前就接触了 Hexo 博客,当时 GitHub Actions 的正式版才推出不久(2019/11/13)2,参考了韩骏大佬的博客3使用了 AppVeyor 作为 CI/CD 工具,只可惜其免费版本不支持私有仓库。博客源代码仓库包含了非公开的配置信息(服务的tokensecret)以及加密文章的原文、草稿箱的草稿,因此源代码仓库设置为私有是必要的。而 GitHub Actions 作为 GitHub 自家的产品,对于当前的使用场景即使是私有仓库也完美支持。有一点需要注意的是对于私有仓库 GitHub Actions 有额度限制,不过即使是最低级别的 GitHub Free 也有相当可观的额度,具体可以查阅官方文档

选定 GitHub Actions 作为 CI/CD 工具后查阅 Hexo 官方给定的文档发现其设定是博客源代码与 GitHub Pages 储存在同一个仓库通过分支进行区分,但将 GitHub Pages 所在的仓库设置为私有需要订阅更高级别的产品4,这显然与我们的需求不符。

GitHub Pages is available in public repositories with GitHub Free and GitHub Free for organizations, and in public and private repositories with GitHub Pro, GitHub Team, GitHub Enterprise Cloud, and GitHub Enterprise Server. For more information, see "GitHub’s products."

通过查阅 Hexo 官方配置文件中复用的 Action peaceiris/actions-gh-pages@v3文档发现,经过简单配置和修改就可以实现我们的需求。

By default, your files are published to the repository which is running this action. If you want to publish to another repository on GitHub, set the environment variable external_repository to <username>/<external-repository>.

先分别贴上原版的配置文件和修改后的配置文件:

  • 原版配置文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    name: Pages

    on:
    push:
    branches:
    - main # default branch

    jobs:
    pages:
    runs-on: ubuntu-latest
    permissions:
    contents: write
    steps:
    - uses: actions/checkout@v3
    with:
    token: ${{ secrets.GITHUB_TOKEN }}
    # If your repository depends on submodule, please see: https://github.com/actions/checkout
    submodules: recursive
    - name: Use Node.js 16.x
    uses: actions/setup-node@v2
    with:
    node-version: '16'
    - name: Cache NPM dependencies
    uses: actions/cache@v2
    with:
    path: node_modules
    key: ${{ runner.OS }}-npm-cache
    restore-keys: |
    ${{ runner.OS }}-npm-cache
    - name: Install Dependencies
    run: npm install
    - name: Build
    run: npm run build
    - name: Deploy
    uses: peaceiris/actions-gh-pages@v3
    with:
    github_token: ${{ secrets.GITHUB_TOKEN }}
    publish_dir: ./public
  • 修改后的配置文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    name: Deploy GitHub Pages

    on:
    push:
    branches:
    - main # default branch

    jobs:
    deploy-gh-pages:
    runs-on: ubuntu-latest
    permissions:
    contents: write
    steps:
    - uses: actions/checkout@v3
    with:
    token: ${{ secrets.GITHUB_TOKEN }}
    # If your repository depends on submodule, please see: https://github.com/actions/checkout
    submodules: recursive
    - name: Install pandoc
    run: sudo apt-get install pandoc
    - name: Use Node.js 19.x
    uses: actions/setup-node@v2
    with:
    node-version: '19'
    - name: Cache NPM dependencies
    uses: actions/cache@v2
    with:
    path: node_modules
    key: ${{ runner.OS }}-npm-cache
    restore-keys: |
    ${{ runner.OS }}-npm-cache
    - name: Install Dependencies
    run: npm install
    - name: Build
    run: npm run build
    - name: Deploy
    uses: peaceiris/actions-gh-pages@v3
    with:
    deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }}
    external_repository: username/external-repository
    publish_dir: ./public
    user_name: 'github-actions[bot]'
    user_email: 'github-actions[bot]@users.noreply.github.com'
    commit_message: ${{ github.event.head_commit.message }}

与原版的配置文件对比只修改了以下3个方面:

  1. 安装 pandoc
    1
    2
    3
    # line 19
    - name: Install pandoc
    run: sudo apt-get install pandoc
    • 与此文无关,因渲染器改为了hexo-renderer-pandoc
  2. 修改 Node.js 主版本:
    1
    2
    3
    4
    5
    # line 21
    - name: Use Node.js 19.x
    uses: actions/setup-node@v2
    with:
    node-version: '19'
    • 在本地使用node --version查看即可
  3. 配置peaceiris/actions-gh-pages@v3的传入参数:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # line 36
    - name: Deploy
    uses: peaceiris/actions-gh-pages@v3
    with:
    deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }}
    external_repository: username/external-repository
    publish_dir: ./public
    user_name: 'github-actions[bot]'
    user_email: 'github-actions[bot]@users.noreply.github.com'
    commit_message: ${{ github.event.head_commit.message }}
    • 删除github_token:对于external_repository使用external_repositorypersonal_token代替
    • 增加deploy_key:生成SSH Key Pair,在 GitHub Pages 仓库使用公钥设置Deploy keys并确保勾选Allow write access,在博客源代码仓库的Actions secrets and variables设置项使用私钥设置Secrets。配置文件里的ACTIONS_DEPLOY_KEY就是之前设置Secrets填的名字
    • 增加external_repository:GitHub Pages 仓库username/external-repository
    • 增加user_name:配置 GitHub Pages 仓库的 commit 用户为github-actions[bot]
    • 增加user_email:配置 GitHub Pages 仓库的 commit 用户邮箱为github-actions[bot]邮箱
    • 增加commit_message:配置 GitHub Pages 仓库的 commit message 为触发该 GitHub Actions 事件源的头部 commit message

修改完配置文件后将配置文件存放到博客源代码仓库的.github/workflows/filename.yml,以后有提交 push 到该仓库的main分支便可触发该配置文件对应的工作流完成 GitHub Pages 的自动部署。

一键推送源码

至此似乎已经大功告成,但博主觉得已经完成建站进入正常更新状态的博客,每次更新还需要git addgit commitgit push三个步骤才能完成博客更新还是略微麻烦,有没有办法能实现一键推送博客源代码?

使用 Hexo Deployer

Hexo Deployer是 Hexo 自带的部署工具,通过编写安装各种各样的 Hexo 插件,可以通过hexo deploy命令将 Hexo 博客一键部署到各种类型的托管服务,Hexo Deployer提供了 API 供开发者开发第三方插件实现该功能。

虽然 Hexo 官方的本意是部署博客,但似乎可以借助这个功能实现我们一键推送源码的需求,于是博主根据 hexo-deployer-git 编写了一个 Hexo 插件 hexo-auto-push-git

  1. 安装

    1
    $ npm install https://github.com/ChiuJun/hexo-auto-push-git.git --save-dev

  2. 配置

    1
    2
    3
    4
    5
    # You can use this:
    deploy:
    type: git
    branch: [branch]
    message: [message]
  3. 使用

    1
    $ hexo deploy

使用 alias shell

修改完博客配置希望看看效果,每次都要输入一串命令

1
$ hexo clean && hexo g && hexo s

想到可以用alias简化操作,突然想到alias也可以完成我们一键推送源码的需求

1
2
3
4
alias hexo_test="hexo clean && hexo generate && hexo server"
alias hexot=hexo_test
alias hexo_post_article_to_source_repo="git add -A && git commit -m \"docs: site updated `date \"+%Y-%m-%d %H:%M:%S\"`\" && git push -u origin HEAD:main"
alias hexop=hexo_post_article_to_source_repo

将后面两行alias命令添加到所使用 shell 的配置文件中,使用source命令更新下配置文件便可以使用别名hexop一键推送源码

1
$ hexop
至此,我们已经完成了一键推送源码以及一键部署博客,以后便可以愉快的写博客了!


  1. Hexo (2023a) Home page , Hexo. Available at: https://hexo.io/zh-cn/ (Accessed: 08 July 2023).↩︎

  2. Niyogi, S. (2019) New from universe 2019: Github for mobile, GitHub Archive Program, and more, The GitHub Blog. Available at: https://github.blog/2019-11-13-universe-day-one/#github-actions (Accessed: 08 July 2023).↩︎

  3. Han, J. (2017) Hexo的版本控制与持续集成, formulahendry. Available at: https://formulahendry.github.io/2016/12/04/hexo-ci/ (Accessed: 08 July 2023).↩︎

  4. GitHub, Inc. (no date) About GitHub Pages, GitHub Docs. Available at: https://docs.github.com/en/pages/getting-started-with-github-pages/about-github-pages (Accessed: 09 July 2023).↩︎

转载信息:Commit message 和 Change log 编写指南(https://www.ruanyifeng.com/blog/2016/01/commit_message_change_log.html)
原文作者:阮一峰(https://www.ruanyifeng.com/)
版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证
发表日期:2016年 1月 6日

Git 每次提交代码,都要写 Commit message(提交说明),否则就不允许提交。

1
$ git commit -m "hello world"

上面代码的-m参数,就是用来指定 commit mesage 的。

如果一行不够,可以只执行git commit,就会跳出文本编辑器,让你写多行。

1
$ git commit

基本上,你写什么都行(这里这里这里)。

img

但是,一般来说,commit message 应该清晰明了,说明本次提交的目的。

img

目前,社区有多种 Commit message 的写法规范。本文介绍Angular 规范(见上图),这是目前使用最广的写法,比较合理和系统化,并且有配套的工具。

Commit message 的作用

格式化的Commit message,有几个好处。

(1)提供更多的历史信息,方便快速浏览。

比如,下面的命令显示上次发布后的变动,每个commit占据一行。你只看行首,就知道某次 commit 的目的。

1
$ git log <last tag> HEAD --pretty=format:%s
img

(2)可以过滤某些commit(比如文档改动),便于快速查找信息。

比如,下面的命令仅仅显示本次发布新增加的功能。

1
$ git log <last release> HEAD --grep feature

(3)可以直接从commit生成Change log。

Change Log 是发布新版本时,用来说明与上一个版本差异的文档,详见后文。

img

Commit message 的格式

每次提交,Commit message 都包括三个部分:Header,Body 和 Footer。

1
2
3
4
5
<type>(<scope>): <subject>
// 空一行
<body>
// 空一行
<footer>

其中,Header 是必需的,Body 和 Footer 可以省略。

不管是哪一个部分,任何一行都不得超过72个字符(或100个字符)。这是为了避免自动换行影响美观。

Header部分只有一行,包括三个字段:type(必需)、scope(可选)和subject(必需)。

(1)type

type用于说明 commit 的类别,只允许使用下面7个标识。

  • feat:新功能(feature)
  • fix:修补bug
  • docs:文档(documentation)
  • style: 格式(不影响代码运行的变动)
  • refactor:重构(即不是新增功能,也不是修改bug的代码变动)
  • test:增加测试
  • chore:构建过程或辅助工具的变动

如果typefeatfix,则该 commit 将肯定出现在 Change log 之中。其他情况(docschorestylerefactortest)由你决定,要不要放入 Change log,建议是不要。

(2)scope

scope用于说明 commit 影响的范围,比如数据层、控制层、视图层等等,视项目不同而不同。

(3)subject

subject是 commit 目的的简短描述,不超过50个字符。

  • 以动词开头,使用第一人称现在时,比如change,而不是changedchanges
  • 第一个字母小写
  • 结尾不加句号(.

Body

Body 部分是对本次 commit 的详细描述,可以分成多行。下面是一个范例。

1
2
3
4
5
6
7
More detailed explanatory text, if necessary.  Wrap it to 
about 72 characters or so.

Further paragraphs come after blank lines.

- Bullet points are okay, too
- Use a hanging indent

有两个注意点。

(1)使用第一人称现在时,比如使用change而不是changedchanges

(2)应该说明代码变动的动机,以及与以前行为的对比。

Footer 部分只用于两种情况。

(1)不兼容变动

如果当前代码与上一个版本不兼容,则 Footer 部分以BREAKING CHANGE开头,后面是对变动的描述、以及变动理由和迁移方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
BREAKING CHANGE: isolate scope bindings definition has changed.

To migrate the code follow the example below:

Before:

scope: {
myAttr: 'attribute',
}

After:

scope: {
myAttr: '@',
}

The removed `inject` wasn't generaly useful for directives so there should be no code using it.

(2)关闭 Issue

如果当前 commit 针对某个issue,那么可以在 Footer 部分关闭这个 issue 。

1
Closes #234

也可以一次关闭多个 issue 。

1
Closes #123, #245, #992

Revert

还有一种特殊情况,如果当前 commit 用于撤销以前的 commit,则必须以revert:开头,后面跟着被撤销 Commit 的 Header。

1
2
3
revert: feat(pencil): add 'graphiteWidth' option

This reverts commit 667ecc1654a317a13331b17617d973392f415f02.

Body部分的格式是固定的,必须写成This reverts commit <hash>.,其中的hash是被撤销 commit 的 SHA 标识符。

如果当前 commit 与被撤销的 commit,在同一个发布(release)里面,那么它们都不会出现在 Change log 里面。如果两者在不同的发布,那么当前 commit,会出现在 Change log 的Reverts小标题下面。

Commitizen

Commitizen是一个撰写合格 Commit message 的工具。

安装命令如下。

1
$ npm install -g commitizen

然后,在项目目录里,运行下面的命令,使其支持 Angular 的 Commit message 格式。

1
$ commitizen init cz-conventional-changelog --save --save-exact

以后,凡是用到git commit命令,一律改为使用git cz。这时,就会出现选项,用来生成符合格式的 Commit message。

img

validate-commit-msg

validate-commit-msg 用于检查 Node 项目的 Commit message 是否符合格式。

它的安装是手动的。首先,拷贝下面这个JS文件,放入你的代码库。文件名可以取为validate-commit-msg.js

接着,把这个脚本加入 Git 的 hook。下面是在package.json里面使用 ghooks,把这个脚本加为commit-msg时运行。

1
2
3
4
5
"config": {
"ghooks": {
"commit-msg": "./validate-commit-msg.js"
}
}

然后,每次git commit的时候,这个脚本就会自动检查 Commit message 是否合格。如果不合格,就会报错。

1
2
3
$ git add -A 
$ git commit -m "edit markdown"
INVALID COMMIT MSG: does not match "<type>(<scope>): <subject>" ! was: edit markdown

生成 Change log

如果你的所有 Commit 都符合 Angular 格式,那么发布新版本时, Change log 就可以用脚本自动生成(例1例2例3)。

生成的文档包括以下三个部分。

  • New features
  • Bug fixes
  • Breaking changes.

每个部分都会罗列相关的 commit ,并且有指向这些 commit 的链接。当然,生成的文档允许手动修改,所以发布前,你还可以添加其他内容。

conventional-changelog 就是生成 Change log 的工具,运行下面的命令即可。

1
2
3
$ npm install -g conventional-changelog
$ cd my-project
$ conventional-changelog -p angular -i CHANGELOG.md -w

上面命令不会覆盖以前的 Change log,只会在CHANGELOG.md的头部加上自从上次发布以来的变动。

如果你想生成所有发布的 Change log,要改为运行下面的命令。

1
$ conventional-changelog -p angular -i CHANGELOG.md -w -r 0

为了方便使用,可以将其写入package.jsonscripts字段。

1
2
3
4
5
{
"scripts": {
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -w -r 0"
}
}

以后,直接运行下面的命令即可。

1
$ npm run changelog

(完)

0%