Làm cách nào để kiểm tra ngày hết hạn chứng chỉ bằng Python?

Sau Heartbleed và tạo ra nhiều chứng chỉ mới khác nhau, tôi đã tìm kiếm một công cụ gửi cảnh báo cho tôi khi chứng chỉ sắp hết hạn. Về cơ bản, tôi cần tự động kiểm tra ngày hết hạn của chứng chỉ. yêu cầu của tôi là

  • kiểm tra hàng ngày;
  • thông báo qua email;
  • kiểm tra các chứng chỉ trên mạng nội bộ và bên ngoài;
  • kiểm tra chứng chỉ trên dịch vụ không phải web [imap, pop, …]

Có một số công cụ đáp ứng một phần yêu cầu của tôi nhưng không có công cụ nào làm được mọi thứ tôi cần. Vì vậy, tôi đã tự làm nó

Kiểm tra ngày hết hạn của chứng chỉ SSL

nhượng lại. py là một tập lệnh python đọc tệp dưới dạng đầu vào [ceds. kiểm tra] và kiểm tra SSL trên mọi máy chủ được liệt kê trong tệp. Tập lệnh có một vài tham số cấu hình nội tuyến

Đây là tập lệnh Python để kiểm tra ngày hết hạn của chứng chỉ TLS/SSL của trang web, được sử dụng để tạo kết nối HTTPS an toàn

Để sử dụng tập lệnh, chỉ cần chạy tập lệnh từ dòng lệnh, cùng với danh sách tên miền bạn muốn kiểm tra. Ví dụ

> python check-certificates.py codebox.net www.codebox.net api.codebox.net oldtime.radio c0debox.net

Checking 5 endpoints...
codebox.net     OK    expires in 48 days
www.codebox.net OK    expires in 48 days
api.codebox.net OK    expires in 48 days
oldtime.radio   WARN  expires in 6 days 21 hours 13 minutes
c0debox.net     ERROR [Errno 8] nodename nor servname provided, or not known

Tập lệnh sẽ liệt kê trạng thái của chứng chỉ của từng miền, hiển thị 'OK' nếu chứng chỉ đã được truy xuất và sắp hết hạn, 'WARN' nếu ngày hết hạn của chứng chỉ sắp đến hoặc 'ERROR' nếu chứng chỉ đã hết hạn hoặc

Theo mặc định, 'WARN' sẽ được hiển thị nếu còn ít hơn 7 ngày cho đến khi chứng chỉ hết hạn, nhưng khoảng thời gian này có thể được thay đổi bằng cách thay đổi giá trị của biến

Nếu bất kỳ miền nào đang sử dụng cổng không chuẩn cho HTTPS thì cổng này phải được chỉ định bằng cách sử dụng ký hiệu thông thường là host:port, chẳng hạn

> python check-certificates.py test.codebox.net:8443

Tập lệnh trả về mã thoát cho biết kiểm tra có đạt hay không, giúp dễ dàng thực hiện hành động thích hợp trong tập lệnh shell [ví dụ: gửi email nếu kiểm tra không thành công]

Mã thoát điều kiệnMọi thứ đều ổn, không có chứng chỉ nào sắp hết hạn0Ít nhất một chứng chỉ sắp hết hạn1Ít nhất một chứng chỉ đã hết hạn, không hợp lệ hoặc không thể truy xuất được2Cả hai điều kiện trước đó đều xảy ra3Không có danh sách miền nào được cung cấp khi chạy tập lệnh9

Kiểm tra chứng chỉ được thực hiện song song, giúp quá trình kiểm tra nhiều miền nhanh hơn nhiều. Số lần kiểm tra đồng thời sẽ được thực hiện được xác định bởi giá trị của biến

Nhận thông tin SSL đã được xác minh bằng Python [3. x] rất dễ dàng. Các ví dụ mã cho nó cũng nằm rải rác khắp nơi trên internet. Đây là một trong những ví dụ chỉ cần vài dòng với các thư viện tích hợp sẵn [socketssl]

import datetime
import socket
import ssl

def get_num_days_before_expired[hostname: str, port: str = '443'] -> int:
    """
    Get number of days before an TLS/SSL of a domain expired
    """    
    context = ssl.create_default_context[]
    with socket.create_connection[[hostname, port]] as sock:
        with context.wrap_socket[sock, server_hostname=hostname] as ssock:
            ssl_info = ssock.getpeercert[]
            expiry_date = datetime.datetime.strptime[ssl_info['notAfter'], '%b %d %H:%M:%S %Y %Z']
            delta = expiry_date - datetime.datetime.utcnow[]
            print[f'{hostname} expires in {delta.days} day[s]']
            return delta.days


if __name__ == '__main__':
    get_num_days_before_expired['google.com']

Hàm getpeercert[] trả về một bản đồ chứa nhiều thông tin từ chứng chỉ SSL, bao gồm cả notAfter đại diện cho ngày hết hạn. Đối với hầu hết các trường hợp, mã này sẽ là đủ

Nhưng trong trường hợp của tôi, tôi cần lấy thông tin từ một miền sử dụng chứng chỉ SSL mà tôi đã tạo thủ công bằng Let's Encrypt certbot và tải nó lên một máy chủ lưu trữ được chia sẻ. Điều này là do gói lưu trữ chia sẻ không bao gồm tính năng tự động tạo chứng chỉ Let's Encrypt và gia hạn chúng. Vì một số lý do, đoạn mã trên sẽ đưa ra ngoại lệ CERTIFICATE_VERIFY_FAILED khi tôi chạy với tên miền đó

ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate [_ssl.c:1131]

Từ các giải pháp khác nhau do internet cung cấp, việc sử dụng gói

ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate [_ssl.c:1131]
0 [
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate [_ssl.c:1131]
1] ban đầu có vẻ phù hợp với tôi vì nó không đưa ra bất kỳ lỗi nào và trả về thông tin notAfter. Đây là một trong những mã tôi nhận được từ Stackoverflow

import datetime
import socket
import ssl
import OpenSSL

def get_num_days_before_expired[hostname: str, port: str = '443'] -> int:
    """
    Get number of days before an TLS/SSL of a domain expired
    """    
    cert = ssl.get_server_certificate[[hostname, int[port]]]
    x509 = OpenSSL.crypto.load_certificate[OpenSSL.crypto.FILETYPE_PEM, cert]
    expiry_date = datetime.datetime.strptime[x509.get_notAfter[].decode['utf-8'], '%Y%m%d%H%M%S%z']
    delta = expiry_date - datetime.datetime.now[datetime.timezone.utc]
    print[f'{hostname} expires in {delta.days} day[s]']
    return delta.days

Nhưng vì lý do nào đó, mã này không trả về cùng thông tin notafter với mã mà chúng tôi thấy bằng công cụ Chrome cho miền của tôi. Rõ ràng, đó là do hành vi của

ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate [_ssl.c:1131]
3 như được mô tả trong câu trả lời Stackoverflow này

Mặc dù không được ghi lại như vậy 

ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate [_ssl.c:1131]
4 chỉ coi 
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate [_ssl.c:1131]
5 đã cho là mục tiêu để kết nối nhưng không đặt 
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate [_ssl.c:1131]
6 là 
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate [_ssl.c:1131]
7 trong tiện ích mở rộng TLS SNI giống như các trình duyệt sẽ làm

https. // stackoverflow. com/a/52867893/1862500

Vì vậy, cuối cùng, tôi quyết định chỉ kết hợp hai giải pháp trên 😅

from urllib.request import ssl, socket
from datetime import datetime, timezone
import OpenSSL

def get_num_days_before_expired[hostname: str, port: str = '443'] -> int:
    """
    Get number of days before an TLS/SSL of a domain expired
    """
    context = ssl.SSLContext[]
    with socket.create_connection[[hostname, port]] as sock:
        with context.wrap_socket[sock, server_hostname = hostname] as ssock:
            certificate = ssock.getpeercert[True]
            cert = ssl.DER_cert_to_PEM_cert[certificate]
            x509 = OpenSSL.crypto.load_certificate[OpenSSL.crypto.FILETYPE_PEM, cert]
            cert_expires = datetime.strptime[x509.get_notAfter[].decode['utf-8'], '%Y%m%d%H%M%S%z']
            num_days = [cert_expires - datetime.now[timezone.utc]].days
            print[f'{hostname} expires in {num_days} day[s]']
            return num_days

if __name__ == '__main__':
    get_num_days_before_expired['ssis.sionministry.org']

Sử dụng mã này, tôi có thể truy xuất thông tin hết hạn chính xác từ chứng chỉ SSL chưa được xác minh của mình. Vì vậy, tôi có thể tạo một tập lệnh được lên lịch để thông báo cho tôi khi gần đến ngày hết hạn 🎉

Tôi không thể tìm thấy tài liệu tốt cho đối tượng x509, may mắn thay, chúng tôi vẫn có thể kiểm tra để biết cách tìm nạp thông tin khác. Vì vậy, chúng tôi có thể mở rộng tập lệnh để tìm nạp thông tin về chứng chỉ và tên miền

Làm cách nào để kiểm tra ngày hết hạn chứng chỉ SSL trong Python?

Để kiểm tra ngày hết hạn của chứng chỉ SSL, chúng tôi sẽ sử dụng thư viện OpenSSL . Gói này cung cấp giao diện cấp cao cho các chức năng trong thư viện OpenSSL. Sau đây là đoạn mã để sử dụng. Bạn có thể thực thi nó trong sổ ghi chép jupyter hoặc tạo một.

Làm cách nào để kiểm tra xem chứng chỉ của tôi có hợp lệ không?

Để kiểm tra chứng chỉ SSL trên bất kỳ trang web nào, tất cả những gì bạn cần làm là làm theo hai bước đơn giản. .
Trước tiên, hãy kiểm tra xem URL của trang web có bắt đầu bằng HTTPS không, trong đó S cho biết trang web có chứng chỉ SSL
Thứ hai, nhấp vào biểu tượng ổ khóa trên thanh địa chỉ để kiểm tra tất cả các thông tin chi tiết liên quan đến chứng chỉ

Làm cách nào để kiểm tra ngày hết hạn chứng chỉ X509?

Bạn có thể kiểm tra ngày hết hạn của chứng chỉ [ví dụ: để giúp khắc phục sự cố về chứng chỉ]. .
Mở cửa sổ dòng lệnh UNIX
Enter a query openssl s_client -servername -connect 2>/dev/null | openssl x509 -noout -dates ..

Làm cách nào để lấy chi tiết chứng chỉ SSL bằng Python?

Python nhận chứng chỉ máy chủ SNI .
Tạo kết nối SSL với tên và cổng DNS đã cho
Tạo một SSLContext
Tạo SSLSocket từ kết nối và ngữ cảnh được tạo ở bước 1 và 2
Nhận chứng chỉ từ ổ cắm và chuyển đổi nó thành đối tượng X509
Đóng ổ cắm
Trả lại chứng chỉ

Chủ Đề