2024-01-19

Mã hóa dữ liệu


Trong cuộc sống ngày nay, mạng internet có lẽ đã trở thành một thứ không thể thiếu đối với chúng ta. Mạng internet giúp chúng ta tra cứu thông tin, làm việc, học tập, kết nối với mọi người. Nếu thiếu nó thì có lẽ thế giới này sẽ trở lên lạc hậu hơn rất nhiều so với bây giờ.

Vậy có bao giờ bạn nghĩ đến thực chất internet là gì, mà tại sao nó lại quan trọng đến vậy? Về cơ bản, ta có thể coi internet như là một môi trường trung gian để trao đổi THÔNG TIN, mọi hoạt động trên internet đều HẦU NHƯ CHỈ xoay quanh việc tiếp nhận, trao đổi và truyền nhận THÔNG TIN. Qua đó ta có thể thấy được thông tin quan trọng như thế nào.

Thật sự thông tin còn quan trọng hơn những gì bạn tưởng. Một số thông tin có thể quan trọng hơn bất cứ thứ gì hữu hình trên thế giới này, nó có thể đáng giá hàng trăm tỷ, hàng tỷ tỷ đô la, hoặc có thể là vô giá.

Vì thông tin là quan trọng, nên chúng ta sẽ cần phải có những phương pháp để bảo vệ nó. Đó là Mã hóa dữ liệu. Vậy, mã hóa là gì, các phương pháp mã hóa như thế nào, chúng ta hãy cùng tìm hiểu nhé.

I. KHÁI NIỆM

Hiểu nôm na Mã hóa là phương thức ngụy trang hoặc che giấu một tin nhắn bằng cách áp dụng một số bước lập trình máy tính thành các chuỗi ký tữ đặc biệt để ngăn những người không phận sự tiếp cận vào thông tin đó. Ví dụ: tin nhắn có nội dung như “tôi sẽ gặp anh vào ngày mai tại Hà Nội” được chuyển thành tin nhắn mã hóa như “p98hUls#yeb!”. Làm thế nào để mã hóa được như vậy thì chúng ta cần có thuật toán mã hóa.

Mã hóa (encryption) tức là biến đổi “thông tin gốc” dạng tường minh (plaintext) thành “thông tin mã hóa” dạng ẩn tàng (cipher text) bằng cách sử dụng một khóa mã (thuật toán mã hóa) nào đó. Chỉ có những người giữ chìa khóa (key) bí mật mới có thể giải mã (decryption) thông tin dạng ẩn tàng trở lại thành dạng thông tin có dạng tường minh.

Giải mã (decrypt, decipher, decryption) đó là quá trình ngược lại với mã hóa, khôi phục lại những thông tin dạng ban đầu từ thông tin ở dạng đã được mã hóa.

Thuật toán mã hóa là một tập hợp các quy tắc được sử dụng để thực hiện quá trình mã hóa và giải mã nhằm che giấu thông tin hoặc giải mã thông tin

Ví dụ :

Người Hebrew (Do Thái cổ) đã sáng tạo một thuật toán mã hóa đơn giản và hiệu quả gọi là thuật toán atbash mà chìa khóa mã hóa và giải mã là một sự thay thế (substitution) trong bảng chữ cái. Giả sử dùng chìa khóa mã hóa là bảng hoán vị:

Khi đó chẳng hạn từ gốc (plaintext): JERUSALEM sẽ được mã hóa thành từ mã (ciphertext): QVIFHZOVN. Nếu người nhận tin có chìa khóa thì việc biến đổi QVIFHZOVN trở lại thành JERUSALEM là điều hoàn toàn đơn giản, nhưng nếu không có chìa khóa thì quả là khó khăn, người nhận được thông điệp không thể nào hiểu nổi QVIFHZOVN có nghĩa là gì cả! Cho dù biết rằng quy luật mã hóa chỉ là một sự thay thế của 25 chữ cái nhưng nếu tấn công bạo lực thì phải thử lần lượt hết mọi khả năng tạo chìa khóa, tức là phải thử 25! khả năng (tất nhiên về sau người ta có rất nhiều biện pháp để giảm bớt khả năng dò tìm, chẳng hạn nếu plaintext có độ dài khá lớn thì có thể sử dụng dò tìm theo tần suất xuất hiện của các ký tự).

Thuật toán mã hóa là một thuật toán nhằm mã hóa thông tin của chúng ta, biến đổi thông tin từ dạng rõ sang dạng mờ, để ngăn cản việc đọc trộm nội dung của thông tin (Dù hacker có được thông tin đó cũng không hiểu nội dung chứa trong nó là gì).

Thông thường các thuật toán sử dụng một hoặc nhiều key (Một chuỗi chìa khóa để mã hóa và giải mã thông tin) để mã hóa và giải mã (Ngoại trừ những thuật toán cổ điển). Bạn có thể coi key này như một cái password để có thể đọc được nội dung mã hóa. Người gửi sẽ dùng key mã hóa để mã hóa thông tin sang dạng mờ, và người nhận sẽ sử dụng key giải mã để giải mã thông tin sang dạng rõ. Chỉ những người nào có key giải mã mới có thể đọc được nội dung.

Nhưng đôi khi "kẻ thứ ba" (hacker) không có key giải mã vẫn có thể đọc được thông tin, bằng cách phá vỡ thuật toán. Và có một nguyên tắc là bất kì thuật toán mã hóa nào cũng đều có thể bị phá vỡ. Do đó không có bất kì thuật toán mã hóa nào được coi là an toàn mãi mãi. Độ an toàn của thuật toán được dựa vào nguyên tắc:

Nếu chi phí để giải mã một khối lượng thông tin lớn hơn giá trị của khối lượng thông tin đó thì thuật toán đó được tạm coi là an toàn. (Không ai lại đi bỏ ra 50 năm để giải mã một thông tin mà chỉ mang lại cho anh ta 1000 đô).

Nếu thời gian để phá vỡ một thuật toán là quá lớn (giả sử lớn hơn 100 năm, 1000 năm) thì thuật toán được tạm coi là an toàn.

II. PHÂN LOẠI MÃ HÓA

Ngày nay người ta phân biệt ra hai nhóm thuật toán mã hóa chính là: Các thuật toán mã hóa cổ điển và các thuật toán hiện đại.

1. Các thuật toán cổ điển

Các thuật toán cổ điển: (những thuật toán này ngày nay đôi khi vẫn còn được dùng chẳng hạn trong trò chơi tìm mật thư) gồm: Thuật toán thay thế, Thuật toán chuyển vị.

1.1. Thuật toán thay thế

Thuật toán thay thế (Substitution) là thuật toán mã hóa trong đó từng ký tự (hoặc từng nhóm ký tự) của plaintext được thay thế bằng một (hay một nhóm) ký tự khác. Thuật toán atbash của người Hebrew hay thuật toán vòng của Caesar đều là các thuật toán thay thế. Chính ý tưởng của mã vòng Caesar đã được ứng dụng trong máy Enigma.

Đây là phương pháp mã hóa đầu tiên, và cố xưa nhất, và hiện nay rất ít được dùng đến so với các phương pháp khác. Ý tưởng của phương pháp này rất đơn giản, bên A mã hóa thông tin bằng thuật toán mã hóa cổ điển, và bên B giải mã thông tin, dựa vào thuật toán của bên A, mà không dùng đến bất kì key nào. Do đó, độ an toàn của thuật toán sẽ chỉ dựa vào độ bí mật của thuật toán, vì chỉ cần ta biết được thuật toán mã hóa, ta sẽ có thể giải mã được thông tin.

1.2. Thuật toán chuyển vị

Thuật toán chuyển vị (Transposition) là thuật toán mã hóa trong đó các ký tự trong văn bản ban đầu chỉ thay đổi vị trí cho nhau còn bản thân các ký tự không hề bị biến đổi.

2. Các thuật toán hiện đại

Thuật toán hiện đại là thuật ngữ dùng để chỉ các thuật toán được phát triển trong những năm gần đây, thường dựa trên các lý thuyết và kỹ thuật tiên tiến. Thuật toán hiện đại thường có hiệu quả hơn các thuật toán truyền thống, và có thể giải quyết các vấn đề mà các thuật toán truyền thống không thể. Người ta thường chia thành 3 loại sau đây : Mã hóa đối xứng hay khóa bí mật SKC (Secret Key Cryptography), Mã hóa bất đối xứng hay khóa công khai và khóa riêng PKC (Public and Private Keys Cryptography) và Hàm băm (Hash function)

2.1. Hàm băm (Hash function)

Hàm băm (Hash function): Mã hóa một chiều (one-way cryptography) dùng một biến đổi toán học để “mã hóa” thông tin gốc thành một dạng không biến đổi ngược được: không có chìa khóa vì từ ciphertext không tìm ngược lại được plaintext!

Đôi khi ta chỉ cần mã hóa thông tin chứ không cần giải mã thông tin, khi đó ta sẽ dùng đến phương pháp mã hóa một chiều (Chỉ có thể mã hóa chứ không thể giải mã). Thông thường phương pháp mã hóa một chiều sử dụng một hàm băm (hash function) để biến một chuỗi thông tin thành một chuỗi hash có độ dài nhất định. Ta không có bất kì cách nào để khôi phục (hay giải mã) chuỗi hash về lại chuỗi thông tin ban đầu.

Hàm băm (Hash function) là một hàm mà nó nhận vào một chuỗi có độ dài bất kì, và sinh ra một chuỗi kết quả có độ dài cố định (Gọi là chuỗi hash), dù hai chuỗi dữ liệu đầu vào, được cho qua hàm băm thì cũng sinh ra hai chuỗi hash kết quả khác nhau rất nhiều. Ví dụ như đối với kiểu dữ liệu Hash-table, ta có thể coi đây là một dạng kiểu dữ liệu mảng đặc biệt mà index nó nhận vào là một chuỗi, nó được định nghĩa bằng cách bên trong nó chứa một mảng thông thường, mỗi khi truyền vào index là một chuỗi, thì chuỗi này sẽ đi qua hàm băm và ra một giá trị hash, giá trị này sẽ tương ứng với index thật của phần tử đó trong mảng bên dưới.

Đặc điểm của hash function là khi thực hiên băm hai chuỗi dữ liệu như nhau, dù trong hoàn cảnh nào thì nó cũng cùng cho ra một chuỗi hash duy nhất có độ dài nhất định và thường nhỏ hơn rất nhiều so với chuỗi gốc, và hai chuỗi thông tin bất kì dù khác nhau rất ít cũng sẽ cho ra chuỗi hash khác nhau rất nhiều. Do đó hash function thường được sử dụng để kiểm tra tính toàn vẹn của dữ liệu.

Ngoài ra có một ứng dụng mà có thể bạn thường thấy, đó là để lưu giữ mật khẩu. Vì mật khẩu là một thứ cực kì quan trọng, do đó ta không nên lưu mật khẩu của người dùng dưới dạng rõ, vì như vậy nếu bị hacker tấn công, lấy được CSDL thì hacker có thể biết được mật khẩu của người dùng. Do đó, mật khẩu của người dùng nên được lưu dưới dạng chuỗi hash, và đối với server thì chuỗi hash đó chỉnh là “mật khẩu” đăng nhập (lúc đăng nhập thì mật khẩu mà người dùng nhập cũng được mã hóa thành chuỗi hash và so sánh với chuỗi hash trong CSDL của server). Dù hacker có lấy được CSDL thì cũng không tài nào có thể giải mã được chuỗi hash để tìm ra mật khẩu của người dùng.

Các loại Thuật toán mã hóa một chiều (hàm băm) phổ biến là:

+ MD5 (Message Digest Algorithm 5): Dù vẫn còn sử dụng, nhưng đã bị coi là không an toàn do nhiều lỗ hổng bảo mật. Độ dài hash là 128-bit.

+ SHA-1 (Secure Hash Algorithm 1): Cũng không an toàn nữa do các phương pháp tấn công đã được phát triển. Độ dài hash là 160-bit.

+ SHA-256, SHA-384, SHA-512: Là các biến thể của SHA-2, cung cấp độ dài hash lớn hơn (tương ứng là 256-bit, 384-bit, và 512-bit) và được xem là an toàn cho đến thời điểm hiện tại.

+ Whirlpool: Một hàm băm có độ dài hash lên đến 512-bit, được coi là an toàn và phổ biến trong một số ứng dụng bảo mật.

+ bcrypt: Thường được sử dụng cho lưu trữ mật khẩu, có thể được điều chỉnh để yêu cầu nhiều tài nguyên tính toán, làm chậm quá trình tấn công brute-force.

+ Scrypt: Tương tự như bcrypt, nhưng còn sử dụng bộ nhớ nhiều hơn, làm cho nó trở nên khó khăn hơn cho tấn công brute-force.

+ SHA-3 (Secure Hash Algorithm 3): Một trong những tiếp nối của họ SHA, được thiết kế để cung cấp một lựa chọn mới với cấu trúc khác biệt.

2.2. Mã hóa đối xứng hay khóa bí mật SKC

Mã hóa đối xứng hay khóa bí mật SKC (Secret Key Cryptography): Là phương pháp mã hóa mà key mã hóa và key giải mã là như nhau (Sử dụng cùng một secret key để mã hóa và giải mã). Đây là phương pháp thông dụng nhất hiện nay dùng để mã hóa dữ liệu truyền nhận giữa hai bên. Vì chỉ cần có secret key là có thể giải mã được, nên bên gửi và bên nhận cần làm một cách nào đó để cùng thống nhất về secret key.

Để thực hiện mã hóa thông tin giữa hai bên thì:

Đầu tiên bên gửi và bên nhận bằng cách nào đó sẽ phải thóa thuận secret key (khóa bí mật) được dùng để mã hóa và giải mã. Vì chỉ cần biết được secret key này thì bên thứ ba có thể giải mã được thông tin, nên thông tin này cần được bí mật truyền đi (bảo vệ theo một cách nào đó).

Sau đó bên gửi sẽ dùng một thuật toán mã hóa với secret key tương ứng để mã hóa dữ liệu sắp được truyền đi. Khi bên nhận nhận được sẽ dùng chính secret key đó để giải mã dữ liệu. Vấn đề lớn nhất của phương pháp mã hóa đối xứng là làm sao để “thỏa thuận” secret key giữa bên gửi và bên nhận, vì nếu truyền secret key từ bên gửi sang bên nhận mà không dùng một phương pháp bảo vệ nào thì bên thứ ba cũng có thể dễ dàng lấy được secret key này.

Các loại mã hóa đối xứng phổ biến là:

+ DES (Data Encryption Standard): Một trong những thuật toán mã hóa đối xứng đầu tiên và phổ biến, được sử dụng trong nhiều ứng dụng.

+ AES (Advanced Encryption Standard): Tiêu chuẩn hiện đại thay thế DES, với khóa có thể là 128, 192, hoặc 256 bit.

+ IDEA (International Data Encryption Algorithm) :Một thuật toán mã hóa đối xứng mạnh mẽ và an toàn.

+ Blowfish: Thuật toán mã hóa đối xứng với khóa có thể được điều chỉnh linh hoạt.

+ Twofish: Một phiên bản nâng cao của Blowfish, thiết kế để cung cấp độ bảo mật cao.

+ RC4 (Rivest Cipher 4): Một thuật toán mã hóa dựa trên việc tạo ra dãy khóa ngẫu nhiên, thường được sử dụng trong các ứng dụng như SSL/TLS.

+ 3DES (Triple DES): Sự kết hợp của ba chu kỳ mã hóa DES, tăng cường độ bảo mật so với DES.

+ RC6: Một thuật toán mã hóa đối xứng phức tạp dựa trên cấu trúc Feistel.

2.3. Mã hóa bất đối xứng hay khóa công khai và khóa riêng PKC

Mã hóa bất đối xứng hay khóa công khai và khóa riêng PKC (Public and Private Keys Cryptography): Sử dụng hai khóa riêng biệt: một khóa để mã hóa (khóa công khai: public key) và một khóa khác để giải mã (khóa riêng: private key).

Nghĩa là key ta sử dụng để mã hóa dữ liệu sẽ khác với key ta dùng để giải mã dữ liệu. Tất cả mọi người đều có thể biết được public key (kể cả hacker), và có thể dùng public key này để mã hóa thông tin. Nhưng chỉ có người nhận mới nắm giữ private key, nên chỉ có người nhận mới có thể giải mã được thông tin.

Để thực hiện mã hóa bất đối xứng thì:

Bên nhận sẽ tạo ra một gặp khóa (public key và private key). Bên nhận sẽ dữ lại private key và truyền cho bên gửi public key. Vì public key này là công khai nên có thể truyền tự do mà không cần bảo mật.

Bên gửi trước khi gửi dữ liệu sẽ mã hóa dữ liệu bằng thuật toán mã hóa bất đối xứng với key là public key từ bên nhận.

Bên nhận sẽ giải mã dữ liệu nhận được bằng thuật toán được sử dụng ở bên gửi, với key giải mã là private key.

Điểm yếu lớn nhất của mã hóa bất đối xứng là tốc độ mã hóa và giải mã rất chậm so với mã hóa đối xứng, nếu dùng mã hóa bất đối xứng để mã hóa dữ liệu truyền – nhận giữa hai bên thì sẽ tốn rất nhiều chi phí.

Do đó, ứng dụng chỉnh của mã hóa bất đối xứng là dùng để bảo mật secret key cho mã hóa đối xứng: Ta sẽ dùng phương pháp mã hóa bất đối xứng để truyền secret key của bên gửi cho bên nhận. Và hai bên sẽ dùng secret key này để trao đổi thông tin bằng phương pháp mã hóa đối xứng.

Các loại mã hóa bất đối xứng phổ biến là:

+ RSA (Rivest–Shamir–Adleman): Sử dụng cặp khóa công khai và khóa riêng tư, phổ biến trong bảo mật truyền thông và chữ ký số.

+ Diffie-Hellman: Dùng để trao đổi khóa bí mật qua kênh không an toàn, thường được sử dụng trong quá trình thiết lập kết nối an toàn.

+ Elliptic Curve Cryptography (ECC): Sử dụng toán học đại số đồng dư trên các đường cong elip, cung cấp độ bảo mật cao với chi phí tính toán thấp.

+ DSA (Digital Signature Algorithm): Một thuật toán chữ ký số được sử dụng để xác minh tính toàn vẹn của dữ liệu và xác nhận danh tính của người ký.

+ ElGamal: Kết hợp việc trao đổi khóa Diffie-Hellman với mã hóa bất đối xứng, thường được sử dụng trong bảo mật truyền thông.

+ Blum Blum Shub: Sử dụng số nguyên tố và toán học modular, thường được sử dụng trong sinh số ngẫu nhiên an toàn.

+ Lattice-based Cryptography: Dựa trên khái niệm lưới số học, được xem là an toàn trước nhiều thuật toán phân tích của máy tính lượng tử.

III. TẠI SAO PHẢI MÃ HÓA

Bởi vì mã hóa là một phần quan trọng của bảo mật thông tin và bảo vệ dữ liệu giúp giảm bị mất hoặc đánh cắp dữ liệu khi bị tấn công từ môi trường bên ngoài .

Dưới đây là một số lý do chính tại sao phải sử dụng mã hóa:

+ Bảo vệ dữ liệu riêng tư : Mã hóa giúp bảo vệ thông tin cá nhân và nhạy cảm khỏi sự truy cập trái phép. Điều này đặc biệt quan trọng khi truyền dữ liệu qua các kênh không an toàn như internet.

+ Ngăn chặn thay đổi dữ liệu hay lộ lọt dữ liệu : Mã hóa giúp đảm bảo tính toàn vẹn của dữ liệu bằng cách ngăn chặn sự thay đổi trái phép từ bên thứ ba.

+ Bảo vê mật khẩu: Khi lưu trữ mật khẩu, sử dụng hàm băm hoặc mã hóa đảm bảo rằng mật khẩu không thể dễ dàng bị đọc được nếu hệ thống bị tấn công.

+ Bảo mật thông tin truyền tải :Trong quá trình truyền tải dữ liệu giữa hai đối tượng, mã hóa giúp ngăn chặn sự đánh cắp thông tin trên đường truyền.

+ Đảm bảo quy chuẩn trong an toàn thông tin: Nhiều lĩnh vực, như y tế và tài chính, yêu cầu việc bảo mật thông tin. Mã hóa giúp tự động đáp ứng nhiều yêu cầu an ninh thông tin.

+ Bảo vệ dữ liệu cất trữ : Khi lưu trữ dữ liệu trên các thiết bị lưu trữ hoặc trong các cơ sở dữ liệu, mã hóa ngăn chặn sự truy cập trái phép vào dữ liệu đó.

Tóm lại, việc sử dụng mã hóa đóng vai trò quan trọng trong việc bảo vệ dữ liệu, đảm bảo an toàn thông tin và tuân thủ các chuẩn mực an ninh.

IV. CHI TIẾT MỘT SỐ THUẬT TOÁN MÃ HÓA THƯỜNG DÙNG

1. DES

1.1. Tổng quan về DES

DES (Data Encryption Standard) là chuẩn mã hóa dữ liệu đầu tiên trên thế giới, do Cơ quan an ninh Quốc gia Hoa Kỳ (NSA) đề xuất trên cơ sở cải tiến thuật toán Lucifer do hãng IBM công bố năm 1964. DES đã được sử dụng rộng rãi ở Hoa Kỳ và nhiều quốc gia khác trong các thập kỷ 70, 80, 90 cho đến khi được thay thế bởi Tiêu chuẩn mã hóa dữ liệu tiên tiến AES (Advanced Encryption Standard) vào năm 2002.

Đầu vào của DES là khối 64 bit, đầu ra cũng là khối 64 bit. Khóa mã hóa có độ dài 56 bit, nhưng thực chất ban đầu là 64 bit, được lấy đi các bit ở vị trí chia hết cho 8 dùng để kiểm tra tính chẵn lẻ.

1.2. Thuật toán

DES là thuật toán mã hóa theo khối, nó xử lý từng khối thông tin của bản rõ có độ dài xác định là 64 bit. Trước khi đi vào 16 chu trình chính, khối dữ liệu cần bảo mật sẽ được tách ra thành từng khối 64 bit, và từng khối 64 bit này sẽ lần lượt được đưa vào 16 vòng mã hóa DES để thực hiện. Input: Bản rõ M = m1m2…m64 là một khối 64 bit, khóa 64 bit K = k1k2…k64. Output: Bản mã 64 bit C = c1c2… c64

+ Bước 1: Sinh khóa con: Sử dụng thuật toán sinh khóa con từ khóa K ta sẽ được 16 khóa con K1, K2, … K16

+ Bước 2: Sử dụng phép hoán vị khởi đầu IP (Initial Permutation) để hoán vị các bit của M, kết quả nhận được chia thành 2 nửa là L0 = m63m62…m32, R0 = m31m30…m0.

+ Bước 3: Với i chạy từ i = 1 đến 16 thực hiện: Tính các Li và Ri theo công thức: Li = Ri-1 Ri = Li-1 XOR f(Ri-1, Ki) trong đó f(Ri-1, Ki) = R(S(E(Ri-1) XOR Ki)). Việc tính f(Ri-1, Ki) sẽ được trình bày chi tiết ở phần sau.

+ Bước 4: Đổi vị trí khối L16, R16 ta được khối R16L16 = b1b2…b64.

+ Bước 5: Sử dụng phép hoán vị kết thúc FP(Final Permutation – nghịch đảo với hoán vị khởi đầu IP) ta thu được bản mã cần tìm : C = IP-1(b1b2…b64)



1.3. Quá trình sinh khóa con

16 vòng lặp của DES chạy cùng thuật toán như nhau nhưng với 16 khóa con khác nhau. Các khóa con đều được sinh ra từ khóa chính của DES bằng thuật toán sinh khóa con.

Khóa ban đầu là 1 xâu có độ dài 64 bit, bit thứ 8 của mỗi byte sẽ được lấy ra để kiểm tra phát hiện lỗi, tạo ra chuỗi 56 bit. Sau khi bỏ các bit kiểm tra ta sẽ hoán vị chuỗi 56 bit này. Hai bước trên được thực hiện thông qua hoán vị ma trận PC-1 (Permuted choice 1). 

Tiếp theo ta kết quả sau khi PC-1 thành 2 phần : C0 : 28 bit đầu. D0 : 28 bit cuối. Mỗi phần sẽ được xử lý 1 cách độc lập. Ci = LSi(Ci-1) Di = LSi(Ci-1) với 1 ≤ i ≤ 16. LSi là biểu diễn phép dịch bit vòng (cyclic shift) sang trái 1 hoặc 2 vị trí tùy thuộc vào i.

Cuối cùng sử dụng hoán vị cố định PC-2 (Permuted choice 2) để hoán vị chuỗi CiDi 56 bit tạo thành khóa Ki với 48 bit.

1.4. Quá trình mã hóa DES

Chia thành 3 giai đoạn:

Giai đoạn 1:

Với bản rõ cho trước x, 1 xâu x’ sẽ được tạo ra bằng cách hoán vị các bit của x theo hoán vị ban đầu IP.

Tiếp theo x’ sẽ được chia thành 2 phần L0,R0. x’ = IP(x) = L0R0 Trong đó L0 là 32 bit đầu, R0 là 32 bit cuối.

Giai đoạn 2:

Tính toán 16 lần bằng 1 hàm xác định. Ta sẽ tính Li, Ri (1 ≤ i ≤ 16) theo quy tắc: Li = Ri-1. Ri = Li-1 XOR f(Ri-1, Ki). Với Ki là khóa được sinh ra ở quá trình tạo khóa, f là một hàm sẽ được trình bày ở phần sau.

Giai đoạn 3:

Áp dụng hoán vị kết thúc FP cho xâu bit R16L16 ta thu được bản mã y: y = FP(R16L16).

 

1.5. Giải mã DES

Quá trình giải mã của DES cũng tương tự quá trình mã hóa. Chỉ khác nhau ở: Li = Ri-1. Ri = Li-1 XOR f(Ri-1, K16-i+1). Như vậy khóa K của hàm F sẽ đi từ khóa K16 đến khóa K1.

1.6. Hàm F

Đầu vào hàm f có 2 biến:

+ Biến thứ nhất: Ri-1 là xâu bit có độ dài 32 bit. - Biến thứ hai: Ki là xâu bit có độ dài 48 bit. Đầu ra của hàm f là xâu có độ dài 32 bit. Quy trình hoạt động của hàm f như sau:

+ Biến thứ nhất Ri-1 được mở rộng thành một xâu có độ dài 48 bit theo một hàm mở rộng hoán vị E (Expansion permutation). Thực chất hàm mở rộng E(Ri-1) là một hoán vị có lặp trong đó lặp lại 16 bit của Ri-1.

+ Tính E(Ri-1) XOR Ki.

+ Tách kết quả của phép tính trên thành 8 xâu 6 bit B1, B2, …, B8.

+ Đưa các khối 8 bit Bi vào 8 bảng S1, S2, …, S8 (được gọi là các hộp S-box). Mỗi hộp S-Box là một bảng 4*16 cố định có các cột từ 0 đến 15 và các hàng từ 0 đến 3. Với mỗi xâu 6 bit Bi = b1b2b3b4b5b6 ta tính được SiBi như sau: hai bit b1b6 xác định hàng r trong hộp Si, bốn bit b2b3b4b5 xác định cột c trong hộp Si. Khi đó, Si(Bi) sẽ xác định phần tử Ci = Si(r,c), phần tử này viết dưới dạng nhị phân 4 bit. Như vậy, 8 khối 6 bit Bi (1 ≤ i ≤ 8) sẽ cho ra 8 khối 4 bit Ci với (1 ≤ i ≤ 8).

+ Xâu bit C = C1C2C3C4C5C6C7C8 có độ dài 32 bit được hoán vị theo phép toán hoán vị P (hộp P-Box). Kết quả P(C) sẽ là kết quá của hàm f(Ri-1,Ki).

Hàm mở rộng E

Hàm ở rộng E sẽ tăng độ dài Ri-1 từ 32 bit lên 48 bit bằng cách thay đổi thứ tự các bit cũng như lặp lại các bit. Việc thực hiện này nhằm hai mục đích:

+ Làm độ dài của Ri-1 cùng cỡ với khóa K để thực hiện việc cộng modulo XOR.

+ Cho kết quả dài hơn để có thể được nén trong suốt quá trình thay thế. Tuy nhiên, cả hai mục đích này nhằm một mục tiêu chính là bảo mật dữ liệu. Bằng cách cho phép 1 bit có thể chèn vào hai vị trí thay thế, sự phụ thuộc của các bit đầu ra với các bit đầu vào sẽ trải rộng ra.

 

Các hộp S-box

Sau khi thực hiện phép XOR giữa E(Ri-1) và Ki, kết quả thu được chuỗi 48 bit chia làm 8 khối đưa vào 8 hộp S-box. Mỗi hộp S-Box sẽ có 6 bit đầu vào và 4 bit đầu ra. Kết quả thu được là một chuỗi 32 bit tiếp tục vào hộp P-Box.

+ Mỗi hàng trong mỗi hộp S là hoán vị của các số nguyên từ 0 đến 15

+ Các hộp S-box phi tuyến tính. nói cách khác, đầu ra không phải là biến đối tuyến tính của đầu vào.

+ Sự thay đổi của một bit, hai bit hoặc nhiều hơn sẽ dẫn đến sự biến đổi ở đầu ra.

+ Nếu hai đầu vào của một S-box bất kì chỉ khác nhau 2 bit ở giữa (bit 3 và 4) thì đầu ra sẽ khác nhau ít nhất 2 bit. Nói cách khác, S(x) và S(x XOR 001100) phải khác nhau ít nhất 2 bit.



Hộp P-box

Mỗi 4 bit đầu ra của các hộp S-box sẽ được ghép lại, theo thứ tự các hộp và được đem vào hộp P-box. Hộp P-Box đơn giản chỉ là hoán vị các bit với nhau.

Ví dụ:

Ta có 1 bản tin M, M = 0123456789ABCDEF và một khóa K = 13345799BBCDDFF1 với M, K được định dạng dưới dạng hệ thập lục phân, ta tiến hành mã hóa và giải mã theo giải thuật DES theo các bước sau. Ta viết lại M, K dưới dạng nhị phân , chúng ta nhận được khối 64bit của của văn bản M và khóa K M = 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111 chia 64 bit trên thành 2 nửa trái và phải, lấy từ trái qua phải mỗi phần 32 bit L = 0000 0001 0010 0011 0100 0101 0110 0111
R = 1000 1001 1010 1011 1100 1101 1110 1111 DES hoạt động trên các khối 64-bit sử dụng kích thước chính của 56- bit. Các khóa thực sự được lưu trữ dài 64 bit, nhưng mỗi bit thứ 8 trong khóa không được sử dụng (tức là bit số 8, 16, 24, 32, 40, 48, 56, và 64). Tuy nhiên, chúng ta sẽ vẫn đánh số các bit 1-64, đi từ trái sang phải, trong các tính toán sau đây. Tuy nhiên, ta sẽ thấy, tám bit đề cập đến được loại trừ khi chúng ta tạo ra khóa. Đưa K về dạng nhị phân: K = 00010011 00110100 01010111 01111001 10011011 10111100 11011111 11110001 Thuật toán DES sử dụng các bước sau:

Bước 1: Tạo 16 khóa con, mỗi khóa có chiều dài 48 bit Khóa 64 bit được hoán vị theo bảng dưới đây, PC-1. Chỉ có 56 bit của khóa ban đầu xuất hiện trong khóa hoán vị PC-1

Từ khóa 64 bit ban đầu: K = 00010011 00110100 01010111 01111001 10011011 10111100 11011111 11110001 Từ bảng hoán vị trên ta thấy: bit thứ 57 của K là bit ‘1’, thứ 49 của K là bit ‘1’, bít thứ 41, 33, 25, 17, 9 lần lượt là 11000…. Chúng ta nhận được hoán vị 56 bit: K+ = 1111000 0110011 0010101 0101111 0101010 1011001 1001111 0001111 Tiếp theo, chia khóa này thành 2 nửa trái và phải , C0 và D0, mỗi nửa 28 bit, ta được: C0 = 1111000 0110011 0010101 0101111 D0 = 0101010 1011001 1001111 0001111 Với C0 và D0 đã được xác định, bây giờ chúng ta tạo ra 16 khối CnDn 1<=n<=16. Mỗi cặp CnDn được hình thành từ các cặp trước nó Cn-1Dn-1 bằng cách sử dụng quy luật sau đối với khối trước nó

Điều này có nghĩa là , ví dụ C3, D3 thu được nhờ sự dịch trái 2 lần C2, D2, hay C2, D2 thu được nhờ sự dịch trái 1 lần của C1, D1 Ta đã có C0 = 1111000 0110011 0010101 0101111 D0 = 0101010 1011001 1001111 0001111

Từ bảng quy luật trên ta thấy, C1D1 được sinh ra khi thực hiện dịch trái 1 lần đối với C0D0 ta thu được : C1 = 111000 0110011 0010101 01011111 D1 = 101010 1011001 1001111 00011110 Tương tự ứng với bảng quy luật trên ta thu được các khối CnDn (1<=n<=16) như sau:

C2 = 1100001100110010101010111111

D2 = 0101010110011001111000111101

C3 = 0000110011001010101011111111

D3 = 0101011001100111100011110101

C4 = 0011001100101010101111111100

D4 = 0101100110011110001111010101

C5 = 1100110010101010111111110000

D5 = 0110011001111000111101010101

C6 = 0011001010101011111111000011

D6 = 1001100111100011110101010101

C7 = 1100101010101111111100001100

D7 = 0110011110001111010101010110

C8 = 0010101010111111110000110011

D8 = 1001111000111101010101011001

C9 = 0101010101111111100001100110

D9 = 0011110001111010101010110011

C10 = 0101010111111110000110011001

D10 = 1111000111101010101011001100

C11 = 0101011111111000011001100101

D11 = 1100011110101010101100110011

C12 = 0101111111100001100110010101

D12 = 0001111010101010110011001111

C13 = 0111111110000110011001010101

D13 = 0111101010101011001100111100

C14 = 1111111000011001100101010101

D14 = 1110101010101100110011110001

C15 = 1111100001100110010101010111

D15 = 1010101010110011001111000111

C16 = 1111000011001100101010101111

D16 = 0101010101100110011110001111

Bây giờ chúng ta xây dựng các khóa Kn (1<=k<=16) bằng cách áp dụng bảng hoán vị sau cho mỗi cặp CnDn. Mỗi cặp có 56 bit, nhưng PC-2 chỉ sử dụng 48 trong số này. PC-2

Từ C1D1 = 1110000 1100110 0101010 1011111 1010101 0110011 0011110 0011110 Từ bảng trên ta thấy: bit thứ 14 của C1D1 là bít đầu tiên của K1 tức là bit ‘0’, bit thứ 17 là bít thứ 2 của K1, tức bit ‘0’…bít cuối cùng của K1 là bít thứ 32 của C1D1 tức là bit ‘0’

K1 = 000110 110000 001011 101111 111111 000111 000001 110010

Tương tự cho các khóa khác, ta có:

K2 = 011110 011010 111011 011001 110110 111100 100111 100101

K3 = 010101 011111 110010 001010 010000 101100 111110 011001

K4 = 011100 101010 110111 010110 110110 110011 010100 011101

K5 = 011111 001110 110000 000111 111010 110101 001110 101000

K6 = 011000 111010 010100 111110 010100 000111 101100 101111

K7 = 111011 001000 010010 110111 111101 100001 100010 111100

K8 = 111101 111000 101000 111010 110000 010011 101111 111011

K9 = 111000 001101 101111 101011 111011 011110 011110 000001

K10 = 101100 011111 001101 000111 101110 100100 011001 001111

K11 = 001000 010101 111111 010011 110111 101101 001110 000110

K12 = 011101 010111 000111 110101 100101 000110 011111 101001

K13 = 100101 111100 010111 010001 111110 101011 101001 000001

K14 = 010111 110100 001110 110111 111100 101110 011100 111010

K15 = 101111 111001 000110 001101 001111 010011 111100 001010

K16 = 110010 110011 110110 001011 000011 100001 011111 110101

Bước 2: Mã hóa 64 bit khối dữ liệu Có một hoán vị IP ban đầu của 64 bit của dữ liệu M, sắp xếp lại các bit này theo quy luật bảng dưới đây IP

Áp dụng hoán vị cho khối văn bản ban đầu M, ta nhận được: M = 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111 IP = 1100 1100 0000 0000 1100 1100 1111 1111 1111 0000 1010 1010 1111 0000 1010 1010 Chia khối IP hoán vị thành 1 nửa trái L0 32 bit và một nửa phải R0 32bit, từ IP ta nhận được: L0 = 1100 1100 0000 0000 1100 1100 1111 1111 R0 = 1111 0000 1010 1010 1111 0000 1010 1010 Bây giờ chúng ta tiến hành thông qua 16 vòng lặp, sử dụng một hàm f mà hoạt động trên 2 khối – một khối dữ liệu 32bit và 1 khóa Kn 48bit để sinh ra một khối 32bit Ln = Rn-1 Rn = Ln-1 f(Rn-1, Kn) For n = 1, ta có K1 = 000110 110000 001011 101111 111111 000111 000001 110010 L1 = R0= 1111 0000 1010 1010 1111 0000 1010 1010 R1 = L0 f(R0,K1) Để tính hàm f, chúng ta mở rộng mỗi khối Rn-1 từ 32bit lên 48bit bằng cách sử dụng một bảng một bảng lựa chọn mà lặp đi lặp lại một số bit trong Rn-1. Ta gọi việc sử dụng mỗi lựa chọn bảng này là hàm E. Vì vậy, E(Rn-1) có khối đầu vào 32bit và một khối đầu ra 48bit 48bit đầu ra được viết như 8 khối 6bit thu được bằng cách chọn các các bit trong đầu vào của nó theo theo bảng sau: E BIT-SELECTION TABLE

Ta tính E(R0) từ R0 như sau: R0 = 1111 0000 1010 1010 1111 0000 1010 1010 E(R0) = 011110 100001 010101 010101 011110 100001 010101 010101 Ta thấy mỗi khối 4bit ban đầu đã được mở rộng đến một khối 6 bit đầu ra. Tiếp theo để tính f , ta XOR đầu ra E(Rn-1) với khóa Kn: Kn E(Rn-1) Ví dụ: Cho K1, E(R0) ta có K1= = 000110 110000 001011 101111 111111 000111 000001 110010 E(R0) = 011110 100001 010101 010101 011110 100001 010101 010101 K1 E(R0) = 011000 010001 011110 111010 100001 100110 010100 100111 Tiếp theo ta sẽ sử dụng mỗi nhóm 6bit như các địa chỉ trong bảng gọi lại “S boxes”. Mỗi nhóm 6bit sẽ cung cấp 1 địa chỉ trong một S box khác nhau. Nằm tại địa chỉ đó sẽ có 1 số 4bit. Số 4bit này sẽ thay thế 6bit ban đầu . Kết quả cuối cùng là 8 nhóm 6bit được chuyển thành 8 nhóm 4bit, tổng số sẽ là 32 bit. Viết kết quả trước đó với 48bit dưới dạng sau: Kn E(Rn-1) = B1B2B3B4B5B6B7B8 Trong đó mỗi Bi là một nhóm 6 bit như ta đã xã định với trường hợp K1 E(R0) ở trên. Bây giờ ta phải tính S1(B1)S2(B2)S3(B3)S4(B4)S5(B5)S6(B6)S7(B7)S8(B8) Với Si(Bi) tương ứng là đầu ra của hộp thứ i S box Mỗi hàm S1, S2,…, S8 có một khối 6bit là đầu vào và một khối 4bit là đầu ra. Bảng xác định S1: S1

S1 là hàm được xác định trong bảng này và B là một khối 6 bit đầu vào, S1(B) được xác định như sau: xác định bit đầu tiên và cuối cùng của B: nó là một số nhị phân có dạng 00,01, 10 hoặc 11, tức là có giá trị từ 0-3 dạng thập phân. Đặt giá trị này là i. còn lại 4 bit giữa của B sẽ có giá trị trong khoảng từ 0 đến 15 dạng thập phân (vì dạng nhị phân của B nằm trong khoảng 0000-1111). Đặt giá trị này là j. Tra trong bảng số S1 ở trên hàng thứ i và cột thứ j. Đó là một số trong khoảng từ 0 đến 15 và biểu diễn nó bởi một khối 4 bit. Khối đó là đầu ra S1(B) của S1 cho đầu vào B. Ví dụ, khối đầu vào B1 = 011000, bit đầu tiên là "0" và bit cuối cùng "0" 00= 0 => i=0. Đây là hàng 0. bốn bit giữa là "1100", 1101 = 12=> j=12. vì vậy cột là cột số 12. Tìm tới hàng 0 cột 12 (0,12) = 5 , 5 nhị phân là 0101. Do đó S1(B1) = 0101. Ta có các bảng xác định S2, S3, …S8 như sau:

S2

Đối với vòng đầu tiên, đã đã tính toán và có: K1 E(R0) = 011000 010001 011110 111010 100001 100110 010100 100111 Từ các bảng trên với B1, B2, …, B8 đã xác định ta tính được:

S1(B1)S2(B2)S3(B3)S4(B4)S5(B5)S6(B6)S7(B7)S8(B8) =

0101 1100 1000 0010 1011 0101 1001 0111

Cuối cùng trong việc tính toán f là làm một hàm hoán vị P của các đầu ra S-box để có được giá trị cuối cùng của f:

f(R0,K1) = P(S1(B1)S2(B2)S3(B3)S4(B4)S5(B5)S6(B6)S7(B7)S8(B8) )

Hoán vị P được xác định trong bảng dưới đây: P

Từ đầu ra của 8 boxes:

 S1(B1)S2(B2)S3(B3)S4(B4)S5(B5)S6(B6)S7(B7)S8(B8) = 0101 1100 1000 0010 1011 0101 1001 0111

Ta có: f(R0,K1) = 0010 0011 0100 1010 1010 1001 1011 1011 R1 = L0 f(R0,K1) = 1100 1100 0000 0000 1100 1100 1111 1111 0010 0011 0100 1010 1010 1001 1011 1011 = 1110 1111 0100 1010 0110 0101 0100 0100

Vòng lặp tiếp theo , sẽ có L2 = R1, R2 = L1 f(R1, K2) cứ làm tương tự trên cho 16 vòng lặp.

Vòng cuối cùng ta có các khối L16R16. Ta đảo ngược thứ tự của 2 khối và áp dụng một hoán vị IP­-1 được xác định bởi bảng sau : IP-1

Xử lý tất cả 16 khối sử dụng phương pháp xác định như trên, tới vòng lặp thứ 16 ta có được : L16 = 0100 0011 0100 0010 0011 0010 0011 0100 R16 = 0000 1010 0100 1100 1101 1001 1001 0101 Chúng ta đảo ngược thứ tự của 2 khối này và áp dụng hoán vị cuối cùng R16L16 = 0000 1010 0100 1100 1101 1001 1001 01010100 0011 0100 0010 0011 0010 0011 0100 IP-1 = 10000101 11101000 00010011 01010100 00001111 00001010 10110100 00000101 Chuyển đổi IP-1 về dạng thập lục phân ta được : 85E813540F0AB405 

Kết luận : Vậy dạng mã hóa của M = 0123456789ABCDEF là C = 85E813540F0AB405 

Giải mã : Quá trình giải mã chỉ đơn giản là nghịch đảo của mã hóa, các bước thực hiện tương tự như trên nhưng đảo ngược thứ tự các khóa được áp dụng, tức là áp dụng khóa K16 tới K1, làm hoàn toàn giống các bước và trình tự như mã hóa.

2. RSA

RSA là một thuật ngữ vô cùng quen thuộc đối với những người học trong lĩnh vực mật mã học. RSA đánh dấu sự phát triển vượt bậc trong lĩnh vực mật mã học nói chung và được sử dụng rộng rãi trong các hoạt động thương mại điện tử. 

RSA là một sản phẩm nghiên cứu với sự hợp lực của 3 nhà khoa học lớn là Adi Shamir, Len Adleman và Ron Rivest và được đưa ra mô tả lần đầu vào năm 1977 tại Học viện MIT. Cái tên RSA được lấy từ những chữ cái đầu tiên của 3 nhà khoa học.

RSA là một thuật toán hay còn được gọi là hệ mã hóa đối xứng có phạm vi ứng dụng rộng rãi và phổ biến. Người ta thường sử dụng RSA ở công tác mã hóa hay thiết lập chữ ký điện tử với vai trò là mã hóa khóa công khai. Bất kỳ ai cũng có thể sử dụng khóa công khai để mã hóa được dữ liệu muốn gửi đi nhưng để giải mã được chúng cần phải có sự hỗ trợ của khóa bí mật.

Hoạt động gửi và nhận cần có sự can thiệp bởi RSA vì bản thân nó chứa hai khóa là công khai và bí mật để đảm nhận hai nhiệm vụ bất đối xứng là mã hóa và giải mã. Điều này cũng tương tự như cơ chế đóng và mở khóa cửa vậy tuy nhiên RSA sẽ phức tạp hơn nhiều vì nó là một thuật toán. Việc mã hóa giúp bảo mật thông tin nhưng cũng gây bất lợi cho người nhận khi không biết cách giải mã để xem được những thông tin bên trong. Chính vì lý do này mà khóa bí mật luôn được đi kèm với việc mã hóa.

Khác với các loại mã hóa có khóa đối xứng, khóa bí mật của RSA không truyền được tin ra bên ngoài kể cả có kẻ tấn công nếu không có khóa bí mật cũng sẽ không giải mã được những thông tin đó. Như vậy ta có thể thấy hai tính năng mã hóa và giải mã tối ưu đến tuyệt đối của RSA, đây cũng là lý do chúng được sử dụng ở hầu hết các trường hợp cần bảo mật thông tin. Giao thức SSL hay HTTPS cùng với chứng chỉ điện tử đều sử dụng RSA.

2.1. RSA hoạt động như thế nào?

Hoạt động của RSA dựa trên 4 bước chính: sinh khóa, chia sẻ key, mã hóa và giải mã. Mấu chốt cơ bản của việc sinh khóa trong RSA là tìm được bộ 3 số tự nhiên ed và n sao cho:

và một điểm không thể bỏ qua là cần bảo mật cho d sao cho dù biết en hay thậm chí cả m cũng không thể tìm ra d được.

Cụ thể, khóa của RSA được sinh như sau:

+ Chọn 2 số nguyên tố p và q

+ Tính n = pq. Sau này, n sẽ được dùng làm modulus trong cả public key và private key.

+ Tính một số giả nguyên tố bằng phi hàm Carmichael như sau: λ(n) = BCNN(λ(p), λ(q)) = BCNN(p − 1, q − 1). Giá trị này sẽ được giữ bí mật.

+ Chọn một số tự nhiên e trong khoảng (1, λ(n)) sao cho ƯCLN(e, λ(n)) = 1, tức là e và λ(n) nguyên tố cùng nhau.

 

+ Tính toán số d sao cho d ≡ 1/e (mod λ(n)) hay viết dễ hiểu hơn thì de ≡ 1 (mod λ(n)). Số d được gọi là số nghịch đảo modulo của e (theo modulo mod λ(n)).

Public key sẽ là bộ số (n, e), và private key sẽ là bộ số (n, d). Chúng ta cần giữ private key thật cẩn thận cũng như các số nguyên tố p và q vì từ đó có thể tính toán các khóa rất dễ dàng.

Trong thực hành, chúng ta thường chọn e tương đối nhỏ để việc mã hóa và giải mã nhanh chóng hơn. Giá trị thường được sử dụng là e = 65537. Ngoài ra, chúng ta có thể tính số giả nguyên tố bằng phi hàm Euler φ(n) = (p − 1)(q − 1) và dùng nó như λ(n). Vì φ(n) là bội của λ(n) nên các số d thỏa mãn de ≡ 1 (mod φ(n)) cũng sẽ thỏa mãn d ≡ 1/e (mod λ(n)). Tuy nhiên, một số tác dụng phụ của việc này là d thường sẽ trở nên lớn hơn mức cần thiết.

2.2. Mã hóa và giải mã

Trong phần này, chúng ta sẽ tìm hiểu cách mã hóa với public key (n, e) và giải mã với private key (n, d).

Nếu chúng ta có bản rõ M, chúng ta cần chuyển nó thành một số tự nhiên m trong khoảng (0, n) sao cho mn nguyên tố cùng nhau. Việc này rất dễ dàng thực hiện bằng cách thêm một các kỹ thuật padding. Tiếp theo, chúng ta sẽ má hóa m, thành c như sau:

Sau đó giá trị c sẽ được chuyển cho người nhận.

Ở phía người nhận, họ sẽ giải mã từ c để lấy được m như sau:

Từ m có thể lấy lại được bản tin bằng cách đảo ngược padding

Chúng ta lấy một ví dụ đơn giản như sau:

p = 5, q = 7

=> n = pq = 35

=> φ(n) = 24

Chúng ta chọn e = 5 vì ƯCLN(5, 24) = 1, cuối cùng chọn d = 29 vì ed - 1 = 29x5 - 1 chia hết cho 24.

Giả sử m = 32 (dấu cách), chúng ta sẽ mã hóa m và thu được

c = 32 ^ 5 % 35 = 2

Giải mã c để thu được m

m = 2 ^ 29 % 35 = 32

Đây chính là m ban đầu. Bạn có thể thứ m với các giá trị khác nhau để thấy thuật toán hoàn toàn chính xác. Tất nhiên rồi, nó đã được chứng minh mà. Việc chứng minh RSA hoạt động chính xác là một vấn đề toán học khá phức tạp mà bài viết này sẽ không đi vào chi tiết của nó.

Mức độ bảo mật của RSA phụ thuộc rất lớn vào khả năng phân tích thừa số nguyên tố của các số lớn. Bởi vì chúng ta cung cấp public một cách rộng rãi, nếu việc phân tích thừa số nguyên tố đơn giản, thì việc bị lộ private là không thể tránh khỏi.

Vì vậy, khi sinh khóa, chúng ta cần chọn các số nguyên tố p và q một cách ngẫu nhiên. Bản thân hai số nguyên tố này cũng rất lớn, và để việc phân tích thừa số nguyên tố khó khăn hơn, hai số nguyên tố này sẽ không có cùng độ dài. Trong tương lai gần, có lẽ vẫn chưa có một phương pháp hiệu quả nào cho phép thực hiện điều này với các máy tính cá nhân.

Tuy nhiên, với sự phát triển của công nghệ, các siêu máy tính xuất hiện ngày càng nhiều. Cùng với chúng ta máy tính lượng tử cho phép tính toán với tốc độ cao hơn rất nhiều có thể sẽ phá vỡ sự bảo mật của RSA.

Ngày từ năm 1993, thuật toán Shor đã được phát triển và chỉ ra rằng máy tính lượng tử có thể giải bài toán phân tích ra thừa số trong thời gian đa thức. Rất may là những điều này mới chỉ là lý thuyết vì đến thời điểm hiện tại và trong vài năm tới, máy tính lượng tử vẫn chưa hoàn thiện.

Một câu hỏi khá thú vị là có thể đảo vai trò của public key và private key hay không? Tức là chúng ta dùng private key để mã hóa và dùng public key để giải mã hay không? Câu trả lời là có. Tuy nhiên, không ai làm như vậy cả vì rất đơn giản: từ public key rất khó để suy ra private key nhưng từ private để suy ra public thì rất đơn giản. Nếu cho người khác private key (để họ mã hóa), những kẻ tấn công man in the middle sẽ dễ dàng tính toán được public và thay đổi hoàn toàn cuộc hội thoại của chúng ta.

Tuy nhiên, có một trường hợp chúng ta đã đảo vai trò của private key và public key như vậy. Đó chính là khi chúng ta sử dụng chữ ký số.

2.3. Ứng dụng của RSA trong cuộc sống

RSA trong bảo mật dữ liệu

RSA ra đời với mục đích bảo vệ dữ liệu, do vậy chúng được ứng dụng rất nhiều trong hoạt động hiện đại. Những ứng dụng của RSA trong bảo mật dữ liệu như:

Chứng thực dữ liệu: chắc hẳn các bạn đã từng gặp tình trạng yêu cầu xác minh bằng cách đưa ra các con số gửi về email hay số điện thoại trước khi đăng nhập. Đây chính là phương pháp bảo mật thông tin, dữ liệu ứng dụng thuật toán RSA để tránh những tình trạng mạo danh, hack tài khoản gây ảnh hưởng cho người dùng và xã hội. Việc chứng thực giúp bảo vệ được tài khoản của bản thân người sử dụng giúp an tâm hơn khi sử dụng các dịch vụ trực tuyến. Truyền tải dữ liệu an toàn: hiện nay tình trạng nghe lén, theo dõi hoạt động cũng như lấy cắp dữ liệu cá nhân trên mạng xã hội bị lên án và chỉ trích rất nhiều, bao gồm cả ông lớn Facebook. Không chỉ những trang mạng xã hội, các trang web cũng không tránh khỏi việc lưu lại các hoạt động, hành vi truy cập để phục vụ các mục đích Marketing. Do đó với thuật toán RSA giúp dữ liệu khỏi các cuộc tấn công của kẻ xấu. Chữ ký số/ chữ ký điện tử: trên các thẻ ATM luôn có phần chữ ký điện tử đã được mã hóa từ chữ ký của khách hàng khi đăng ký tài khoản tại ngân hàng. Có thể nói, trong lĩnh vực ngân hàng, vấn đề bảo mật thông tin của khách hàng cần được đặt lên hàng đầu, chúng quyết định chất lượng của dịch vụ. RSA được ứng dụng để bảo mật dữ liệu khi người dùng thực hiện những giao dịch ngân hàng, đem lại trải nghiệm tốt và giúp khách hàng an tâm hơn.

Ứng dụng của RSA trong công nghệ thông tin

Trong ngôn ngữ lập trình Java, các nhà lập trình viên thường sử dụng những đoạn code chứa RSA để tăng tính bảo mật cho trang web và ứng dụng cũng như đảm bảo an toàn cho người sử dụng.

Các đoạn code RSA này có thể hoạt động dưới bất kỳ sự thay đổi nào của môi trường. Ngoài ra, các lập trình viên cũng sử dụng các ngôn ngữ lập trình khác bên cạnh Java có thể tìm hiểu và ứng dụng những tính năng của RSA trong hoạt động làm việc và bảo mật thông tin.

Ngày nay việc sử dụng các ứng dụng, trang web trên internet ngày càng gia tăng khiến cho vấn đề bảo mật dữ liệu càng được chú trọng. Những dữ liệu này có thể là những thông tin bí mật cá nhân, thông tin về tài chính,… gây không ít nguy hại cho người sử dụng. Cũng chính vì lý do này mà thuật toán RSA được biết đến và sử dụng nhiều hơn trong tất cả các lĩnh vực đặc biệt là trong ngành ngân hàng. Với những chia sẻ trong bài viết, chúng tôi hy vọng bạn đã giải mã được câu hỏi RSA là gì cũng như những thông tin liên quan đến thuật toán này. Mong rằng các bạn đã có thêm một bí quyết để bảo vệ những dữ liệu của mình một cách tốt hơn nhé!

Chữ ký số sử dụng hệ mã hóa RSA

Việc ký tên và xác thực chữ ký số sử dụng hệ mã hóa RSA tương tự như quá trình mã hóa mà giải mã ở trên. Tuy nhiên vai trò của public key và private thì có thay đổi đôi chút.

Để tạo chữ ký, người gửi sẽ dùng private key và người nhận sẽ dùng public key để xác thực chữ ký đó.

Tuy nhiên, vì bản tin rất dài nên việc mã hóa toàn bộ bản tin sẽ rất mất thời gian. Vì vậy, trong thực hành, chữ ký số thường sử dụng phương pháp mã hóa giá trị hash của bản tin. Việc này mang lại rất nhiều lợi ích như:

+ Các hàm hash là hàm 1 chiều, vì vậy dù có được hash cũng không thể biết được bản tin gốc như thế nào.

+ Độ dài hash là cố định và thường rất nhỏ, vì vậy chữ số sẽ không chiếm quá nhiều dung lượng.

+ Giá trị hash còn có thể dùng để kiểm tra lại bản tin nhận được có nguyên vẹn hay không?

Chữ ký số đem lại nhiều giá trị hơn chữ ký tay rất nhiều. Có lẽ cũng vì vậy, việc xử lý chữ ký số phức tạp hơn hẳn chữ ký tay truyền thống.

Xác định nguồn gốc

Hệ mã hóa bất đối xứng cho phép tạo chữ ký với private key mà chỉ người chủ mới biết. Khi nhận gói tin, người nhận xác thực chữ ký bằng cách dùng public key giải mã, sau đó tính giá trị hash của bản tin gốc và so sánh với hash trong gói tin nhận được. Hai chuỗi này phải trùng khớp với nhau. Tất nhiêu hệ mã hóa RSA vẫn có những thách thức về an ninh nhất định nhưng dù sao thì nó vẫn khá an toàn.

Dữ liệu được giữ một cách toàn vẹn

Tin nhắn gửi từ chủ private key rất khó có thể bị giả mạo. Bởi vì nếu thay đổi tin nhắn thì giá trị hash cũng phải thay đổi theo. Những kẻ nghe lén trong mạng đương nhiên là có thể tìm cách đọc tin nhắn gốc và cả hash của nó. Nhưng hắn ta không thể thay đổi tin nhắn được vì hắn không có private key để sửa đổi chữ ký số cho phù hợp.

Chữ ký số không thể phủ nhận

Trong giao dịch, một gói tin kèm chữ ký số rất dễ dàng tìm ra được nguồn gốc của chữ ký đó. Bởi vì private key là bí mật và chỉ người chủ của nó mới có thể biết, họ không thể chối cãi rằng chữ ký này không phải do họ phát hành. Cũng tương tự trường hợp trên, hệ mã hóa RSA hay bất kỳ hệ mã hóa nào khác cũng đều có những vấn đề về an ninh nên việc này không thể đảm bảo tuyệt đối chính xác được.

2.4. Kết luận

Công nghệ ngày càng phát triển, các giao dịch điện tử, đặc biệt là thương mại điện tử đang được thực hiện liên tục hàng ngày, hàng giờ. Chữ ký số là một công nghệ cho phép xác định chủ nhân của dữ liệu nào đó và kiểm tra dữ liệu có nguyên vẹn hay không.

Ở nước ta, chữ ký số áp được áp dụng trong một số hoạt động ngành ngân hàng và kế toán. Nhưng trong thời gian tới, rất có thể chữ ký số sẽ phổ biến hơn, khi chúng ta đang dần dần hướng đến một thế giới không còn giấy bút.

3. AES

AES là một mã khối, nhưng khác với các mã khối khác được biết đến trước đây (DES, IDEA,…), dữ liệu trong AES không được biểu diễn dưới dạng một mảng các byte hay các bit mà được biểu diễn dưới dạng một ma trận 4xNb và được gọi là mảng trạng thái (state). Trong đó, đối với AES, Nb luôn có giá trị bằng 4. Trong khi thuật toán Rijndael hỗ trợ ba giá trị của Nb là 4, 6, 8 tương ứng với kích thước khối 128, 192 và 256 bit Dữ liệu đầu vào được đọc vào ma trận state theo từng cột, theo thứ tự từ trên xuống dưới, từ trái qua phải. Dữ liệu đầu ra được đọc từ ma trận cũng theo quy tắc trên.

Khóa vòng trong AES cũng được biểu diễn hoàn toàn tương tự như cách biểu diễn dữ liệu. Tuy nhiên, tùy vào kích thước khóa mà số cột của ma trận khóa vòng Nk sẽ khác nhau. Cụ thể, Nk nhận các giá trị 4, 6, 8 tương ứng với các kích thước khóa là 128, 192 và 256 bit. Đây chính là điểm mạnh của thuật toán AES trong vấn đề mở rộng khóa.

Số vòng lặp ký hiệu là Nr, phụ thuộc vào hai đại lượng Nb và Nk. Vì Nb trong AES có giá trị cố định nên Nr chỉ phụ thuộc vào Nk. Giá trị của Nr tương ứng với ba giá trị của Nk là Nr = 10, 12, 14. Cụ thể, giá trị Nr được xác định bởi

3.1. Khái niệm từ (Word) trong AES

Bốn byte trên mỗi cột trong mảng trạng thái state tạo thành 1 từ 32 bit, trong đó số thứ tự của hàng r (0≤r<4) cho biết chỉ số của bốn byte trong mỗi từ. Từ định nghĩa state ở trên có thể coi state là mảng một chiều chứa các từ 32 bit

Tương tự như đối với mảng khóa cũng có thể biểu diễn thành mảng một chiều chứa các từ 32 bit như công thức dưới đây với số lượng từ khóa phụ thuộc vào Nk (Nk=4, 6, 8).

3.2. Thuật toán của AES

Thuật toán AES khá phức tạp, được mô tả khái quát gồm 3 bước như sau:

+ 1 Vòng khởi tạo chỉ gồm phép AddRoundKey

+ Nr -1 Vòng lặp gồm 4 phép biển đổi lần lượt: SubBytes, ShiftRows, MixColumns, AddRoundKey.

+ 1 Vòng cuối gồm các phép biến đổi giống vòng lặp và không có phép MixColumns.

Khái quát: 

·       Mở rộng khóa - Các khóa phụ dùng trong các vòng lặp được sinh ra từ khóa chính AES sử dụng thủ tục sinh khóa Rijndael.

·       InitialRound - AddRoundKey— Mỗi byte trong state được kết hợp với khóa phụ sử dụng XOR

·       Rounds - SubBytes—bước thay thế phi tuyến tính, trong đó mỗi byte trong state được thay thế bằng một byte khác sử dụng bảng tham chiếu - ShiftRows—bước đổi chỗ, trong đó mỗi dòng trong state được dịch một số bước theo chu kỳ - MixColumns—trộn các cột trong state, kết hợp 4 bytes trong mỗi cột - AddRoundKey

·       Final Round (không MixColumns) - SubBytes - ShiftRows - AddRoundKey.

Thuật toán giải mã khá giống với thuật toán mã hóa về mặt cấu trúc nhưng 4 hàm sử dụng là 4 hàm ngược của quá trình mã hóa. Riêng đối với cấu trúc giải mã trong AES gồm 2 chế độ giải mã:

- Ở cấu trúc giải mã ngược, gồm vòng khởi tạo, Nr-1 vòng lặp và vòng kết thúc. Trong đó vòng khởi tạo chỉ có phép biến đổi AddRounKey, vòng lặp gồm lần lượt 4 phép biến đổi chính: InvShiftRows, InvSubBytes, AddRounKey, InvMixColumns; vòng kết thúc khác với vòng lặp chính ở chỗ không có phép InvMixColumns.

- Ngược lại với cấu trúc giải mã ngược là cấu trúc giải mã xuôi, việc ngược lại thể hiện ở điểm: trong cấu trúc giải mã xuôi việc sắp xếp các phép biến đổi ngược giống hệt với cấu trúc mã hóa, cụ thể bao gồm: vòng khởi tạo, Nr-1 vòng lặp và vòng kết thúc. Trong đó vòng khởi là phép AddRounKey; ở vòng lặp thứ tự các phép biến đổi ngược lần lượt là: InvSubBytes, InvShiftRows, InvMixColumns, AddRounKey; vòng kết thúc giống vòng lặp nhưng được lược bỏ phép InvMixColumns. Một điểm khác biệt nữa trong hai cấu trúc giải mã ngược và giải mã xuôi đó là: Trong giải mã ngược khóa vòng giải mã chính là khóa vòng mã hóa với thứ tự đảo ngược. Còn trong giải mã xuôi thì khóa giải mã ngoài việc đảo ngược thứ tự khóa vòng mã hóa còn phải thực hiện phép InvMixColumns đối với các khóa vòng của vòng lặp giải mã.

3.3. SubBytes và InvSubBytes

Phép biến đổi SubBytes: Là phép thay thế byte phi tuyến tính, ở phép thay thế này nó tác động độc lập đến từng byte trong trạng thái hiện hành. Phép biến đổi SubBytes được thực hiện bằng cách tra cứu bảng thay thế (S-box) với tham số đầu vào là các byte trong bảng trạng thái. S-box được xây dựng như sau: Bước 1: Điền các con số từ 0 đến 255 vào bảng theo từng hàng. Vậy hàng 0 gồm các con số {00}, {01}, …{0F} (thập lục phân). Hàng 1 gồm các con số {10}, {11},…, {1F}. Điều này có nghĩa là tại hàng x cột y có giá trị {xy}. Bước 2: Thay thế mỗi byte trong bảng bằng giá trị nghịch đảo trong trường GF(28 ). Quy ước nghịch đảo của {00} cũng là {00}. Bước 3: Mỗi byte trong ma trận state được thay thế bởi 1 byte trong Rijndael S-box, hay bij = S(aij).

trong đó, 0 ≤ i ≤8 là bit thứ i của byte b tương ứng và ci là bit thứ thứ i của byte c với giá trị {63} hay {01100011}.


Trong đó phép cộng thực hiện như phép XOR. Bảng sau trình bày nội dung bảng S-box sau khi tính toán.


              Phép biến đổi ngược InvSubBytes: là phép thay thế biến đổi ngược với SubBytes. Là một phép thay thế byte, các byte thay thế được thực hiện bằng cách tra bảng thay thế ngược IS. Bảng thay thế ngược IS này được xây dựng như sau: Trước tiên, cũng phải xây dựng một bảng Inverse SubBytes (IS- box). Nghĩa là nếu với đầu vào {95}, S-box cho ra kết quả {2A}, thì với đầu vào là {2A}, IS sẽ cho ra lại kết quả {95}. Việc xây dựng hộp IS cũng giống như xây dựng S-box tại bước 1 và bước 2. Tại bước 3, IS thực hiện phép thay thế sau:

              Với di là bit thứ i của số {05} tức d7 d6 d0 = 00000101.

              Bảng sau trình bày nội dung bảng thay thế ngược IS sau khi tính toán.

Như vậy: phép biến đổi InvSubBytes thực hiện như sau: Mỗi byte trong ma trận state S, dưới dạng thập lục phân là {xy}, được thay thế bằng giá trị trong bảng IS tại dòng x cột y.

Mục đích của phép biến đổi SubBytes: S-box dùng để chống lại hình thức tấn công thám mã vi sai và thám mã tuyến tính. Giữa input và output của phép Substitute bytes không thể mô tả bằng một công thức toán đơn giản

3.4. ShiftRows và InvShiftRows

Phép biến đổi ShiftRows: Thao tác ShiftRows thực hiện hoán vị các byte trong ma trận state theo cách thức sau:

·        Dòng thứ nhất giữ nguyên

·        Dòng thứ 2 dịch vòng trái 1 byte

·        Dòng thứ 3 dịch vòng trái 2 byte

·        Dòng thứ 4 dịch vòng trái 3 byte


Phép biến đổi InvShiftRows: Phép biến đổi InvShiftRows thực hiện ngược lại với phép ShiftRows, nghĩa là:

·        Dòng thứ nhất giữ nguyên

·        Dòng thứ 2 dịch vòng phải 1 byte

·        Dòng thứ 3 dịch vòng phải 2 byte

·        Dòng thứ 4 dịch vòng phải 3 byte Mục đích của ShiftRows: Xáo trộn các byte để tạo các cột khác nhau trước khi sử dụng cột cho thao tác MixColumns.

3.5. MixColumns và InvMixColumns

Phép biến đổi MixColumns: Phép biến đổi MixColumns thực hiện biến đổi độc lập từng cột trong ma trận state bằng một phép nhân đa thức. Mỗi cột của state đươc coi là biểu diễn của một đa thức f(x) trong GF(2^8) như vậy phép biến đổi MixColumns chính là phép nhân theo modulo với x^4+1 với một đa thức cố định định nghĩa như sau:

              Phép nhân đa thức trên có thể biểu diễn dưới dạng phép nhân ma trận như sau:

              Phép biến đổi ngược InvMixColumns: Là phép biến đổi ngược với phép biến đổi MixColumns. InvMixColumns cũng thực hiện thao tác theo từng cột của trạng thái, xem mỗi cột như một đa thức bậc 3 gồm 4 hạng tử trên trường GF(2^8). Các cột của phép InvMixColumns được nhân theo modulo ( x^4 + 1 ) với đa thức nghịch đảo a(x) chính là đa thức a^-1(x) được định nghĩa:

              Như vậy phép InvMixColumns cũng được biểu diễn tương đương với phép nhân ma trận sau

Mục đích của MixColumns: Việc coi mỗi cột là một đa thức bậc 3, rồi nhân mỗi cột với đa thức a(x) sau đó modulo ( x4 +1 ) đã làm cho mỗi byte trong cột kết quả đều phụ thuộc vào bốn byte trong cột ban đầu. Thao tác MixColumns kết hợp với ShiftRows đảm bảo rằng sau một vài vòng biến đổi, 128 bit trong kết quả đều phụ thuộc vào tất cả 128 bit ban đầu. Điều này tạo ra tính khuếch tán (diffusion) cần thiết cho mã hóa.

3.6. AddRoundKey

Trong thao tác AddRoundKey, 128 bit của ma trận state sẽ được XOR với 128 bit của khóa con của từng vòng. Vì sử dụng phép XOR nên phép biến đổi ngược của AddRoundKey trong cấu trúc giải mã cũng chính là AddRoundKey. Việc kết hợp với khóa bí mật tạo ra tính làm rối (confusion) của mã hóa. Sự phức tạp của thao tác mở rộng khóa (KeySchedule) giúp gia tăng tính làm rối này.

3.7. Mở rộng khóa (ExpandKey )

ExpandKey là thao tác tạo lược đồ khóa hay mở rộng khóa, tạo ra Nr+1 khóa vòng từ khóa chính K, mỗi khóa vòng gồm Nb từ 32 bit, trong đó đối với AES thì Nb = 4, còn Nr được xác định theo. Các phép biến đổi để tạo khóa vòng trong ExpandKey là khác nhau đối với các giá trị khác nhau của kích thước khóa K. Sau đây là việc mở rộng khóa đối với khóa mã 128 bit



Trong thao tác mở rộng khóa với khóa mã 128 bit có đầu vào là 16 byte (4 word) của khóa mã, và sinh ra một mảng khóa vòng (Nr+1)x4=44 từ (word) hay 176 byte. 44 word này được sử dụng cho 11 vòng mã hóa của AES, mỗi vòng dùng 4 word. Từ bốn word đầu vào w0w1w2w3, trong lần lặp đầu tiên thao tác ExpandKey sinh ra bốn word w4w5w6w7, lần lặp thứ 2 từ w4w5w6w7 sinh ra w8w9w10w11 , cứ như thế cho đến lần lặp thứ 10 (tùy thuộc chiều dài khóa) sinh ra bốn word cuối cùng w40w41w42w43 như hình vẽ

Mục đích của ExpandKey: dùng để chống lại known-plaintext attack

·        Biết một số bit của khóa hay khóa con cũng không thể tính các bit còn lại.

·        Không thể tính ngược: biết một khóa con cũng không thể tính lại các khóa con trước đó.

·        Tính khuếch tán: một bit của khóa chính tác động lên tất cả các bit của các khóa con.

         4. Hàm băm MD5

         MD5 được phát minh bởi Ron Rivest ,người đã tham gia xây dựng RSA. MD5 viết tắt của chữ Message Digest,được phát triển lên từ MD4 và trước đó là MD2,do MD2 và MD4 không còn an toàn. Kích thước của MD5 là 128 bit được tính giá trị của thông điệp có kích thước tối đa 2^4 bit.

4.1. Thuật toán MD5

-Input: xâu đầu vào x có độ dài tối đa 2^64

-Output: chuỗi băm 128 bit

-Sơ đồ thuật toán:

Trước tiên thông điệp được đệm vào dãy padding 100...00. Chiều dài của dãy padding được chọn sao cho thông điệp cuối cùng có thể chia làm N block 512 bit M1,M2,...,MN. Quá trình tính giá trị băm của thông điệp là quá trình lũy tiến. Trước tiên block M1 kết hợp với giá trị khởi tạo H0 thông qua hàm F để tính giá trị hash H1. Sau đó block M2 đượckết hợp với H1 để cho ra giá trị hash là H2. Block M3 kết hợp với H2 cho ra giá trị H3. Cứ như vậy cho đến block MN thì có giá trị băm của toàn bộ thông điệp là HN.

·        H0 là một dãy 128 bit được chia thành 4 từ 32 bit, ký hiệu 4 từ 32 bit trên là abcd. Với a, b, c, d là các hằng số như sau (viết dưới dạng thập lục phân):
a = 01234567

b = 89abcdef

c = fedbca98

d = 76543210

4.2. Cấu trúc của hàm F như sau:

Tại mỗi bước lũy tiến, các giá trị abcd của giá trị hash Hi-1 được biến đổi qua 64 vòng từ 0 đến 63. Tại vòng thứ j sẽ có 2 tham số là Kj và Wj đều có kích thước 32 bit. Các tham số Kj được tính từ công thức: Kj là phần nguyên của số 2^32 abs(sin(i)) với i biểu diễn theo rad.

              Giá trị block Mi 512 bit được biến đổi qua một hàm message schedule cho ra 64 giá trị W0, W1,…, W63 mỗi giá trị 32 bit. Block Mi 512 bit được chia thành 16 block 32 bit ứng với các giá trị W0, W1, …, W15 (16×32=512). Tiếp theo, 16 giá trị này được lặp lại 3 lần tạo thành dãy 64 giá trị.
Sau vòng cuối cùng, các giá trị abcde được cộng với các giá trị abcd của Hi-1 để cho ra các giá trị abcd của Hi. Phép cộng ở đây là phép cộng modulo 232.
Tiếp theo tìm hiểu cấu trúc của một vòng. Việc biến đổi các giá trị abcd trong vòng thứ i được thể hiện trong hình bên dưới.

              Note:Phép + trong sơ đồ trên là phép cộng modul 2^32
Ở đây c lấy giá trị của b, d lấy giá trị của c, a lấy giá trị của d.

              Giá trị b được tính qua hàm:

              t = a + f(b,c,d) + Wi + Ki

              b = b + ROTL(t,s)

              Trong đó : Hàm f(x,y,z):

f (x,y,z) = (x ^ y) v (_x ^ z) nếu vòng từ 0 đến 15

f (x,y,z) = (z ^ x) v (_z ^ y) nếu vòng từ 16 đến 32

f (x,y,z) = x xor y xor z nếu vòng từ 32 đến 48

f (x,y,z) = y xor (x v _z) nếu vòng từ 49 đến 63

Hàm ROTL(t, s): t được dịch vòng trái s bit, với s là các hằng số cho vòng thứ i như sau:

5. SHA-256

SHA-2 (Thuật toán băm an toàn 2), trong đó SHA-256 cũng là một phần, chúng là một trong những thuật toán băm phổ biến nhất hiện nay. Ở bài viết này, mình sẽ chia sẻ cho các bạn cách thức mà thuật toán hoạt động. Với SHA-2 được biết đến với khả năng bảo mật cấp cao (không giống như SHA-1) và tốc độ của nó. Trong các trường hợp không tạo key, chẳng hạn như khai thác Bitcoin, thuật toán băm nhanh như SHA-2 sẽ là lợi thế cho bạn.

5.1. Hàm băm là gì ?

Hàm băm được chia làm 3 loại chính:

·        Xáo trộn dữ liệu.

·        Chấp nhận đầu vào tùy ý và đầu ra cố định.

·        Giúp thao tác dữ liệu mà không thay đổi.

SHA-2 có họ hàng với SHA-256 ?

SHA-2 là một thuật toán băm dữ liệu. Chúng đều sử dụng cùng một thuật toán nhưng sử dụng các hằng số khác nhau. Ví dụ: SHA-256 đặt các hằng số bổ sung xác định hành vi của thuật toán SHA-2, một trong những hằng số này là kích thước đầu ra, 256 và 512 trong SHA-256 và SHA-512 tham chiếu đến kích thước tương ứng với các bit.

SHA-2 là con của SHA-1 ?

SHA-2 là sự kế thừa của hàm băm SHA-1 và vẫn là một trong những hàm băm mạnh nhất được sử dụng ngày nay. SHA-256, trái ngược với SHA-1, chúng không bị xâm phạm. Vì lý do này, bạn chẳng có lý do gì để sử dụng SHA-1 vì nó không an toàn. Tính linh hoạt của kích thước đầu ra (224, 256, 512, ....) cũng cho phép SHA-2 ghép nối tốt với các KDF và mật mã phổ biến như AES-256.

"hello world" đầu tiên

Bước xử lý:

Trước tiên ta cần đổi chữ hello world thành nhị phân.

              01101000 01100101 01101100 01101100 01101111 00100000 01110111 01101111 01110010 01101100 01100100

              Tiếp đên ta nối 1 sau chuỗi nhị phân.

              01101000 01100101 01101100 01101100 01101111 00100000 01110111 01101111 01110010 01101100 01100100 1

              Nối 0 cho đến khi dữ liệu là bội số của 512, ít hơn 64 bit (Ở đây của mình là 448 bit).

01101000 01100101 01101100 01101100 01101111 00100000 01110111 01101111 01110010 01101100 01100100 10000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

              Nối 64 bit vào cuối, trong đó 64 bit là số nguyên big-endian đại diện cho độ dài của đầu vào ban đầu ở dạng nhị phân. Ở đây của mình là 88 hoặc trong hệ nhị phân là “1011000”.

01101000 01100101 01101100 01101100 01101111 00100000 01110111 01101111 01110010 01101100 01100100 10000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 01011000

              Bây giờ đã có đầu vào của mình, và đầu vào đó sẽ luôn chia hết cho 512.

Khởi tạo Giá trị băm (h)

Bây giờ chúng ta tạo 8 giá trị băm. Đây là các hằng số được mã hóa cứng đại diện cho 32 bit đầu tiên của phần phân số của căn bậc hai của 8 số nguyên tố đầu tiên: 235711131719.

h0 := 0x6a09e667

h1 := 0xbb67ae85

h2 := 0x3c6ef372

h3 := 0xa54ff53a

h4 := 0x510e527f

h5 := 0x9b05688c

h6 := 0x1f83d9ab

h7 := 0x5be0cd19

Khởi tạo Hằng số tròn (k)

Tương tự như trên đây, ở đây ta tạo một số hằng số (Tìm hiểu thêm về hằng số và khi nào sử dụng chúng). Lần này chúng ta có 64 hằng số, mỗi giá trị (0-63) là 32 bit đầu tiên của các phần phân số của căn bậc hai của 64 số nguyên tố đầu tiên (2 - 311).

0x428a2f98 0x71374491 0xb5c0fbcf 0xe9b5dba5 0x3956c25b 0x59f111f1 0x923f82a4 0xab1c5ed5 0xd807aa98 0x12835b01 0x243185be 0x550c7dc3 0x72be5d74 0x80deb1fe 0x9bdc06a7 0xc19bf174 0xe49b69c1 0xefbe4786 0x0fc19dc6 0x240ca1cc 0x2de92c6f 0x4a7484aa 0x5cb0a9dc 0x76f988da 0x983e5152 0xa831c66d 0xb00327c8 0xbf597fc7 0xc6e00bf3 0xd5a79147 0x06ca6351 0x14292967 0x27b70a85 0x2e1b2138 0x4d2c6dfc 0x53380d13 0x650a7354 0x766a0abb 0x81c2c92e 0x92722c85 0xa2bfe8a1 0xa81a664b 0xc24b8b70 0xc76c51a3 0xd192e819 0xd6990624 0xf40e3585 0x106aa070 0x19a4c116 0x1e376c08 0x2748774c 0x34b0bcb5 0x391c0cb3 0x4ed8aa4a 0x5b9cca4f 0x682e6ff3 0x748f82ee 0x78a5636f 0x84c87814 0x8cc70208 0x90befffa 0xa4506ceb 0xbef9a3f7 0xc67178f2

Vòng lặp Chunk

Các bước sau sẽ xảy ra cho mỗi "đoạn" dữ liệu 512-bit từ đầu vào của mình. Ở đây, vì “hello world” quá ngắn, nên mình chỉ có một đoạn. Tại mỗi lần lặp lại của vòng lặp, mình sẽ thay đổi các giá trị băm h0-h7 và đây sẽ là đầu ra.

Tạo lịch trình nhắn tin (w)

Sao chép dữ liệu đầu vào từ bước 1 vào một mảng mới trong đó mỗi mục nhập là một từ 32 bit:

01101000011001010110110001101100 01101111001000000111011101101111

01110010011011000110010010000000 00000000000000000000000000000000

00000000000000000000000000000000 00000000000000000000000000000000

00000000000000000000000000000000 00000000000000000000000000000000

00000000000000000000000000000000 00000000000000000000000000000000

00000000000000000000000000000000 00000000000000000000000000000000

00000000000000000000000000000000 00000000000000000000000000000000

00000000000000000000000000000000 00000000000000000000000001011000

              Thêm 48 từ khác được khởi tạo bằng 0, như vậy là có một mảng w[0… 63]

01101000011001010110110001101100 01101111001000000111011101101111

01110010011011000110010010000000 00000000000000000000000000000000

00000000000000000000000000000000 00000000000000000000000000000000

00000000000000000000000000000000 00000000000000000000000000000000

00000000000000000000000000000000 00000000000000000000000000000000

00000000000000000000000000000000 00000000000000000000000000000000

00000000000000000000000000000000 00000000000000000000000000000000

00000000000000000000000000000000 00000000000000000000000001011000

00000000000000000000000000000000 00000000000000000000000000000000

00000000000000000000000000000000 00000000000000000000000000000000

00000000000000000000000000000000 00000000000000000000000000000000

00000000000000000000000000000000 00000000000000000000000000000000

...

...

00000000000000000000000000000000 00000000000000000000000000000000

Sửa đổi các chỉ mục zero-ed ở cuối mảng bằng cách sử dụng thuật toán sau: Đối với mình từ w[16… 63]:

·        s0 = (w[i-15] rightrotate 7) xor (w[i-15] rightrotate 18) xor (w[i-15] rightshift 3)

·        s1 = (w[i- 2] rightrotate 17) xor (w[i- 2] rightrotate 19) xor (w[i- 2] rightshift 10)

·        w[i] = w[i-16] + s0 + w[i-7] + s1

Với [16] hãy xem cách hoạt động của nó như thế nào.

              w[1] rightrotate 7:

    01101111001000000111011101101111

-> 11011110110111100100000011101110

              w[1] rightrotate 18:

              01101111001000000111011101101111

              -> 00011101110110111101101111001000

              w[1] rightshift 3:

              01101111001000000111011101101111

              -> 00001101111001000000111011101101

              s0 = 11011110110111100100000011101110

              XOR 00011101110110111101101111001000

              XOR 00001101111001000000111011101101

 

              s0 = 11001110111000011001010111001011

              w[14] rightrotate 17:

              00000000000000000000000000000000

              -> 00000000000000000000000000000000

              w[14] rightrotate19:

              00000000000000000000000000000000

              -> 00000000000000000000000000000000

              w[14] rightshift 10:

              00000000000000000000000000000000

              -> 00000000000000000000000000000000

 

              s1 = 00000000000000000000000000000000

              XOR 00000000000000000000000000000000

              XOR 00000000000000000000000000000000

              s1 = 00000000000000000000000000000000

 

              w[16] = w[0] + s0 + w[9] + s1

              w[16] = 01101000011001010110110001101100 + 11001110111000011001010111001011 + 00000000000000000000000000000000 + 00000000000000000000000000000000

              // Mình bổ sung thêm được tính theo modulo 2 ^ 32

              w[16] = 00110111010001110000001000110111

              Điều này để lại cho mình 64 từ trong nội dung thông báo (w):

01101000011001010110110001101100 01101111001000000111011101101111

01110010011011000110010010000000 00000000000000000000000000000000

00000000000000000000000000000000 00000000000000000000000000000000

00000000000000000000000000000000 00000000000000000000000000000000

00000000000000000000000000000000 00000000000000000000000000000000

00000000000000000000000000000000 00000000000000000000000000000000

00000000000000000000000000000000 00000000000000000000000000000000

00000000000000000000000000000000 00000000000000000000000001011000

00110111010001110000001000110111 10000110110100001100000000110001

11010011101111010001000100001011 01111000001111110100011110000010

00101010100100000111110011101101 01001011001011110111110011001001

00110001111000011001010001011101 10001001001101100100100101100100

01111111011110100000011011011010 11000001011110011010100100111010

10111011111010001111011001010101 00001100000110101110001111100110

10110000111111100000110101111101 01011111011011100101010110010011

00000000100010011001101101010010 00000111111100011100101010010100

00111011010111111110010111010110 01101000011001010110001011100110

11001000010011100000101010011110 00000110101011111001101100100101

10010010111011110110010011010111 01100011111110010101111001011010

11100011000101100110011111010111 10000100001110111101111000010110

11101110111011001010100001011011 10100000010011111111001000100001

11111001000110001010110110111000 00010100101010001001001000011001

00010000100001000101001100011101 01100000100100111110000011001101

10000011000000110101111111101001 11010101101011100111100100111000

00111001001111110000010110101101 11111011010010110001101111101111

11101011011101011111111100101001 01101010001101101001010100110100

00100010111111001001110011011000 10101001011101000000110100101011

01100000110011110011100010000101 11000100101011001001100000111010

00010001010000101111110110101101 10110000101100000001110111011001

10011000111100001100001101101111 01110010000101111011100000011110

10100010110101000110011110011010 00000001000011111001100101111011

11111100000101110100111100001010 11000010110000101110101100010110

Nén

·        Khởi tạo các biến abcdefgh và đặt chúng bằng các giá trị băm hiện tại tương ứng. h0h1h2h3h4h5h6h7

·        Chạy vòng lặp nén. Vòng lặp nén sẽ thay đổi các giá trị từ a…h. Vòng lặp nén như sau:

o   Từ 0 đến 63

§  S1 = (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25)

§  ch = (e and f) xor ((not e) and g)

§  temp1 = h + S1 + ch + k[i] + w[i]

§  S0 = (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22)

§  maj = (a and b) xor (a and c) xor (b and c)

§  temp2 := S0 + maj

§  h = g

§  g = f

§  f = e

§  e = d + temp1

§  d = c

§  c = b

§  b = a

§  a = temp1 + temp2

Hãy xem qua lần lặp đầu tiên, tất cả phép cộng được tính theo modulo 2 ^ 32:

a = 0x6a09e667 = 01101010000010011110011001100111

b = 0xbb67ae85 = 10111011011001111010111010000101

c = 0x3c6ef372 = 00111100011011101111001101110010

d = 0xa54ff53a = 10100101010011111111010100111010

e = 0x510e527f = 01010001000011100101001001111111

f = 0x9b05688c = 10011011000001010110100010001100

g = 0x1f83d9ab = 00011111100000111101100110101011

h = 0x5be0cd19 = 01011011111000001100110100011001

              e rightrotate 6:

  01010001000011100101001001111111

-> 11111101010001000011100101001001

              e rightrotate 11:

                01010001000011100101001001111111

              -> 01001111111010100010000111001010

              e rightrotate 25:

  01010001000011100101001001111111

-> 10000111001010010011111110101000

              S1 = 11111101010001000011100101001001

XOR 01001111111010100010000111001010

XOR 10000111001010010011111110101000

S1 = 00110101100001110010011100101011

              e and f:

              01010001000011100101001001111111

              & 10011011000001010110100010001100 =

              00010001000001000100000000001100

              not e:

               01010001000011100101001001111111

              -> 10101110111100011010110110000000

(not e) and g:

    10101110111100011010110110000000

  & 00011111100000111101100110101011 =

    00001110100000011000100110000000

ch = (e and f) xor ((not e) and g)

               = 00010001000001000100000000001100

              xor 00001110100000011000100110000000

               = 00011111100001011100100110001100

 

// k[i] là hằng số vòng

// w[i] là batch

              temp1 = h + S1 + ch + k[i] + w[i]

              temp1 = 01011011111000001100110100011001

+ 00110101100001110010011100101011

+ 00011111100001011100100110001100

+ 01000010100010100010111110011000

+ 01101000011001010110110001101100

temp1 = 01011011110111010101100111010100

 

              a rightrotate 2:

  01101010000010011110011001100111

-> 11011010100000100111100110011001

a rightrotate 13:

  01101010000010011110011001100111

-> 00110011001110110101000001001111

a rightrotate 22:

  01101010000010011110011001100111

-> 00100111100110011001110110101000

              S0 = 11011010100000100111100110011001

XOR 00110011001110110101000001001111

XOR 00100111100110011001110110101000

S0 = 11001110001000001011010001111110

 

              a and b:

    01101010000010011110011001100111

  & 10111011011001111010111010000101 =

    00101010000000011010011000000101

              a and c:

    01101010000010011110011001100111

  & 00111100011011101111001101110010 =

    00101000000010001110001001100010

              b and c:

              10111011011001111010111010000101

                & 00111100011011101111001101110010 =

                 00111000011001101010001000000000

              maj = (a and b) xor (a and c) xor (b and c)

               = 00101010000000011010011000000101

xor 00101000000010001110001001100010

xor 00111000011001101010001000000000

               = 00111010011011111110011001100111

 

              temp2 = S0 + maj

      = 11001110001000001011010001111110

+ 00111010011011111110011001100111

      = 00001000100100001001101011100101

 

h = 00011111100000111101100110101011

g = 10011011000001010110100010001100

f = 01010001000011100101001001111111

e = 10100101010011111111010100111010

+ 01011011110111010101100111010100

  = 00000001001011010100111100001110

d = 00111100011011101111001101110010

c = 10111011011001111010111010000101

b = 01101010000010011110011001100111

a = 01011011110111010101100111010100

+ 00001000100100001001101011100101

  = 01100100011011011111010010111001

              Toàn bộ phép tính đó được thực hiện thêm 63 lần nữa, sửa đổi các biến a-h trong suốt. Chúng tôi sẽ không làm điều đó bằng tay nhưng chúng tôi sẽ kết thúc bằng:

h0 = 6A09E667 = 01101010000010011110011001100111

h1 = BB67AE85 = 10111011011001111010111010000101

h2 = 3C6EF372 = 00111100011011101111001101110010

h3 = A54FF53A = 10100101010011111111010100111010

h4 = 510E527F = 01010001000011100101001001111111

h5 = 9B05688C = 10011011000001010110100010001100

h6 = 1F83D9AB = 00011111100000111101100110101011

h7 = 5BE0CD19 = 01011011111000001100110100011001

 

a = 4F434152 = 01001111010000110100000101010010

b = D7E58F83 = 11010111111001011000111110000011

c = 68BF5F65 = 01101000101111110101111101100101

d = 352DB6C0 = 00110101001011011011011011000000

e = 73769D64 = 01110011011101101001110101100100

f = DF4E1862 = 11011111010011100001100001100010

g = 71051E01 = 01110001000001010001111000000001

h = 870F00D0 = 10000111000011110000000011010000

Sửa đổi giá trị

Sau khi vòng lặp nén, ở trên trong vòng lặp chunk, mình sửa đổi các giá trị băm bằng cách thêm các biến tương ứng a-h. Và tất cả các phép cộng ở đây đều là modulo 2 ^ 32.

h0 = h0 + a = 10111001010011010010011110111001

h1 = h1 + b = 10010011010011010011111000001000

h2 = h2 + c = 10100101001011100101001011010111

h3 = h3 + d = 11011010011111011010101111111010

h4 = h4 + e = 11000100100001001110111111100011

h5 = h5 + f = 01111010010100111000000011101110

h6 = h6 + g = 10010000100010001111011110101100

h7 = h7 + h = 11100010111011111100110111101001

Kết hợp Băm cuối cùng

Cuối cùng ghép tất cả lại với nhau và tèn tèn, ta có một phép nối chuỗi đơn giản.

              digest = h0 append h1 append h2 append h3 append h4 append h5 append h6 append h7

= B94D27B9934D3E08A52E52D7DA7DABFAC484EFE37A5380EE9088F7ACE2EFCDE9

              Nếu bạn muốn xem tất cả các bước mình vừa làm ở trên, thì đây mình sẽ giải thích rõ hơn:

              Bước 1: Tất cả các biến là số nguyên không dấu 32 bit và phép cộng được tính theo modulo 2 ^ 32

              Bước 2: Đối với mỗi vòng lặp, có một hằng số vòng k[i] và một mục nhập trong mảng thông báo w[i], 0 ≤ i ≤ 63

              Bước 3: Sử dụng 8 biến từ a đến h

              Bước 4: Sử dụng quy ước big-endian khi biểu thị các hằng số trong mã hóa này, và khi phân tích cú pháp tin nhắn sẽ chặn dữ liệu từ byte thành từ, **Ví dụ:** từ đầu tiên của tin nhắn đầu vào "abc" sau phần đệm là 0x61626380

              Khởi tạo giá trị băm (32 bit đầu tiên của phần phân số của căn bậc hai của 8 số nguyên tố đầu tiên 2..19):

h0 := 0x6a09e667

h1 := 0xbb67ae85

h2 := 0x3c6ef372

h3 := 0xa54ff53a

h4 := 0x510e527f

h5 := 0x9b05688c

h6 := 0x1f83d9ab

h7 := 0x5be0cd19

              Khởi tạo mảng các hằng số vòng (32 bit đầu tiên của phần phân số của căn bậc hai của 64 số nguyên tố đầu tiên 2..311):

k[0..63] :=

0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2

 

Pre-processing (Padding):

Bắt đầu với nội dung có độ dài L bit

Nối bit '1' duy nhất

Nối K '0' bit, trong đó K là số nhỏ nhất >= 0 sao cho L + 1 + K + 64 là bội số của 512

Thêm L dưới dạng số nguyên big-endian 64bit, làm cho tổng độ dài sau xử lý là bội số của 512bit

              Xử lý tin nhắn theo các phần 512bit liên tiếp, chia nhỏ tin nhắn thành các đoạn 512bit

              for each chunk

              create a 64-entry message schedule array w[0..63] of 32-bit words

               (Các giá trị ban đầu trong w[0..63] không quan trọng) sao chép đoạn vào 16 từ đầu tiên w[0..15] của mảng tin nhắn

 

              Kéo dài 16 từ đầu tiên thành 48 từ còn lại w[16..63] của mảng tin nhắn:

              for i from 16 to 63

              s0 := (w[i-15] rightrotate  7) xor (w[i-15] rightrotate 18) xor (w[i-15] rightshift  3)

              s1 := (w[i- 2] rightrotate 17) xor (w[i- 2] rightrotate 19) xor (w[i- 2] rightshift 10)

              w[i] := w[i-16] + s0 + w[i-7] + s1

    Khởi tạo các biến thành giá trị băm hiện tại:

    a := h0

    b := h1

    c := h2

    d := h3

    e := h4

    f := h5

    g := h6

    h := h7

              Nén vòng lặp chính:

              for i from 0 to 63

        S1 := (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25)

        ch := (e and f) xor ((not e) and g)

        temp1 := h + S1 + ch + k[i] + w[i]

        S0 := (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22)

        maj := (a and b) xor (a and c) xor (b and c)

        temp2 := S0 + maj

        h := g

        g := f

        f := e

        e := d + temp1

        d := c

        c := b

        b := a

        a := temp1 + temp2

    Thêm đoạn đã nén vào giá trị băm hiện tại:

    h0 := h0 + a

    h1 := h1 + b

    h2 := h2 + c

    h3 := h3 + d

    h4 := h4 + e

    h5 := h5 + f

    h6 := h6 + g

    h7 := h7 + h

              Tạo giá trị băm cuối cùng (big-endian): digest: = hash: = h0 append h1 append h2 append h3 append h4 append h5 append h6 append h7.

                                                            Nguồn: Internet, VIBLO

Share:

Related Posts:

0 comments:

Đăng nhận xét

Bài Đăng Nổi Bật

hội đồng quản trị, hội đồng thành viên

  I. Tìm hiểu về hội đồng quản trị và hội đồng thành viên Link tham khảo: https://luatvietnam.vn/doanh-nghiep/chu-tich-hoi-dong-quan-tri-v...

Tổng Số Lượt Xem Trang

22

Bài Đăng Phổ Biến