GnuPG 是非对称加密专利软件 PGP 的开源版本,在日常使用 GPG 时,密钥通常保存在所有者的电脑上,这对于希望密钥随身携带的用户来说,支持 OpenPGP 的物理密钥就帮上大忙了。
OpenPGP 物理密钥(官方叫做“OpenPGP 智能卡 / OpenPGP Card”),由于无法从物理密钥提取私钥,相比于通过 USB 闪存盘存取私钥,对私钥的保护更好,无需将私钥暴露给其他程序,只能将数据交给物理密钥进行加解密处理,确保了私钥的安全性。
由于国内对 OpenPGP 的文档较少,所以写下一篇 GPG 物理密钥从安装到使用的攻略。
本教程将通过 Gpg4Win v3.1.15 (2021-01-12) 安装并使用 GnuPG v2.2.27 版本进行演示。
2023-03-13 更新:虽然本教程使用指定版本的 GPG 进行编写,但也请始终使用可信的最新版本 GPG,操作上基本相同。
物理密钥也有很多类型,有 USB 类型、NFC 智能卡类型等,可根据需要进行选择。
初始化密钥
在本教程中,假定你已经完成了密钥对的创建,由于网上对 GPG 的入门教程众多且都说明了生成密钥对的方法,故本文不再说明密钥对的生成。
首先,接入密钥,打开命令行,通过 GPG 打开物理密钥编辑:
$ gpg --edit-card
当 GPG 未找到物理密钥时,将输出以下内容:
gpg: selecting card failed: No such device
gpg: OpenPGP card not available: No such device
请检查物理密钥与系统是否连接成功。
如果连接正常,且 GPG 成功识别到了物理密钥,那么它将显示密钥的信息:
(由于密钥已经安装过,故直接删除相关信息,如果输出类似格式即代表成功识别)
Reader ...........: xxxxxxx
Application ID ...: xxxxxxx
Application type .: OpenPGP
Version ..........: x.x
Manufacturer .....: xxxxxxx
Serial number ....: xxxxxxx
Name of cardholder:
Language prefs ...:
Salutation .......:
URL of public key :
Login data .......:
Signature PIN ....: xxxxxxx
Key attributes ...:
Max. PIN lengths .:
PIN retry counter : xxxxxxx
Signature counter :
Signature key ....:
created ....:
Encryption key....:
created ....:
Authentication key:
created ....:
General key info..:
gpg/card>
一旦我们确定了物理密钥已经就绪,我们就可以将现有的密钥对迁移到物理密钥了。输入 “quit” 退出智能卡编辑模式,进入下一步。
将现有密钥迁移到物理密钥
密钥要求
强烈建议使用子密钥进行加解密/签名操作!
GPG 的子密钥经过主密钥签名(类似于对他人密钥进行信任签名一样),身份得到了主密钥的保证,且与主密钥不相同,在子密钥遭到泄漏,主密钥仍然安全的情况下,所有者可以直接取消子密钥,通过主密钥重新创建新的子密钥,无需发布主密钥撤销来废弃主密钥。
在本教程同样强烈不推荐将主密钥迁移到物理密钥!
创建子密钥(可选,但强烈推荐)
首先,进入密钥编辑模式:
$ gpg --edit-key 5BBF0456101825EB(填自己的密钥对Id,就是密钥指纹后 16 位)
gpg (GnuPG) 2.2.27; Copyright (C) 2021 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Secret key is available.
sec rsa4096/5BBF0456101825EB
created: 2021-02-26 expires: 2023-02-26 usage: SC
trust: ultimate validity: ultimate
[ultimate] (1). Echo <[email protected]>
gpg>
通过 “addkey” 命令分别创建签名用子密钥和加密用子密钥(如果没有的话):
gpg> addkey
Please select what kind of key you want:
(3) DSA (sign only)
(4) RSA (sign only)
(5) Elgamal (encrypt only)
(6) RSA (encrypt only)
(14) Existing key from card
# 创建签名用子密钥,选择与主密钥相同的 RSA 算法,而且要选择 Sign Only(仅签名)
Your selection? 4
RSA keys may be between 1024 and 4096 bits long.
# 密钥长度建议 4096,实际要看物理密钥是否支持
What keysize do you want? (3072) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
0 = key does not expire
<n> = key expires in n days
<n>w = key expires in n weeks
<n>m = key expires in n months
<n>y = key expires in n years
# 子密钥有效期,根据个人需要选择,此处设为永久。
Key is valid for? (0) 0
Key does not expire at all
# 如果确定填写信息正确,选 y
Is this correct? (y/N) y
# 确定创建密钥,选 y
Really create? (y/N) y
# 稍等片刻
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
sec rsa4096/5BBF0456101825EB
created: 2021-02-26 expires: 2023-02-26 usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/FB003570421AB3DC # 这个就是新建的子密钥
created: 2021-02-26 expires: never usage: S
[ultimate] (1). Echo <[email protected]>
# 用同样的命令创建 加密用子密钥
# (如果在创建 GPG 密钥对时已经创建了, 那就不需要在这里重复创建)
gpg> addkey
Please select what kind of key you want:
(3) DSA (sign only)
(4) RSA (sign only)
(5) Elgamal (encrypt only)
(6) RSA (encrypt only)
(14) Existing key from card
# 选择 RSA (encrypt only:仅加密)
Your selection? 6
RSA keys may be between 1024 and 4096 bits long.
# 密钥长度建议 4096,实际要看物理密钥是否支持
What keysize do you want? (3072) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
0 = key does not expire
<n> = key expires in n days
<n>w = key expires in n weeks
<n>m = key expires in n months
<n>y = key expires in n years
# 根据个人需要选择。
Key is valid for? (0) 0
Key does not expire at all
# 确定信息无误后选 y
Is this correct? (y/N) y
# 确定创建,选 y
Really create? (y/N) y
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
sec rsa4096/5BBF0456101825EB
created: 2021-02-26 expires: 2023-02-26 usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/FB003570421AB3DC # 这是上一条命令创建的签名用密钥,usage 为 S(Sign,签名)
created: 2021-02-26 expires: never usage: S
ssb rsa4096/0B94994886DD9F52 # 这是新建的加密用密钥,usage 为 E(Encrypt,加密)
created: 2021-02-26 expires: never usage: E
[ultimate] (1). Echo <[email protected]>
gpg>
在完成子密钥的创建后,保存并退出密钥编辑模式:
# 密钥编辑后,需要用【save】退出,如果使用【quit】退出,将会丢弃本次编辑的所有修改!
gpg> save
# GPG 保存后退出,回到命令行
bash$
添加认证密钥
添加认证密钥需要以专家模式进入密钥编辑模式,由于认证密钥使用频率并不算高(我个人认为),所以单独在此讲解:
# 添加【--expert】进入专家模式
$ gpg --expert --edit-key 5BBF0456101825EB
gpg (GnuPG) 2.2.27; Copyright (C) 2021 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Secret key is available.
sec rsa4096/5BBF0456101825EB
created: 2021-02-26 expires: 2023-02-26 usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/FB003570421AB3DC
created: 2021-02-26 expires: never usage: S
ssb rsa4096/0B94994886DD9F52
created: 2021-02-26 expires: never usage: E
[ultimate] (1). Echo <[email protected]>
# 跟上面步骤一样,添加子密钥
gpg> addkey
# 由于进入了专家模式,选择会更多一些
Please select what kind of key you want:
(3) DSA (sign only)
(4) RSA (sign only)
(5) Elgamal (encrypt only)
(6) RSA (encrypt only)
(7) DSA (set your own capabilities)
(8) RSA (set your own capabilities)
(10) ECC (sign only)
(11) ECC (set your own capabilities)
(12) ECC (encrypt only)
(13) Existing key
(14) Existing key from card
# 这里我们选择【8】,选择 RSA 算法,并由我们选择密钥类型
Your selection? 8
# 这里开始选择密钥类型
Possible actions for a RSA key: Sign Encrypt Authenticate
Current allowed actions: Sign Encrypt # 注意这里,GPG 默认为我们选择了【签名】和【加密】,我们要取消这两个类型,单独设置【认证】
(S) Toggle the sign capability
(E) Toggle the encrypt capability
(A) Toggle the authenticate capability
(Q) Finished
# 选择【加密】,由于加密类型已经存在,将会取消加密类型
Your selection? E
Possible actions for a RSA key: Sign Encrypt Authenticate
Current allowed actions: Sign # 【加密】已经被取消了,还要取消【签名】
(S) Toggle the sign capability
(E) Toggle the encrypt capability
(A) Toggle the authenticate capability
(Q) Finished
# 选择【签名】,由于签名类型已经存在,将会取消签名类型
Your selection? S
Possible actions for a RSA key: Sign Encrypt Authenticate
Current allowed actions:
# 现在所有类型都被取消
(S) Toggle the sign capability
(E) Toggle the encrypt capability
(A) Toggle the authenticate capability
(Q) Finished
# 选择【认证】类型,由于认证类型没选择,就会被选中
Your selection? A
Possible actions for a RSA key: Sign Encrypt Authenticate
Current allowed actions: Authenticate # 确定【认证】类型被选中
(S) Toggle the sign capability
(E) Toggle the encrypt capability
(A) Toggle the authenticate capability
(Q) Finished
# 选【Finished】完成选择
Your selection? Q
RSA keys may be between 1024 and 4096 bits long.
# 密钥长度推荐选择 4096,但实际上要看你的物理密钥是否支持该长度。
What keysize do you want? (3072) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
0 = key does not expire
<n> = key expires in n days
<n>w = key expires in n weeks
<n>m = key expires in n months
<n>y = key expires in n years
# 根据个人需要选择。
Key is valid for? (0) 0
Key does not expire at all
# 确定信息无误后选 y
Is this correct? (y/N) y
# 确定创建,选 y
Really create? (y/N) y
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
sec rsa4096/5BBF0456101825EB
created: 2021-02-26 expires: 2023-02-26 usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/FB003570421AB3DC
created: 2021-02-26 expires: never usage: S
ssb rsa4096/0B94994886DD9F52
created: 2021-02-26 expires: never usage: E
ssb rsa4096/152054DF6AD9C2AA # 认证类型密钥创建完成,Usage 为 A(Authentication)
created: 2021-02-26 expires: never usage: A
[ultimate] (1). Echo <[email protected]>
# 保存退出即可
gpg> save
备份私钥
在迁移密钥之前,强烈建议将 主密钥与子密钥 冷备份到一个离线存储容器中(例如开启了 BitLocker 的移动硬盘),一旦密钥被迁移到了物理密钥,将没有机会再取出来,如果弄丢了物理密钥,将导致使用物理密钥进行加密的加密内容无法解密的情况。(亲身经历)
首先保存主密钥:
$ gpg -o test.pri.gpg(密钥保存路径) --export-secret-keys 5BBF0456101825EB(要保存的密钥Id)
执行后会要求输入私钥密码(如果未设置则直接执行成功),成功后无提示运行结束,同时私钥被导出到文件。
上面步骤仅备份了主密钥,还记得密钥要求所说的吗?主密钥与子密钥是两个不同的东西,子密钥仅仅是经过了主密钥的签名,由主密钥承认了身份的密钥而已,所以一旦子密钥丢失,即使有主密钥备份,也无法找回子密钥,更无法解密通过子密钥加密的文件。
这也是很多 GPG 教程里的一个重大疏漏:备份主密钥不等于备份了子密钥!
(更新:经测试似乎新版本已经改了,主密钥私钥备份会连着子密钥私钥一起备份,不过如果往下做也不会有啥问题,只是单独备份了子密钥私钥而已。)
备份子密钥跟备份主密钥类似:
$ gpg -o test.prisub.gpg(密钥保存路径) --export-secret-subkeys 5BBF0456101825EB(要保存的密钥Id)
执行后同意要求输入子密钥密码(如果未设置则直接执行成功),成功后无提示运行结束,同时子密钥被导出到文件。
将主密钥与子密钥备份后,务必将其迅速转移至冷备份加密存储中,妥善保管,且确保你已经生成了主密钥的撤销证书,关于撤销证书的生成,已有教程说明,故本教程不再详细说明。
迁移密钥
在确定了主密钥和子密钥已经备份完成,物理密钥已经就绪后,我们就可以开始迁移密钥了。
连接物理密钥,重复初始化密钥的步骤,确保物理密钥已经成功连接,然后进入要迁移密钥的密钥编辑模式:
$ gpg --edit-key 5BBF0456101825EB
gpg (GnuPG) 2.2.27; Copyright (C) 2021 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Secret subkeys are available.
sec rsa4096/5BBF0456101825EB
created: 2021-02-26 expires: 2023-02-26 usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/FB003570421AB3DC
created: 2021-02-26 expires: never usage: S
ssb rsa4096/0B94994886DD9F52
created: 2021-02-26 expires: never usage: E
ssb rsa4096/152054DF6AD9C2AA # 认证类型密钥创建完成,Usage 为 A(Authentication)
created: 2021-02-26 expires: never usage: A
[ultimate] (1). Echo <[email protected]>
gpg>
迁移的方法很简单,依次选择对应类型的密钥,然后迁移到物理密钥即可,输入【key x】选择密钥,例如:
gpg> key 1
sec rsa4096/5BBF0456101825EB # 主密钥的序号为 0
created: 2021-02-26 expires: 2023-02-26 usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/FB003570421AB3DC # 看到前面的【ssb】后加了个【*】吗?这代表了该密钥被选中了的意思
created: 2021-02-26 expires: never usage: S
ssb rsa4096/0B94994886DD9F52
created: 2021-02-26 expires: never usage: E
ssb rsa4096/152054DF6AD9C2AA
created: 2021-02-26 expires: never usage: A
[ultimate] (1). Echo <[email protected]>
gpg>
这就相当于是这个样子:
选中密钥后,将其迁移到物理密钥中:
gpg> keytocard
Please select where to store the key:
(2) Encryption key
Your selection? 2
(由于我的物理密钥已经迁移了密钥,所以不再进一步操作,执行后如需输入密码,请输入对应子密钥的密码)
根据密钥的类型不同,可以选择的存储位也不一样,一般来说,物理密钥有三个密钥存储位:
- 加密密钥:对文件或消息进行加解密,对应了 GPG 的 E 类型密钥(Encrypt)
- 签名密钥:对文件或消息进行签名或验签,对应了 GPG 的 S 类型密钥(Sign)
- 认证密钥:进行 SSH 登录等身份认证类操作,对应了 GPG 的 A 类型密钥(Authentication)
添加完之后,更改选中密钥:
# 首先,取消选中上一个子密钥,不然会拒绝迁移
gpg> key 1
sec rsa4096/5BBF0456101825EB
created: 2021-02-26 expires: 2023-02-26 usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/FB003570421AB3DC
created: 2021-02-26 expires: never usage: S
ssb rsa4096/0B94994886DD9F52
created: 2021-02-26 expires: never usage: E
ssb rsa4096/152054DF6AD9C2AA
created: 2021-02-26 expires: never usage: A
[ultimate] (1). Echo <[email protected]>
# 然后选中第二个子密钥
gpg> key 2
sec rsa4096/5BBF0456101825EB
created: 2021-02-26 expires: 2023-02-26 usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/FB003570421AB3DC
created: 2021-02-26 expires: never usage: S
ssb* rsa4096/0B94994886DD9F52
created: 2021-02-26 expires: never usage: E
ssb rsa4096/152054DF6AD9C2AA
created: 2021-02-26 expires: never usage: A
[ultimate] (1). Echo <[email protected]>
gpg>
直观一点的话,就像这样:
很简单对吧!然后再使用命令迁移到物理密钥:
gpg> keytocard
Please select where to store the key:
(1) Signature key
(3) Authentication key
Your selection? 1
注意,对于 Sign 密钥类型,不仅支持设为签名密钥,也能设为认证密钥,但建议按照密钥类型进行设置,当前选择密钥为 Sign,选择【1】。
确认后输入子密钥密码,无错误信息后该子密钥迁移完成。
认证密钥的迁移方式与上述相同,可自行尝试。
检查密钥迁移情况
将密钥迁移到物理密钥后,确保你已经正确无误的将主密钥和子密钥备份并妥善存储到了一个安全的存储器里(这件事情非常重要,所以需要强调很多次)。
如果确定迁移正确,备份正常,那么现在只需要将存储在电脑上的私钥删除就可以了:
$ gpg --delete-secret-keys 5BBF0456101825EB
gpg (GnuPG) 2.2.27; Copyright (C) 2021 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
sec rsa4096/5BBF0456101825EB 2021-02-26 Echo <[email protected]>
# 确定从密钥环(就是存储密钥的本地存储库)删除这个私钥?
Delete this key from the keyring? (y/N) y
# “这是个私钥!真的要删除?”(看来这个警告真的很重要wwww)
This is a secret key! - really delete? (y/N) y
# 然后 GPG 会通过警告框,向你确定主密钥与子密钥的删除,全部选【Delete Key】即可
删除后,电脑上就只存在公钥了,这个就是用来公开的,所以不用担心。
确定一下密钥的情况:
$ gpg --list-keys
C:/Users/LamGC/AppData/Roaming/gnupg/pubring.kbx
------------------------------------------------
# 主密钥从【sec】变为【pub】,代表仅存储了公钥
pub rsa4096 2021-02-26 [SC] [expires: 2023-02-26]
240528AD2D3033D0BFAC92C35BBF0456101825EB
uid [ultimate] Echo <[email protected]>
# 而子密钥从【ssb】变成了【sub】,也代表了只有公钥
sub rsa4096 2021-02-26 [S]
sub rsa4096 2021-02-26 [E]
sub rsa4096 2021-02-26 [A]
确定了密钥在电脑上只剩下公钥后,密钥迁移就完成了!
配置物理密钥基本信息
光是迁移了密钥,还不足够,物理密钥还需要配置相关信息才能正常使用。
将密钥发布到网上
密钥既可以发布到密钥服务器上,也可以发布在一些受信任的网站上,Lam 推荐密钥除了发布到密钥服务器上,也可以发布在以下网站:
- Github:通过将 GPG 密钥导入到自己的 Github 帐号上,不仅能让 Github 显示提交签名认证信息,还能让他人下载自己的 GPG 密钥。
- 公钥下载方式:https://github.com/<帐号名>.gpg
- KeyBase:Github 旗下的去中心化身份证明服务,可在 KeyBase 通过证书链证明自己的多种社交网络帐号,也能证明 GPG 密钥和域名为自己所有,同时它还支持端到端加密聊天通讯。
- 公钥下载方式(指定公钥):https://keybase.io/<用户名>/pgp_keys.asc?fingerprint=<指定公钥的指纹>
将密钥的公钥上传到上述任意一个网站后,根据自己的情况选择一种公钥下载方式,记住 URL 后进入到下一步。
配置密钥获取 Url
(下面以我的物理密钥为例子)
$ gpg --edit-card
# 智能卡(物理密钥在 GPG 叫做智能卡)名称
Reader ...........: xxxxxx
# 固件 Id
Application ID ...: xxxxxx
# 固件类型
Application type .: OpenPGP
# 固件版本
Version ..........: xxxxxx
# 供应商
Manufacturer .....: xxxxxx
# 唯一识别码
Serial number ....: xxxxxx
# 智能卡持有者
Name of cardholder: GC Lam
# 持有者语言代码
Language prefs ...: zh
# 称呼
Salutation .......: Mr.
# 【重点】获取密钥的 URL,如果没有配置这个,将无法在新电脑上使用密钥
URL of public key : https://github.com/LamGC.gpg
# 登录数据
Login data .......: xxxxxx
# 签名用 PIN(这里可能会显示错误,具体得看智能卡的配置程序)
Signature PIN ....: not forced
# 密钥属性
Key attributes ...: rsa4096 rsa4096 rsa4096
# PIN 最长长度
Max. PIN lengths .: xxx xxx xxx
# PIN 尝试计次
# PIN 错误过多将会锁卡,此时需要用 PUK 码解锁,如果没有 PUK 码,或者 PUK 码错误过多,就会永久锁定,只能删除卡内数据重新配置,该设计仅防止爆破 PIN 使用密钥(密钥迁移到智能卡后,就没有密钥密码的事情了,用的是智能卡的 PIN 码)
# 之前输错过没问题,只要没锁卡,输入正确密码之后,计次会重置
# 该设置要通过物理密钥的配置软件进行配置
PIN retry counter : x x x
# 不明(签名计次?)
Signature counter : xxx
# 密钥信息(都公开在公钥上了)
# 签名密钥
Signature key ....: B1B8 9449 E587 7614 EA67 2F5F 6C5A E2A9 1394 1E1D
created ....: 2020-06-26 02:48:20
# 加密密钥
Encryption key....: 67BE D867 1E46 F6D7 9698 21F7 1A62 594C 3F2F CD28
created ....: 2020-06-26 02:42:51
# 认证密钥
Authentication key: 6446 7B3C F453 E1BE 0C61 06B9 DE9A FD43 A834 F474
created ....: 2020-06-26 02:52:15
General key info..:
# 下面是密钥对(就是主密钥和子密钥)的信息
sub rsa4096/6C5AE2A913941E1D 2020-06-26 LamGC <[email protected]>
sec# rsa4096/CE57746D7E5EE6EF created: 2020-06-26 expires: 2022-06-26
ssb> rsa4096/1A62594C3F2FCD28 created: 2020-06-26 expires: 2022-06-26
card-no: 0006 11023229
ssb> rsa4096/6C5AE2A913941E1D created: 2020-06-26 expires: 2022-06-26
card-no: 0006 11023229
ssb> rsa4096/DE9AFD43A834F474 created: 2020-06-26 expires: 2022-06-26
card-no: 0006 11023229
# 启用管理模式
gpg/card> admin
Admin commands are allowed
# 此处仅配置 url,因为这个是获取公钥,让物理密钥在新电脑上可用的关键配置
gpg/card> url
URL to retrieve public key: https://github.com/LamGC.gpg
# 这里要求输入智能卡管理员 PIN 码,如果没设置过,请咨询你的物理密钥供应商
# 成功后返回卡编辑命令行,如需修改其他配置,可通过【help】查询可配置信息
# 此处直接退出即可,对智能卡的编辑是直接反馈进去的。
gpg/card> quit
URL 配置好后,物理密钥的关键配置完成。
使用物理密钥
通过物理密钥加解密与通过本地密钥加密并无多大差异,区别在于密钥的导入与绑定。
仅导入公钥的情况下,GPG 并不能认定该密钥是属于你的,所以在新电脑上执行 GPG 操作前,需要先导入并绑定公钥。
导入并绑定公钥
物理密钥导入公钥的方式很简单,首先需要进入智能卡编辑模式:
$ gpg --edit-card
进入后直接执行【fetch】:
gpg/card> fetch
gpg: requesting key from 'https://github.com/LamGC.gpg'
gpg: key CE57746D7E5EE6EF: public key "LamGC <[email protected]>" imported
gpg: Total number processed: 1
gpg: imported: 1
如果公钥获取顺利,GPG 就会自动导入,并将其视为绝对信任(即认定证书是你的),后续就可以执行 GPG 操作了。
本文章采用知识共享协议4.0 CC BY-NC-SA 发布,根据协议:任何人不允许将本文章用于商业用途,转载请声明作者和来源,二次创作请保持该协议发布。
——LamGC