OpenSSL学习笔记

OpenSSL 是一个开源软件库,提供了一组用于处理安全通信的工具和库函数,它可以用于多种不同的用途。

对称加密AES

AES ECB

ECB是不需要初始化向量,每个数据块单独进行加密,没有依赖关系,相同的明文块将始终加密为相同的密文块,无法隐藏明文块的模式。

随机生成256比特的密钥:

1
openssl rand -out ecb.key 32

在vscode中可以用hexdump插件查看:

1
ee11f36abfa2835cea739da5b954e7d031ee90647c4c2c0ac9c760ccef623ba7

加密:

1.txt的内容:

1
test

用256位密钥进行ecb模式的aes加密。

1
openssl enc -e -aes-256-ecb -in 1.txt -out enc -pass file:ecb.key

结果为:

1
53616c7465645f5f80e6464003d40b8142a8df7a239f9d0ee94a28c9a93118c9

解密:

1
openssl enc -d -aes-256-ecb -in enc -out 2.txt -pass file:ecb.key

解密成功:

2.txt

前缀Salted

但是这样openssl加密的密文前缀都有”Salted”,因为enc加密默认是加盐值的。在密码学中,使用盐值(salt)进行加密是一种增强密码安全性的技术。盐值是一个随机生成的固定长度的字符串,它与密码结合使用,以产生密文。但这里不知道为什么前缀都是’Salted’,容易被识别出是加密以后的密文。

openssl enc -help

-nosalt可以去掉这个前缀。

1
openssl enc -e -aes-256-ecb -nosalt  -in 2.txt -out enc4 -pass file:ecb.key

enc4没有前缀

前缀出现的原因

openssl默认加盐,但没有指定盐值,所以盐值都是随机生成的,所以会把盐也写到密文文件中。-p命令可以打印本次加密的向量和密钥值。

1
-p                  Print the iv/key

测试一下:

1
openssl enc -e -aes-256-ecb -in 1.txt -out out2 -pass file:ecb.key -p

控制台输出

再打开out2文件可以发现,前缀Salted__后面紧跟的就是本次随机生成的盐值。

out2

python验证加密结果

写一个简单的AES加密python脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from Crypto.Cipher import AES
def pkcs7_padding(data, block_size):
padding_length = block_size - (len(data) % block_size)
padding_value = chr(padding_length).encode()
padded_data = data + padding_value * padding_length
return padded_data
if __name__=="__main__":
data="test"
raw_key="073F0AC5E53DB74FE534DD7141B9317F633C5DED35392713F4DA7809E73267F0"
enc_key=bytes.fromhex(raw_key)
aes_model=AES.new(enc_key,AES.MODE_ECB)
data=data.encode()
data=pkcs7_padding(data,16)
enc_data=aes_model.encrypt(data)
for i in enc_data:
print(hex(i)[2:],end="")
print()

运行结果:

python output

和上图对比可知,加密结果和openssl一致。

指定盐值

-S来指定盐值。

指定盐

这样既保证了安全性,生成的文件中又仅有密文:

out3

AES CBC

实际生产环境中一般用更安全的CBC模式。CBC(Cipher Block Chaining)模式使用前一个密文块与当前明文块进行异或运算,然后再加密,需要一个初始化向量(IV),用于第一个明文块的加密。CBC模式具有扩散效应,相同的明文块在不同的加密过程中会产生不同的密文块。此外,CBC模式支持并行加密解密操作。

前文是根据文件生成密钥的,如果不指定file,也可以根据控制台输入来生成密钥和向量:

1
openssl enc -e -aes-256-cbc -in 1.txt -out cbc1 -p -pbkdf2

CBC

-pbkdf2是选用更推荐的密钥生成函数。

解密:

1
openssl enc -d -aes-256-cbc -in cbc1 -p -pbkdf2

解密结果

非对称加密RSA

RSA(Rivest-Shamir-Adleman)是一种非对称加密算法,广泛用于数据加密、数字签名和密钥交换等安全应用中。RSA 算法的基本原理是基于数论中的大数分解难题。它使用两个密钥,一个是公钥用于加密数据,另一个是私钥用于解密数据。公钥可以公开,而私钥必须保密。

以下是 RSA 算法的主要步骤:

  1. 密钥生成:

    • 选择两个大素数 p 和 q,计算它们的乘积 n = p * q。
    • 计算欧拉函数值 φ(n) = (p - 1) * (q - 1)。
    • 选择一个小于 φ(n) 的整数 e,作为公钥指数。
    • 计算与 e 互素的整数 d,作为私钥指数。即满足 (e * d) mod φ(n) = 1。
    • 公钥是 (e, n),私钥是 (d, n)。
  2. 加密:

    • 将明文消息转换为整数 m,使得 0 ≤ m < n。
    • 使用公钥 (e, n) 对 m 进行加密,得到密文 c = m^e mod n。
  3. 解密:

    • 使用私钥 (d, n) 对密文 c 进行解密,得到明文消息 m = c^d mod n。

RSA 算法的安全性基于大整数分解的困难性。要破解 RSA,需要通过分解 n 来计算出 p 和 q,这在当前技术下对于足够大的素数 p 和 q 是非常困难的。

密钥生成

生成私钥:

1
openssl genrsa -out pri_key.pem 1024

1024是私钥长度。

大概是base64编码存储的

根据私钥生成公钥:

1
openssl rsa -in pri_key.pem -pubout -out pub_key.pem

公钥文件

加密解密

公钥加密:

1
openssl pkeyutl -encrypt -pubin -inkey pub_key.pem -in 1.txt -out rsa1

私钥解密:

1
openssl pkeyutl -decrypt  -inkey pri_key.pem -in rsa1

结果如下:

结果

python验证

这里发生了小插曲,突然发现rsa加密每次结果都是不一样的,这是因为加密过程中使用了随机生成的填充数据。参考这篇文章:

https://blog.csdn.net/guyongqiangx/article/details/74930951

1
openssl rsautl -decrypt  -raw -inkey pri_key.pem -in rsa1 -out out1

可以看到中间填充的数据应该是随机的:

-raw保留原始数据

虽然每次生成的密文不一样,但只要私钥正确,都可以正常解密出来。

python rsa库

rsa库的常用方法:

1
2
3
4
5
6
7
8
9
10
import rsa
plaintext = "test" # 明文
with open("pub_key.pem", 'r') as x:
public_key = rsa.PublicKey.load_pkcs1_openssl_pem(x.read())
cipher_text = rsa.encrypt(plaintext.encode(), public_key) # 使用公钥加密
print(cipher_text)
with open("private_pkcs1.pem", 'rb') as x:
private_key = rsa.PrivateKey.load_pkcs1(x.read())
text = rsa.decrypt(cipher_text, private_key) # 使用私钥解密
print(text)

此处出现了一些问题,RSA库用的是pkcs1格式的密钥文件,而本文之前生成的是pkcs8格式,会出现错误:

1
No PEM start marker "b'-----BEGIN RSA PRIVATE KEY-----'" found

对于公钥读取,可以用load_pkcs1_openssl_pem方法代替load_pkcs1;对于私钥,只能用openssl命令把pkcs8格式转换为pkcs1了:

1
openssl pkcs8 -in pri_key.pem -nocrypt -out private_pkcs1.pem -traditional

似乎有的版本不需要-traditional

pkcs1.pem的开头为BEGIN RSA PRIVATE KEY

解密openssl生成的密文:

1
2
out=rsa.decrypt(bytes.fromhex("94c21061a08cdf3043c089e5d7b8229e139a60a095ea64b02d1d43d90c1773b4ebb491802b13cfa53f88c9c3e27ed4ab30fc3238fb983ae792f8425aebd077d34661f93ada40a5a768bb608bed43d152004ddb5390e7cac322af3d39685a70757f962dcaeeace23510e60086c7451e22d466e098dc33fa943bac52083a332f32"),private_key)
print(out)

python Crypto

常用方法:

1
2
3
4
5
6
7
8
9
10
11
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
with open('pri_key.pem', 'r') as f:
private_key = RSA.import_key(f.read())
cipher_rsa = PKCS1_v1_5.new(private_key.publickey()) # 创建 RSA 加密对象

plaintext = "test" # 明文
ciphertext=cipher_rsa.encrypt(plaintext.encode())
cipher = PKCS1_v1_5.new(private_key) # 生成一个解密的类
back_text = cipher.decrypt(ciphertext, 0)
print(back_text)

解密openssl生成的密文:

1
print(cipher.decrypt(bytes.fromhex("94c21061a08cdf3043c089e5d7b8229e139a60a095ea64b02d1d43d90c1773b4ebb491802b13cfa53f88c9c3e27ed4ab30fc3238fb983ae792f8425aebd077d34661f93ada40a5a768bb608bed43d152004ddb5390e7cac322af3d39685a70757f962dcaeeace23510e60086c7451e22d466e098dc33fa943bac52083a332f32"),0))

apache配置https

本文的apache环境是之前基于wamp配置的,理论上配置https比原生apache服务器难。参考

https://blog.csdn.net/qq_46680028/article/details/109133461

openssl部分

创建证书请求文件:

CSR 文件是一个包含有关证书请求者(例如服务器或个人)信息的文件,需要经过 CA 的验证和签名,以生成最终的数字证书(CRT 文件)。

1
openssl req -new -key pri_key.pem -out server.csr

创建证书,域名我写的是localhost

签发证书:

CRT 文件是证书颁发机构对证书请求者进行验证后生成的,用于证明证书请求者的身份和提供公钥给通信对方。

1
openssl x509 -req -days 365 -sha256 -in server.csr -signkey pri_key.pem -out servernew.crt

apache部分

servernew.crtpri_key.pem文件移到apache的conf目录下。

apache调用ssl模块:

在httpd.conf文件中开启如下选项:

169

567

修改conf/extra/httpd-ssl.comf文件:

84

73 74注释掉

C:\Windows\System32\drivers\etc\hosts中添加(一般已经添加过了):

1
127.0.0.1 localhost

访问https://localhost

localhost

高级:

自签名

使用 OpenSSL 生成证书时,默认情况下生成的证书是自签名的(self-signed)。自签名证书意味着该证书的颁发者和主题是相同的,没有经过第三方可信的证书颁发机构(CA)进行验证和签名。大多数浏览器和操作系统会发出类似的警告信息,提示证书不被信任。这是因为浏览器和操作系统内置了一组受信任的根证书颁发机构列表,用于验证和信任通过这些机构签名的证书。自签名证书不在这个受信任列表中,因此会被视为不可信。

接收风险并继续即可。

访问成功

为了每次新访问都出现警告,可以导入自签名证书到受信任的根证书列表,点击CRT证书:

安装 选择本地计算机 受信任的根证书颁发机构

用别的浏览器访问,没有危险警告了。

image-20230528221740210

可以通过windows+R,运行certmgr.msc进行证书管理。

https流量分析

基于前文配置好的https,用wires hark进行抓包进行数据分析。篇幅问题记录在HTTPS流量抓包分析(附代码验证)