安全加密基础—基本概念、keytool、openssl
目录
前言
(1)本文不涉及源码、底层。只是讲解大概的密码演变过程和基本概念。能让接触到相关名词的人知道这些名词是干嘛的,为什么要有它。专业人士可以当作概念梳理,非专业人士可以当作科普。
(2)本文你将了解到的概念:明文通信、无密钥密文通信、对称加密、非对称加密、信息摘要、数字签名、CA与数字证书、https、常用的密钥扩展名。
(3)本文应用实践有:用java的Keytool生成密钥,keystore格式转pem,openssl生成私钥和证书。
一、概念
-
明文通信
不是私密的信息可以采用明文。
-
无密钥密文通信
对于私密数据,如何远距离传输并且在被其他人看到的情况下,还能保证数据的私密性?
最简单想到的就是通信双方约定好一种加密方式,对要说的话进行加解密,如图:
发送方将要发送的信息进行enc加密,接收方通过dec解密。
比如我要说的话是:123。我们提前约定好我会将我发的数字每位+1,这样我发出去的就是234。你收到我发的信息后,将每一位减1就能得到真实的信息。
为了让偷听者不能从密文中恢复出明文,必须保证算法不被外人所知。如果别人知道了你的加密算法,就能轻而易举破解。这样做有很多弊端,比如:
-
- 如果算法泄露,则必须设计新的算法,而设计加密算法并不容易。如果同时有很多用户的使用这套算法,换一套算法就更麻烦。
- 加密解密的人总要知道算法,所以算法的保密很难做到。
- 算法被设计出来,肯定需要很多人去测试它的安全性和正确性,这样知道这个算法的人就会很多,无法保证算法不被泄露。
虽然有这些弊端,但事实上人们历来都是这么干的。
直到Auguste Kerckhoffs 在 1883年提出了一条原则:加密算法不应该是秘密,即便落入敌人之手,也不会带来不便 。这条原则被现代密码学广泛接受,并被称为柯克霍夫原则(Kerckhoff’s Principle)。后来,信息论的奠基者香农把这条原则形象地表述为:“敌人知道系统。”通俗的说就是算法不应该是秘密,但是必须有一个秘密信息参与到算法中,才能保证信息的安全性。这个秘密信息就是密钥。
-
对称加密
通信双方约定好一个密钥,然后用这个密钥进行加密算法。比如我要说的是:123。加密算法会让我发的数字每位+x,这个x具体是多少就是我们通信双方要约定好的密钥了。这样就实现了加密算法是公开的,但是只要密钥不被盗取,信息就不会泄漏。而且如果密钥泄漏,我们可以更改密钥即可。
常见的对称加密有: DES、2DES和3DES,AES。
但是这样又出现了一个问题,就是通信双方如何约定密钥?在网络上,发的任何信息都有可能被劫持,所以对称加密的问题就是通信双发如何能拿到密钥。
-
非对称加密
对称加密的问题在于互联网环境下没法传递密钥,因为密钥有可能泄漏。
非对称加密有两个密钥,一个公钥一个私钥。私钥只有消息接收者自己持有。公钥加密后只有私钥能解密,私钥加密后只有公钥能解密。
用公钥加密后的信息只有私钥的持有者能解密,如果双方想要用非对称加密进行通信的话,只要双方各持有一个公钥和一个私钥。然后各自用对方的公钥加密要发的数据,就能不被其他人破解。
或者只要A有一个公钥和一个私钥,然后A约定好一个对称加密的密钥,将这个密钥用公钥加密然后发给B,B收到后双方就可以用对称加密来通信。
总结上面这段就是,如果用非对称加密来通信有两种方式:
1一种是通信双方各持有一个公钥和私钥,然后用各自的非对称加密算法来通信。
2另一种是用非对称加密算法来约定对称加密算法中的密钥。之后用对称加密算法来通信。
常见的非对称加密算法有:DSA,ECC,RSA,DH
但是上面存在一个问题,通信双方都是私钥的持有者,来接收公钥加密后的信息,那么怎么保证自己发出的公钥加密信息不会被他人篡改和替换?
-
数字签名
要想解决自己发出的公钥信息不会被篡改和替换,只要在信息上留下别人无法修改的记号就可以了。这就是数字签名。只要信息发送者在信息中加入用自己私钥加密的一段信息,消息接收者用消息发送者的公钥能解开那段信息,就能保证信息没有被别人替换。那么加密一段什么信息比较好呢?这就要引出消息摘要算法。
发送方:密文(数字签名) = RSA(信息摘要,发送方私钥)
接收方:明文(信息摘要) = RSA(信息摘要,发送方公钥)
-
消息摘要(MD5)
消息摘要不是加密算法,他是不可逆的。它是对一段信息进行处理然后得到一串定长的消息摘要,同样的信息内容得到的消息摘要是相同的,不同的信息内容得到的消息摘要是不同的。所以一般用消息摘要可以判断信息内容是否被修改,而且因为不管消息内容有多少,得到的消息摘要都是一样长度的。
常见的信息摘要有:MD5、SHA1。
特性:定长,不可逆,相同明文加密后的摘要也相同且恒相同。一般是MD5串
用途:判断信息内容是否被篡改
所以现在是不是能实现完美的加密通信了呢?双发各持有一个公钥和一个私钥。要发消息时,先对消息内容进行消息摘要。在对消息内容用对方的公钥加密。最后再用自己的私钥对消息摘要加密形成数字签名。这样保证接收方收到内容时,内容不会被泄漏伪造和替换。
例如:
发送方(发送方私钥,接收方公钥)
信息明文 = a=1&b=2&c=999.19&d=图书
消息摘要 = md5(a=1&c=999.19) = D08751F4CD7C0376127C19D84220A395
数字签名加密 = RSA( ${消息摘要}, 发送方私钥)【加密】
信息密文:RSA(“a=1&b=2&c=999.19&d=图书”,接收方公钥)【加密】
发送内容:msg=信息密文&sign=数字签名
接收方(接收方私钥,发送方公钥)
接收内容:msg=信息密文&sign=数字签名
信息明文:RSA(${信息密文},接收方私钥)【解密】
a=1&b=2&c=999.19&d=图书
数字签名解密:RSA(${数字签名},发送方公钥)【解密】
摘要 = D08751F4CD7C0376127C19D84220A395
自制摘要:md5(a=1&c=999.19) = D08751F4CD7C0376127C19D84220A395
验证摘要: ${摘要} == ${自制摘要} ===> success
-
CA数字证书(解决公钥分发的问题)
但是回到非对称加密的起点,公钥怎么公开又是个问题。如果你虽然对外公开了你的公钥。但是你把公钥发给别人的时候被别人替换成他的公钥怎么办?你可能会说,用数字签名。你刚刚才说数字签名和保证内容不被替换和修改。但是数字签名的前提是你要通信的那个人已经拿到了你的公钥,然后你用私钥对消息签名,他才能用你的公钥来判断是不是你的私钥签的名。现在的问题是他怎么能正确的拿到你的公钥,如果你的公钥被别人替换,那么你的后续的数字签名就没有用了。
那么如下图。既然在一开始传递公钥时,小红没法自己签名。那么找一个大家的认可的第三方(CA),把小红的个人信息和公钥告诉第三方,第三方对这些内容进行数字签名(相当于盖了一个公章),形成一个数字证书。然后小明用第三方的公钥如果能解开数字签名(鉴别公章是不是假的),就说明里面的内容是得到权威认证的。而且里面的内容有你的公钥和个人信息,这样小明就得到了小红的公钥,而且这个公钥不可能是被别人替换或者修改过的。
公钥发送方:
公钥=D08751F4CD7C0376127C19D84220A395
个人信息:公司名称,住址,电话
CA机构:
数字签名加密 = RSA( ${公钥}+${个人信息}, CA私钥)【加密】
数字签名: U2FsdGVkX1/BtnM+Wi3cLDD54LjYACXz9M1+nyRLf6rADT5bmS75RdenbUJE80BD
GSW8cz+hZUC96hyc1DEkxi9r0KdmbNrreFbBhAkEGNU=
形成所谓证书,其实就是个数字签名,本质是一串加密字符串
公钥接收方:
接收数字签名:U2FsdGVkX1/BtnM+Wi3cLDD54LjYACXz9M1+nyRLf6rADT5bmS75RdenbUJE80BD
GSW8cz+hZUC96hyc1DEkxi9r0KdmbNrreFbBhAkEGNU=
数字签名解密:RSA( ${公钥}+${个人信息}, CA公钥)【解密】
公钥=D08751F4CD7C0376127C19D84220A395
个人信息:公司名称,住址,电话
成功获得发送方公钥
CA公钥怎么来的?硬件机器内置的。
现在问题来了,上图的第四步,小明在验证小红的证书时。需要验证数字签名确实是权威CA签的,需要用CA的公钥才能解开数字签名。那么小明怎么得到CA的公钥呢?
其实,信任是有起点的。
CA 不仅为他人生成证书,也生成自己的证书,CA 为自己生成的证书里包含了CA的公钥。CA 的证书在电脑、手机等设备出场的时候就会预置在系统里、浏览器里。因此,当小明验证小红的证书时,会在系统里寻找能够解开小红证书的CA 公钥,若是找到则说明小明证书的颁发机构是可信任的,既然信任了该证书,那么从证书里取出的公钥,小明也认可是小红的。如果在本机中没找到对应的公钥,那么小明就会认为这个证书是不可信的,就会提示用户是否要继续访问。
-
HTTPS
https是应用层协议,它会结合传输层和应用层之前的ssl一起使用,实现加密传输。(ssh也是加密协议,通常用在客户端远程访问)。https大概流程如下:
1客户端通过发送Client Hello报文开始SSL通信。报文中包含客户端支持的SSL的指定版本、加密组件(Cipher Suite)列表(所使用的加密算法及密钥长度等)。
注意:客户端还会附加一个随机数,这里记为A。
2服务器可进行SSL通信时,会以Server Hello报文作为应答。和客户端一样,在报文中包含SSL版本以及加密组件。服务器的加密组件内容是从接收到的客户端加密组件内筛选出来的。
注意:这里服务器同样会附加一个随机数,发给客户端,这里记为B。
3之后服务器发送Certificate报文。报文中包含公开密钥证书。(具体的数字签名请看证书一节)
4最后服务器发送Server Hello Done报文通知客户端,最初阶段的SSL握手协商部分结束。
5 SSL第一次握手结束后,客户端会对服务器发过来的证书进行验证,如果验证成功,解密取出证书中的公钥。(具体查看证书一节)
接着,客户端以Client Key Exchange报文作为回应。报文中包含通信加密中使用的一种被称为Pre-master secret的随机密码串。该报文使用从证书中解密获得的公钥进行加密(其实就是服务器的公钥)。
6客户端继续发送Change Cipher Spec报文。用于告知服务端,客户端已经切换到之前协商好的加密套件(Cipher Suite)的状态,准备使用之前协商好的加密套件加密数据并传输了。
7客户端发送Finished报文。该报文包含连接至今全部报文的整体校验值(也就是HASH值),用来供服务器校验。
8服务器接收到客户端的请求之后,使用私钥解密报文,把Pre-master secret取出来。接着,服务器同样发送Change Cipher Spec报文。
9服务器同样发送Finished报文,用来供客户端校验。
10服务器和客户端的Finished报文交换完毕之后,SSL连接就算建立完成。当然,通信会受到SSL的保护。从此处开始进行应用层协议的通信,即发送HTTP请求。
11应用层协议通信,即发送HTTP响应。
12最后由客户端断开连接。断开连接时,发送close_notify报文。上图做了一些省略,这步之后再发送TCP FIN报文来关闭与TCP的通信。
-
相关文件扩展名
私钥、公钥、证书这三个相关的文件一般有以下扩展名:
-
常用后缀名
证书(Certificate) - *.cer *.crt
私钥(Private Key) - *.key
证书签名请求(Certificate signing request) - *.csr
证书吊销列表(Certificate Revocation List) - .crl
X.509是常见通用的证书格式。所有的证书都符合为PublicKeyInfrastructure(PKI)制定的 ITU-T X509 国际标准。
pem(base64)和der(二进制)是证书的编码方式,以.pem结尾的文件既可以是证书也可以是私钥。(X.509是证书的内容格式,pem是编码方式)
.jks和.p12是是一种容器格式,可以同时保存多个证书和私钥。
注意,后缀名只是一种命名规范,只是让使用者能知道这个文件代表这什么。
-
普通的pem文件内容
-----BEGIN PRIVATE KEY-----
ksldfjs
ksdjflsdf
...
....
sfjlskjdfljf
ksdfjlsdkf
-----END PRIVATE KEY-----
二、keytool
在java中,jdk提供了管理公钥、私钥和证书的工具keytool。Keytool将密钥(key)和证书(certificates)存在一个称为keystore的文件中。也就是说,在keystore里只包含两种数据:
- 密钥实体:如果采用非对称加密形式,则包含私钥和配对公钥,否则只包括密钥。
- 可信任的证书实体:只包含公钥
keystore文件俗称密钥库,可以保存多个密钥和证书,密钥库的默认格式为jks,命名一般叫xxx.keystore。
keystore(密钥库)是一个用于存储密钥对、数字证书和可信证书的安全文件。它通常以文件的形式存在,受密码保护。keystore 可以包含用于身份验证、数字签名、加密等目的的密钥。
在 Java 中,keystore通常用于安全通信、数字签名和加密等场景。它提供了一种安全的方式来存储和管理密钥和证书,以确保它们不会被未经授权的人访问或修改。keystore通常以文件的形式存储在文件系统中,也可以存储在数据库或其他存储介质中。常见的 keystore类型包括 JKS(Java Key Store)、PKCS12(Public Key Cryptography Standards 12)和 BKS(Bouncy Castle Key Store)等。
JKS - 即Java Key Store,这是Java的专利 和 OpenSSL作用差不多。利用Java的一个叫"keytool"的工具,可以生成密钥对。
2.1常用的命令如下
常用命令:https://blog.csdn.net/jobjava/article/details/135343775
2.1.1生成密钥库并创建第一个条目(密钥)
秘钥需要存储在秘钥库中,秘钥库可以理解为一个存储了一个或多个秘钥的文件。一个秘钥库可以存储多个密钥对,每个秘钥对都需要给它们取一个别名。
因为不存储任何条目的秘钥库是没有意义的,所以我们在生成秘钥库的时候需要指定一个条目,如果不指定,默认是的条目名称是mykey。
我们在 E:\keystore 目录下生成一个文件名为test.keystore的秘钥库,因为这个文件是第一次生成,必须同时生成一个条目,我决定将该秘钥库存储的第一个秘钥对的条目取名为mytest。命令是:
keytool -genkeypair -keystore "E:\keystore\test.keystore" -alias mytest -keyalg RSA -validity 365 -keysize 1024
- -genkeypair : 表示生成自签名密钥对,可简写-genkey
- -keystore:每个 keytool 命令都有一个 -keystore 选项,用于指定 keytool 管理的密钥仓库的永久密钥仓库文件名称及其位置。如果不指定 -keystore 选项,则缺省密钥仓库将是宿主目录中(由系统属性的"user.home"决定)名为 .keystore 的文件。如果该文件并不存在,则它将被创建。密钥库扩展名可以是.store或.jks。
- -alias :别名,指定密钥条目的别名,该别名是公开的。使用者通过该名称来操作密钥对。(因为一个密钥库会有多个密钥对)
- -keyalg :指定密钥生成算法RSA,默认DSA。
- DSA只能用来签名(签名是私钥加密,公钥解密)
- RSA既可以用来签名也能用来明文加密。(明文加密是公钥加密,私钥解密)
- -keysize: 密钥位大小(字节)1024
- -validity:有效天数,365表示1年。
其他参数
- -groupname <name> 组名,例如:an Elliptic Curve name.
- -sigalg <sigalg> 签名算法名称(RSA\DSA)
- -storetype <storetype> 密钥库类型(PKCS12、jks,默认jks)
- -destalias <destalias> 目标别名(和上面的别名一样即可)
- -dname <dname> 唯一判别名(如果没配置,输入命令时会提示输入相关信息,随便填就好了),例如:-dname "CN=(名字与姓氏), OU=(组织单位名称), O=(组织名称), L=(城市或区域名称), ST=(州或省份名称), C=(单位的两字母国家代码)"
- -startdate <startdate> 证书有效期开始日期/时间(使用默认的即可)
- -validity <valDays> 有效天数
- -keypass <arg> 密钥口令(别名密码)
- -storepass <arg> 密钥库口令
- -v 详细输出
5、注意事项
1、如果指定的密钥库是第一次创建,则必须在创建时初始化一个条目。
2、密钥库的密码至少必须6个字符,可以是纯数字或者字母或者数字和字母的组合等。
3、"名字与姓氏"应该是输入域名,而不是我们的个人姓名,其他的可以不填。
执行完上述命令后,在操作系统的指定目录E:\keystore下生成了一个"test.keystore"的文件。
备注:需要设置密钥库口令和密钥口令,实现双保险的目的。
2.1.2生成秘钥(对称加密的秘钥)
keytool -genseckey -alias mytest -keystore "E:\keystore\test.keystore" -keyalg DES
- -genseckey : 生成密钥
- -alias :别名,指定密钥条目的别名,该别名是公开的。使用者通过该名称来操作密钥对。(因为一个密钥库会有多个密钥对)
- -keystore:每个 keytool 命令都有一个 -keystore 选项,用于指定 keytool 管理的密钥仓库的永久密钥仓库文件名称及其位置。如果不指定 -keystore 选项,则缺省密钥仓库将是宿主目录中(由系统属性的"user.home"决定)名为 .keystore 的文件。如果该文件并不存在,则它将被创建。密钥库扩展名可以是.store或.jks。
- -keyalg :指定密钥生成算法AES/DES,默认AES。
- -keysize: 密钥位大小(字节),取值:128,192或256
其他参数
- -storetype <storetype> 密钥库类型(PKCS12、jks,默认jks)
- -storepass <arg> 密钥库口令
- -v 详细输出
2.1.3根据证书请求生成证书
keytool -gencert
用于向CA授信机构发请求,没研究过
2.1.4从密钥库中导出crt证书
keytool -export -keystore "E:\keystore\test.keystore" -alias privatekeys -file certfile.cer
- -export : 表示导出
- -keystore:每个 keytool 命令都有一个 -keystore 选项,用于指定 keytool 管理的密钥仓库的永久密钥仓库文件名称及其位置。如果不指定 -keystore 选项,则缺省密钥仓库将是宿主目录中(由系统属性的"user.home"决定)名为 .keystore 的文件。如果该文件并不存在,则它将被创建。密钥库扩展名可以是.store或.jks。
- -alias :别名,指定密钥条目的别名,该别名是公开的。使用者通过该名称来操作密钥对。(因为一个密钥库会有多个密钥对)
- -file :公钥文件存放路径
2.1.5将证书导入到公钥库
keytool -import -keystore "E:\keystore\test.keystore" -alias publiccert -file certfile.cer
- -import : 表示导入
- -keystore:每个 keytool 命令都有一个 -keystore 选项,用于指定 keytool 管理的密钥仓库的永久密钥仓库文件名称及其位置。如果不指定 -keystore 选项,则缺省密钥仓库将是宿主目录中(由系统属性的"user.home"决定)名为 .keystore 的文件。如果该文件并不存在,则它将被创建。密钥库扩展名可以是.store或.jks。
- -alias :别名,指定密钥条目的别名,该别名是公开的。使用者通过该名称来操作密钥对。(因为一个密钥库会有多个密钥对)
- -file :公钥文件存放路径
我们通常把第一步生成的称为私钥库,其中包含私钥和证书。这里生成的叫公钥库,其中只含有证书。
私钥库是服务端使用,公钥库是给客户端使用。
2.1.6查看密钥库信息
keytool -list -v -keystore "E:\keystore\test.keystore"
- -list:条目显示
- -v:详细输出
- -keystore:每个 keytool 命令都有一个 -keystore 选项,用于指定 keytool 管理的密钥仓库的永久密钥仓库文件名称及其位置。如果不指定 -keystore 选项,则缺省密钥仓库将是宿主目录中(由系统属性的"user.home"决定)名为 .keystore 的文件。如果该文件并不存在,则它将被创建。密钥库扩展名可以是.store或.jks。
- -rfc 以 RFC 样式输出
- -alias 要处理的条目的别名
- -storepass 密钥库口令
2.1.7更改条目的密码口令
keytool -keypasswd -v -alias signtest2 -keypass a123456 -new 123456 -keystore ./signtest3.keystore -storepass a123456
- -alias 要处理的条目的别名
- -keypass 密钥口令(别名alias原密码)
- -new 新口令
- -keystore 密钥库名称
- -storepass 密钥库口令
- -v 详细输出
2.1.8更改密码库的存储口令
keytool -storepasswd -v -keystore ./signtest3.keystore -storepass a123456 -new 123456
- -new 新口令
- -keystore 密钥库名称
- -storepass 密钥库口令
- -v 详细输出
2.1.9 将jks转为p12文件
keytool -importkeystore -srckeystore x:\xxx\xxx.jks(jks文件路径) -srcstoretype JKS -deststoretype PKCS12 -destkeystore x:\xxx\xxx.p12(后缀改为p12)
三、openssl
3.1格式转换
通常keytool生成的jks格式的密钥库,只用在java相关的程序中使用,例如tomcat。如果要在其他应用中使用,就需要进行格式转换。例如nginx中使用的就是pem格式的证书和私钥,其中私钥扩展名使用.key,公钥扩展名使用.crt。当然你都取名为pem也是没问题的。
3.1.1 jks格式的密钥库转为pem格式,需要以下步骤
3.1.1.1 keytool将jsk转为p12文件
3.1.1.2 p12转pem
openssl pkcs12 -nodes -in csii.p12 -out csii.pem
因为p12文件可能包含多个私钥和证书,所以生成的pem文件中也可能包含多个私钥和证书。我们打开转换后的pem文件,将其中-----BEGIN CERTIFICATE-----和-----END CERTIFICATE-----部分的内容复制到cert.pem中,把-----BEGIN PRIVATE KEY-----和-----END PRIVATE KEY-----部分的内容复制到pri_key.pem中。这样我们就得到了私钥文件pri_key.pem和证书文件cert.pem。
3.1.2从证书文件得到公钥,可以使用:
openssl x509 -in cert.pem -pubkey -out pb.pem
这样就得到了公钥文件pb.pem。
3.2 openssl生成私钥和证书
如果你想直接生成私钥和证书,而不是通过keytool生成的密钥库进行格式转换,可以使用如下方法:
openssl genrsa -des3 -out server.key 2048 #生成私钥文件server.key
openssl req -new -key server.key -out server.csr #生成证书请求文件server.csr
cp server.key server.key.org # 备份一下 server.key
openssl rsa -in server.key.org -out server.key #生成无秘钥 server.key
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt #通过私钥和证书请求文件生成证书
四 其他计算机语言类似安全加密的工具简介
是的,其他计算机语言和平台也有类似的安全存储机制,尽管具体实现和术语可能有所不同。以下是一些示例:
Microsoft Cryptographic API (CAPI): Windows 操作系统使用 CAPI 来管理密钥和证书。它使用 PFX 格式的文件来存储密钥和证书。
Keychain: macOS 和 iOS 操作系统使用 Keychain 来存储和管理密钥、证书和其他安全信息。
Android Keystore System: Android 操作系统有一个 Android Keystore System,用于存储密钥和证书,支持硬件支持的密钥存储。
NSS (Network Security Services): NSS 是一组用于支持安全网络通信的库,包括 Mozilla Firefox 和 Thunderbird 使用的安全模块。它支持存储证书和密钥。
OpenSSL: OpenSSL 是一个开源的密码库,可以用于创建和管理数字证书、私钥和其他安全元素。它通常使用 PEM 或 DER 格式来存储证书和密钥。
Python:Python 有一个名为 PyCrypto 的第三方库,它提供了一个类似于 Java Keystore 的功能,用于存储和管理密钥和证书。PyCrypto 库提供了一系列加密算法和安全功能,例如对称加密、非对称加密、数字签名和证书验证等。
Go:Go 有一个名为 crypto/tls 的标准库,它提供了一个类似于 Java Keystore 的功能,用于存储和管理 TLS 证书。crypto/tls 库提供了一系列加密算法和安全功能,例如对称加密、非对称加密、数字签名和证书验证等。
C++:C++ 有一个名为 OpenSSL 的第三方库,它提供了一个类似于 Java Keystore 的功能,用于存储和管理密钥和证书。OpenSSL 库提供了一系列加密算法和安全功能,例如对称加密、非对称加密、数字签名和证书验证等。
这些工具和框架都提供了安全存储和管理密钥、证书等敏感信息的功能,用于保护加密、身份验证和数字签名等安全任务。