数字证书的具体实现过程大致是按一定顺序排列用户和CA认证的信息与公钥,然后使用私钥进行加密,最后把所有信息按照X509的格式排列好。说干就干。因此,我们需要密钥对生成模块、CSR生成模块、证书生成模块、证书和密钥存储模块以及证书验证模块。
不废话代码起手
密钥生成模块:
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend
import os
# 生成密钥对
def get_keys():
private_key_path = 'private_key.pem'
public_key_path = 'public_key.pem'
# 检查公钥和私钥文件是否存在
if not os.path.exists(private_key_path) or not os.path.exists(public_key_path):
print("公钥或私钥不存在,正在生成...")
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
with open(private_key_path, 'wb') as f:
f.write(private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption()
))
public_key = private_key.public_key()
# 序列化公钥并保存到文件
with open(public_key_path, 'wb') as f:
f.write(public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
))
else:
print("正在加载...")
with open(private_key_path, 'rb') as f:
private_key = serialization.load_pem_private_key(
f.read(),
password=None,
backend=default_backend()
)
# 加载公钥
with open(public_key_path, 'rb') as f:
public_key = serialization.load_pem_public_key(
f.read(),
backend=default_backend()
)
return private_key, public_key
通过对网上搜集到的相关函数的改编,得到了这个。作为新手有以下问题:
1、private_key的格式是什么样的?(因为我知道如果不将它序列化无法通过json发送)
2、Generate_private_key函数具体过程是如何?
3、Pem文件是什么
4、Serialization是做什么的?
第四个问题很好回答,检查代码后得知Serialization是序列化,编码,解码密钥公钥有关信息的。
为了知其所以然,我们直接查看源代码。
def generate_rsa_private_key(
self, public_exponent: int, key_size: int
) -> rsa.RSAPrivateKey:
rsa._verify_rsa_parameters(public_exponent, key_size)
rsa_cdata = self._lib.RSA_new()
self.openssl_assert(rsa_cdata != self._ffi.NULL)
rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
bn = self._int_to_bn(public_exponent)
bn = self._ffi.gc(bn, self._lib.BN_free)
res = self._lib.RSA_generate_key_ex(
rsa_cdata, key_size, bn, self._ffi.NULL
)
self.openssl_assert(res == 1)
evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata)
# We can skip RSA key validation here since we just generated the key
return _RSAPrivateKey(
self, rsa_cdata, evp_pkey, unsafe_skip_rsa_key_validation=True
)
在密钥生成函数中,先进行参数检查:指数是否在3到65537之间,若不在提示你改指数,并告知你最好选择65537.同时需要key_size大于512.
生成rsa_cdata(rsa结构体)后,是一个断言assert。assert(也叫断言)在Dafny语言中经常被使用,是用来检测当前环境是否满足断言条件,若不满足退出程序,通常可以用来做数学证明题(requires-ensures-assume-assert体系)。这里首先断言rsa不为空。
这里的gc函数没有被定义。在ffi的定义页没有找到

查询资料,FFI是一种对于C语言级别资源的管理函数。C语言级别资源意思是由C语言分配和管理的资源。gc函数是Garbage Collection,垃圾管理。询问文心一言得知:
关联资源:它将rsa_cdata(一个通过RSA_new()创建的RSA结构体的cdata对象)与self._lib.RSA_free(一个指向OpenSSL库中RSA_free函数的指针)关联起来。RSA_free函数是OpenSSL中用于释放RSA结构体所占用的资源的函数。
注册垃圾收集回调:ffi.gc还注册了一个垃圾收集时的回调函数。这个回调函数实际上并没有在代码中显式写出,但它是ffi.gc函数内部实现的一部分。当LuaJIT的垃圾收集器决定回收rsa_cdata对象时,它会调用这个回调函数,而回调函数内部会调用self._lib.RSA_free(rsa_cdata)来释放RSA结构体所占用的资源。
自动资源管理:通过这种方式,您不需要手动调用RSA_free来释放RSA结构体。相反,LuaJIT的垃圾收集器会为您处理这一切,只要rsa_cdata对象在Lua层面不再被引用,它就会在适当的时候被回收,并且关联的C语言级别资源也会被自动释放。
更新rsa_cdata变量:需要注意的是,在您的代码片段中,rsa_cdata变量在调用ffi.gc之后被重新赋值了。然而,这个重新赋值实际上并没有改变rsa_cdata对象本身或其与RSA_free函数的关联。ffi.gc返回的是原始的rsa_cdata对象,但是为了方便起见,您通常会选择将其重新赋值给同一个变量(尽管这不是必需的)。重要的是要理解ffi.gc的调用是在原地修改或“包装”了rsa_cdata对象,以便在垃圾收集时触发资源释放。
后续不再理会ffi相关函数。之后,把65537转换为BIGNUM结构,生成大数的函数也无法跳转。然后调用RSA.GENERATE_KEY_EX函数,这个函数也来自于C语言。在成功生成了密钥对之后,转换为evp_pkey格式。最后调用_RSAPrivateKey生成私钥类型。跳转到这个类型,有三个属性:_evp_pkey,_rsa_cdata和_key_size.
Print一下_evp_pkey看看:

似乎存的是地址,另外一个_rsa_cdata也是地址。似乎要调用它自己的函数来读取。
decrypt方法,需要输入bytes类型的密文,以及padding,最后输出明文。
Public_key方法,无参数,其获取自身的evp_pkey,从中找到公钥。
Private_numbers方法,从rsa_cdata中找到大数n,e,d,p,q,都是数组存储。然后调用bntoint方法,返回int类型的大数。得到的是这些大数的集合-RSAPrivateNumbers。
Private_bytes方法:输入解码,格式,加密算法(这三个参数都是serialization中的类),最后给出BIO格式的数据。检查代码发现,这三个参数都在库中定义好了。Encoding可选pem,der,openssh,raw,ansi,s/mime,其它的不在这里列举了。我们试试打印密钥。查询了格式相关,得知:
ASN.1 ------(序列化)------ DER ------(Base64编码)------ PEM
选择原始数据不加密(Raw)似乎无法打印出来,选择ASN也没有打印出来,最后选择der+pkcs8:

得到了一系列16进制的数,并且有‘;’隔开。这里需要用ASN解码器才能看到原始密钥。使用ASN解码看看:
https://holtstrom.com/michael/tools/asn1decoder.php

这样,就看到了RSA的相关密钥。
按照顺序,为版本号,n,长度,d,p,q,dmod(p-1), d mod(q-1),q-1modp,e

被折叠的 条评论
为什么被折叠?



