北京电子科技学院
《信息安全工程技术应用》课程设计报告
基于OpenSSL的证书格式转换工具设计与实现
小组成员姓名:20175119郑楚琪
20175122邱昕
20175129陈尧
指导教师:娄家鹏
提交时间:2020年5月4日
一、设计方案及可行性分析
1、任务简介:
.pem .pfx /.keystore .crt .cer .der 这些格式的文件用openssl命令行的产生方式及查看,了解哪些格式可以互相转换并使用openssl编程实现。
2、openssl简介:
OpenSSL 是一个安全套接字层密码库,囊括主要的密码算法、常用的密钥和证书封装管理功能及SSL协议,并提供丰富的应用程序供测试或其它目的使用。密钥和证书管理是PKI的一个重要组成部分,OpenSSL为之提供了丰富的功能,支持多种标准。
首先,OpenSSL实现了ASN.1的证书和密钥相关标准,提供了对证书、公钥、私钥、证书请求以及CRL等数据对象的DER、PEM和BASE64的编解码功能。OpenSSL提供了产生各种公开密钥对和对称密钥的方法、函数和应用程序,同时提供了对公钥和私钥的DER编解码功能。并实现了私钥的PKCS#12和PKCS#8的编解码功能。OpenSSL在标准中提供了对私钥的加密保护功能,使得密钥可以安全地进行存储和分发。
OpenSSL实现了对证书的X.509标准编解码、PKCS#12格式的编解码以及PKCS#7的编解码功能。并提供了一种文本数据库,支持证书的管理功能,包括证书密钥产生、请求产生、证书签发、吊销和验证等功能。
3、常见数字证书格式:
1)、pem:Privacy Enhanced Mail,一般为文本格式,以-----BEGIN...开头,以-----END...结尾。中间的内容是 BASE64 编码。这种格式可以保存证书和私钥,有时我们也把PEM 格式的私钥的后缀改为.key以区别证书与私钥。
BEGIN CERTIFICATE指证书,BEGIN RSA PRIVATE KEY为私钥。
2)、der:用二进制der编码方法储存的证书,二进制格式,只有证书信息,没有私钥。
3)、crt:有可能用der,也有可能用pem编码
4)、jks:Java Key Storage,JAVA的专属格式,利用keytool可以进行格式转换。一般用于 Tomcat 服务器。
5)、 PFX 或 P12公钥加密标准 #12 (PKCS#12) 可包含所有私钥、公钥和证书。其以二进制格式存储,也称为 PFX 文件。通常可以将Apache/OpenSSL使用的“KEY文件 + CRT文件”格式合并转换为标准的PFX文件。转换时需要输入PFX文件的加密密码。
4、设计模型:
Python是一种跨平台的计算机程序设计语言。 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。最初被设计用于编写自动化脚本(shell),随着版本的不断更新和语言新功能的添加,越多被用于独立的、大型项目的开发。openssl本身有丰富的对证书处理的语句,经过我们小组讨论,可以使用python调用openssl语句,实现对数字证书的格式转换。
二、详细设计思路
1、openssl命令:
1)、openssl查看证书命令:
der:openssl x509 -in certificate.der -inform der -text -noout
pem:openssl x509 -in certificate.pem -text -noout
pfx:openssl pkcs12 -in for-iis.pfx
2)、openssl转化格式命令:
der转化为pem
openssl x509 -in cert.crt -inform der -outform pem -out cert.pem
pem转化为der
openssl x509 -in cert.pem -outform der -out cert.der
pfx转化为pem
openssl pkcs12 -in for-iis.pfx -out for-iis.pem -nodes
3)、openssl生成证书:
生成rsa私钥:openssl genrsa -out rsa_private.key 2048
生成rsa公钥:openssl rsa -in
rsa_private.key -pubout -out rsa_public.key
利用已有的公钥私钥生成证书:openssl req -new -x509 -days 365 -key rsa_private.key -out cert.crt
将证书转化为der格式:openssl x509 -in cert.pem -outform der -out cert.der
2、x509:
1) X509_STORE_add_crl
将crl添加到X509_STORE中。
2) void X509_STORE_set_flags(X509_STORE *ctx, long flags)
将flags赋值给ctx里面的flags,表明了验证证书时需要验证哪些项。
4) X509_TRUST_set_default
设置默认的X509_TRUST检查函数。
5)int X509_verify(X509 *a, EVP_PKEY *r)
验证证书的签名。
6) X509_verify_cert
验证证书,用法可参考apps/verify.c。
7) X509_verify_cert_error_string
根据错误号,获取错误信息。
8) X509_add1_ext_i2d
根据具体的扩展项数据结构添加一个扩展项。
9) X509_add_ext
X509_EXTENSION堆栈中,在指定位置添加一项。
10)X509_ALGOR_dup
算法拷贝。
11)X509_alias_get0/X509_alias_set1
获取/设置别名。
12)X509_asn1_meth
获取X509的ASN1_METHOD,包括new、free、i2d和d2i函数。
13)X509_certificate_type
获取证书和公钥类型。
14)int X509_check_issued(X509 *issuer, X509 *subject);
检查subject证书是否由issuer颁发,如果是则返回X509_V_OK,即0。
15)X509_check_private_key
检查私钥与证书中的公钥是否匹配,匹配返回1。
16)X509_cmp
证书比较。
17) int X509_cmp_current_time(ASN1_TIME *s)
将s与当前时间进行比较,返回值小于0则s早于当前时间,大于0则s晚与当前时间。
18)int X509_cmp_time(ASN1_TIME *ctm, time_t *cmp_time)
如果ctm时间在cmp_time之后,则返回值大于0。
19) X509_delete_ext
删除扩展项堆栈中指定位置的扩展项。
20)X509_digest
根据指定的摘要算法对X509结构做摘要。
20) X509_dup
拷贝函数。
21)X509_find_by_issuer_and_serial
根据颁发者的X509_NAME名称和证书序列号,在X509堆栈中查找对应的证书并返回。
22) X509_find_by_subject
从证书堆栈中根据持有者名字查询证书,并返回。
23)X509_get0_pubkey_bitstr
获取X509结构中的DER编码的公钥信息。
24)X509_load_cert_crl_file
加载证书和crl,用于验证证书。
25)X509_PURPOSE_get0
根据X509_PURPOSE的位置获取对应的X509_PURPOSE。
26)X509_PURPOSE_get0_name
获取X509_PURPOSE的名字。
27)X509_PURPOSE_get0_sname
获取X509_PURPOSE的别名。
28)X509_PURPOSE_get_by_id
根据证书用途ID获取X509_PURPOSE在当前数组(xstandard)或堆栈(xptable)中的位置,如果没有返回-1。
29)X509_PURPOSE_get_by_sname
根据别名获取对应的X509_PURPOSE在数组或堆栈中的位置。
30)X509_PURPOSE_get_count
获取所有的X509_PURPOSE个数,包括标准的和用户动态添加的。
31)X509_PURPOSE_get_id
获取X509_PURPOSE的ID。
32) int X509_PURPOSE_set(int *p, int purpose)
检查是否有purpose标识的X509_PURPOSE,并将purpose值写入p。
33) STACK_OF(X509_EXTENSION) X509v3_add_ext
(STACK_OF(X509_EXTENSION) **x, X509_EXTENSION *ex, int loc)
添加扩展项,堆栈操作,将ex表示的扩展项根据loc指定的位置插入到X509_EXTENSION堆栈中。
34) X509v3_delete_ext
堆栈操作,去除指定位置的扩展项。
35)int X509V3_EXT_print(BIO *out, X509_EXTENSION *ext,
unsigned long flag, int indent)
本函数用于打印单个扩展项,out为BIO类型的输出对象,ext为扩展项,flag表明不支持扩展项的处理方式,indent表明输出时第一列的位置。
flag的值在x509v3.h中定义,可以有:
#define X509V3_EXT_DEFAULT 0
打印DER编码内容,调用M_ASN1_OCTET_STRING_print。
#define X509V3_EXT_ERROR_UNKNOWN (1L << 16)
打印一行语句。
#define X509V3_EXT_PARSE_UNKNOWN (2L << 16)
分析扩展项的DER编码,并打印。
#define X509V3_EXT_DUMP_UNKNOWN (3L << 16)
打印出DER编码的内容,调用BIO_dump_indent。
36)int X509V3_extensions_print(BIO *bp, char *title,
STACK_OF(X509_EXTENSION) *exts, unsigned long flag, int indent)
本函数将堆栈中的所有扩展项打印,参数意义同上。
37) int X509v3_get_ext_by_critical(const STACK_OF(X509_EXTENSION) *sk, int crit, int lastpos)
获取扩展项在堆栈中的位置,crit表面扩展项是否关键,lastpos为指定堆栈搜索起始位置。此函数从给定的lastpos开始搜索扩展项堆栈,找到与crit匹配的扩展项后,返回其位置,如果找不到扩展项,返回-1。
38)int X509v3_get_ext_by_NID(const STACK_OF(X509_EXTENSION) *x, int nid,int lastpos)
获取扩展项在其堆栈中的位置,此函数根据扩展项标识nid以及堆栈搜索的起始进行搜索,如果找到,返回它在堆栈中的位置,如果没找到,返回-1。
39) int X509v3_get_ext_by_OBJ(const STACK_OF(X509_EXTENSION) *sk, ASN1_OBJECT *obj, int lastpos)
40)X509_EXTENSION *X509v3_get_ext(const STACK_OF(X509_EXTENSION) *x,
int loc)
获取扩展项,loc为扩展项在堆栈x中的位置,如果不成功,返回NULL。
41)int X509v3_get_ext_count(const STACK_OF(X509_EXTENSION) *x)
获取扩展项的个数,此函数调用堆栈操作sk_X509_EXTENSION_num(x)来获取扩展项的个数。
42)STACK_OF(CONF_VALUE) * X509V3_get_section(X509V3_CTX *ctx, char *section)
获取配置信息,section为配置信息中的“段”信息。比如有配置信息如下:
[CA]
Name1=A
Name2=B
则section应是”CA”,返回的信息为它包含的内容信息。
43)char * X509V3_get_string(X509V3_CTX *ctx, char *name, char *section)
调用此函数时name为”Name1”,sectionwei “CA”,则返回值为”A”。
44)int X509V3_get_value_bool(CONF_VALUE *value, int *asn1_bool)
判断配置信息的布尔值,如果value表示的值为true、TRUE、y、Y、yes、YES,*asn1_bool 的值设为xff,并返回1,如果为false、FALSE、n、N、NO、no, *asn1_bool设置为 0,并返回1。此函数调用不成功时返回0。
45) int X509V3_get_value_int(CONF_VALUE *value, ASN1_INTEGER **aint)
将value中的值转换为ASN1_INTEGER类型,结果存放在**aint中,函数调用成功返回1,否则返回0。
46)STACK_OF(CONF_VALUE) *X509V3_parse_list(const char *line)
3、pem:
1)、 PEM_write_XXXX/PEM_write_bio_XXXX
将XXXX代表的信息类型写入到文件/bio中。
2)、 PEM_read_XXXX/PEM_read_bio_XXXX
从文件/bio中读取PEM的XXXX代表类型的信息。
XXXX可用代表的有:SSL_SESSION、X509、X509_REQ、X509_AUX、X509_CRL、RSAPrivateKey、RSAPublicKey、DSAPrivateKey、PrivateKey、PKCS7、DHparams、NETSCAPE_CERT_SEQUENCE、PKCS8PrivateKey、DSAPrivateKey、DSA_PUBKEY、DSAparams、ECPKParameters、ECPrivateKey、EC_PUBKEY等。
3)、 PEM_ASN1_read/PEM_ASN1_read_bio
比较底层的PEM读取函数,2)中的函数都调用了这两个函数。
4)、 PEM_ASN1_write/PEM_ASN1_write_bio
比较底层的PEM读取函数,1)中的函数都调用了这两个函数。
5)、 PEM_read_bio
读取PEM文件的各个部分,包括文件类型、头信息以及消息体(base64解码后的结果)。
6)、PEM_get_EVP_CIPHER_INFO
根据头信息获取对称算法,并加载初始化向量iv。
7)、 PEM_do_header
根据对称算法,解密数据。
8)、PEM_bytes_read_bio
获取PEM数据,得到的结果为一个DER编码的明文数据,该函数先后调用了5)、6)和7)函数。
4、python程序流程图:
三、设计特色
1、使用了python语句,易读易维护
2、可以被Java调用
四、源代码及注释(没代码的不用写)
def
set_win_center(root, curWidth=200, curHight=200):
‘‘‘
设置窗口大小,并居中显示
:param root:主窗体实例
:param curWidth:窗口宽度,非必填,默认200
:param curHight:窗口高度,非必填,默认200
:return:无
‘‘‘
if not curWidth:
‘‘‘获取窗口宽度,默认200‘‘‘
curWidth = root.winfo_width()
if not curHight:
‘‘‘获取窗口高度,默认200‘‘‘
curHight = root.winfo_height()
# print(curWidth, curHight)
# 获取屏幕宽度和高度
scn_w, scn_h = root.maxsize()
# print(scn_w, scn_h)
# 计算中心坐标
cen_x = (scn_w - curWidth) / 2
cen_y = (scn_h - curHight) / 2
# print(cen_x, cen_y)
# 设置窗口初始大小和位置
size_xy = ‘%dx%d+%d+%d‘ % (curWidth,
curHight, cen_x, cen_y)
root.geometry(size_xy)
def pem_2_der():
os.system("openssl x509 -outform
der -in " + w1.get() + " -out " + w2.get())
tkinter.messagebox.showinfo(title=‘提示‘, message=‘转换成功!‘)
def pem_2_p7b():
os.system("openssl crl2pkcs7
-nocrl -certfile " + w1.get() + " -out " + w2.get())
tkinter.messagebox.showinfo(title=‘提示‘, message=‘转换成功!‘)
def pem_2_pfx():
os.system("openssl pkcs12
-export -in " + w1.get() + "-inkey " +e1.get()+" -out
" + w2.get())
tkinter.messagebox.showinfo(title=‘提示‘, message=‘转换成功!‘)
def pem_2_keystore():
os.system("keytool -import -file
" + w1.get() + "-keystore " + w2.get() +"; y ;")
tkinter.messagebox.showinfo(title=‘提示‘, message=‘转换成功!‘)
def der_2_pem():
os.system("openssl x509 -inform
der -in " + w1.get() + " -out " + w2.get())
tkinter.messagebox.showinfo(title=‘提示‘, message=‘转换成功!‘)
def p7b_2_pem():
os.system("openssl pkcs7
-print_certs -in " + w1.get() + " -out " + w2.get())
tkinter.messagebox.showinfo(title=‘提示‘, message=‘转换成功!‘)
def pfx_2_pem():
os.system("openssl pkcs12 -in
" + w1.get() + " -out " + w2.get() + " -nodes ;" +
e2.get() + ";")
tkinter.messagebox.showinfo(title=‘提示‘, message=‘转换成功!‘)
def choose_file():
"""
选择需要处理的文件
:return:
"""
"""打开选择文件夹对话框"""
filepath =
filedialog.askopenfilename(title=u‘选择被检测文件‘,
initialdir=(os.path.abspath("..")))
# 获得选择好的文件
w1.set(filepath)
return filepath
def save_file():
path =
filedialog.asksaveasfilename(title=u‘保存文件‘,
initialfile="{}.".format(
datetime.strftime(datetime.now(), "%Y-%m-%d %H-%M-%S"))) # 保存文件路径对话框
w2.set(path)
return path
window = tkinter.Tk()
# 第2步,给窗口的可视化起名字
window.title(‘openssl证书格式转换工具‘)
# 第3步,设定窗口的大小(长 * 宽)
set_win_center(window, 720, 550)
w1 = tkinter.StringVar()
w1.set(‘输入文件路径‘)
w2 = tkinter.StringVar()
w2.set(‘输出文件路径‘)
tkinter.Entry(window, show=None, font=(‘楷体‘, 14),
textvariable=w1).place(x=80, y=48, anchor=‘nw‘,
width=500,
height=35)
tkinter.Entry(window, show=None, font=(‘楷体‘, 14),
textvariable=w2).place(x=80, y=98, anchor=‘nw‘,
width=500,
height=35)
tkinter.Button(window, text=‘选择文件‘, font=(‘宋体‘, 14), command=choose_file).place(x=600, y=50, anchor=‘nw‘)
tkinter.Button(window, text=‘保存位置‘, font=(‘宋体‘, 14), command=save_file).place(x=600, y=100, anchor=‘nw‘)
tkinter.Button(window, text=‘pem转der‘, font=(‘宋体‘, 14), command=pem_2_der).place(x=80, y=200, anchor=‘nw‘)
tkinter.Button(window, text=‘pem转p7b‘, font=(‘宋体‘, 14), command=pem_2_p7b).place(x=190, y=200, anchor=‘nw‘)
tkinter.Button(window, text=‘pem转pfx‘, font=(‘宋体‘, 14), command=pem_2_pfx).place(x=290, y=200, anchor=‘nw‘)
tkinter.Button(window, text=‘pem转keystore‘, font=(‘宋体‘, 14), command=pem_2_keystore).place(x=390, y=200, anchor=‘nw‘)
tkinter.Button(window, text=‘der转pem‘, font=(‘宋体‘, 14), command=der_2_pem).place(x=80, y=250, anchor=‘nw‘)
tkinter.Button(window, text=‘p7b转pem‘, font=(‘宋体‘, 14), command=p7b_2_pem).place(x=190, y=250, anchor=‘nw‘)
tkinter.Button(window, text=‘pfx转pem‘, font=(‘宋体‘, 14), command=pfx_2_pem).place(x=290, y=250, anchor=‘nw‘)
e1 = tkinter.Entry(window, show=‘*‘, font=(‘Arial‘, 14)) # 显示成密文形式
e2 = tkinter.Entry(window, show=None, font=(‘Arial‘, 14)) # 显示成明文形式
e1.place(x=80,y=300)
e2.place(x=80,y=350)
l = tkinter.Label(window, text=‘保存位置请自己加上文件名后缀‘,
font=(‘Arial‘, 12), width=30, height=2)
# 说明: bg为背景,font为字体,width为长,height为高,这里的长和高是字符的长和高,比如height=2,就是标签有2个字符这么高
# 第5步,放置标签
l.place(x=80, y=150, anchor=‘nw‘) # Label内容content区域放置位置,自动调节尺寸
# 放置lable的方法有:1)l.pack(); 2)l.place();
# 主窗口循环显示
window.mainloop()
五、个人报告
郑楚琪(40%):实现c语言代码,完成总结报告,调试代码命令
陈尧(20%):搜集资料,完成任务,学习代码和openssl命令知识。
2、个人报告
附:20175119郑楚琪个人报告:
https://www.cnblogs.com/20175129cy/p/12828497.html
20175129陈尧个人报告:
https://www.cnblogs.com/20175119zcq/p/12828513.html
20175122邱昕个人报告:
https://www.cnblogs.com/qiuxin/p/12827541.html
六、参考文献:
【1】《openssl与网络信息安全——基础、结构和指令》
【2】《openssl编程》
【3】https://www.cnblogs.com/chevin/p/11041763.html
【4】https://stackoverflow.com/questions/256405/programmatically-create-x509-certificate-using-openssl/15082282#15082282
原文:https://www.cnblogs.com/20175129cy/p/12828597.html