1. Trang chủ >
  2. Công Nghệ Thông Tin >
  3. Kỹ thuật lập trình >

Gửi một bí mật bằng phép mật hóa bất đối xứng

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (7.15 MB, 706 trang )


569

Chương 14: Mật mã

Encrypt và khóa công khai (public key) của người nhận để mật hóa thông điệp.



Sau đo, người nhận sẽ sử dụng phương thức RSACryptoServiceProvider.Decrypt

và khóa riêng (private key) để giải mật hóa bí mật đã-được-mật-hóa.

.NET Framework định nghĩa một hệ thống phân cấp theo lớp cho các giải thuật bất đối xứng

tương tự như đã định nghĩa cho các giải thuật đối xứng (đã được thảo luận trong mục 14.6).

Tất cả các giải thuật bất đối xứng phải thừa kế một lớp cơ sở trừu tượng chung có tên là

System.Security.Cryptography.AsymmetricAlgorithm. Có hai hiện thực giải thuật bất đối

xứng cụ thể:







System.Security.Cryptography.RSACryptoServiceProvider

System.Security.Cryptography.DSACryptoServiceProvider



Vì có đuôi là CryptoServiceProvider nên cả hai lớp này đều bọc lấy các chức năng do Win32

CryptoAPI cung cấp. Tuy nhiên, chỉ có lớp RSACryptoServiceProvider là hỗ trợ việc mật hóa

dữ liệu. Lớp DSACryptoServiceProvider hiện thực Digital Signature Algorithm (DSA), bạn có

thể sử dụng giải thuật này chỉ để tạo chữ ký số (xem Federal Information Processing

Standard [FIPS] 186-2 tại [http://www.itl.nist.gov/fipspubs] để biết thêm chi tiết về DSA).

Mặc dù bạn có thể tạo một đối tượng giải thuật bất đối xứng bằng phương thức tĩnh Create

của lớp cơ sở AsymmetricAlgorithm, nhưng bù lại bạn sẽ phải trả giá chút ít cho việc này. Lớp

AsymmetricAlgorithm không khai báo các phương thức mà RSACryptoServiceProvider sử

dụng để mật hóa và giải mật hóa dữ liệu. Thay vào đó, bạn phải trực tiếp thể hiện hóa lớp

RSACryptoServiceProvider bằng một trong các phương thức khởi dựng của nó.

Trước khi mật hóa hay giải mật hóa dữ liệu với đối tượng RSACryptoServiceProvider, bạn cần

truy xuất các khóa thích hợp. Khóa của giải thuật bất đối xứng khác nhiều so với khóa của giải

thuật đối xứng. Thứ nhất, nó có hai thành phần: khóa công khai (public key) và khóa riêng

(private key). Thứ hai, thay vì chỉ là một dãy các byte được sinh ngẫu nhiên, khóa bất đối

xứng được tạo theo một cách thức đặc biệt. Có một mối quan hệ toán đặc biệt giữa khóa công

khai và khóa riêng; mối quan hệ này cho phép giải thuật bất đối xứng mật hóa dữ liệu bằng

một khóa và giải mật hóa dữ liệu bằng một khóa khác. Mỗi giải thuật bất đối xứng sử dụng

cách thức tạo khóa của chính nó, và các lớp hiện thực cụ thể đóng gói các chức năng cần thiết

để tạo ra các khóa mới.

Khóa công khai không cần được giữ bí mật và chủ sở hữu có thể tùy ý gửi nó cho bạn thông

qua e-mail, hoặc post nó lên một website hay một server phân phối khóa để mọi người cùng

thấy. Những ai muốn gửi bí mật thì sử dụng khóa công khai để mật hóa bí mật. Sau đó, người

nhận sử dụng khóa riêng để giải mật hóa bí mật. Khóa riêng phải được giữ bí mật; những ai sở

hữu khóa riêng đều có thể giải mật hóa dữ liệu đã-được-mật-hóa bằng khóa công khai.

Để tạo một bí mật được-mật-hóa-bất-đối-xứng, bạn phải có khóa công khai của người nhận và

nạp nó vào một đối tượng RSACryptoServiceProvider. Có hai cách nạp khóa công khai:





Sử dụng phương thức RSACryptoServiceProvider.ImportParameters để nhập một cấu

trúc System.Security.Cryptography.RSAParameters, cấu trúc này chứa thông tin khóa

công khai của người nhận. Chủ sở hữu có thể tạo cấu trúc RSAParameters bằng phương

thức RSACryptoServiceProvider.ExportParameters và gửi nó cho bạn. Tuy nhiên,

người này có thể gửi cho bạn khóa công khai ở dạng byte, và bạn phải tự nạp giá trị này

vào cấu trúc RSAParameters.



570

Chương 14: Mật mã











Sử dụng phương thức RSACryptoServiceProvider.FromXmlString để nạp dữ liệu khóa

công khai từ một chuỗi XML. Chủ sở hữu có thể tạo dữ liệu XML này bằng phương thức

RSACryptoServiceProvider.ToXmlString và gửi nó cho bạn.

Cả



phương



thức





ToXmlString

của

lớp

RSACryptoServiceProvider đều nhận một đối số luận lý, nếu là true, đối tượng

RSACryptoServiceProvider sẽ xuất cả khóa công khai và khóa riêng. Bạn chỉ định

giá trị này là false khi cần xuất khóa cho mục đích phân phối hay lưu trữ.

ExportParameters



Một khi đã nạp khóa công khai của người nhận vào đối tượng RSACryptoServiceProvider,

bạn có thể mật hóa dữ liệu. Giải thuật bất đối xứng chậm hơn giải thuật đối xứng khi mật hóa

và giải mật hóa dữ liệu. Vì lý do này, bạn không nên sử dụng giải thuật bất đối xứng để mật

hóa lượng dữ liệu lớn. Thông thường, nếu cần mật hóa lượng dữ liệu lớn, bạn nên sử dụng

giải thuật đối xứng và rồi mật hóa khóa đối xứng bằng giải thuật bất đối xứng để bạn có thể

gửi khóa đối xứng cùng với dữ liệu. Mục 14.10 sẽ thảo luận về vấn đề này. Để bảo đảm tính

nhất quán trong việc sử dụng, lớp RSACryptoServiceProvider không hỗ trợ mô hình mật hóa

và giải mật hóa dựa-trên-System.IO.Stream (đã được sử dụng trong mục 14.6).

Để mật hóa dữ liệu với đối tượng RSACryptoServiceProvider, bạn hãy gọi phương thức

Encrypt, truyền cho nó một mảng byte chứa plaintext; Encrypt sẽ trả về một mảng byte chứa

ciphertext. Phương thức Encrypt cũng mhận một đối số luận lý cho biết kiểu padding mà đối

tượng RSACryptoServiceProvider sẽ sử dụng. Padding cho biết đối tượng bất đối xứng sẽ xử

lý plaintext như thế nào trước khi mật hóa. Padding bảo đảm giải thuật bất đối xứng không

cần xử lý từng khối dữ liệu, và bảo vệ ciphertext đối với các dạng tấn công bằng mật mã. Diễn

giải các dạng padding vượt quá phạm vi của quyển sách này. Nói chung, nếu đang sử dụng

Microsoft Windows XP trở về sau, bạn nên chỉ định đối số padding là true; nếu không, bạn

phải chỉ định đối số padding là là false (nếu không thì Encrypt sẽ ném ngoại lệ

System.Security.Cryptography.CryptographicException).

Giải mật hóa dữ liệu cũng đơn giản như mật hóa dữ liệu. Người nhận cần tạo một đối tượng

RSACryptoServiceProvider và nạp nó cùng với khóa riêng. Thông thường, khóa này sẽ được

lưu trữ trong một kho chứa khóa (key container) do CryptoAPI quản lý (sẽ được thảo luận kỹ

hơn trong mục 14.9). Người nhận gọi RSACryptoServiceProvider.Decrypt và truyền cho nó

ciphertext mà bạn đã gửi. Người nhận phải chỉ định cơ chế padding, và nó cũng phải giống

như khi mật hóa dữ liệu. Phương thức Decrypt trả về một mảng byte chứa plaintext đã-đượcgiải-mật-hóa. Nếu plaintext mô tả một chuỗi, người nhận phải chuyển mảng byte thành giá trị

chuỗi thích hợp bằng lớp System.Text.Encoding.







Lớp



Lớp RSACryptoServiceProvider thừa kế các phương thức có tên là EncryptValue

và DecryptValue từ lớp cha của nó là System.Security.Cryptography.RSA. Lớp

RSACryptoServiceProvider không hiện thực các phương thức này và ném ngoại lệ

System.NotSupportedException nếu bạn gọi chúng.



dưới đây trình bày cách

RSACryptoServiceProvider để mật hóa một chuỗi và rồi giải mật hóa:

AsymmetricEncryptionExample



using System;



sử



dụng



lớp



571

Chương 14: Mật mã

using System.Text;

using System.Security.Cryptography;

public class AsymmetricEncryptionExample {

public static void Main(string[] args) {

// 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.

using (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);

}

// Hiển thị thông điệp plaintext gốc.

Console.WriteLine("Original message = {0}", args[0]);

// Chuyển thông điệp gốc thành mảng byte. Tốt nhất là không

// truyền các thông tin bí mật ở dạng chuỗi giữa các phương thức.



572

Chương 14: Mật mã



byte[] plaintext = Encoding.Unicode.GetBytes(args[0]);

// Mật hóa thông điệp bằng phương thức EncryptMessage.

// Phương thức này cần PUBLIC KEY của người nhận.

byte[] ciphertext = EncryptMessage(plaintext,

recipientsPublicKey);



// Hiển thị ciphertext do phương thức EncryptMessage trả về.

// Sử dụng phương thức BitConverter.ToString method cho đơn giản

// mặc dù nó chèn dấu gạch nối (-) vào giữa các giá trị byte

// (không đúng với biểu diễn dữ liệu trong bộ nhớ).

Console.WriteLine("Formatted Ciphertext = {0}",

BitConverter.ToString(ciphertext));

// Giải mật hóa thông điệp (đã-được-mật-hóa) bằng phương thức

// DecryptMessage. Phương thức này cần truy xuất PRIVATE KEY

// của người nhận (chỉ có người nhận mới có thể truy xuất được).

// Chúng ta sẽ truyền cho nó một đối tượng CspParameters

// (cho biết PRIVATE KEY được lưu trữ trong kho chứa khóa nào).

// Giải pháp này an toàn hơn là truyền PRIVATE KEY thô

// giữa các phương thức.

byte[] decData = DecryptMessage(ciphertext, cspParams);

// Chuyển thông điệp đã-được-giải-mật-hóa từ mảng byte

// thành chuỗi và hiển thị nó ra cửa sổ Console.

Console.WriteLine("Decrypted message = {0}",

Encoding.Unicode.GetString(decData));

// Nhấn Enter để kết thúc.

Console.ReadLine();

}



// Phương thức dùng để mật hóa (theo RSA) một thông điệp bằng

// PUBLIC KEY (nằm trong một cấu trúc RSAParameters).

private static byte[] EncryptMessage(byte[] plaintext,

RSAParameters rsaParams) {



573

Chương 14: Mật mã



// Khai báo mảng byte chứa ciphertext.

byte[] ciphertext = null;



// Tạo một thể hiện của giải thuật RSA.

using (RSACryptoServiceProvider rsaAlg =

new RSACryptoServiceProvider()) {

rsaAlg.ImportParameters(rsaParams);

// Mật hóa plaintext bằng OAEP padding

// (chỉ được hỗ trợ trên Windows XP trở về sau).

ciphertext = rsaAlg.Encrypt(plaintext, true);

}

// Xóa các giá trị được giữ trong mảng byte chứa plaintext.

// Điều này bảo đảm dữ liệu bí mật không còn trong bộ nhớ

// sau khi bạn giải phóng tham chiếu đến nó.

Array.Clear(plaintext, 0, plaintext.Length);

return ciphertext;

}

// Phương thức dùng để giải mật hóa một thông điệp (đã-được-mật-hóa// theo-RSA) bằng PRIVATE KEY (do đối tượng CspParameters chỉ định).

private static byte[] DecryptMessage(byte[] ciphertext,

CspParameters cspParams ) {

// Khai báo mảng byte chứa plaintext (đã-được-giải-mật-hóa).

byte[] plaintext = null;

// Tạo một thể hiện của giải thuật RSA.

using (RSACryptoServiceProvider rsaAlg =

new RSACryptoServiceProvider(cspParams)) {

// Giải mật hóa plaintext bằng OAEP padding.

plaintext = rsaAlg.Decrypt(ciphertext, true);

}



574

Chương 14: Mật mã



return plaintext;

}

}



Lệnh AsymmetricEncryptionExample "I love you!" sẽ sinh ra kết xuất tương tự như sau:

Original message = I love you!

Formatted Ciphertext = 1F-53-05-2B-9D-CC-20-6B-5D-D3-D4-0B-C9-5F-CA-FA-C1-61-6C3B-5B-9E-EA-B9-D0-AF-E5-2B-05-BC-D4-94-DD-71-D6-21-2A-B0-82-6B-16-C0-89-3E-24-B3B3-A3-15-FE-16-7A-B0-58-14-43-CD-69-1A-FD-08-39-2D-09-A6-41-86-96-78-B4-3D-D6-C739-8A-90-84-D6-68-E6-5D-86-32-14-67-51-A7-B7-5A-EF-CF-F4-6D-E4-B0-18-6A-16-2A-AF54-B7-3C-B8-19-6E-A5-86-BF-3E-B2-6D-17-E3-1D-E8-AD-D1-A8-D9-54-93-8E-F1-E8-5D-AC4A

Decrypted message = I love you!







9.



Nếu bạn chạy ví dụ này nhiều lần với cùng thông điệp và khóa, ciphertext sẽ

khác nhau. Đó là vì cơ chế padding sinh ra dữ liệu ngẫu nhiên để tránh các dạng

tấn công bằng mật mã. Mặc dù hơi rắc rối nhưng đây chính là cách hành xử mà

ta mong đợi.



Lưu trữ khóa bất đối xứng một cách an toàn







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.



575

Chương 14: Mật mã



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à MyKeys. 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));

}

}



Xem Thêm
Tải bản đầy đủ (.pdf) (706 trang)

×