SSL證書自簽名使用及監控

前言

證書簡介

信息安全愈來愈受重視,HTTPS已經至關普及,要讓咱們的HTTP接口支持HTPPS,只須要一個SSL證書就能夠啦html

  • 全稱公鑰證書(Public-Key Certificate, PKC),裏面保存着歸屬者的基本信息,以及證書過時時間、歸屬者的公鑰,並由認證機構(Certification Authority, CA)施加數字簽名,代表,某個認證機構認定該公鑰的確屬於此人
  • 自簽名的證書,就是咱們來充當 認證機構,這種證書推薦在測試開發過程當中,生產環境直接上CA證書省心!

實踐

證書生成

準備

  • 確保有openssl庫java

  • 建立目錄python

    # 根證書目錄
    [root@centos7 ~]# mkdir -p /var/ssl/crt/root
    # 服務器端證書目錄
    [root@centos7 ~]# mkdir -p /var/ssl/crt/server

生成根證書

  • 私鑰,RSA對稱加密,aes比des更安全,密鑰長度2048nginx

    openssl genrsa -aes256 -out /var/ssl/crt/root/ca.key -passout pass:"123456" 2048
  • 請求流程,包含證書信息,其中比較關鍵的是CN,是填你的域名,根證書不起服務能夠隨便寫sql

    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"
  • 頒發證書,這個生成的就是能夠用的證書了,注意不加v3_ca這個插件,瀏覽器導入不了數據庫

    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"

生成服務端證書

有了根證書以後,咱們將全部的服務端證書都從根證書籤出,方便客戶端用根證書統一訪問centos

  • 私鑰,RSA對稱加密,aes比des更安全,密鑰長度2048瀏覽器

    openssl genrsa -aes256 -out /var/ssl/crt/server/svc1-server.key -passout pass:"123456" 2048
  • 請求流程,包含證書信息,其中比較關鍵的是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.svc1.com/emailAddress=my@mail.com" -passin pass:"123456"
  • 頒發證書,這個生成的就是能夠用的證書了,注意不加v3_ca這個插件,瀏覽器導入不了bash

    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

# 到最後加上以下內容
server {
    listen       443 ssl http2 default_server;
    listen       [::]:443 ssl http2 default_server;
    server_name  www.svc1.com;
    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.svc1.com

啓動

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.svc1.com
    --2019-10-18 16:37:48--  https://www.svc1.com/
    Resolving www.svc1.com (www.svc1.com)... 127.0.0.1
    Connecting to www.svc1.com (www.svc1.com)|127.0.0.1|:443... connected.
    ERROR: cannot verify www.svc1.com'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.svc1.com insecurely, use `--no-check-certificate'.
  • 用根證書訪問 成功

    root@CN-00013965:/# wget --ca-certificate=/var/ssl/crt/root/ca.crt https://www.svc1.com
    --2019-10-18 16:39:50--  https://www.svc1.com/
    Resolving www.svc1.com (www.svc1.com)... 127.0.0.1
    Connecting to www.svc1.com (www.svc1.com)|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]

導入Java應用

java應用要讀取服務端證書須要經過pkcs12格式的keystore文件,咱們能夠把不一樣的服務端證書用別名區分。而後咱們讀取trustkeystore去訪問HTTPS其餘服務

  • 生成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下執行,-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"

監控

證書起到服務端口上了,咱們怎麼查看證書信息,或者實時檢查證書過時信息呢,已默認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.svc1.com, emailAddress = my@mail.com
notBefore=Oct 18 08:09:32 2019 GMT
notAfter=Oct 15 08:09:32 2029 GMT

若是在用postgresql數據庫起的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()

使用方法:複製上面腳本,文件名get_postgres_cert.py

python get_postgres_cert.py 127.0.0.1:5432
相關文章
相關標籤/搜索