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ốnSẽ 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
0Thậ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útthư 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
5Sở 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 đẹpPyOpenSSL
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 x509Vì đâ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 danhSau đó, 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 PyOpenSSLTạ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