Mã hóa / Giải mã RSA

Tạo cặp khóa RSA trong trình duyệt, mã hóa và giải mã với RSA-OAEP, ký với RSA-PSS hoặc PKCS#1 v1.5, và chuyển đổi khóa giữa các định dạng PEM SPKI, PKCS#8 và PKCS#1. Mọi thứ chạy cục bộ với engine WebCrypto — không có khóa hay thông điệp nào rời khỏi trang này.
RSA dùng để làm gì: thông điệp nhỏ và chữ ký, không phải dữ liệu khối. RSA mã hóa tối đa k−2·hashLen−2 byte mỗi lệnh gọi (190 byte cho khóa 2048-bit với SHA-256) và chậm hơn AES khoảng 1000×. Hệ thống thật dùng mã hóa lai: RSA bọc một khóa AES ngẫu nhiên, AES mã hóa dữ liệu (xem công cụ AES). Trang này tồn tại để học, kiểm thử và kiểm tra tương tác.
Kích thước modulus:
Hàm băm: Nhãn:
PKCS#1 v1.5 mã hóa cố ý không được triển khai: nó không có tính toàn vẹn bản mã và bị phá bởi tấn công oracle padding (Bleichenbacher). OAEP là chế độ mã hóa duy nhất mà thiết kế mới nên dùng.
Sơ đồ: Hàm băm:
Sơ đồ: Hàm băm:
Một lệnh gọi RSA mang được bao nhiêu? Giới hạn thông điệp OAEP = số byte khóa − 2×số byte hàm băm − 2:
ModulusSHA-256SHA-384SHA-512
1024-bit62 byte30 byte0 byte
2048-bit190 byte126 byte62 byte
3072-bit318 byte254 byte190 byte
4096-bit446 byte382 byte318 byte
Chữ ký không có giới hạn thông điệp — thông điệp được băm trước; bản thân chữ ký luôn đúng bằng kích thước khóa (256 byte cho 2048-bit).
Độ mạnh và vòng đời khóa (NIST SP 800-57 Phần 1 Bản 5): 1024-bit bị cấm dùng mới từ 2013; 2048-bit là mức tối thiểu hiện nay và còn chấp nhận đến 2030; 3072-bit trở lên là lựa chọn thoải mái cho 2031+. 8192-bit chạy được ở đây nhưng việc tạo có thể mất một phút hoặc hơn trong trình duyệt và đổi lấy độ an toàn mà hiện chẳng ai cần — ưu tiên 3072/4096.
Tương tác OpenSSL — các PEM mà trang này tạo ra đi vòng được trên dòng lệnh:
openssl pkeyutl -encrypt -pubin -inkey pub.pem -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256
openssl pkeyutl -decrypt -inkey priv.pem -pkeyopt rsa_padding_mode:oaep ...
openssl dgst -sha256 -sign priv.pem -out msg.sig msg.txt (PKCS#1 v1.5)  /  openssl dgst -sha256 -verify pub.pem -signature msg.sig msg.txt
PSS: thêm -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:32 vào cả hai lệnh dgst. Chuyển đổi định dạng khóa: openssl rsa -in key.pem -RSAPublicKey -out pubkey.pem chuyển SPKI ↔ PKCS#1 trên đĩa; trang này làm việc đó trong trình duyệt.
Tự kiểm tra
Chạy engine trang với các mốc độc lập: tạo khóa ở ba kích thước modulus, các vòng mã hóa/giải mã OAEP, ký/xác minh PSS và PKCS#1 v1.5 qua các hàm băm, một vector chữ ký v1.5 xác định khớp từng byte (được tạo bởi một triển khai native độc lập), một bản mã OAEP cố định và chữ ký PSS do OpenSSL tạo mà trang này phải giải mã/xác minh được (tương tác chéo triển khai), các vòng chuyển đổi định dạng PEM, và kiểm thử phủ định (chữ ký bị sửa, khóa sai phải thất bại). Không gì được gửi đi đâu cả.
Mã hóa và chữ ký RSA được giải thích từ đầu
Trong một câu: RSA là mật mã khóa công khai — ai cũng có thể mã hóa cho bạn bằng khóa công khai hoặc xác minh chữ ký của bạn, nhưng chỉ khóa riêng mới giải mã hoặc ký được — và độ an toàn của nó dựa trên việc phân tích một số 2048-bit thành hai số nguyên tố nằm ngoài khả năng của mọi máy tính hiện có.
RSA thực sự tính toán gì

Hai số nguyên tố và một cửa sập. Một khóa RSA bắt đầu là hai số nguyên tố ngẫu nhiên lớn p và q; tích của chúng n (modulus, do đó là "khóa 2048-bit") được công bố trong khóa công khai cùng số mũ e (hầu như luôn là 65537). Khóa riêng là số d, nghịch đảo modular của e − được tính từ p và q — cùng các phím tắt dẫn ra từ các số nguyên tố giúp thao tác khóa riêng nhanh gấp 4 lần.

Mã hóa và ký là cùng một phép lũy thừa. Mã hóa một khối thông điệp m nghĩa là tính me mod n; giải mã là cd mod n. Ký một khối đã định dạng sẵn bằng số mũ riêng và xác minh bằng số mũ công khai là cùng một thao tác theo chiều ngược lại. Mọi thứ khác trên trang này — OAEP, PSS, PKCS#1 — là về việc định dạng khối đó an toàn để lũy thừa thô không bao giờ bị lộ.

Vì sao nó chậm. Một phép toán RSA trên khóa 2048-bit tốn CPU gấp cỡ một triệu lần so với mã hóa một khối bằng AES. Đó là lý do TLS chỉ dùng RSA (hoặc ECC) để thống nhất một khóa AES ngẫu nhiên, rồi chuyển sang AES. Quy tắc tương tự áp dụng cho mọi thứ bạn xây: RSA cho khóa, AES cho dữ liệu.

Kích thước khóa. Độ dài modulus là núm an ninh: 1024-bit bị coi là đã phá vỡ theo chính sách (phân tích thừa số gần trong tầm với của các quốc gia), 2048-bit ≈ độ mạnh đối xứng 112-bit, 3072-bit ≈ 128-bit, 15360-bit sẽ tương đương khóa đối xứng 256-bit. Ghi chú hậu lượng tử: thuật toán Shor phá RSA hoàn toàn trên máy tính lượng tử lớn — một lý do nữa để giữ vòng đời khóa hữu hạn.

OAEP: padding mã hóa duy nhất bạn nên dùng

Vấn đề với RSA thô. RSA sách giáo khoa (me mod n trên thông điệp thô) là xác định và có thể bị bẻ cong: các thông điệp giống nhau mã hóa giống nhau, một kẻ tấn công có thể nhân bản mã với mã hóa của 2 để làm đôi bản rõ, và thông điệp ngắn bị phá bằng tấn công gặp-giữa. Không bao giờ được dùng trực tiếp.

OAEP làm gì. OAEP (PKCS#1 v2, RFC 8017) trộn thông điệp với một hạt giống ngẫu nhiên qua mạng Feistel dựa trên hàm băm trước khi lũy thừa. Kết quả: mã hóa có tính ngẫu nhiên (cùng bản rõ cho bản mã khác mỗi lần — chạy Mẫu OAEP hai lần rồi so sánh), can thiệp được phát hiện khi giải mã, và thông điệp phải vừa trong modulus với chỗ cho hàm băm — giới hạn k−2·hashLen−2 được liệt kê ở trên.

Nhãn. Một chuỗi công khai tùy chọn được ràng buộc vào padding. Hai bên phải cung cấp cùng nhãn hoặc giải mã thất bại — giống AAD trong AES-GCM, nó cho phép bạn ràng buộc bản mã với ngữ cảnh (tên tệp, mã thuật toán). Để trống là bình thường.

Cố ý không có mã hóa PKCS#1 v1.5. Padding thời 1993 không có tính toàn vẹn và rò rỉ thông tin qua hành vi lỗi giải mã (tấn công Bleichenbacher 1998 biến một oracle padding thành giải mã đầy đủ với vài triệu bản mã được tạo; nó vẫn phá các hệ thống thật như ROBOT, 2017). WebCrypto từ chối triển khai nó; trang này cũng vậy.

Chữ ký PSS so với PKCS#1 v1.5

Ký = băm, rồi đệm, rồi lũy thừa. Cả hai lược đồ đều băm thông điệp (SHA-256/384/512 ở đây), định dạng bản tóm tắt thành một khối, và áp dụng số mũ riêng. Xác minh định dạng lại khối bằng khóa công khai và so sánh. Khác biệt nằm ở cách định dạng:

Sơ đồNội dung khốiThuộc tính
RSASSA-PKCS1-v1_5digest + header digest-info, xác địnhMọi chữ ký của cùng một thông điệp đều giống hệt từng byte (kiểm tra tự động: vector v1.5 của nó là cố định). Hỗ trợ kiểu cũ phổ quát: chứng chỉ X.509, JWT RS*, TLS trước 1.3. Không có cách giả mạo đã biết khi xác minh được triển khai đúng.
RSA-PSSdigest + salt ngẫu nhiên có độ dài hàm băm, che MGF1Ngẫu nhiên — mỗi chữ ký khác nhau. An toàn đã được chứng minh trong mô hình random-oracle; được yêu cầu trong chứng chỉ TLS 1.3 và các giao thức mới hơn. Bên xác minh phải biết độ dài salt (trang này dùng độ dài hàm băm, mặc định phổ biến).

Nên chọn cái nào. Thiết kế mới: PSS. Tương tác với chứng chỉ, JWT (RS256) hiện có hoặc các bên cũ: v1.5. Cả hai đều an toàn như lược đồ chữ ký — câu chuyện thảm họa của v1.5 liên quan đến *padding mã hóa*, một cấu trúc khác dù có tên gần giống.

PKCS#1 so với PKCS#8 so với SPKI: vì sao cùng một khóa có bốn PEM

PEM chỉ là DER bọc base64. Tệp PEM là một nhãn cộng base64 của một cấu trúc DER (ASN.1 nhị phân). Nhãn cho biết cấu trúc nào — và đó là toàn bộ khác biệt giữa bốn định dạng mà trang này tạo ra:

Nhãn PEMCấu trúc DERChứa
-----BEGIN PUBLIC KEY-----SubjectPublicKeyInfo (SPKI)OID thuật toán + khóa công khai (n, e). "Khóa công khai" mặc định ở mọi nơi.
-----BEGIN RSA PUBLIC KEY-----PKCS#1 RSAPublicKeyThuần (n, e). OpenSSL cũ và một số stack nhúng.
-----BEGIN PRIVATE KEY-----PKCS#8 PrivateKeyInfoOID thuật toán + khóa riêng. Vỏ chứa không phụ thuộc thuật toán, mặc định hiện đại.
-----BEGIN RSA PRIVATE KEY-----PKCS#1 RSAPrivateKeyn, e, d, p, q, …. Đầu ra genrsa của OpenSSL kinh điển.

Cùng một khóa, vỏ bọc khác nhau. Bốn ô phía trên sau khi Tạo đều chứa cùng một cặp khóa — SPKI và PKCS#1 công khai chứa cùng n, e; PKCS#8 và PKCS#1 riêng chứa cùng các giá trị riêng. Trang này chuyển đổi giữa chúng không mất mát trong trình duyệt (tự kiểm tra của nó chạy vòng chuyển đổi). Nhầm lẫn định dạng là nguyên nhân phổ biến nhất của lỗi "khóa không hợp lệ" khi dán khóa giữa các công cụ.

PEM được mã hóa (-----BEGIN ENCRYPTED PRIVATE KEY-----) là PKCS#8 với lớp mã hóa dựa trên mật khẩu. Trang này không nhận chúng — hãy giải mã ngoại tuyến trước (openssl pkcs8 -in enc.pem -out plain.pem).

Những lỗi thường gặp
  • Mã hóa dữ liệu khối lớn bằng RSA. Ngoài giới hạn kích thước OAEP cứng, nó chậm một cách vô lý. Mẫu là: tạo một khóa AES ngẫu nhiên, mã hóa khóa AES bằng RSA-OAEP, mã hóa dữ liệu bằng AES-GCM, gửi cả hai phần.
  • Gửi khóa riêng vì "đây chỉ là thử nghiệm". Các khóa mẫu nhúng trong vector của trang này được công bố có chủ ý — chúng chỉ có thể ký dữ liệu thử nghiệm. Khóa của bạn không như vậy.
  • Xác minh bằng khóa riêng. Điều này thường "chạy được" về toán và một số công cụ cho phép, nhưng phân phát khóa riêng cho người xác minh phá hỏng toàn bộ mô hình. Hãy xác minh bằng khóa công khai (trang này buộc điều đó).
  • So sánh chữ ký từng byte để kiểm tra mã. PSS (và OAEP) có tính ngẫu nhiên: hai lần chạy đúng cho đầu ra khác nhau. So sánh bằng cách xác minh, không bằng đẳng thức — chỉ chữ ký v1.5 là xác định.
  • Khóa 1024-bit "vì nhanh hơn". Chúng cũng nằm trong tầm phân tích của những kẻ có nhiều tiền. Lập luận về tốc độ đã chết cùng với mã hóa lai: RSA chạy một lần mỗi phiên, không phải mỗi byte.
  • Mã hóa cứng độ dài salt khác độ dài hàm băm cho PSS. Bên xác minh phải đồng ý; độ dài hàm băm (lựa chọn của trang này và -sigopt rsa_pss_saltlen:32 của OpenSSL) là mặc định tương tác được.
Câu hỏi thường gặp

Vì sao giải mã của tôi thất bại với "sai khóa, sai hàm băm hoặc bản mã bị hỏng"? Giải mã OAEP thất bại khi bất kỳ điều nào sau đây xảy ra: đó không phải khóa riêng tương ứng, hàm băm khác với phía mã hóa, nhãn khác, hoặc một byte bản mã bị đổi. Thông báo lỗi cố ý giống hệt nhau cho mọi trường hợp — tiết lộ kiểm tra nào thất bại chính là rò rỉ oracle padding.

Có thể lấy khóa công khai từ khóa riêng không? Có — các định dạng riêng cũng chứa n và e. Trang này hiển thị cả hai sau khi Tạo; OpenSSL làm việc đó bằng openssl pkey -in priv.pem -pubout.

Vì sao dùng 65537 làm số mũ? Nó là số nguyên tố, chỉ có hai bit 1 (lũy thừa nhanh), và đủ lớn để tránh các tấn công số mũ nhỏ nhắm vào e=3. Nó là lựa chọn chuẩn từ thập niên 1990; một số mũ "lạ" thường có nghĩa là khóa yếu hoặc không chuẩn.

RSA có chết vì máy tính lượng tử không? Chưa — chưa có máy nào chạy được thuật toán Shor trên modulus 2048-bit. Nhưng dữ liệu cần giữ bí mật sang những năm 2030 trở đi đã đang được di chuyển; các lược đồ lai PQC là lời giải kỹ thuật.

Còn openssl rsautl thì sao? Nó là tổ tiên đã bị phản đối của pkeyutl. Nó mặc định dùng padding mã hóa v1.5 — thêm một lý do nó bị loại bỏ.