Python tạo chứng chỉ SSL

Nếu bạn có biểu tượng ổ khóa bên cạnh URL trong trình duyệt, điều đó có nghĩa là máy chủ đã có chứng chỉ SSL. Chứng chỉ này thường được ký bởi Cơ quan cấp chứng chỉ, như Digicert, Comodo, Let's Encrypt, v.v. Chứng chỉ SSL sẽ tải xuống trình duyệt của bạn và xác minh rằng miền hợp lệ bởi cơ quan cấp chứng chỉ đáng tin cậy

hình ảnh từ ssl. com

Chứng chỉ SSL tự ký

Cơ quan cấp chứng chỉ của bên thứ ba có nhiều chi phí hơn, nhưng bạn vẫn có thể sử dụng Chứng chỉ SSL cục bộ của mình. Điều đó có nghĩa là chứng chỉ của bạn sẽ do chính bạn ký và hoàn toàn miễn phí

Yêu cầu ký chứng chỉ [CSR]

CSR là tệp chứa bất kỳ thông tin nào để yêu cầu khóa chung có chữ ký của Tổ chức phát hành chứng chỉ [CA]. Khi yêu cầu chứng chỉ cho CA, trước tiên bạn phải tạo tệp CSR và tải tệp đó lên CA

Cơ quan cấp chứng chỉ địa phương

Cách dễ dàng để tạo Cơ quan cấp chứng chỉ [CA] chỉ với bốn bước với lệnh openssl
1. Tạo tập tin cấu hình và lưu nó vào ca. cnf

[req]
default_bits = 2048
prompt = no
default_md = sha256
encrypt_key = no
distinguished_name = dn
[dn]
C = ID # country code
O = Local Digital Cert Authority # organization
OU = www.ca.local # organization unit/department
CN = Self-Signed Root CA # common name / your cert name

2. Tạo khóa riêng cho CA

openssl genrsa -out ca-private.key 2048

3. Tạo CSR với tệp cấu hình và ca-private. Chìa khóa

openssl req -new -key ca-private.key -out ca.csr -config ca.cnf

4. Tạo chứng chỉ CA tự ký

openssl x509 -req -days 3650 -n ca.csr -signkey ca-private.key -out ca-public.crt

Giải trình
-days →how long your CA certificate is valid, usually CA have 25 years validity or 9125 days.

Và cuối cùng chúng ta có một số tệp trong thư mục

  • ca. cnf → tệp cấu hình chứa thông tin thuộc tính
  • ca-riêng. khóa → khóa riêng để ký chứng chỉ máy chủ
  • ca. csr → yêu cầu ký chứng chỉ để tạo khóa công khai CA
  • ca-công. crt → chứng chỉ gốc để ký chứng chỉ máy chủ

Để thực hiện lệnh này một cách đơn giản, tôi sử dụng Python và mô-đun quy trình con

Tập lệnh Python để đăng ký CSR

Nếu bạn đã từng yêu cầu chứng chỉ để thanh toán cho Tổ chức phát hành chứng chỉ [Digicert/ Comodo/ Let's Encrypt/ v.v. ], bạn phải tải Yêu cầu ký chứng chỉ [CSR] của mình lên trang web

Lấy cảm hứng từ quy trình đó, tôi muốn tạo một tập lệnh Python để tự động hóa quy trình ký của cơ quan cấp chứng chỉ cục bộ chỉ bằng cách sử dụng tệp CSR

Tập lệnh này rất đơn giản, giống như tập lệnh bash. Nhưng trong thời gian này, tôi chỉ muốn sử dụng tập lệnh Python. Chỉ cần sao chép và sử dụng tập lệnh của tôi bên dưới

Tập lệnh này đã được thử nghiệm trên Linux, nếu bạn sử dụng Windows, tôi khuyên bạn nên sử dụng Git Bash để chạy tập lệnh Python này vì Git Bash đã cài đặt sẵn OpenSSL

Cách sử dụng tập lệnh Python

ca. crt là chứng chỉ gốc và máy chủ. crt là khóa công khai được ký bởi CA

Chứng chỉ CA gốc

Chứng chỉ tên miền

Thưởng

Bạn có thể tìm thấy kho lưu trữ của tôi trong liên kết này bên dưới

GitHub - febimudiyanto/local-ca

Đóng góp cho sự phát triển của febimudiyanto/local-ca bằng cách tạo một tài khoản trên GitHub

github. com

Phần kết luận

Chứng chỉ tự ký được sử dụng miễn phí nhưng bạn phải nhập chứng chỉ gốc vào tất cả các trình duyệt theo cách thủ công. Theo mặc định, trình duyệt của bạn sẽ tải xuống chứng chỉ từ Tổ chức phát hành chứng chỉ nổi tiếng. Tôi khuyên bạn nên sử dụng chứng chỉ tự ký này trong ứng dụng hoặc máy chủ web cục bộ của mình. Chứng chỉ tự ký được sử dụng miễn phí nhưng bạn phải nhập chứng chỉ gốc vào tất cả các trình duyệt theo cách thủ công. Theo mặc định, trình duyệt của bạn sẽ tải xuống chứng chỉ từ Tổ chức phát hành chứng chỉ nổi tiếng. Tôi khuyên bạn nên sử dụng chứng chỉ tự ký này trong ứng dụng hoặc máy chủ web cục bộ của mình

Thêm nội dung tại PlainEnglish. io. Đăng ký nhận bản tin hàng tuần miễn phí của chúng tôi. Theo dõi chúng tôi trên Twitter và LinkedIn. Tham gia cộng đồng của chúng tôi

Đôi khi tôi cần viết một máy chủ mạng đơn giản để mô phỏng một ứng dụng mà tôi đang tích hợp. Thông thường, đây cuối cùng là một tập lệnh Python bỏ đi cho phép tôi dễ dàng kiểm tra theo yêu cầu và trả về phản hồi cơ bản. Thật tiện lợi để xác minh những gì tôi đang gửi không đúng định dạng. Ngoài ra, nó giúp đảm bảo trình phân tích cú pháp phản hồi của tôi ít nhất là lành mạnh

Phần mềm tôi làm việc yêu cầu kết nối bảo mật TLS tới tất cả các điểm cuối từ xa. Vì đây là những tập lệnh bỏ đi nên tôi thấy mình chạy dòng lệnh

    import random
    from OpenSSL import crypto
0 thường xuyên hơn tôi muốn

Sẽ thật lý tưởng nếu có một mô-đun Python sẽ tạo các tệp chứng chỉ và khóa cho tôi. Thứ gì đó mà tôi có thể giữ lại, thả vào một trong những tập lệnh này và có TLS mà không cần các bước bên ngoài để chạy

    import random
    from OpenSSL import crypto
0

Thật không may, Python không có mô-đun tích hợp để tạo hoặc thao tác chứng chỉ x509. Điều này thật đáng tiếc vì điều đó có nghĩa là chúng ta cần dựa vào các thành phần được cài đặt bên ngoài

Tôi nhận ra rằng tôi đang tiết kiệm cho mình 2 phút bằng cách không chạy

    import random
    from OpenSSL import crypto
0. Điều đó nói rằng, có kịch bản xử lý mọi thứ giúp chia sẻ với đồng nghiệp dễ dàng hơn. Cung cấp cho họ các hướng dẫn như “chạy tập lệnh” so với “chạy lệnh này, sau đó chạy tập lệnh” giúp tôi dễ dàng hơn một chút

thư viện

Để tạo chứng chỉ, hai thư viện tôi đã tìm thấy là PyOpenSSL và pyca/cryptography. Vì cả hai đều không phải là một phần của Python tiêu chuẩn, tôi đã triển khai tính năng phát hiện và dự phòng với hai thư viện này. Nếu cả hai đều không được cài đặt, bạn không gặp may

Nếu bạn làm việc nhiều với Python, rất có thể một hoặc cả hai đã được cài đặt. Khi tôi cài đặt Mật mã, tôi phát hiện ra rằng nó đã được cài đặt như một phần phụ thuộc cho một ứng dụng khác mà tôi có

Mật mã

self_sign_cert. py

import socket

def _gen_openssl[]:
    import random
    from OpenSSL import crypto

    pkey = crypto.PKey[]
    pkey.generate_key[crypto.TYPE_RSA, 2048]

    x509 = crypto.X509[]
    subject = x509.get_subject[]
    subject.commonName = socket.gethostname[]
    x509.set_issuer[subject]
    x509.gmtime_adj_notBefore[0]
    x509.gmtime_adj_notAfter[5*365*24*60*60]
    x509.set_pubkey[pkey]
    x509.set_serial_number[random.randrange[100000]]
    x509.set_version[2]
    x509.add_extensions[[
        crypto.X509Extension[b'subjectAltName', False,
            ','.join[[
                'DNS:%s' % socket.gethostname[],
                'DNS:*.%s' % socket.gethostname[],
                'DNS:localhost',
                'DNS:*.localhost']].encode[]],
        crypto.X509Extension[b"basicConstraints", True, b"CA:false"]]]

    x509.sign[pkey, 'SHA256']

    return [crypto.dump_certificate[crypto.FILETYPE_PEM, x509],
        crypto.dump_privatekey[crypto.FILETYPE_PEM, pkey]]

def _gen_cryptography[]:
    import datetime
    from cryptography import x509
    from cryptography.hazmat.backends import default_backend
    from cryptography.hazmat.primitives import hashes
    from cryptography.hazmat.primitives.asymmetric import rsa
    from cryptography.hazmat.primitives import serialization
    from cryptography.x509.oid import NameOID

    one_day = datetime.timedelta[1, 0, 0]
    private_key = rsa.generate_private_key[
            public_exponent=65537,
            key_size=2048,
            backend=default_backend[]]
    public_key = private_key.public_key[]

    builder = x509.CertificateBuilder[]
    builder = builder.subject_name[x509.Name[[x509.NameAttribute[NameOID.COMMON_NAME, socket.gethostname[]]]]]
    builder = builder.issuer_name[x509.Name[[x509.NameAttribute[NameOID.COMMON_NAME, socket.gethostname[]]]]]
    builder = builder.not_valid_before[datetime.datetime.today[] - one_day]
    builder = builder.not_valid_after[datetime.datetime.today[] + [one_day*365*5]]
    builder = builder.serial_number[x509.random_serial_number[]]
    builder = builder.public_key[public_key]
    builder = builder.add_extension[
        x509.SubjectAlternativeName[[
            x509.DNSName[socket.gethostname[]],
            x509.DNSName['*.%s' % socket.gethostname[]],
            x509.DNSName['localhost'],
            x509.DNSName['*.localhost'],
        ]],
        critical=False]
    builder = builder.add_extension[x509.BasicConstraints[ca=False, path_length=None], critical=True]

    certificate = builder.sign[
        private_key=private_key, algorithm=hashes.SHA256[],
        backend=default_backend[]]

    return [certificate.public_bytes[serialization.Encoding.PEM],
        private_key.private_bytes[serialization.Encoding.PEM,
            serialization.PrivateFormat.PKCS8,
            serialization.NoEncryption[]]]

def gen_self_signed_cert[]:
    '''
    Returns [cert, key] as ASCII PEM strings
    '''
    try:
        return _gen_openssl[]
    except:
        try:
            return _gen_cryptography[]
        except:
            return [None, None]
    return [None, None]

if __name__ == '__main__':
    print[gen_self_signed_cert[]]

Lazy loading để dự phòng

PyOpenSSL và Mật mã đều được tải lười biếng trong các chức năng tương ứng của chúng. Điều này cho phép phát hiện bằng cách thử gọi hàm trong khối

    import random
    from OpenSSL import crypto
3. Nếu không chạy được thì rất có thể là do không có mô-đun. Đây chính xác là cách chức năng chính
    import random
    from OpenSSL import crypto
4 hoạt động. Thật mệt mỏi khi tạo bằng PyOpenSSL và nếu điều đó không thành công, hãy thử Mật mã học. Nếu cả hai đều không thành công thì bạn không may mắn và chứng chỉ và chìa khóa được trả lại đều là
    import random
    from OpenSSL import crypto
5

Sở thích của PyOpenSSL so với Mật mã là tùy ý. Cả hai đều có khả năng tạo chứng chỉ tự ký. Tôi không lo cái nào hoạt động tốt hơn vì các chức năng này không làm được gì nhiều. Ngoài ra, nó không giống như những thứ này sẽ được gọi lặp đi lặp lại trong mã quan trọng về hiệu năng

Cuối cùng, việc cuối cùng được sử dụng thực sự không quan trọng

Một lưu ý về SubjectAlternativeName

Một

    import random
    from OpenSSL import crypto
6 rất quan trọng. Chúng tôi sẽ sử dụng tên máy chủ làm tên chung, điều này thật tuyệt nếu bạn chỉ muốn truy cập hệ thống bằng tên máy chủ của mình. Hầu hết thời gian tôi thấy
    import random
    from OpenSSL import crypto
7 dễ dàng hơn một chút. Có nó được liệt kê là một
    import random
    from OpenSSL import crypto
6 làm cho mọi thứ dễ dàng hơn. Trong khi chúng tôi đang ở đó, chúng tôi có thể thêm
    import random
    from OpenSSL import crypto
9 thẻ đại diện cho cả tên máy chủ và
    import random
    from OpenSSL import crypto
7 bởi vì, tại sao không. Điều này sẽ cung cấp cho chúng tôi một số tính linh hoạt tốt đẹp

PyOpenSSL

    import random
    from OpenSSL import crypto

Bắt đầu bằng cách nhập PyOpenSSL

    pkey = crypto.PKey[]
    pkey.generate_key[crypto.TYPE_RSA, 2048]

Tiếp theo, chúng tôi sẽ tạo khóa cho chứng chỉ

    x509 = crypto.X509[]
    subject = x509.get_subject[]
    subject.commonName = socket.gethostname[]
    x509.set_issuer[subject]

Chúng tôi sẽ cần một đối tượng

    pkey = crypto.PKey[]
    pkey.generate_key[crypto.TYPE_RSA, 2048]
1 để đặt chủ đề và người phát hành. Tuy nhiên, hàm tạo cho lớp
    pkey = crypto.PKey[]
    pkey.generate_key[crypto.TYPE_RSA, 2048]
2 yêu cầu một đối tượng
    pkey = crypto.PKey[]
    pkey.generate_key[crypto.TYPE_RSA, 2048]
2 khác để sao chép. Chúng tôi không thể tạo một cái trực tiếp vì vậy chúng tôi sẽ lấy đối tượng trống ra khỏi x509 mà chúng tôi đang làm việc. Đối tượng chủ đề vẫn thuộc sở hữu của x509 nên việc sửa đổi nó cũng sẽ sửa đổi x509

Vì đây là bản tự ký nên chúng tôi cũng là công ty phát hành

    x509.gmtime_adj_notBefore[0]
    x509.gmtime_adj_notAfter[5*365*24*60*60]

Cách dễ nhất để đặt ngày cho đối tượng là sử dụng hàm

    pkey = crypto.PKey[]
    pkey.generate_key[crypto.TYPE_RSA, 2048]
4 Chúng tôi sẽ sử dụng phạm vi từ bây giờ [0] đến 5 năm trong tương lai

    x509.set_pubkey[pkey]

Liên kết khóa chúng tôi đã tạo với chứng chỉ

    x509.set_serial_number[random.randrange[100000]]

Số sê-ri cần phải là duy nhất nên chúng tôi sẽ sử dụng số ngẫu nhiên

    x509.set_version[2]
    x509.add_extensions[[
        crypto.X509Extension[b'subjectAltName', False,
            ','.join[[
                'DNS:%s' % socket.gethostname[],
                'DNS:*.%s' % socket.gethostname[],
                'DNS:localhost',
                'DNS:*.localhost']].encode[]],
        crypto.X509Extension[b"basicConstraints", True, b"CA:false"]]]

Chúng ta phải đặt phiên bản chứng chỉ thành 3. Mã cho biết 2 nhưng PyOpenSSL bắt đầu đếm phiên bản ở mức 0. Vì vậy,

    pkey = crypto.PKey[]
    pkey.generate_key[crypto.TYPE_RSA, 2048]
5 nghĩa là đây là chứng chỉ phiên bản 3. Điều này là cần thiết để
    import random
    from OpenSSL import crypto
6 được vinh danh

Sau đó, chúng tôi sẽ thêm tất cả các tên thay thế chủ đề mà chúng tôi đã thảo luận trước đó

    x509.sign[pkey, 'SHA256']

    return [crypto.dump_certificate[crypto.FILETYPE_PEM, x509],
        crypto.dump_privatekey[crypto.FILETYPE_PEM, pkey]]

Cuối cùng, chúng tôi sẽ ký và kết xuất chứng chỉ và dữ liệu chính

mật mã

Mật mã làm cho quy trình tạo chứng chỉ dễ dàng hơn nhiều so với OpenSSl vì nó có một lớp

    pkey = crypto.PKey[]
    pkey.generate_key[crypto.TYPE_RSA, 2048]
7 tiện dụng. Phần lớn mã ở đây là từ ví dụ. Tôi sẽ không đi vào quá nhiều chi tiết vì điều này phản ánh quy trình được mô tả trong phần PyOpenSSL

Tạo trình xây dựng và bắt đầu thiết lập các thuộc tính bằng cách sử dụng các hàm setter dễ sử dụng. Điều đó nói rằng, đôi khi nó hơi dài dòng. Chỉ định RSA

    pkey = crypto.PKey[]
    pkey.generate_key[crypto.TYPE_RSA, 2048]
8 là bắt buộc ngay cả khi tài liệu của anh ấy nói rằng luôn sử dụng 65537. Tôi ngạc nhiên rằng đây không phải là một đối số tùy chọn với giá trị mặc định đó. Một nơi khác là đặt tên phổ biến và tên nhà phát hành yêu cầu chỉ định đúng
    pkey = crypto.PKey[]
    pkey.generate_key[crypto.TYPE_RSA, 2048]
9

    return [certificate.public_bytes[serialization.Encoding.PEM],
        private_key.private_bytes[serialization.Encoding.PEM,
            serialization.PrivateFormat.PKCS8,
            serialization.NoEncryption[]]]

Không có trong ví dụ về tài liệu đang xuất chứng chỉ cuối cùng

Phần kết luận

Tôi thấy thú vị về sự khác biệt của API PyOpenSSL và Cryptography. Mặc dù PyOpenSSL không sạch bằng nhưng Mật mã dài dòng hơn rất nhiều. Điều đó nói rằng, cả hai đều dễ sử dụng như nhau

Bây giờ tôi có một mô-đun rất dễ sử dụng, thả vào để tạo chứng chỉ tự ký. Điều này sẽ làm cho việc viết các điểm cuối kiểm tra dễ dàng hơn một chút và chắc chắn sẽ dễ dàng chia sẻ các tập lệnh hơn

Làm cách nào để tạo chứng chỉ SSL bằng Python?

Quá trình tự tạo chứng chỉ SSL cho ứng dụng Python cục bộ của chúng tôi có ba bước. .
Tạo khóa RSA riêng
Tạo yêu cầu ký chứng chỉ [CSR] bằng khóa riêng
Ký yêu cầu CSR để tạo chứng chỉ

Làm cách nào để tạo chứng chỉ bằng Python?

Tập lệnh Python để tạo chứng chỉ từ CSV đã chuẩn bị .
Chuẩn bị đầu vào
Thử nghiệm với PILLOW để đặt văn bản vào vị trí
Chạy Script để tạo tất cả các chứng chỉ

Tôi có thể tạo chứng chỉ SSL của riêng mình không?

Về mặt kỹ thuật, bất kỳ ai cũng có thể tạo chứng chỉ SSL của riêng mình bằng cách tạo cặp khóa công khai-riêng tư và bao gồm tất cả thông tin được đề cập ở trên . Các chứng chỉ như vậy được gọi là chứng chỉ tự ký vì chữ ký số được sử dụng, thay vì từ CA, sẽ là khóa riêng của trang web.

Python sử dụng chứng chỉ SSL nào?

Theo mặc định, mô-đun ssl của Python sử dụng gói chứng chỉ CA hệ thống - /etc/pki/tls/certs/ca-bundle. crt - được vận chuyển như một phần của gói chứng chỉ ca. Bên trong mạng nội bộ của công ty, các máy chủ thường sử dụng chứng chỉ do CA công ty nội bộ cấp thay vì CA Internet công cộng.

Chủ Đề