https学习笔记三----OpenSSL⽣成rootCA及签发证书
⼀、openssl 简介
⼆、安装openssl(linux CentOS7 32位)
如果使⽤的是unix操作系统,可能安装系统的时候,这个库就已经有且存在了。但是在使⽤前,需要注意下当前openssl的库的版本。
openssl version
OpenSSL 1.0.114 Mar 2012
因为版本1.0.1是⼀个很重要的风⽔岭版本。因为1.0.1是第⼀个⽀持TLS1.1和1.2的版本。⽀持新的协议。操作系统的选择也很重要,⽐如Ubuntu 12.04 LTS,客户端不⽀持SSL2。这⾥安装以CentOS7系统为例:
B、将下载的压缩包放在根⽬录下,解压缩,进⼊解压缩⽂件(得到openssl-openssl-1.0.0⽂件夹)cd openssl-1.0.0
C、编译前配置openssl,执⾏命令:./config --prefix=/usr/local/openssl,其中 ( --prefix )参数为欲安装之⽬录,也就是安装后的档案会出现在该⽬录下。
D、编译openssl,执⾏命令: make install
⼩插曲:安装openssl报错
1、问题描述:安装完成,查看版本信息的时候报错了,缺少⼀个库⽂件libssl.so.1.1。
[root@b6e4cbd27773 /usr/local/openssl/bin]# openssl version
openssl: error while loading shared libraries: libssl.so.1.1: cannot open shared
object file: No such file or directory
2、解决⽅法:有依赖没装libssl。在/etc/f⽂件中写⼊openssl库⽂件的搜索路径,使⽤修改后的conf⽣效即可:
echo "/usr/local/lib64" >> /etc/f
ldconfig -v
三、使⽤openssl⽣成RSA密钥对
使⽤openssl的私钥产⽣公钥前,需要了解以下⼏点:
1、key算法:openssl ⽀持⽣成RSA,DSA,ECDSA的密钥对,但是RSA是⽬前使⽤最普遍的。
2、Key长度:RSA的2048是公认较⽐较安全的key长度。
3、密码(Passphrase):在key上使⽤密码是⼀个可选值,但是⼀般都是强烈建议的(官⽹这样写的,实际项⽬中很多都没有设置⼝令),这样每次使⽤key⽂件时,都需要输⼊这个密码才能使⽤,增强了其安全性,但是随之⽽来的易⽤性也会变差。
使⽤genrsa命令来⽣成RSA key(产⽣DSA其他算法的key⽂件,可以直接参考学习官⽹教程,在此处以常⽤的为例),2步骤能完成:
A、⽣成私钥:
使⽤命令:openssl genrsa -aes128 -out fd.key 2048 。以下输⼊了为这个key值设置了密码,且密码使⽤aes128加密保存。
openssl genrsa -aes128 -out fd.key 2048
Generating RSA private key, 2048 bit long modulus
....+++ ...................................................................................+++
e is 65537 (0x10001)
Enter pass phrase for fd.key: ****************
Verifying - Enter pass phrase for fd.key: ****************
这个key⽂件就是私钥⽂件。可以查看下⽂件内容:
cat fd.key
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,01EC21976A463CE36E9DB59FF6AF689A
vERmFJzsLeAEDqWdXX4rNwogJp+y95uTnw+bOjWRw1+O1qgGqxQXPtH3LWDUz1Ym
mkpxmIwlSidVSUuUrrUzIL+V21EJ1W9iQ71SJoPOyzX7dYX5GCAwQm9Tsb40FhV/
[21 ]
4phGTprEnEwrffRnYrt7khQwrJhNsw6TTtthMhx/UCJdpQdaLW/TuylaJMWL1JRW
i321s5me5ej6Pr4fGccNOe7lZK+563d7v5znAx+Wo1C+F7YgF+g8LOQ8emC+6AVV
-----END RSA PRIVATE KEY-----
B、⽣成公钥:
使⽤命令:openssl rsa -in fd.key -pubout -out fd-public.key
openssl rsa -in fd.key -pubout -out fd-public.key
Enter pass phrase for fd.key: ****************
查看这个key⽂件,就是公钥:
cat fd-public.key
-
----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnlccwQ9FRyJYHM8sFNsY
PUHJHJzhJdwcS7kBptutf/L6OvoEAzCVHi/m0qAA4QM5BziZgnvv+FNnE3sgE5pz
iovEHJ3C959mNQmpvnedXwfcOIlbrNqdISJiP0js6mDCzYjSO1NCQoy3UpYwvwj7
0ryR1F+abARehlts/Xs/PtX3VamrljiJN6JNgFICy3ZvEhLZEKxR7oob7TnyZDrj
IHxBbqPNzeiqLCFLFPGgJPa0cH8DdovBTesvu7wr/ecsf8CYyUCdEwGkZh9DKtdU
HFa9H8tWW2mX6uwYeHCnf2HTw0E8vjtOb8oYQxlQxtL7dpFyMgrpPOoOVkZZW/P0
NQIDAQAB
-----END PUBLIC KEY-----
四、获取权威机构颁发证书步骤
获取权威机构颁发的证书,需要先得到私钥的key⽂件(.key),然后使⽤私钥的key⽂件⽣成sign req
⽂件(.csr),最后把csr⽂件发给权威机构,等待权威机构认证,认证成功后,会返回证书⽂件(.crt)。
A:⽣成私钥key。
与第⼆节使⽤openssl⽣成RSA密钥对的步骤A⼀致。使⽤命令:openssl genrsa -aes128 -out fd.key 2048
B:私钥的key⽂件⽣成sign req ⽂件(.csr)
⽣成csr⽂件时,需要填写⼀些关于待签⼈或者公司的⼀些信息,⽐如国家名,省份名,组织机构名,主机名,email名,有些信息可以不填写,使⽤.标识。
使⽤命令:openssl req -new -key fd.key -out fd.csr。过程如下:
$ openssl req -new -key fd.key -out fd.csr
Enter pass phrase for fd.key: ****************
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) [AU]:GB
State or Province Name (full name) [Some-State]:.
Locality Name (eg, city) []:London
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Feisty Duck Ltd
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:www.feistyduck
Email Address []:webmaster@feistyduck
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
C、把csr⽂件发给权威机构,等待权威机构认证,交费获取证书即可。
五、OpenSSL⽣成root CA及签发证书
有时候,使⽤SSL协议是⾃⼰内部服务器使⽤的,这时可以不必去第三⽅权威的CA机构做证书,可以做⾃签证书(⾃⼰创建root CA(⾮权威))主要有以下三个步骤。
A:创建opensslf在使⽤default-ca时需要使⽤的SSL的⼯作⽬录(第⼀次必须要设置)。
1、查看openssl的配置⽂件:
openssl version -a
OpenSSL 1.0.1e-fips 17 Nov 2016
built on: Fri Nov 1816:28:23 CST 2016
platform: linux-x86_64
options:  bn(64,64) md2(int) rc4(16x,int) des(idx,cisc,16,int) idea(int) blowfish(idx)
compiler: gcc -fPIC -DOPENSSL_PIC -DZLIB -DOPENSSL_THREADS -D_REENTRANT -DDSO_DLFCN -DHAVE_DLFCN_H -DKRB5_MIT -m64 -DL_ENDIAN -DTERMIO -Wall -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fsta OPENSSLDIR: "/etc/pki/tls"
engines:  rdrand dynamic
2、到OPENSSLDIR: "/etc/pki/tls"的配置⽂件opensslf
根据配置⽂件下的[ CA_default ]节点默认值,创建对应⽂件夹和⽂件。
按顺序在/etc/pki/CA下执⾏以下命令创建⽂件夹和⽂件:
其中,certs:存放已颁发的证书;newcerts:存放CA指令⽣成的新证书;private:存放私钥;crl:存放已吊销的整数;:penSSL定义的已签发证书的⽂本数据库⽂件,这个
⽂件通常在初始化的时候是空的;serial:证书签发时使⽤的序列号参考⽂件,该⽂件的序列号是以16进制格式进⾏存放的,该⽂件必须提供并且包含⼀个有效的序列号。
执⾏完后,当前⽬录为:
[tt@SWEBMYVMM000210 /etc/pki/CA]$ll
total 20
drwxrwxrwx 2 root root 4096 Mar 1211:08 certs
drwxrwxrwx 2 root root 4096 Nov 182016 crl
-rwxrwxrwx 1 root root    0 Mar 1211:
drwxrwxrwx 2 root root 4096 Nov 182016 newcerts
drwxrwxrwx 2 root root 4096 Nov 182016private
-rwxrwxrwx 1 root root  33 Mar 1211:12 serial
⼩插曲:使⽤⾃签证书签名⽤户证书时报错,⽂件不存在
1、问题描述:
openssl ca -in server.csr - - -keyfile ca.key
Using configuration from /etc/pki/tls/opensslf
/etc/pki/CA/serial: No such file or directory
error while loading serial number
139996157081440:error:02001002:system library:fopen:No such file or directory:bss_file.c:398:fopen('/etc/pki/CA/serial','r')
139996157081440:error:20074002:BIO routines:FILE_CTRL:system lib:bss_file.c:400:
2、问题解决:
如果不设置⼯作⽬录,后续第三步的最后⼀⼩步,使⽤openssl的ca命令产⽣⽤户的ca证书时会报错,创建opensslf在使⽤default-ca时需要使⽤的SSL的⼯作⽬录即可。
B:⽣成CA根证书(root ca证书)。
步骤:⽣成CA私钥(.key)-->⽣成CA证书请求(.csr)-->⾃签名得到根证书(.crt)(CA给⾃已颁发的证书)。
# Generate CA private key  --->ca.key
openssl genrsa -out ca.key 2048
# Generate CSR  --->ca.csr
openssl req -new -key ca.key -out ca.csr
# Generate Self Signed certificate(CA 根证书)  ---> ca.crt
openssl x509 -req -days 365 -in ca.csr -signkey ca.key -
⼩插曲:直接根据key⽂件获取CA根证书的命令
⽅法:在得到key⽂件后,执⾏以下命令:
openssl req -new -x509 -days 365 -key fd.key -
如果不想填写那些注册信息,执⾏以下命令:
openssl req -new -x509 -days 365 -key fd.key - subj "/C=GB/L=London/O=Feisty Duck Ltd/CN=www.feistyduck
C:⽤⾃签根证书 ca.crt  给⽤户证书签名。
步骤:⽣成私钥(.key)-->⽣成证书请求(.csr)-->⽤CA根证书签名得到证书(.crt)
#  private key  --->server.key
openssl genrsa  -out server.key 1024
# generate csr  --->server.csr
openssl req -new -key server.key -out server.csr
# generate certificate  ---&
openssl ca -in server.csr - - -keyfile ca.key
⼩插曲:⽤CA根证书签名时报错,The mandatory stateOrProvinceName field was missing
1、问题描述:
sudo openssl ca -in server.csr - - -keyfile ca.key
Using configuration from /etc/pki/tls/opensslf
Check that the request matches the signature
Signature ok
The mandatory stateOrProvinceName field was missing
2、出现原因:opensslf中CA policy有三个match,必须要填⼀样的,或者改成optional
3、解决⽅法:修改配置⽂件,修改后为:
# For the CA policy
[ policy_match ]
countryName = optional
stateOrProvinceName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
D:证书的简单使⽤。
把以及server.key保存在服务器端等待程序加载使⽤;把ca.key保存在客户端,如果客户端需要验证服务器端发的证书时使⽤。
六、遇到过的问题⼩结
问题1:对服务器发送https请求失败,连上服务器之后,服务器返回为空。
测试后台服务器写的测试桩,更多的是写服务器端接收处理数据,因为现在桩需要实现透传功能,所以桩是客户端也是服务器端,于是引⼊了以前测试时使⽤的客户端,代码如下:
class HttpsClient(object):
def __init__(self, target_ip, target_port,pem_path):
self.ip = target_ip
self.port = target_port
self.pem_path = pem_path
self.ssl_content =_pem(self.pem_path)
def get_pem(self,pem_path):
'''
:return:  获取content_ssl
'''
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
context.set_default_verify_paths()
context.verify_mode = ssl.CERT_NONE
# context.load_verify_locations(self.pem_path)
context.load_verify_locations('C:/Users/Administrator/PycharmProjects/untitled/test/loleina/ca.pem')
return  context
def https_send(self,data,timeout=30):
'''
'''
package = self.__socket_send_recive( self.ssl_content ,data)
return package
# return self.__parse_response(package)
def __socket_send_recive(self,ssl_content, data,timeout=30):
root的初始密码'''
:param request:  需要发送的内容
:param type:  请求类型,https type=1 http type=0
:param timeout:  请求超时时间
:return:  收到的响应内容
'''
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(timeout)
connect_sock = ssl_content.wrap_socket(sock)
t((self.ip,int(self.port)))
connect_sock.send(data)
BUFLEN = 1024
package = ''
while True:
buf = v(BUFLEN)
package += buf
# chunked
if package.find('Transfer-Encoding: chunked') > 0:
#print 'chunked'
ls = package.strip().split('\r\n');
if ls[-1].strip() == '0':
break;
else:
head, body, blength = self.__analyze_package(package);
# 如果没有分解出head部分,说明返回的不是标准的http协议
if head is None:
print 'header is None'
if len(buf) < BUFLEN:
break;
# 100-continue
elif head.find('Expect: 100-continue') > 0:
print 'Expect: 100-continue'
if len(body) >= blength:
package = place('Expect: 100-continue\r\n', '');
break;
else:
if len(buf) < BUFLEN:
connect_sock.send('HTTP/1.1 100 Continue\r\n\r\n')
else:
pass;
else:
# 如果head中没有content-length,
if blength == -1:
if len(buf) < BUFLEN:
break;
else:
if len(body) >= blength:
break;
return package
def __analyze_package(self, package):
'''
:param package:  收到的socket响应包
:return:  按照http协议解析出来的(head,body,body长度字段)
'''
idx = package.find('\r\n\r\n');
# 如果没有发现分隔⾏,返回None
if idx < 0:
return (None, None, None);
head = package[:idx];
body = package[idx + 4:];
blength = -1;
# 分析body长度
for ln in head.split('\r\n'):
if ln.find('Content-Length') == 0:
blength = ln.replace('Content-Length:', '').strip();
break;
return (head, body, int(blength))
调⽤后,就⼀直收不到真实环境的返回,抓包分析发现3次握⼿能建⽴,但是之后服务器端就断开了与客户端的连接。后⾯换了⼀个httplib库, httplib.HTTPSConnection获取httpClient后,调⽤ quest(method='POST', url=url.path, body=params, headers=headers)调⽤成功。对⽐了下2次调⽤的不同点,更多的不同在发包的时候request有写出资源路径/preSvr,⽽之前的没写。下⾯是关键代码段:
url = urlparse(url_str)
httpClient = httplib.HTTPSConnection(url.hostname + ':' + str(url.port))
httpClient.host = url.hostname
httpClient.port = url.port
httpClient.key_file = ''
<_file = ''
sock = ate_connection((httpClient.host, httpClient.port))
httpClient.sock = ssl.wrap_socket(sock, httpClient.key_file, _file,ssl_version=ssl.PROTOCOL_TLSv1_2)
response = sponse().read()
问题2:服务器端返回报⽂乱码。双⽅约定好的编码格式就是utf-8,服务器端都正常返回了,客户端收到的数据就是乱码....
当时尝试了各种转码的⽅法⼀直都有问题,后⾯带着问题百度查了下尝试从其他⾓度来分析问题,后⾯果然问题是服务器端返回的数据是压缩的,客户端接收到数据后需要解压。
compressedstream = StringIO.StringIO(returnData)
gziper = gzip.GzipFile(fileobj=compressedstream)
returnData = ad()
⽽之所以返回压缩格式是因为请求包的头⽂件⾥有设置Accept-Encoding: deflate, gzip,这条信息代表本地可以接收压缩格式的数据,⽽服务器在处理时就将⼤⽂件压缩再发回客户端。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。