Mỗi khi mật hóa dữ liệu bằng giải thuật đối xứng để chuyển giao, bạn nên tạo một khóa mới, được gọi là khóa phiên session key.. Sử dụng khóa phiên có hai lợi ích chính: • Nếu ai đó khôn
Trang 11.1 Lưu trữ khóa bất đối xứng một cách an toàn
V Bạn cần lưu trữ cặp khóa bất đối xứng vào một nơi an toàn để ứng dụng của bạn có thể truy xuất được dễ dàng
# Dựa vào chức năng lưu trữ khóa do hai lớp giải thuật bất đối xứng cung cấp (RSACryptoServiceProvider và DSACryptoServiceProvider—thuộc không gian tên System.Security.Cryptography)
Cả hai lớp giải thuật bất đối xứng—RSACryptoServiceProvider và
DSACryptoServiceProvider—đều bọc lấy các chức năng do CSP (Cryptographic Service
Provider—một thành phần của Win32 CryptoAPI) hiện thực Ngoài các dịch vụ như mật
hóa, giải mật hóa, và chữ ký số, mỗi CSP còn cung cấp một kho chứa khóa (key
container)
Kho chứa khóa là vùng lưu trữ dành cho các khóa mà CSP quản lý; CSP sử dụng cơ chế
bảo mật của hệ điều hành và phép mật hóa mạnh để bảo vệ nội dung của kho chứa khóa Kho chứa khóa cho phép ứng dụng dễ dàng truy xuất khóa mà không ảnh hưởng đến tính
bảo mật của khóa Khi gọi các hàm của một CSP, ứng dụng cần chỉ định tên của kho chứa khóa và CSP sẽ truy xuất các khóa cần thiết Vì khóa không truyền từ CSP đến ứng
dụng nên ứng dụng không thể làm hại tính bảo mật của khóa
Lớp RSACryptoServiceProvider và DSACryptoServiceProvider cho phép bạn cấu hình
hiện thực CSP nằm dưới bằng một thể hiện của lớp System.Security.Cryptography
CspParameters Để cấu hình cho một đối tượng RSACryptoServiceProvider hay DSACryptoServiceProvider sử dụng một kho chứa khóa cụ thể, bạn phải hoàn tất các bước dưới đây:
1 Tạo một đối tượng CspParameters
2 Thiết lập trường KeyContainerName của đối tượng CspParameters là một giá trị chuỗi mô tả tên của kho chứa khóa cần sử dụng; chuỗi có thể chứa khoảng trắng
3 Tạo một đối tượng RSACryptoServiceProvider hay DSACryptoServiceProvider, và truyền đối tượng CspParameters làm đối số cho phương thức khởi dựng
Nếu kho chứa khóa tồn tại bên trong tầm vực của CSP và chứa các khóa thích hợp, CSP
sẽ sử dụng các khóa này khi thực hiện các thao tác mật mã Nếu kho chứa khóa hay khóa
không tồn tại, CSP sẽ tự động tạo khóa mới Để buộc CSP lưu trữ các khóa mới được tạo
vào kho chứa khóa, bạn phải thiết lập thuộc tính PersistKeyInCsp (của đối tượng RSACryptoServiceProvider hay DSACryptoServiceProvider) là true
Phương thức LoadKeys dưới đây là một đoạn trích trong file
StoreAsymmetricKeyExample.cs (xem đĩa CD đính kèm) LoadKeys tạo một đối tượng
RSACryptoServiceProvider và cấu hình cho nó sử dụng một kho chứa khóa có tên là
Trang 2MyKeys Bằng cách chỉ định PersistKeyInCsp là true, giải thuật sẽ tự động lưu trữ các khóa mới được tạo vào kho chứa khóa này
// Phương thức này tạo một đối tượng RSACryptoServiceProvider
// và nạp các khóa từ một kho chứa khóa nếu chúng tồn tại; nếu không,
// RSACryptoServiceProvider sẽ tự động tạo các khóa mới và lưu
// chúng vào kho chứa khóa để sử dụng sau này
public static void LoadKeys(string container) {
// Tạo một đối tượng CspParameters và thiết lập trường
// KeyContainerName là tên của kho chứa khóa
System.Security.Cryptography.CspParameters cspParams =
new System.Security.Cryptography.CspParameters();
cspParams.KeyContainerName = container;
// Tạo một đối tượng giải thuật RSA và truyền đối tượng
// CspParameters làm đối số trong phương thức khởi dựng
using (System.Security.Cryptography.RSACryptoServiceProvider
rsaAlg = new
System.Security.Cryptography.RSACryptoServiceProvider(cspParams)){
// Cấu hình cho đối tượng RSACryptoServiceProvider
// lưu trữ khóa vào kho chứa khóa
rsaAlg.PersistKeyInCsp = true;
// Hiển thị PUBLIC KEY
System.Console.WriteLine(rsaAlg.ToXmlString(false));
}
}
Lớp RSACryptoServiceProvider và DSACryptoServiceProvider không cung cấp phương thức nào trực tiếp gỡ bỏ kho chứa khóa Để xóa các khóa đã được lưu trữ, bạn hãy thiết lập giá trị của PersistKeyInCsp là false và gọi phương thức Clear hay Dispose của đối tượng RSACryptoServiceProvider hay DSACryptoServiceProvider Phương thức DeleteKeys dưới đây sẽ trình bày kỹ thuật này:
// Phương thức này tạo một đối tượng RSACryptoServiceProvider
// và xóa các khóa hiện có khỏi kho chứa khóa
public static void DeleteKeys(string container) {
// Tạo một đối tượng CspParameters và thiết lập trường
// KeyContainerName là tên của kho chứa khóa cần xóa
Trang 3System.Security.Cryptography.CspParameters cspParams =
new System.Security.Cryptography.CspParameters();
cspParams.KeyContainerName = container;
// Tạo một đối tượng giải thuật RSA và truyền đối tượng
// CspParameters làm đối số trong phương thức khởi dựng
using (System.Security.Cryptography.RSACryptoServiceProvider
rsaAlg = new
System.Security.Cryptography.RSACryptoServiceProvider(cspParams)){
// Cấu hình cho đối tượng RSACryptoServiceProvider
// không lưu trữ khóa vào kho chứa khóa
rsaAlg.PersistKeyInCsp = false;
// Hiển thị PUBLIC KEY Vì chúng ta gọi Dispose()
// sau lời gọi này nên các khóa hiện có sẽ không thay đổi
// cho đến khi phương thức được gọi lần thứ hai
System.Console.WriteLine(rsaAlg.ToXmlString(false));
// Vì mã lệnh nằm trong khối "using" nên Dispose được gọi
// trên đối tượng RSACryptoServiceProvider Vì đối tượng
// này được cấu hình là không lưu trữ khóa nên kho chứa khóa
// sẽ bị xóa Thay vì gọi Dispose(), gọi rsaAlg.Clear()
// sẽ có cùng tác dụng, vì nó gián tiếp gọi Dispose()
}
}
Win32 CryptoAPI hỗ trợ cả user-key-store và machine-key-store Hệ điều hành Windows
bảo đảm một user-key-store chỉ có thể được truy xuất bởi người đã tạo ra nó, nhưng một
machine-key-store có thể được truy xuất bởi bất kỳ người dùng nào của máy Theo mặc
định, lớp RSACryptoServiceProvider và DSACryptoServiceProvider sẽ sử dụng
user-key-store Bạn có thể chỉ định sử dụng machine-key-store bằng cách thiết lập thuộc tính
tĩnh UseMachineKeyStore của lớp RSACryptoServiceProvider hay DSACryptoServiceProvider là true Điều này sẽ có tác dụng với tất cả mã lệnh đang chạy trong miền ứng dụng hiện hành Nếu muốn kiểm soát chặt chẽ hơn, bạn có thể thiết lập thuộc tính CspParameters.Flags là giá trị System.Security.Cryptography.CspProviderFlags.UseMachineKeyStore trước khi tạo đối tượng mật hóa bất đối xứng
# Bạn nên xét các yêu cầu bảo mật một cách cẩn thận trước khi chọn sử dụng
machine-key-store Thực tế là người dùng nào có quyền truy xuất máy đều có
Trang 4thể giành được quyền truy xuất các khóa trong kho lưu trữ, điều này phủ định hầu hết các lợi ích do phép mật hóa bất đối xứng mang lại
1.2 Trao đổi khóa phiên đối xứng một cách an toàn
V Bạn cần trao đổi dữ liệu đã-được-mật-hóa-đối-xứng với ai đó, và bạn cần một
biện pháp an toàn để phân bổ khóa phiên (session key) đối xứng cùng với dữ
liệu
# Sử dụng cơ chế trao đổi khóa do lớp System.Security.Cryptography RSACryptoServiceProvider hiện thực Theo cơ chế này, khóa đối xứng sẽ được
mật hóa bất đối xứng bằng khóa công khai (public key) của người nhận Theo
đó, bạn có thể gửi khóa đối xứng mật-hóa cùng với dữ liệu
đã-được-mật-hóa Người nhận phải giải mật hóa khóa đối xứng bằng khóa riêng (private
key), rồi mới tiến hành giải mật hóa dữ liệu
Mỗi khi mật hóa dữ liệu (bằng giải thuật đối xứng) để chuyển giao, bạn nên tạo một khóa
mới, được gọi là khóa phiên (session key) Sử dụng khóa phiên có hai lợi ích chính:
• Nếu ai đó (không được phép) lấy được nhiều khối ciphertext đã được mật hóa bằng cùng một khóa đối xứng, khả năng người đó giải được dữ liệu sẽ tăng cao
• Nếu ai đó tìm được khóa phiên của bạn, người này chỉ có thể truy xuất được một tập
dữ liệu nào đó đã-được-mật-hóa, chứ không phải tất cả các bí mật của bạn ở quá khứ và tương lai
Vấn đề đối với khóa phiên là phân bổ và bảo mật khóa Một giải pháp là thỏa thuận một lượng lớn khóa phiên với những người mà bạn cần trao đổi dữ liệu với họ Thật không may, việc này nhanh chóng trở nên khó quản lý; và thực tế là tất cả các khóa của bạn trong tương lai đều được lưu trữ tại một nơi nào đó, điều này tăng khả năng chúng sẽ bị xâm hại Cách tốt hơn là gửi khóa phiên theo một dạng được mật hóa mạnh cùng với dữ
liệu mà bạn đã mật hóa với khóa đó—quá trình này được gọi là trao đổi khóa (key
exchange)
Quá trình trao đổi khóa sử dụng phép mật hóa bất đối xứng để mật hóa khóa phiên đối
xứng Nếu muốn gửi dữ liệu cho ai đó, bạn tạo một khóa phiên đối xứng, mật hóa dữ liệu,
và rồi mật hóa khóa phiên bằng khóa công khai của người nhận Khi nhận được dữ liệu, người nhận giải mật hóa khóa phiên bằng khóa riêng của họ, và rồi giải mật hóa dữ liệu
Quan trọng là việc trao đổi khóa cho phép bạn trao đổi các lượng dữ liệu lớn
(đã-được-mật-hóa) với bất cứ ai, thậm chí những người bạn chưa từng tiếp xúc trước đây, miễn là bạn có thể truy xuất khóa công khai đối xứng của họ
# Một cách lý tưởng, bạn sử dụng một giải thuật bất đối xứng để mật hóa tất cả
dữ liệu, như thế tránh được nhu cầu trao đổi các khóa đối xứng Tuy nhiên, tốc
độ của các giải thuật bất đối xứng khi mật hóa và giải mật hóa dữ liệu khiến
Trang 5chúng không thực tế cho việc sử dụng với các lượng lớn dữ liệu Sử dụng các giải thuật bất đối xứng để mật hóa các khóa phiên đối xứng là một giải pháp tuy phức tạp hơn, nhưng là tốt nhất ở cả hai mặt: tính linh hoạt và tính nhanh chóng
Thư viện lớp NET Framework hỗ trợ việc trao đổi khóa chỉ với giải thuật RSA, nhưng bạn phải lựa chọn giữa hai formatting scheme: Optimal Asymmetric Encryption Padding (OAEP) và PKCS #1 v 1.5 Bàn về các formatting scheme này vượt quá phạm vi của quyển sách này Nói chung, bạn nên sử dụng OAEP formatting trừ khi bạn có nhu cầu giao tiếp với một hệ thống cũ có sử dụng PKCS formatting Hai lớp dưới đây hiện thực
cơ chế trao đổi khóa, mỗi cơ chế ứng với một formatting scheme:
• System.Security.Cryptography.RSAOAEPKeyExchangeFormatter
• System.Security.Cryptography.RSAPKCS1KeyExchangeFormatter
Để chuẩn bị một khóa đối xứng dùng cho trao đổi, bạn phải tạo một đối tượng formatter
với kiểu như mong muốn và rồi ấn định một đối tượng giải thuật bất đối xứng
(RSACryptoServiceProvider) cho formatter bằng phương thức SetKey của formatter
Bạn phải cấu hình cho giải thuật bất đối xứng sử dụng khóa công khai của người nhận
Khi đã cấu hình xong, gọi phương thức CreateKeyExchange của formatter và truyền một
mảng byte chứa khóa phiên đối xứng mà bạn cần định dạng Phương thức CreateKeyExchange trả về một mảng byte chứa dữ liệu bạn sẽ gửi đi
Giải định dạng cho khóa ngược với quá trình định dạng Có hai lớp deformatter, mỗi lớp ứng với một formatting scheme
• System.Security.Cryptography.RSAOAEPKeyExchangeDeformatter
• System.Security.Cryptography.RSAPKCS1KeyExchangeDeformatter
Để giải định dạng một khóa phiên đã được định dạng, hãy tạo một đối tượng deformatter
với kiểu phù hợp rồi gọi phương thức SetKey của nó để ấn định một đối tượng giải thuật bất đối xứng Bạn phải nạp khóa riêng của bạn vào giải thuật bất đối xứng Cuối cùng, gọi phương thức DecryptKeyExchange với đối số là dữ liệu trao đổi Phương thức này trả
về một mảng byte chứa khóa phiên đối xứng gốc
File KeyExchangeExample.cs chứa ví dụ minh họa cho việc trao đổi khóa Phương thức
Main mô phỏng việc tạo, định dạng, trao đổi, và giải định dạng một khóa phiên đối xứng
Nó sẽ tạo một cặp khóa bất đối xứng để sử dụng cho cả ví dụ này Thực tế, người gửi (người tạo khóa đối xứng) chỉ có khóa công khai của người nhận; người nhận có khóa riêng (được giữ bí mật)
# Cũng như nên sử dụng một khóa đối xứng có chiều dài phù hợp với tính bí mật của dữ liệu đang được bảo vệ, bạn nên mật hóa khóa phiên bằng một giải thuật bất đối xứng và chiều dài khóa ít nhất cũng phải tương đương với khóa đối
Trang 6xứng Nếu khóa bất đối xứng yếu hơn khóa đối xứng, có khả năng kẻ tấn công
sẽ phá vỡ giải thuật bất đối xứng và thu lấy khóa đối xứng thay vì cố giải mật
hóa dữ liệu đã-được-mật-hóa-đối-xứng Xem [http://www.ietf.org/rfc/rfc3766.txt]
(có trong đĩa CD đính kèm) để biết chi tiết về sự tương đương giữa chiều dài khóa đối xứng và bất đối xứng
Kế đó, phương thức Main gọi phương thức FormatKeyExchange với đối số là một mảng byte chứa khóa đối xứng và một đối tượng RSAParameters chứa khóa công khai của người nhận Phương thức FormatKeyExchange trả về một mảng byte chứa khóa đối xứng đã-được-mật-hóa và đã-được-định-dạng, chuẩn bị gửi đi Kế tiếp, phương thức Main gọi phương thức DeformatKeyExchange với đối số là dữ liệu trao đổi đã được định dạng và
đối tượng CspParameters chứa một tham chiếu đến kho chứa khóa MyKeys (chứa khóa
riêng của người nhận) Trong suốt quá trình này, phương thức Main sẽ hiển thị khóa phiên gốc, dữ liệu trao đổi đã được định dạng, và cuối cùng là khóa phiên đã được giải định dạng
using System;
using System.Text;
using System.Security.Cryptography;
public class KeyExchangeExample {
public static void Main() {
// Khai báo một biến RSAParameters, biến này sẽ
// chứa thông tin PUBLIC KEY của người nhận
RSAParameters recipientsPublicKey;
// Khai báo một biến CspParameters, biến này sẽ cho biết
// PRIVATE KEY được lưu trữ trong kho chứa khóa nào
// Thông thường, chỉ có người nhận mới có thể truy xuất
// thông tin này Với mục đích minh họa, chúng ta sẽ tạo
// một cặp khóa ngay đầu ví dụ và sử dụng các khóa này
// cho cả bên gửi và bên nhận
CspParameters cspParams = new CspParameters();
cspParams.KeyContainerName = "MyKeys";
// Tạo cặp khóa bất đối xứng bằng lớp RSACryptoServiceProvider
// Lưu các khóa này vào một kho chứa khóa có tên là "MyKeys"
// và trích thông tin PUBLIC KEY vào biến recipientsPublicKey
Trang 7using (RSACryptoServiceProvider rsaAlg =
new RSACryptoServiceProvider(cspParams)) {
// Cấu hình cho giải thuật lưu khóa vào kho chứa khóa rsaAlg.PersistKeyInCsp = true;
// Trích PUBLIC KEY
recipientsPublicKey = rsaAlg.ExportParameters(false); }
// Tạo giải thuật đối xứng Triple-DES và sử dụng
// khóa được sinh tự động làm khóa phiên
using (SymmetricAlgorithm symAlg =
SymmetricAlgorithm.Create("3DES")) {
// Hiển thị khóa phiên gốc
Console.WriteLine("Session Key at Source = {0}\n\r", BitConverter.ToString(symAlg.Key));
// Chuẩn bị khóa phiên đối xứng dùng cho trao đổi
// (sử dụng phương thức FormatKeyExchange, phương thức // này cần khóa dùng để mật hóa và PUBLIC KEY
// của người nhận)
byte[] exchangeData =
FormatKeyExchange(symAlg.Key, recipientsPublicKey);
// Hiển thị khóa phiên đã-được-mật-hóa (do phương thức // FormatKeyExchange trả về)
Console.WriteLine("Exchange Data = {0}\n\r",
BitConverter.ToString(exchangeData));
// ****** GỬI KHÓA ******
// Bây giờ, khóa phiên có thể được gửi đi bằng các
// kênh giao tiếp bình thường
// Trích khóa phiên từ dữ liệu trao đổi bằng
// phương thức DeformatKeyExchange
byte[] sessionKey = DeformatKeyExchange(exchangeData, cspParams);
Trang 8
// Hiển thị khóa phiên vừa được trích
Console.WriteLine("Session Key at Destination = {0}\n\r",
BitConverter.ToString(sessionKey));
// Nhấn Enter để kết thúc
Console.ReadLine();
}
}
// Phương thức dùng để mật hóa và định dạng khóa phiên đối xứng // Để mật hóa khóa phiên, chúng ta cần truy xuất PUBLIC KEY
// của người nhận (trong cấu trúc RSAParameters)
private static byte[] FormatKeyExchange(byte[] sessionKey,
RSAParameters rsaParams) {
// Tạo một giải thuật bất đối xứng RSA
using (RSACryptoServiceProvider asymAlg =
new RSACryptoServiceProvider()) {
// Nạp PUBLIC KEY của người nhận
asymAlg.ImportParameters(rsaParams);
// Tạo một RSA OAEP formatter để định dạng dữ liệu trao đổi RSAOAEPKeyExchangeFormatter formatter
= new RSAOAEPKeyExchangeFormatter();
// Chỉ định giải thuật RSA dùng để mật hóa khóa phiên
formatter.SetKey(asymAlg);
// Mật hóa và định dạng khóa phiên rồi trả về kết quả
return formatter.CreateKeyExchange(sessionKey);
}
}
// Phương thức dùng để giải mật hóa dữ liệu trao đổi và trích khóa phiên // đối xứng Để giải mật hóa dữ liệu trao đổi, chúng ta cần truy xuất // PRIVATE KEY (từ kho chứa khóa do đối số cspParams chỉ định) private static byte[] DeformatKeyExchange(byte[] exchangeData,
Trang 9CspParameters cspParams) {
// Tạo một giải thuật bất đối xứng RSA
using (RSACryptoServiceProvider asymAlg =
new RSACryptoServiceProvider(cspParams)) {
// Tạo một RSA OAEP deformatter để trích khóa phiên
// từ dữ liệu trao đổi
RSAOAEPKeyExchangeDeformatter deformatter
= new RSAOAEPKeyExchangeDeformatter();
// Chỉ định giải thuật RSA dùng để giải mật hóa dữ liệu trao đổi
deformatter.SetKey(asymAlg);
// Giải mật hóa dữ liệu trao đổi và trả về khóa phiên
return deformatter.DecryptKeyExchange(exchangeData);
}
}
}
Chạy KeyExchangeExample sẽ sinh ra kết xuất tương tự như sau:
Session Key at Source = EE-5B-16-5B-AC-46-3D-72-CC-73-19-D9-0B-8A-19-E2-A6-02-13-BE-F8-CE-DF-40
Exchange Data = 60-FA-3B-63-41-25-F1-AD-08-F9-FC-67-CD-C6-FB-3E-0F-C3-62-
C6-3F-5C-C0-7E-D1-60-2D-19-58-07-EE-BB-7C-53-A5-C2-FB-CA-D7-64-FF-BA- 33-77-AC-52-87-5F-75-E7-57-99-01-90-CD-70-36-1E-53-0C-82-C6-CE-B8-BC- 8B-C9-39-6F-29-39-5F-6C-A6-43-E5-B0-A1-42-46-1C-9B-1C-72-EB-5E-67-06-44- C0-CE-AB-70-B8-39-8E-9F-01-E8-49-51-36-D6-27-09-94-DA-42-CE-79-C2-72-88-4D-CE-63-B4-A0-AC-07-AF-26-A7-76-DE-21-BE-A5
Session Key at Destination = EE-5B-16-5B-AC-46-3D-72-CC-73-19-D9-0B-8A-19-E2-A6-02-13-BE-F8-CE-DF-