06/12/2018, 16:00

Cryptography - Nó là gì và hoạt động như thế nào ?

Cryptography - không chỉ là một phần của nền tảng kỹ thuật số Như Bruce Scheneider đã đề cập tới trong quyển sách Applied Cryptography - " The art and science of keeping messages secure is cryptography ". Cryptography (mật mã học) - thứ mà ngày nay được coi như là một phần trong nền ...

Cryptography - không chỉ là một phần của nền tảng kỹ thuật số

Như Bruce Scheneider đã đề cập tới trong quyển sách Applied Cryptography - "The art and science of keeping messages secure is cryptography". Cryptography (mật mã học) - thứ mà ngày nay được coi như là một phần trong nền tảng ngành công nghiệp kĩ thuật số - không thực sự chỉ gắn liền với máy tính. Thực ra, nó đã xuất hiện từ rất lâu trước thời đại của máy tính rồi, dưới nhiều hình dạng khác nhau. Từ bài viết Lịch sử của Cryptography:

Ứng dụng sớm nhất của mật mã được tìm thấy và ghi nhận là các chữ tượng hình - thứ được khắc lên tường của một hầm mộ Ai Cập cổ đại có tiên đại khoảng 1900 TCN. Trong suốt chiều dài lịch sử của con người, có nhiều ví dụ cho thấy mật mã đã được sử dụng từ rất sớm. Các học giả Do Thái đã sử dụng các thuật toán thay thế đơn giản ( ví dụ như mật mã Atbash) từ khoảng 500-600 TCN. Ở Ấn Độ từ những năm 400 TCN cho tới 200 SCN, kĩ thuật Mlecchita vikalpa đã được ghi lại trong các tài liệu Kama Sutra như một phương thức liên lạc bí mật. Một phần của cuốn Greek Magical Papyri có từ thời AI Cập cổ đại cũng được ghi bằng mật mã.

Ngày này, mật mã học cũng hiện diện phổ biến hơn. Một trong những mật mã thông dụng nhất, được gọi là Caesar cipher, hay shift cipher, được sử dụng rộng rãi trong các trò chơi của trẻ em. Loại mật mã này có cơ chế là sử dụng một bảng chữ cái được chế từ bảng chữ cái bình thường, với các kí tự được dịch chuyển đi 1 khoảng đơn vị.

Ví dụ dưới là của loại ROT13, với các kí tự được dịch chuyển 13 vị trí so với vị trí gốc của chúng:

alt text

Ngày nay, khi nói về mật mã, ta thường nói về chúng trong ứng dụng máy tính. Làm cách nào để các thông tin cá nhân, thông tin tài chính được trao đổi một cách bảo mật qua web (trong các giao dịch ngân hàng online); Làm cách nào để dữ liệu có thể được lưu trữ một các an toàn ?

Nhắc tới mật mã, và việc bảo mật dữ liệu, ta sẽ thường phải quan tấm tới nhiều vấn đề. Chúng bao gồm những khái niệm về confidentiality (bảo mật), integrity (tính toàn vẹn), availability, and non-repudiation.

  • Confidentiality: Dữ liệu của ta không thể bị truy cập / đọc bảo người dùng không được cấp phép.
  • Integrity: Dữ liệu của ta phải 100% nguyên vẹn, đảm bảo là không bị chỉnh sửa, dù là bởi nguyên nhân nào như bị tấn công, mất mát dữ liệu ...
  • Availability: Data có thể truy cập bất cứ khi nào cần thiết.
  • Non-repudiation: Trong một giao dịch, khi một bên A thực hiện gửi data cho bên B, thì người đó sẽ không có khả năng phủ nhận việc đó sau này. Nói cách khác, phải có một cách để đảm bảo rằng không ai khác ngoài A có thể là tác giả và người gửi thông điệp.

Cryptography không giúp chúng ta nhiều về tính availability, nhưng ở các mặt khác, các cơ chế mã hóa điện tử hiện nay thường được sử dụng rộng rãi và giúp ta thực hiện việc đảm bảo 3 mục tiêu còn lại. Khi nói về mã hóa điện tử, đó sẽ thường là việc ám chỉ một trong những kĩ thuật phổ biến như:

  1. Symmetric encryption
  2. Asymmetric encryption
  3. Hash functions
  4. Digital signatures

Trong bài viết này, tôi sẽ giới thiệu lần lượt về từng loại, đồng thời cung cấp một ví dụ đơn giản để giúp người đọc hiểu hơn về chúng.

Symmetric Encryption (Mã hóa đối xứng)

Encryption: Là quá trình mã hóa một thông điệp, làm cho nó trở thành không-đọc-được

Decryption: Quá trình ngược lại với encryption, biến một nội dung đã-mã-hóa trở lại về nội dung có thể hiểu được.

Để tiến hành mã hóa và giải mã data, ta cần có data (nội dung thông điệp mà ta cần mã hóa) cùng với một key (thứ sẽ quy định đầu ra của mã hóa).

Với phương pháp symmetric encryption, key được dùng khi mã hóa và giải mã là một. Ta có thể thử lấy một chuỗi string và encrypt nó:

> require 'openssl'
=> false

> data = 'Chuoi can ma hoa'                        # => data cần mã hóa
=> "Chuoi can ma hoa"

> cipher = OpenSSL::Cipher.new('aes256')
=> #<OpenSSL::Cipher:0x005573bca92880>

> cipher.encrypt
=> #<OpenSSL::Cipher:0x005573bca92880>

> key = cipher.random_key
=> "xADxF8xFCx02xF.cDx0Fx1DxAAxF0_xD7xFBxA4YSgxC6=_x83xCCdxA1yxC4x97xD7l"

> iv = cipher.random_iv
=> "xC9xBFnxC8RxBAx12xDA xED/sx9Cm@M"

> output = cipher.update(data) + cipher.final
=> "x8Bz2x82xCA<x12 CxD01xDAOxCDx92x9ExB9x80dxB6x15fxCEHxB0xBBCbxAFxB2xAA"

Như vậy là từ data ban đầu là 'Chuoi can ma hoa', sau quá trình encrypt nó đã trở thành 1 chuỗi kí tự không-có-ý-nghĩa-gì x8Bz2x82xCA<x12 CxD01xDAOxCDx92x9ExB9x80dxB6x15fxCEHxB0xBBCbxAFxB2xAA.

Giờ thì thử quay ngược lại và giải mã chuỗi output, sử dụng cái key mà ta lấy ra ở trên.

> cipher.decrypt
=> #<OpenSSL::Cipher:0x005573bca92880>

> cipher.iv = iv
=> "xC9xBFnxC8RxBAx12xDA xED/sx9Cm@M"
> cipher.key = key
=> "xADxF8xFCx02xF.cDx0Fx1DxAAxF0_xD7xFBxA4YSgxC6=_x83xCCdxA1yxC4x97xD7l"

> data_decrypted = cipher.update(output) + cipher.final
=> "Chuoi can ma hoa"

Trong trường hợp này, sử dụng cùng một key dùng khi mã hóa để giải mã, ta lấy lại được chuỗi ban đầu.

Asymmetric Encryption (Mã hóa bất đối xứng)

Như đã nói ở trên, mã hóa đối xứng là trường hợp mà key dùng để mã hóa cũng như giải mã là một. Cơ chế này sẽ sinh ra một vấn đề: Nếu như ta cần phải gửi data đi trong một môi trường không an toàn (qua mạng internet ...) ? Dùng cùng một key để encrypt và decrypt data, có thể trước tiên ta sẽ phải gửi cái key đó cho phía đối tác. Nhưng điều đó cũng có nghĩa là ta gửi key qua một môi trường không an toàn !

Đây là lúc mà asymmetric encryption xuất hiện.

Về cơ bản, đối với asymmetric encryption, thay vì sử dụng 1 key như trên, ta sẽ phải sinh ra 2 key, và 2 key này sẽ có quan hệ về mặt toán học với nhau. Một trong hai key sẽ là private key, là cái mà chỉ có ta mới có, và không được chia sẻ cho ai; cái còn lại sẽ là public key, là cái mà ta sẽ dùng để gửi cho phía đối tác.

Về cơ chế, quá trình mã hóa này sẽ bao gồm:

  • Khi phía client yêu cầu thiết lập một kết nối an toàn với server, server sẽ gửi public key của mình cho client.
  • Client generate ra một symmetric cipher và encrypt nó, sử dụng public key được server gửi cho, rồi gửi lại về server.
  • Server decrypt message nhận lại từ client - sử dụng private key - và lấy sra được symmetric cipher.
  • Bây giờ, cả 2 phía client-server đều đã có chung một symmetric cipher, và lại dùng nó để thiết lập một secure connection sử dụng Symmetric Encryption như trên.

Giờ, lại có một vấn đề khác nảy sinh: làm sao để biết chắc cái public key kia là chính xác (vd. thực sự là do server tạo ra chứ không phải hàng fake ? ). Nói chung, có một vài cách để giải quyết vấn đề này, trong đó cách thông thường nhất (và được sử dụng rộng rãi nhất) là sử dụng Public key Infrastructure (PKI).

Trong môi trường internet, đây là vai trò của Certificate Authority - một nơi (hay một thư mục) chứa tất cả các địa chỉ website cần certificate, cùng với public key của chúng. Khi ta kết nối tới một website nào đó, trước hết, public key của nó sẽ được chứng thực bởi Certificate Authority.

Nhà cung cấp chứng thực số (Certificate Authority) đóng vai trò là bên thứ ba (được cả hai bên tin tưởng) để hỗ trợ cho quá trình trao đổi thông tin an toàn. Các nhà cung cấp chứng thực số là thành phần trung tâm trong nhiều mô hình hạ tầng khóa công khai (PKI). Hiện nay có nhiều CA thương mại mà người dùng phải trả phí khi sử dụng dịch vụ. Các tổ chức và chính phủ cũng có thể có những CA của riêng họ. Bên cạnh đó cũng có những CA cung cấp dịch vụ miễn phí.

Quay lại với ví dụ ở phía đầu bài, lần này, ta sẽ thử với một cặp public/private key:

> require 'openssl'
=> false

> data = 'Chuoi can ma hoa'                        # => data cần mã hóa
=> "Chuoi can ma hoa"

> key = OpenSSL::PKey::RSA.new(2048)
=> #<OpenSSL::PKey::RSA:0x005573bc97aab0>

> key
=> #<OpenSSL::PKey::RSA:0x005573bc97aab0>
> key.public_key
=> #<OpenSSL::PKey::RSA:0x005573bc966808>

> output = key.private_encrypt(data)
=> "x89xA7}:x14xD7A=xA1xA8x9CexEAfxCFx03`xAAPxEFx97t1x1Ax16x11xA4xC3ExA4"xF4wxCAxBDxE1xF5xBDxFBxCBhxCD\]Yy%xB6?
xACbOEx90fxF2XxCCxF7xCAx99x9E}x0Ex96xFAxD1qxC8xC11xB4ox9D]Hv,@x91xC9\x13xA2x91x03x86xBFxC7BjxBEx15xF5xDEv,dx99x89x86xB5(x804fxCB
OxA2#xAA{x8DxB7`xDCx1ExCAxECxACfx8BxB2xAFxE1xDBx1E)YLxC7@nx87jx8FxFCxEBxC5xBFmOxA6x95x13xCCxFFxA1bx12YxEDxE0(x00XkxB3SwxD4xBFqxA8x8AjxD8x1CxA1xF1x1FGx1FxDBxDExE5bxA7xCFnx83xC5xCBxA3x041xD3x8ExB7w%MxA4xD4x8AxCAxE2yxF6x93x06x1Dtx96Wjr}kxB4xB1x15vixACxF9&x1A#x1Edox0FxxE8axCAxE6x9C1xxC4x9EWlx89APVJ"xF3	SxA6x812x97*x12]xCCxA3"

Đầu tiên, chú ý rằng key và public key của ta là 2 object riêng biệt với object ID khác nhau. Sử dụng hàm #private_encrypt, ta có thể mã hóa data sử dụng private key.

Chuỗi đã mã hóa có thể giải mã sử dụng public key.

> data_decrypted = key.public_decrypt(output)
=> "Chuoi can ma hoa"

Sử dụng #public_decrypt, ta có thể lấy lại được message ban đầu mà KHÔNG cần sử dụng private key - thứ mà ta chỉ dùng để encrypt thông điệp, và chỉ giữ lại cho ta mà thôi.

Hashing functions

Một hàm hashing , ngược lại với cơ chế mã hóa / giải mã như trên, là hàm một chiều. Điều này có nghĩa là ta có thể tạo một hash từ data đầu vào, nhưng sẽ không có cách gì để đảo ngược quá trình và lấy lại data ban đầu từ hash được sinh ra. Như vậy, đây có thể không phải là một cách hay để lưu trữ dữ liệu, nhưng lại rất hữu ích khi muốn verify tính chính xác của data.

Một hàm hashing sẽ nhận đầu vào lại một data, và đầu ra sẽ là một chuỗi nhìn-có-vẻ-là-random (nhưng không hề             </div>
            
            <div class=

0