一直搞不太清楚HTTPS以及各种加密算法到底是什么意思,特写此博客记录学习心得
两台主机的通信,其实本质是进程的跨主机通信。
进程通常工作在用户空间,是一个具体的应用的静态代码的动态表现,是程序文件+数据的动态表示。
通信协议大体上可以分为两层:
所以通信子网主要考虑的是报文如何安全无误的送到对端 ,资源子网考虑的是 数据如何组织与交换,数据该如何表示,该如何完成双方在应用层的交换。
在Linux内核中,我们可以通过编译内核来决定是否启用哪一个协议,也就是说,通信子网的协议是在内核中通过代码实现的
而在用户空间的资源子网,比如应用层的很多协议,如http协议,FTP协议,SSH协议等,这些协议具体由我们安装的软件来实现客户端或者服务端。比如httpd可以作为http协议的服务端,浏览器作为http的客户端实现。
所以用户空间 是靠一个一个的具体的能够进行网络通信的程序来实现,这些程序启动为进程之后,可以向内核发起系统调用,调用内核的通信子网,来完成数据报文封装和网络通信。
同一主机内的进程间通信:
IPC:消息队列,共享内存(shm),信号量(semaphore)
不同主机的进程间通信:
socket(ip+port,端口是用来标识进程地址,因为一个主机上多个进程对外提供通信。port用来标识一个主机上不同进程的所拥有的地址,由于port是内核的传输层协议提供,所以进程启动时,要想通过某个port来获取一个地址用来通信,所以要向内核申请注册某端口,申请成功后,该端口被该进程独占。)
由于是两个进程互相通信,所以是一对socket进行通信。
客户端往往是随机申请一个较大的端口,而服务端则是需要进程一直启动以便随时监听请求,是一些全默认端口(如http 80端口,https 443端口等),半默认端口(mysql 3306端口等)
传统上,tcp/ip通信过程中,具体的应用层协议,绝大多数都没有自己实现加密解密功能。
所以,直接使用http协议 是无法保证传输安全,任何一个环节都可以完整的看到传输的数据实体。
所以如果一方能够将数据编码为 其他人看不懂的格式,而对端能看懂即可。
所以客户端和服务端需要提供加密解密的功能,而一般的即时通信工具如QQ,则提供了加密解密功能,但是普通的http服务 或者web服务,是没有此类功能。
后来,网景公司设计了一种可以被调用的公共模块,将这个模块(库)放在了应用层和传输层之间(算是半层),任何本身不具备加解密功能的程序如果在开发时调用了这个库,则可以使用加密功能,如果不调用,则还是使用明文传输。这个半层,则叫做SSL(安全套接字层 Secure Sockets Layer)。
SSL只是一个协议,程序需要遵循这个规范,才叫做一种实现(OpenSSL,JSSE)。
任何程序要想实现加解密功能,其实没必要自己写一遍,可以调用SSL库提供的加解密功能,所以这个库有两个功能:提供基本的加解密功能,加密解密后基于网络通信的方式完成密钥的分发。
HTTPS:HTTP协议调用了SSL库就变成了HTTPS协议。
为了达到安全目标,主要采取了两个层面的措施:
所谓加密,就是使用某种算法,将明文转换为密文
解密,就是使用同样的机制,将密文还原。
而加密算法基本都是公开的,所以加密本身不能依赖于算法,而需要依赖于第三者(密钥的复杂度)。假设在不使用密钥的情况下,无法暴力的还原密文。
而密钥如何安全的送到对端,就显得异常重要。
替代加密
置换加密
现代块加密方法:将数据切割为大小固定的块,每一块单独加密,后一块和前一块有某种关系(通常有置换单元,替换单元,异或单元,位移要素,对换要素等),后一块的加密结果与前一块做异或,将异或的结果发到对端,所以攻击者只得到其中一块的话而没有前一块,也无法还原为明文,还可以基于链式机制,对数据做了特殊运算,使得数据被窃取时必须完整拿到所有数据,才能拿到完整的密文,进而才可能还原为明文。
对称加密
公钥加密(非对称)
单向加密
密钥交换协议:DH算法,ECDH(椭圆曲线DH),公钥加密RSA算法
认证机制
访问控制
对于Linux来说,实现了上述的加密算法协议和服务的工具有两组:
OpenSSL 由三部分组成:
大多数Linux的服务,如果使用SSL功能,都是调用OpenSSL的库(某大学研究生,将知名的安全算法全部实现了一遍)。所以OpenSSL 是一个基础设施,所以必须保证安全,由一个开源组织维护。
加密和解密使用同一个密钥,但是加密解密算法可以不同。
对称加密特性:
加密解密都是同一个密钥
将原始数据分割为固定大小块,逐个加密
缺陷:
密钥过多
密钥分发难(比如使用DH算法,用于密钥交换)
密钥成对出现,分为公钥和与之配对的私钥。
公钥:从私钥中提取产生,公开给所有人,public key
私钥:给所有人留存,通过某种工具和算法创建。必须保证私密性,secret key
特点:加密解密所需要的密钥互为相反,即加密用公钥,解密则用私钥;如果加密用私钥,则解密用公钥。
用途:
缺点:
效率比对称加密慢得多,比对称加密慢3个数量级。所以真正的数据传输不使用非对称加密。
如果只是用于身份认证或者数据完整性,方法则是提取数据的特征码,也叫作数字指纹(使用MD5或者sha256),然后使用私钥签名,最终接收方可以比对这个特征码来确认数据是否完整。
工作模式:
公钥和私钥都可以用来加密和解密,所以有两种工作方式。
方式1:公钥持有者(public),使用公钥加密数据发送出去,由于只能被配对的私钥解密,可以保证这段数据只能被私钥持有者解密,其他人拿到之后是无法破解的。(所以,公钥的主要作用是加密,保证数据只能被对端识别)
方式2:私钥持有者(secret),使用私钥加密数据,发送到网络上,那么所有持有公钥的人都可以解密这段数据,但是,能用一个公钥解密,说明这段数据一定是由其配对的私钥加密的,也就是某个私钥持有者发出的。所以私钥加密的主要作用不是加密,而是标识了发送者的身份,也就是数字签名。接收方接收到数字签名以及数据包之后,先用公钥解密签名,得到特征码。然后使用指定的算法再计算一次整个数据的特征码,与解密来的特征码进行比对,比对一致后表示数据完整。所以,私钥加密不能保证保密性,只能保证完整性和身份认证。而密钥交换时,则必须服务端使用客户端的公钥,加密一个自己生成的对称加密密码,则只有对方可以解密,用于约定之后通信的密钥。
注意:以上场景是比较简单的点对点双向通信时的场景,前提是双方都提前拥有了对方的公钥(使用了非对称加密,对称加密和单向加密技术),但是互联网上那么多网站,我们不可能提前拥有所有网站的公钥。
点对点通信过程总结:(以A 发送给B 为例,提前双方都持有对方的公钥)
1、A 将 数据通过单向加密算法生成特征码(完整性验证),并用A的私钥加密这段特征码生成数字签名(身份认证)
2、A 生成一个临时对称密钥,用这个密钥加密整段数据
3、A使用B的公钥来 加密这个密钥,将加密后的内容附加在 数据段后,一起发送给B
B收到数据后,先用自己的私钥解密得到 临时的对称密钥,然后用对称密钥解密 整个数据
然后用A的 公钥 解密特征码,来验证A的身份。
最后用同样的单向加密算法来计算整个数据的特征码,验证两个特征码是否相同
风险:双方在获取对方公钥的时候(互联网环境,A 想获取B的公钥,是不容易的),A并不知道这个公钥是不是真正的对方的。黑客可以伪装成一方,与另一方通信,这也叫作中间人攻击
所以,如何可靠的获取服务器的公钥是重要的一环。
互联网用户获取所有网站的公钥不现实,但是可以获取有限个机构的公钥,也就是有限个CA的公钥比较容易(操作系统内置 可靠CA的证书,也就是根证书,根证书是CA自己签发给自己的,不需要证明,任何CA要做根CA的第一步就是self certificated)。CA给自己签署的根证书有根CA自己的专有名称,根CA的公钥以及数字签名
所以申请CA认证的证书是比较困难的,需要提交很多证件。
由CA来确定其本身和其下属CA证书的可靠。通过全球CA的信任链来进行证书的传递信任。
全球有一个根CA,下属了多个CA(全球CA有限,而且是固定的,在windows中,全球著名的CA都内置到了操作系统),Linux需要可靠手段来获取CA的证书。
常用算法:RSA,DSA(有时也叫DSS digital signature standard),ELGamal(商用)
DSA与RSA不同的地方在于,DSA只能签名,不能加密和解密,RSA则两者兼顾,比较强大。
只能加密,不能解密。其主要功能是 提取数据指纹(特征码)
特性:
功能:用于实现完整性验证
常见算法:
IKE (Internet Key Exchange)
常见实现:
公钥加密:有一定风险,因为密码始终是在网络上传送
DH算法(Deffie-Hellman):更安全。不用在网上传送,但是双方可以得到一个双方一致的密钥
通信A B 双方协商生成两个大素数p g(这两个素数在网上传递,明文交换,可被拦截)。
随后A 私下生成一个x,B私下生成y
然后A 做如下运算 (p^x)%g
将结果给B,B 进行(p^y)%g
发送给A 。在数学上,对一个大素数来说,如果大素数先求n次方,通过这个结果求对数或者求次方根是很难的
比如一个大素数 (111111^300)%g = abc
,黑客知道了p=111111,知道了g,也知道abc,但是很难求得次方数,也就是很难通过abc,g以及p来求底数x
接上,A拿到了 B发来的 ((p^y)%g)^x
,也就是A拿到了B发来的第一段结果再求x次方,B同理再求y次方,这两个结果应该一样,应等于 (p^xy)%g
,而这个结果就是密钥。因为黑客无法计算出x,y所以也就无法进行当前这一步计算密钥。
CA:证书颁发机构。保证通信双方都能够可靠地获取对方的公钥,而特定的一个可信的第三方机构。
CA不但要发证,也要有一个证书吊销列表。
以CA为核心,生成的安全架构体系叫做PKI(public key infrastucture)
数字证书格式标准:
X.509 定义证书结构以及认证协议标准:目前用v3
证书有以下内容:
1、证明身份合法:使用CA的公钥来解密数字签名,如果当前证书的颁发CA不是一个可信的CA,那么要沿着其证书的颁发顺序向上追溯,直到找到可信CA后,然后再一步步回来通过可信CA的公钥来验证子证书的有效性(其实就是通过root ca来向下传递可信依赖,证明下一级证书的公钥合法),如果解密成功,则说明当前证书是被ca授信的
2、证明证书内容完整:用同样的单向加密算法计算证书的指纹,与上一步解密而来的指纹比较,指纹相同则说明证书完整性可靠。
3、检查证书有效期
4、检查证书主体是否为当前通信目标
5、检查证书是否被吊销
1、证书是否是信任的有效证书。所谓信任:浏览器内置了信任的根证书,就是看看web服务器的证书是不是这些信任根发的或者信任根的二级证书机构颁发的。所谓有效,就是看看web服务器证书是否在有效期,是否被吊销了。
2、对方是不是上述证书的合法持有者。简单来说证明对方是否持有证书的对应私钥。验证方法两种,一种是对方签个名,我用证书验证签名;另外一种是用证书做个信封,看对方是否能解开。以上的所有验证,除了验证证书是否吊销需要和CA关联,其他都可以自己完成。验证正式是否吊销可以采用黑名单方式或者OCSP方式。黑名单就是定期从CA下载一个名单列表,里面有吊销的证书序列号,自己在本地比对一下就行。优点是效率高。缺点是不实时。OCSP是实时连接CA去验证,优点是实时,缺点是效率不高。
https可以想象为,应用程序通过调用ssl库,基于SSL 代为完成通信,SSL提供了一个隧道,应用程序通过向SSL库发请求,SSL负责调用TCP/IP协议和帧协议,完整网络通信。数字证书是SSL协议规范的一部分,想要完成SSL协议,必须事先准备证书。
TLS:Transport Layer Security 是 SSL的下一代,是国际工程师协会研发,二者基本兼容,IETF 1999年发布
SSL V3已经被谷歌浏览器废弃。TLS 1.3是最新版本,支持椭圆曲线加密算法
日常说的SSL和TLS几乎是一回事,TLS是目前事实上的标准。
TLS的分层设计:
1、最底层:基础算法原语实现,aes,rsa,md5等这些算法该怎么实现,这只是原语,并不是算法实现本身
2、再一层:各种算法实现
3、再一层:组合算法的实现的半成品
4、用各种组件拼装为成品的密码学协议软件
这样好处在于,应用程序可以在不同级别调用SSL库,可以只用算法,也可以直接使用程序
OpenSSL 是SSL最著名的开源实现
如果我们想基于HTTP协议,实现传输数据的加密,但是普通的HTTP 软件是不具备此功能的,所以必须要在软件层面调用SSL/TLS 协议的实现
SSL会话主要分三步:
其中随机数 来保证每次密钥都变化
以下是ssh handshake protocol的分解动作
p1 客户端发出加密通信请求:client_hello:
p2 服务器端回应 server_hello:
p3 客户端收到server_hello:
p4 服务端收到pre-master
openssl 子命令可分为三类:
常用命令:
使用Openssl实现对称加密(使用openssl enc 子命令):
支持算法:3DES,AES…
man enc #查看enc 子命令帮助
openssl enc -ciphername [-in filename] [-out filename] [-pass arg] [-e] [-d] [-a/-base64] [-A] [-k password] [-kfile filename] [-K key] [-iv IV] [-S salt] [-salt] [-nosalt] [-z] [-md] [-p] [-P] [-bufsize number] [-nopad] [-debug] [-none] [-engine id]
# -算法名 -in 加密file -e 表示加密 -a 表示使用base64编码,以文本文件存储,否则使用二进制文件存储
# -salt 加一些杂质
[root@lct-k8s-3 yums-kernel]# openssl enc -e -des3 -a -salt -in kubelet -out kubelet.secret
enter des-ede3-cbc encryption password:
Verifying - enter des-ede3-cbc encryption password:
[root@lct-k8s-3 yums-kernel]# cat kubelet.secret
U2FsdGVkX19SQJs3IpD9T0SsI0NKA2lHRcHt3iILVYonuuYL8Md6gVRntG5X9HQe
8CYTsQOsDrZefjJPa5tNiA==
[root@lct-k8s-3 yums-kernel]# openssl enc -e -des3 -salt -in kubelet -out kubelet.secret2
enter des-ede3-cbc encryption password:
Verifying - enter des-ede3-cbc encryption password:
[root@lct-k8s-3 yums-kernel]# cat kubelet.secret2
Salted__h1d50L;6ό?7
#将文件解密
[root@lct-k8s-3 yums-kernel]# openssl enc -d -des3 -a -salt -in kubelet.secret -out kubelet
enter des-ede3-cbc decryption password:
使用openssl 单向加密(dgst 子命令,生成摘要)
也可以使用本地命令md5sum,sha1sum,sha224sum等
md5sum kubelet
38ef423bfd88e9caa754c61249ba2a05 kubelet
openssl dgst -md5 kubelet
MD5(kubelet)= 38ef423bfd88e9caa754c61249ba2a05
生成Linux用户密码:
[root@lct-k8s-3 yums-kernel]# whatis passwd
passwd (1) - update user's authentication tokens
sslpasswd (1ssl) - compute password hashes
passwd (5) - password file
[root@lct-k8s-3 yums-kernel]# man sslpasswd
openssl passwd [-crypt] [-1] [-apr1] [-salt string] [-in file] [-stdin] [-noverify] [-quiet] [-table] {password}
#-1 表示使用md5
[root@lct-k8s-3 yums-kernel]# openssl passwd -1 -salt 12345678
Password:
$1$12345678$a4ge4d5iJ5vwvbFS88TEN0 #1是算法,12345678是salt,最后是密码
#可以看到,系统自身root的密码是使用6号算法
[root@lct-k8s-3 yums-kernel]# cat /etc/shadow
root:$6$hRNbvs6fvr.AcTwF$qGh0jj.sXSjV6E6M3cA.Ku3duKUfXH59vM0SIoDdQ1dK5v80bVTbWtQcXv9XtIHQxIPiIKMorMx71ZPuCraxr1::0:99999:7:::
# salt 可以使用随机数,随机数也可以使用openssl 生成
openssl rand [-out file] [-rand file(s)] [-base64] [-hex] num
# 一般生成的随机数为二进制编码,可以使用base64转为文本,或者使用hex转为可读的16进制数字文本
[root@lct-k8s-3 yums-kernel]# openssl rand -base64 10
Q2T4S/bd3Fgw2A==
[root@lct-k8s-3 yums-kernel]# openssl rand -hex 10
80369409c8ea0b58251a #确实是10个字节的随机数,相当于20位文本的随机数
openssl passwd -1 -salt $(openssl rand -hex 4) #使用命令替换,输出4个字节随机数为salt,最终文本占位8位随机数
公钥加密
功能:三种功能支持的算法不同
工具:openssl,gpg,rsautl等
生成公钥私钥对:
openssl genrsa -out /root/mykey.private 1024 #但是这个文件是都可读的,更加安全的方式如下
(umask 077;openssl genrsa -out /root/mykey.private 1024) # () 代表启动一个子shell执行,umask代表666-077=600 也就是最终权限为600,这样umask只影响子shell不影响主shell进程,主shell umask默认为0022
#从私钥中提取公钥
openssl rsa -in /root/mykey.private -pubout
公钥加密,对称加密内部依赖随机数:
/dev/random #从熵池中返回随机数,如果随机数用尽,则阻塞进程,等待更多随机数
/dev/urandom #从熵池中返回随机数,如果随机数用尽,则利用软件生成伪随机数,不会阻塞进程,但是伪随机数不安全
熵池就是内核在内存中维护的一个空间,存储了大量随机数,刚启动时熵池为空
熵池的随机数来源: 各种IO中断的时间间隔(当做一个 真正的随机噪声源)
使用工具:
OpenSSL OpenCA
openssl 建立CA时 有配置文件
/etc/pki/tls/openssl.cnf 当使用openssl 配置CA时,可以使用这个配置文件进行一定的配置
如上图,其实openssl就实现了一个简要的PKI体系(默认一个系统就存一个根CA)
这个配置文件中定义了openssl ca和req等子命令的默认参数,比如工作目录,已经颁发的证书存放地,吊销列表存放地,存取库文件(当前CA维护了哪些证书,每个证书对当前CA都有编号),序列号存放文件serial(依次递增?),CA自己的私钥和自签证书也会存在dir下面,证书默认有效期365天,CRL默认有效期30天(30天更新一次CRL列表),默认使用sha256进行消息摘要
构建私有CA配置:
在 确定要作为CA的配置服务器上生成自签证书,并为CA提供工作目录和文件
主要分为 生成私钥和生成自签证书。
因为CA不参与通信过程,所以不需要提供服务,只提供证书签发过程就行,所以openssl 只提供命令行工具就行了
#准备CA私钥
#配置文件中指定了ca key的路径和名字,这里需要保持一致,长度为4096
(umask 077; openssl genrsa -out /etc/pki/CA/private/cakey.pem 4096)
#做req 证书签署请求时 -x509 表示自签,只在生成自签CA时使用 -new 表示生成新的证书签署请求
[root@lct-k8s-3 private]# openssl req -x509 -new -key cakey.pem -out ./cacert.pem -days 3650
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:SD
Locality Name (eg, city) [Default City]:QD
Organization Name (eg, company) [Default Company Ltd]:Sugon
Organizational Unit Name (eg, section) []:dev
Common Name (eg, your name or your server's hostname) []:EDU-CA #CA 主体的名字,也就是其下属证书的issuer的名字
Email Address []:[email protected]
# 为CA提供所需的目录
mkdir -pv /etc/pki/CA/{certs,crl,newcerts}
touch /etc/pki/CA/{serial,index.txt}
echo "01" > /etc/pki/CA/serial
步骤:
1、为server 生成私钥
#不要在CA服务器的 /etc/pki/CA下操作
(umask 077;openssl genrsa -out tomcat.key 2048)
2、生成证书签发请求
[root@lct-k8s-1 tomcat-ssl]# openssl req -new -key tomcat.key -out ./tomcat.csr -days 3650
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:SD
Locality Name (eg, city) [Default City]:QD
Organization Name (eg, company) [Default Company Ltd]:Sugon
Organizational Unit Name (eg, section) []:dev
Common Name (eg, your name or your server's hostname) []:lct-k8s-1.com
Email Address []:[email protected]
3、将csr 拷贝到CA主机(公网上申请CA,可能是CA机构来拷贝csr文件)
scp tomcat.csr root@lct-k8s-3:/root
4、在CA主机签署证书
openssl ca -in /root/tomcat.csr -out /etc/pki/CA/certs/tomcat.crt -days 3650
[外链图片转存失败(img-rU7336mk-1566894616800)(F:\cloudPoint\学习随笔\mage-https\1554881902796.png)]
serial
文件中应该保存的是下一个签发证书的序列号
index.txt
中保存了签发证书的信息,V表示已经签发的,01表示当前证书的序列号
5、最后把tomcat.crt 发给server
openssl x509 -in tomcat.crt -noout -text #查看证书完整信息
openssl x509 -in tomcat.crt -noout -serial -subject #只看差序列号和 subject
[外链图片转存失败(img-PazBJ3F7-1566894616800)(F:\cloudPoint\学习随笔\mage-https\1554883902560.png)]
1、在被吊销的证书所在的主机上执行,获取证书的serial号码
openssl x509 -in xxx.crt -noout -serial -subject
2、CA主机吊销证书,根据客户端提交的serial和subject信息,对比本机数据库index.txt是否一致,如果一致则可以吊销。
CA主机上/etc/pki/CA/newcerts/
目录下保存着签发的证书文件,使用序列号.pem
来命名的,如01.pem
,p2.pem
openssl ca -revoke /etc/pki/CA/newcerts/{serial}.pem #吊销这个序列号的pem证书
3、生成吊销证书的吊销编号(只在当前CA主机第一次吊销证书时执行)
echo 01 > /etc/pki/CA/crlnumber #吊销序列号
4、更新证书吊销列表
openssl ca -gencrl -out {ca_name}.crl
一般私建CA 不会吊销证书,而是直接更新服务端证书即可。