前言
證書簡介
信息安全越來越受重視,HTTPS已經(jīng)相當(dāng)普及,要讓我們的HTTP接口支持HTPPS,只需要一個SSL證書就可以啦
- 全稱公鑰證書(Public-Key Certificate, PKC),里面保存著歸屬者的基本信息,以及證書過期時間、歸屬者的公鑰,并由認(rèn)證機(jī)構(gòu)(Certification Authority, CA)施加數(shù)字簽名,表明,某個認(rèn)證機(jī)構(gòu)認(rèn)定該公鑰的確屬于此人
- 自簽名的證書,就是我們來充當(dāng) 認(rèn)證機(jī)構(gòu),這種證書推薦在測試開發(fā)過程中,生產(chǎn)環(huán)境直接上CA證書省心!
實踐
證書生成
準(zhǔn)備
生成根證書
-
私鑰,RSA對稱加密,aes比des更安全,密鑰長度2048
openssl genrsa -aes256 -out /var/ssl/crt/root/ca.key -passout pass:"123456" 2048
-
請求流程,包含證書信息,其中比較關(guān)鍵的是CN,是填你的域名,根證書不起服務(wù)可以隨便寫,
openssl req -new -key /var/ssl/crt/root/ca.key -out /var/ssl/crt/root/ca.csr -subj "/C=CN/ST=myprovince/L=mycity/O=myorganization/OU=mygroup/CN=www.ca.crt.com/emailAddress=my@mail.com" -passin pass:"123456"
-
頒發(fā)證書,這個生成的就是可以用的證書了,注意不加v3_ca這個插件,瀏覽器導(dǎo)入不了
openssl x509 -req -sha256 -extensions v3_ca -days 3650 -in /var/ssl/crt/root/ca.csr -out /var/ssl/crt/root/ca.crt -signkey /var/ssl/crt/root/ca.key -CAcreateserial -passin pass:"123456"
生成服務(wù)端證書
有了根證書之后,我們將所有的服務(wù)端證書都從根證書簽出,方便客戶端用根證書統(tǒng)一訪問
-
私鑰,RSA對稱加密,aes比des更安全,密鑰長度2048
openssl genrsa -aes256 -out /var/ssl/crt/server/svc1-server.key -passout pass:"123456" 2048
-
請求流程,包含證書信息,其中比較關(guān)鍵的是CN,是填你的域名
openssl req -new -key /var/ssl/crt/server/svc1-server.key -out /var/ssl/crt/server/svc1-server.csr -subj "/C=CN/ST=myprovince/L=mycity/O=myorganization/OU=mygroup/CN=www./emailAddress=my@mail.com" -passin pass:"123456"
-
頒發(fā)證書,這個生成的就是可以用的證書了,注意不加v3_ca這個插件,瀏覽器導(dǎo)入不了
openssl x509 -req -sha256 -extensions v3_req -days 3650 -in /var/ssl/crt/server/svc1-server.csr -out /var/ssl/crt/server/svc1-server.crt -signkey /var/ssl/crt/server/svc1-server.key -CAkey /var/ssl/crt/root/ca.key -CA /var/ssl/crt/root/ca.crt -CAcreateserial -passin pass:"123456"
-
驗證證書
openssl verify -CAfile /var/ssl/crt/root/ca.crt /var/ssl/crt/server/svc1-server.crt
證書使用
Unubtu為例
apt install nginx -y
配置
vi /etc/nginx/sites-available/default
# 到最后加上如下內(nèi)容
server {
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
server_name www.;
root /usr/share/nginx/html;
ssl_certificate "/var/ssl/crt/server/svc1-server.crt";
ssl_certificate_key "/var/ssl/crt/server/svc1-server.key";
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 10m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
include /etc/nginx/default.d/*.conf;
location / {
}
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
vi /etc/hosts
127.0.0.1 www.
啟動
root@CN-00013965:/# sudo service nginx restart
* Restarting nginx nginx Enter PEM pass phrase:
Enter PEM pass phrase: [ OK ]
測試
-
不用證書 失敗
root@CN-00013965:/# wget https://www.
--2019-10-18 16:37:48-- https://www./
Resolving www. (www.)... 127.0.0.1
Connecting to www. (www.)|127.0.0.1|:443... connected.
ERROR: cannot verify www.'s certificate, issued by 'emailAddress=my@mail.com,CN=www.ca.crt.com,OU=mygroup,O=myorganization,L=mycity,ST=myprovince,C=CN’:
Unable to locally verify the issuer's authority.
To connect to www. insecurely, use `--no-check-certificate'.
-
用根證書訪問 成功
root@CN-00013965:/# wget --ca-certificate=/var/ssl/crt/root/ca.crt https://www.
--2019-10-18 16:39:50-- https://www./
Resolving www. (www.)... 127.0.0.1
Connecting to www. (www.)|127.0.0.1|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 612 [text/html]
Saving to: 'index.html.1’
index.html.1 100%[===================================================================================================================>] 612 --.-KB/s in 0s
2019-10-18 16:39:50 (32.4 MB/s) - 'index.html.1’ saved [612/612]
導(dǎo)入Java應(yīng)用
java應(yīng)用要讀取服務(wù)端證書需要通過pkcs12格式的keystore文件,我們可以把不同的服務(wù)端證書用別名區(qū)分。然后我們讀取trustkeystore去訪問HTTPS其他服務(wù)
-
生成keystore
openssl pkcs12 -export -clcerts -in /var/ssl/crt/server/svc1-server.crt -inkey /var/ssl/crt/server/svc1-server.key -out /var/ssl/crt/server/svc1-server.p12 -name svc1 -passin pass:"123456" -password pass:"123456"
keytool命令是JDK自帶的到${JAVA_HOME}/bin下執(zhí)行,-srcstorepass是我們證書的密碼,其他兩個是keystore的密碼
keytool -importkeystore -trustcacerts -noprompt -deststoretype pkcs12 -srcstoretype pkcs12 -srckeystore /var/ssl/crt/server/svc1-server.p12 -destkeystore /var/ssl/crt/server/svc1-server.keystore -alias svc1 -deststorepass "123456" -destkeypass "123456" -srcstorepass "123456"
-
生成trustkeystore
keytool -import -trustcacerts -noprompt -alias CA -file /var/ssl/crt/root/ca.crt -keystore /var/ssl/crt/root/ca.trustkeystore -storepass "123456"
監(jiān)控
證書起到服務(wù)端口上了,我們怎么查看證書信息,或者實時檢查證書過期信息呢,已默認(rèn)443端口為例
root@CN-00013965:/# echo 'Q' | timeout 5 openssl s_client -connect 127.0.0.1:443 2>/dev/null | openssl x509 -noout -subject -dates
subject=C = CN, ST = myprovince, L = mycity, O = myorganization, OU = mygroup, CN = www., emailAddress = my@mail.com
notBefore=Oct 18 08:09:32 2019 GMT
notAfter=Oct 15 08:09:32 2029 GMT
如果在用postgresql數(shù)據(jù)庫起的HTTPS,那么直接openssl不能直接拿到端口證書,我們可以借助python腳本,腳本是Github上找的
#!/usr/bin/env python
import argparse
import socket
import ssl
import struct
import subprocess
import sys
try:
from urlparse import urlparse
except ImportError:
from urllib.parse import urlparse
def main():
args = get_args()
target = get_target_address_from_args(args)
sock = socket.create_connection(target)
try:
certificate_as_pem = get_certificate_from_socket(sock)
print(certificate_as_pem.decode('utf-8'))
except Exception as exc:
sys.stderr.write('Something failed while fetching certificate: {0}\n'.format(exc))
sys.exit(1)
finally:
sock.close()
def get_args():
parser = argparse.ArgumentParser()
parser.add_argument('database', help='Either an IP address, hostname or URL with host and port')
return parser.parse_args()
def get_target_address_from_args(args):
specified_target = args.database
if '//' not in specified_target:
specified_target = '//' + specified_target
parsed = urlparse(specified_target)
return (parsed.hostname, parsed.port or 5432)
def get_certificate_from_socket(sock):
request_ssl(sock)
ssl_context = get_ssl_context()
sock = ssl_context.wrap_socket(sock)
sock.do_handshake()
certificate_as_der = sock.getpeercert(binary_form=True)
certificate_as_pem = encode_der_as_pem(certificate_as_der)
return certificate_as_pem
def request_ssl(sock):
version_ssl = postgres_protocol_version_to_binary(1234, 5679)
length = struct.pack('!I', 8)
packet = length + version_ssl
sock.sendall(packet)
data = read_n_bytes_from_socket(sock, 1)
if data != b'S':
raise Exception('Backend does not support TLS')
def get_ssl_context():
for proto in ('PROTOCOL_TLSv1_2', 'PROTOCOL_TLSv1', 'PROTOCOL_SSLv23'):
protocol = getattr(ssl, proto, None)
if protocol:
break
return ssl.SSLContext(protocol)
def encode_der_as_pem(cert):
cmd = ['openssl', 'x509', '-inform', 'DER']
pipe = subprocess.PIPE
process = subprocess.Popen(cmd, stdin=pipe, stdout=pipe, stderr=pipe)
stdout, stderr = process.communicate(cert)
if stderr:
raise Exception('OpenSSL error when converting cert to PEM: {0}'.format(stderr))
return stdout.strip()
def read_n_bytes_from_socket(sock, n):
buf = bytearray(n)
view = memoryview(buf)
while n:
nbytes = sock.recv_into(view, n)
view = view[nbytes:] # slicing views is cheap
n -= nbytes
return buf
def postgres_protocol_version_to_binary(major, minor):
return struct.pack('!I', major << 16 | minor)
if __name__ == '__main__':
main()
使用方法:復(fù)制上面腳本,文件名get_postgres_cert.py
python get_postgres_cert.py 127.0.0.1:5432
|