Lập trình bảo mật với OpenSSL API, Phần 1: Tổng quan về API

Tạo kết nối bảo mật và không bảo mật cơ bản

Việc tìm hiểu cách sử dụng API với OpenSSL -- thư viện mở nổi tiếng nhất cho truyền thông bảo mật -- có thể làm nản chí người dùng, vì tài liệu hướng dẫn không đầy đủ. Hãy điền vào những chỗ trống và chế ngự API, bằng những lời khuyên trong bài này. Sau khi thiết lập một kết nối cơ bản, hãy xem cách sử dụng thư viện BIO của OpenSSL để thiết lập cả kết nối bảo mật lẫn kết nối không bảo mật. Và cũng tìm hiểu một chút về phát hiện lỗi.

28.06.2012 - Đã thêm liên kết hoạt động vào mã mẫu ví dụ cho bài này trong phần Tải về.

Kenneth Ballard, Kỹ sư phần mềm, MediNotes Corp.

Kenneth là một kỹ sư phần mềm làm việc cho Tập đoàn MediNotes ở West Des Moines, Iowa. Ông đã tốt nghiệp trường Peru State College ở Peru, Nebraska, với một bằng Cử nhân Khoa học về Quản trị Kinh doanh. Ông cũng có một bằng cao đẳng Khoa học về lập trình máy tính của trường Southwestern Community College ở Creston, Iowa. Kenneth đã viết một số ứng dụng và các thư viện lập trình.



28 06 2012 (Xuất bản lần đầu tiên vào ngày 24 08 2012)

Tài liệu hướng dẫn cho API của OpenSSL có đôi chút chưa rõ. Cũng không nhiều hướng dẫn sử dụng của OpenSSL đang có, nên việc lấy nó để làm việc trong các ứng dụng có thể hơi rắc rối một chút đối với những người mới bắt đầu. Vậy thì bạn có thể thực hiện một kết nối bảo mật cơ bản bằng cách sử dụng OpenSSL như thế nào? Hướng dẫn này sẽ giúp giải quyết vấn đề đó.

Một phần của vấn đề này với việc tìm hiểu cách thực hiện OpenSSL có thực tế là tài liệu hướng dẫn không đầy đủ. Một tài liệu hướng dẫn API không đủ thường giấu các nhà phát triển việc sử dụng API, mà điều này thường báo hiệu sự sụp đổ cho OpenSSL. Tuy nhiên, OpenSSL vẫn sống và đang mạnh lên. Tại sao?

OpenSSL là một thư viện mở nổi tiếng nhất cho truyền thông bảo mật. Một tìm kiếm Google về "SSL library" (thư viện SSL) trả về OpenSSL ở trên đầu danh sách. Nó đã bắt đầu hoạt động vào năm 1998, được bắt nguồn từ thư viện SSLeay do Eric Young và Tim Hudson phát triển. Các bộ công cụ SSL khác gồm có GNU TLS (GNU Transport Layer Security - Bảo mật tầng vận chuyển GNU), được phân phối theo giấy phép GNU GPL (GNU General Public License - Giấy phép Công cộng phổ biến GNU) và Mozilla NSS (Mozilla Network Security Services - Các dịch vụ bảo mật mạng của Mozilla) (xem phần Tài nguyên ở cuối bài này để biết thêm thông tin).

Vậy thì điều gì làm cho OpenSSL tốt hơn so với GNU TLS, Mozilla NSS hoặc bất kỳ thư viện nào khác? Việc cấp phép là một vấn đề (xem phần Tài nguyên ). Ngoài ra, GNS TLS (đến lúc này) chỉ hỗ trợ các giao thức TLS v1.0 và SSL v3.0 và chỉ có vậy.

Mozilla NSS được phân phối theo cả hai Giấy phép Công cộng Mozilla và Giấy phép Công cộng phổ biến GNU, cho phép nhà phát triển lựa chọn. Tuy nhiên, Mozilla NSS lớn hơn so với OpenSSL và yêu cầu các thư viện khác bên ngoài để xây dựng thư viện, trong khi OpenSSL là hoàn toàn khép kín độc lập. Và cũng giống như OpenSSL, nhiều NSS API (API của NSS) không được cung cấp tài liệu. Mozilla NSS có sự hỗ trợ PKCS #11, được sử dụng cho các thẻ xác thực mật mã, chẳng hạn như Các thẻ thông minh (Smart Cards). Còn OpenSSL thiếu sự hỗ trợ này.

Các điều kiện tiên quyết

Để nhận được nhiều nhất trong bài này, bạn cần:

  • Thành thạo về lập trình C.
  • Biết rõ về truyền thông Internet và viết các ứng dụng chạy được trên Internet.

Biết rõ về SSL không quá cần thiết, như một lời giải thích ngắn về SSL sẽ được đưa ra sau; tuy nhiên, hãy xem qua phần Tài nguyên nếu bạn muốn biết các liên kết đến các bài thảo luận chi tiết về SSL. Có hiểu biết về mật mã cũng là một ưu thế, nhưng không bắt buộc.

SSL là gì?

SSL là một từ viết tắt của Secure Sockets Layer (Tầng các ổ cắm bảo mật). SSL là tiêu chuẩn phía sau truyền thông bảo mật trên Internet, tích hợp mật mã dữ liệu vào giao thức. Dữ liệu được mã hóa trước khi nó rời khỏi máy tính của bạn và chỉ được giải mã khi nó đạt đến đích dự định của nó. Các chứng nhận và các thuật toán mật mã hỗ trợ tất cả cách mà SSL hoạt động và với OpenSSL, bạn có cơ hội trải nghiệm với cả hai.

Về lý thuyết, nếu dữ liệu mã hóa đã bị thu chặn hoặc nghe trộm trước khi đến đích của nó, không có hy vọng nào về bẻ khóa dữ liệu đó. Nhưng cứ sau mỗi năm, các máy tính trở nên nhanh hơn và các cải tiến mới trong việc giải mã được thực hiện, cơ hội bẻ khóa các giao thức mật mã được sử dụng trong SSL đang bắt đầu tăng lên.

Kết nối SSL và kết nối bảo mật có thể được sử dụng cho bất kỳ loại giao thức nào trên Internet, cho dù đó là HTTP, POP3 hay FTP. Ngoài ra có thể sử dụng SSL để bảo vệ các phiên làm việc Telnet. Trong khi cũng có thể bảo vệ bất kỳ kết nối nào bằng SSL, nhưng không nhất thiết phải sử dụng SSL trên mọi loại kết nối. Nó nên được sử dụng nếu kết nối này sẽ mang thông tin nhạy cảm.


OpenSSL là gì?

OpenSSL không chỉ là SSL. Nó có khả năng về các thuật toán băm (message digest), mã hóa và giải mã các tệp, các chứng nhận số, các chữ ký số và các số ngẫu nhiên. Có khá ít thư viện OpenSSL, nhiều hơn nữa về thư viện này có thể được đưa vào một bài báo khác.

OpenSSL không chỉ là API, nó cũng có một công cụ dòng lệnh. Công cụ dòng lệnh này có thể làm những thứ tương tự như API, nhưng tiến thêm một bước, cho phép có khả năng kiểm tra các máy chủ và các máy khách SSL. Nó cũng cung cấp một ý tưởng về các khả năng OpenSSL cho nhà phát triển. Để có thông tin về cách sử dụng công cụ dòng lệnh OpenSSL, hãy xem trong phần Tài nguyên.


Bạn sẽ cần cái gì

Đầu tiên, bạn sẽ cần phiên bản mới nhất của OpenSSL. Hãy xem phần Tài nguyên để tìm ra nơi bạn có thể lấy mã nguồn mới nhất để tự biên dịch hoặc một thư viện mã nhị phân của phiên bản mới nhất nếu bạn không muốn mất thời gian biên dịch. Tuy nhiên, vì bảo mật, tôi sẽ khuyên bạn nên tải về mã nguồn mới nhất và tự biên dịch nó. Các bản phân phối mã nhị phân thường được các bên thứ ba biên dịch và phân phối, chứ không phải do các nhà phát triển OpenSSL làm.

Một số bản phân phối Linux đi kèm với một phiên bản nhị phân của OpenSSL sẽ làm việc tốt để học cách sử dụng thư viện đó; nhưng hãy chắc chắn lấy được phiên bản mới nhất và duy trì cập nhật nó nếu bạn có ý định làm bất cứ điều gì trong thế giới-thực.

Đối với các bản phân phối Linux được cài đặt từ các RPM (Red Hat, Mandrake và v.v..), điều quan trọng là bạn cập nhật bản phân phối OpenSSL của mình thông qua một gói RPM có sẵn từ nhà sản xuất bản phân phối của bạn. Vì lý do bảo mật, điều quan trọng là bạn có phiên bản mới nhất của bản phân phối của mình. Nếu phiên bản mới nhất của OpenSSL không có sẵn cho bản phân phối của bạn, thì điều quan trọng là các tệp duy nhất mà bạn ghi đè là các thư viện, chứ không phải là các tệp thực thi được. Thông tin chi tiết về điều này có trong các tài liệu Các câu hỏi thường gặp đi kèm với OpenSSL.

Cũng cần lưu ý ở đây là OpenSSL không được hỗ trợ chính thức trên tất cả các nền tảng. Trong khi nhiều nỗ lực đã được thực hiện để làm cho nó càng tương thích giữa các nền tảng càng tốt, có thể là OpenSSL không thể làm việc trên máy tính và/hoặc hệ điều hành của bạn. Xem trang web OpenSSL (được liên kết từ phần Tài nguyên ) để biết thông tin về những nền tảng nào được hỗ trợ.

Nếu bạn sắp sử dụng OpenSSL để thực hiện các yêu cầu chứng nhận và các chứng nhận số, thì phải tạo ra một tệp cấu hình. Một tệp khuôn mẫu có tên là openssl.cnf có sẵn trong thư mục apps (các ứng dụng) của gói OpenSSL. Tôi sẽ không bàn về tệp này, do nó không cần cho phạm vi của bài này. Tuy nhiên, tệp khuôn mẫu được chú thích rất tốt và một tìm kiếm trên Internet sẽ dẫn bạn đến nhiều hướng dẫn bàn về về cách sửa đổi tệp này.


Các phần đầu và khởi tạo

Hướng dẫn này sẽ chỉ sử dụng ba phần đầu là: ssl.h, bio.h và err.h. Tất cả đều nằm trong thư mục con openssl và chúng sẽ đều cần thiết cho việc phát triển dự án của bạn. Ngoài ra có ba dòng duy nhất cần thiết để khởi tạo thư viện OpenSSL. Tất cả đều được liệt kê trong Liệt kê 1. Các phần đầu và/hoặc các hàm khởi tạo khác có thể cần thiết cho các tính năng khác.

Liệt kê 1. Các phần đầu cần thiết
/* OpenSSL headers */

#include "openssl/bio.h"
#include "openssl/ssl.h"
#include "openssl/err.h"

/* Initializing OpenSSL */

SSL_load_error_strings();
ERR_load_BIO_strings();
OpenSSL_add_all_algorithms();

Thiết lập một kết nối không bảo mật

OpenSSL sử dụng một thư viện trừu tượng hóa được gọi là BIO để xử lý truyền thông các loại khác nhau, gồm các tệp và các ổ cắm (socket), cả bảo mật lẫn không bảo mật. Nó cũng có thể được thiết lập là một bộ lọc, như với UU hoặc mã hóa Base64.

Thư viện BIO hơi rắc rối một chút để giải thích đầy đủ ở đây, vì vậy tôi sẽ giới thiệu những thứ lặt vặt của nó khi nó trở nên cần thiết. Trước tiên, tôi sẽ cho bạn thấy cách thiết lập một kết nối ổ cắm tiêu chuẩn. Khi sử dụng thư viện ổ cắm BSD, phải có thêm vài dòng mã nữa.

Trước khi thiết lập một kết nối, cho dù bảo mật hay không bảo mật, cần tạo ra một con trỏ cho BIO. Con trỏ này cũng giống như con trỏ FILE cho một luồng tệp theo C tiêu chuẩn.

Liệt kê 2. Con trỏ
BIO * bio;

Mở một kết nối

Tạo một kết nối mới đòi hỏi một cuộc gọi đến BIO_new_connect. Bạn có thể quy định cả cổng và tên máy chủ host trong cùng một cuộc gọi, như trong Liệt kê 3, mà nó cũng sẽ cố gắng mở kết nối cho bạn. Ngoài ra bạn có thể tách cuộc gọi này thành hai cuộc gọi riêng biệt: một cuộc gọi đến BIO_new_connect để tạo kết nối và đặt tên máy chủ và một cuộc gọi đến BIO_set_conn_port (hoặc BIO_set_conn_int_port) để thiết lập số cổng.

Một khi cả hai tên máy chủ lẫn số cổng được quy định với BIO, cuộc gọi này sẽ cố gắng mở kết nối. Không có bất kỳ cách nào khác để làm việc này. Nếu có một vấn đề về tạo đối tượng BIO, con trỏ sẽ là NULL. Một cuộc gọi đến BIO_do_connect phải được thực hiện để kiểm tra xem kết nối đã thành công chưa.

Liệt kê 3. Tạo và mở một kết nối
bio = BIO_new_connect("hostname:port");
if(bio == NULL)
{
    /* Handle the failure */
}

if(BIO_do_connect(bio) <= 0)
{
    /* Handle failed connection */
}

Ở đây, dòng đầu tiên tạo ra một đối tượng BIO mới với tên máy chủ và cổng đã quy định, được định dạng theo kiểu mẫu đã hiển thị. Ví dụ, nếu bạn muốn kết nối đến cổng 80 tại www.ibm.com, chuỗi ký tự sẽ là www.ibm.com:80. Cuộc gọi đến BIO_do_connect kiểm tra để xem xem kết nối đã thành công chưa. Nó trả về 0 hoặc -1 nếu như có lỗi.

Truyền thông với máy chủ

Việc đọc và viết vào đối tượng BIO, bất kể nó có là một ổ cắm hoặc tệp hay không, sẽ luôn được thực hiện bằng cách sử dụng hai hàm: BIO_readBIO_write. Đơn giản quá, phải không? Và điều tốt là nó vẫn giữ nguyên cách đó.

Hàm BIO_read sẽ cố gắng đọc một số byte nhất định từ máy chủ. Nó trả về số byte đã đọc, hoặc 0 hoặc -1. Trên một kết nối đang bị chặn, một sự trả về là 0 có nghĩa rằng kết nối đã bị đóng, trong khi sự trả về là -1 cho biết đã xuất hiện một lỗi. Trên một kết nối không bị chặn, một sự trả về là 0 có nghĩa đã không có sẵn dữ liệu nào và một sự trả về là -1 cho biết có một lỗi. Để xác định xem lỗi có thể khắc phục được không, hãy gọi hàm BIO_should_retry.

Liệt kê 4. Đọc từ kết nối
int x = BIO_read(bio, buf, len);
if(x == 0)
{
    /* Handle closed connection */
}
else if(x < 0)
{
   if(! BIO_should_retry(bio))
    {
        /* Handle failed read here */
    }

    /* Do something to handle the retry */
}

Hàm BIO_write sẽ cố gắng viết các byte vào ổ cắm. Nó trả về số byte đã viết thực tế, hoặc 0 hoặc -1. Như với hàm BIO_read, sự trả về là 0 hoặc -1 không nhất thiết phải chỉ ra một lỗi. Hàm BIO_should_retry là cách để tìm ra lỗi đó. Nếu hoạt động viết sẽ được thử lại, nó phải có các tham số chính xác giống như trước.

Liệt kê 5. Viết vào kết nối
if(BIO_write(bio, buf, len) <= 0)
{
    if(! BIO_should_retry(bio))
    {
        /* Handle failed write here */
    }

    /* Do something to handle the retry */
}

Đóng kết nối

Việc đóng kết nối cũng đơn giản. Bạn có thể đóng kết nối theo một trong hai kiểu hàm: BIO_reset hoặc BIO_free_all. Nếu bạn sẽ sử dụng lại đối tượng này, hãy sử dụng hàm đầu tiên. Nếu bạn sẽ không sử dụng lại nó, hãy sử dụng hàm thứ hai.

Hàm BIO_reset đóng kết nối và thiết lập lại trạng thái nội bộ của đối tượng BIO sao cho có thể sử dụng lại kết nối này. Điều này là tốt nếu bạn có ý định sử dụng đối tượng tương tự trong suốt ứng dụng, chẳng hạn như với một máy khách trò chuyện (chat) có bảo mật. Nó không trả về một giá trị.

Hàm BIO_free_all không chỉ là những gì như nó nói: nó giải phóng cấu trúc bên trong và giải phóng tất cả các bộ nhớ liên quan, gồm cả việc đóng ổ cắm liên quan. Nếu BIO được nhúng vào trong một lớp, thì BIO này sẽ được sử dụng trong trình xóa sạch (destructor) của lớp đó.

Liệt kê 6. Đóng kết nối
/* To reuse the connection, use this line */

BIO_reset(bio);

/* To free it from memory, use this line */

BIO_free_all(bio);

Thiết lập một kết nối bảo mật

Bây giờ là lúc đưa ra những gì cần thiết để thiết lập một kết nối bảo mật. Điều duy nhất là các thay đổi đang thiết lập và thực hiện kết nối. Tất cả mọi thứ khác đều tương tự.

Các kết nối bảo mật đòi hỏi một sự bắt tay sau khi kết nối được thiết lập. Trong lần bắt tay này, máy chủ sẽ gửi một chứng nhận đến máy khách, rồi máy khách sẽ kiểm tra chứng nhận đó dựa vào một tập các chứng nhận tin cậy. Nó cũng kiểm tra chứng nhận đó để chắc rằng chứng nhận đó vẫn chưa hết hạn. Việc kiểm tra xem chứng nhận có tin cậy không đòi hỏi một kho chứng nhận tin cậy được nạp trước khi thiết lập kết nối này.

Máy khách sẽ gửi một chứng nhận đến máy chủ chỉ khi máy chủ yêu cầu một chứng nhận. Điều này được gọi là xác thực máy khách. Khi sử dụng (các) chứng nhận, các tham số mật mã được chuyển giao giữa máy khách và máy chủ để thiết lập kết nối bảo mật. Dù thực hiện bắt tay sau khi kết nối được thiết lập, thì máy khách hoặc máy chủ có thể yêu cầu một lần bắt tay mới tại bất kỳ thời điểm nào.

Các lần bắt tay và các khía cạnh khác về việc thiết lập một kết nối bảo mật sẽ được thảo luận chi tiết trong các bài Netscape và RFC 2246 được liệt kê trong phần Tài nguyên.

Thiết lập một kết nối bảo mật

Việc thiết lập một kết nối bảo mật đòi hỏi thêm một vài dòng mã nữa. Con trỏ khác yêu cầu kiểu SSL_CTX. Đây là một cấu trúc chứa thông tin SSL. Nó cũng được sử dụng để thiết lập kết nối SSL thông qua thư viện BIO. Cấu trúc này được tạo ra bằng cách gọi SSL_CTX_new với một hàm phương thức SSL, thường là hàm SSLv23_client_method.

Một con trỏ kiểu SSL khác cũng cần chứa cấu trúc kết nối SSL (việc này là bắt buộc đối với một cái gì đó sẽ được thực hiện ngay). Con trỏ SSL này cũng có thể được sử dụng sau để kiểm tra thông tin kết nối hoặc để thiết lập thêm các tham số SSL.

Liệt kê 7. Thiết lập các con trỏ SSL
SSL_CTX * ctx = SSL_CTX_new(SSLv23_client_method());
SSL * ssl;

Nạp kho chứng nhận tin cậy

Sau khi tạo cấu trúc ngữ cảnh, một kho chứng nhận tin cậy phải được nạp. Điều này là rất cần thiết cho việc kiểm tra chứng nhận ngang hàng đến thành công. Nếu không thể kiểm tra chứng nhận này về độ tin cậy, OpenSSL đánh dấu cờ các chứng nhận là không hợp lệ (nhưng kết nối có thể vẫn tiếp tục).

OpenSSL đi kèm với một tập các chứng nhận tin cậy. Chúng có trong thư mục certs của cây nguồn. Tuy nhiên, mỗi chứng nhận là một tệp riêng biệt -- có nghĩa là mỗi chứng nhận phải được nạp riêng biệt. Ngoài ra còn có một thư mục con trong thư mục certs với các chứng nhận đã hết hạn. Việc cố gắng nạp các chứng nhận này sẽ gây ra các lỗi.

Bạn có thể nạp từng tệp riêng nếu bạn muốn, nhưng để đơn giản, các chứng nhận tin cậy từ bản phân phối OpenSSL mới nhất được đưa vào trong kho lưu trữ mã nguồn dưới dạng một tệp duy nhất có tên là "TrustStore.pem". Nếu bạn đã có một tệp kho lưu trữ tin cậy sẽ được sử dụng cho dự án cụ thể của mình, thì chỉ cần thay thế tệp "TrustStore.pem" trong Liệt kê 8 bằng tệp của bạn (hoặc nạp cả hai tệp đó bằng các cuộc gọi hàm riêng biệt).

Hãy gọi SSL_CTX_load_verify_locations để nạp tệp kho lưu trữ tin cậy. Cuộc gọi này có ba tham số: con trỏ ngữ cảnh, đường dẫn và tên tệp của tệp kho lưu trữ tin cậy và đường dẫn đến một thư mục các chứng nhận. Phải quy định một trong hai hoặc tệp kho lưu trữ tin cậy hoặc thư mục các chứng nhận. Nó trả về 1 nếu thành công, trả về 0 nếu đã có vấn đề.

Liệt kê 8. Nạp một kho lưu trữ tin cậy
if(! SSL_CTX_load_verify_locations(ctx, "/path/to/TrustStore.pem", NULL))
{
    /* Handle failed load here */
}

Nếu bạn đang sử dụng một thư mục để lưu kho lưu trữ tin cậy, thì các tệp phải được đặt tên theo một cách nhất định. Tài liệu hướng dẫn OpenSSL giải thích rõ cách này là gì, nhưng có một công cụ đi kèm với OpenSSL có tên là c_rehash chuẩn bị một thư mục để sử dụng làm tham số đường dẫn cho SSL_CTX_load_verify_locations.

Liệt kê 9. Chuẩn bị một thư mục chứng nhận và sử dụng nó
/* Use this at the command line */

c_rehash /path/to/certfolder

/* Then call this from within the application */

if(! SSL_CTX_load_verify_locations(ctx, NULL, "/path/to/certfolder"))
{
    /* Handle error here */
}

Bạn có thể đặt tên cho càng nhiều tệp hay thư mục riêng biệt càng tốt nếu cần để quy định tất cả các chứng nhận kiểm tra mà bạn có thể cần đến. Ngoài ra bạn cũng có thể quy định một tệp và một thư mục cùng một lúc.

Tạo kết nối

Đối tượng BIO được tạo ra bằng cách sử dụng hàm BIO_new_ssl_connect, đưa con trỏ đến ngữ cảnh SSL như là một tham số duy nhất của nó. Cũng cần lấy ra con trỏ đến cấu trúc SSL. Trong bài này, con trỏ này chỉ được sử dụng với hàm SSL_set_mode. Người ta sử dụng hàm đó để thiết lập cờ SSL_MODE_AUTO_RETRY. Với thiết lập tùy chọn này, nếu máy chủ đột nhiên muốn có một lần bắt tay mới, OpenSSL xử lý nó trong nền. Không có tùy chọn này, bất kỳ hoạt động đọc hoặc viết nào sẽ trả về một lỗi, nếu máy chủ muốn một lần bắt tay mới, thiết lập cờ thử lại trong quá trình này.

Liệt kê 10. Thiết lập đối tượng BIO
bio = BIO_new_ssl_connect(ctx);
BIO_get_ssl(bio, & ssl);
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);

Với việc thiết lập cấu trúc ngữ cảnh SSL, kết nối có thể được tạo ra. Tên máy chủ được thiết lập bằng cách sử dụng hàm BIO_set_conn_hostname. Tên máy chủ và cổng được quy định theo định dạng tương tự như trên. Hàm này cũng mở kết nối đến máy chủ. Vẫn phải thực hiện một cuộc gọi đến BIO_do_connect để kiểm tra xem kết nối này đã được mở thành công không. Cuộc gọi như vậy cũng thực hiện bắt tay để thiết lập truyền thông bảo mật.

Liệt kê 11. Mở một kết nối bảo mật
/* Attempt to connect */

BIO_set_conn_hostname(bio, "hostname:port");

/* Verify the connection opened and perform the handshake */

if(BIO_do_connect(bio) <= 0)
{
    /* Handle failed connection */
}

Một khi kết nối được thiết lập, cần kiểm tra chứng nhận để xem nó có hợp lệ không. Trên thực tế, OpenSSL làm việc này thay cho chúng ta. Nếu có các vấn đề nghiêm trọng với chứng nhận đó -- ví dụ, nếu các giá trị băm không hợp lệ -- thì kết nối đó thường sẽ không xảy ra. Nhưng nếu có các vấn đề không nghiêm trọng với chứng nhận đó -- như khi nó đã hết hạn hoặc chưa có hiệu lực -- kết nối đó vẫn có thể được sử dụng.

Để tìm hiểu xem chứng nhận đó đã kiểm tra tốt với OpenSSL chưa, hãy gọi SSL_get_verify_result với cấu trúc SSL làm tham số duy nhất. Nếu chứng nhận đó đã vượt qua các kiểm tra nội bộ của OpenSSL, gồm cả việc kiểm tra về độ tin cậy, thì nó sẽ trả về X509_V_OK. Nếu có một cái gì đó sai, nó sẽ trả về một mã lỗi được ghi lại chi tiết trong tùy chọn verify với công cụ dòng lệnh.

Điều cần lưu ý là việc kiểm tra có lỗi không có nghĩa là không thể sử dụng được kết nối này. Nên hay không nên sử dụng kết nối này phụ thuộc vào kết quả kiểm tra và các xem xét bảo mật. Ví dụ, việc kiểm tra tin cậy có lỗi có thể chỉ đơn giản muốn nói là chứng nhận tin cậy không có sẵn. Kết nối vẫn có thể được sử dụng, chỉ với bảo mật nâng cao trong đầu.

Liệt kê 12. Kiểm tra xem một chứng nhận có hiệu lực không
if(SSL_get_verify_result(ssl) != X509_V_OK)
{
    /* Handle the failed verification */
}

Và đó là tất cả những gì cần thiết. Bất kỳ sự truyền thông nào với máy chủ là chuyện bình thường khi sử dụng hàm BIO_readBIO_write. Việc đóng kết nối đòi hỏi một cuộc gọi đơn giản tới hàm BIO_free_all hoặc hàm BIO_reset, tùy thuộc vào BIO có được sử dụng lại không.

Tại một số thời điểm trước khi kết thúc ứng dụng, cấu trúc ngữ cảnh SSL phải được giải phóng. Gọi SSL_CTX_free để giải phóng cấu trúc này.

Liệt kê 13. Xóa sạch ngữ cảnh SSL
SSL_CTX_free(ctx);

Phát hiện lỗi

Vậy là OpenSSL đã đưa ra một lỗi về loại nào đó. Nó có nghĩa là gì? Trước tiên, bạn cần nhận được chính mã lỗi đó; ERR_get_error thực hiện việc này. Sau đó, bạn cần chuyển mã đó thành một chuỗi ký tự lỗi, là một con trỏ đến một chuỗi ký tự cố định được nạp vào bộ nhớ bằng SSL_load_error_strings hoặc ERR_load_BIO_strings. Việc này có thể được thực hiện trong một cuộc gọi lồng nhau.

Bảng 1 phác thảo các cách để lấy ra một lỗi từ ngăn xếp lỗi. Liệt kê 14 cho thấy cách in ra thông báo lỗi cuối cùng dưới dạng một chuỗi văn bản.

Bảng 1. Lấy ra các lỗi từ ngăn xếp
ERR_reason_error_string Trả về một con trỏ đến một chuỗi ký tự tĩnh, mà sau đó chuỗi này có thể được hiển thị trên màn hình, được viết vào một tệp hoặc bất cứ điều gì bạn muốn làm với nó.
ERR_lib_error_string Cho biết lỗi đã xảy ra trong thư viện nào.
ERR_func_error_string Trả về hàm OpenSSL gây ra lỗi đó.
Liệt kê 14. In ra lỗi cuối cùng
printf("Error: %s\n", ERR_reason_error_string(ERR_get_error()));

Bạn cũng có thể có thư viện cung cấp cho bạn một chuỗi ký tự lỗi được định dạng sẵn. Hãy gọi ERR_error_string để đạt được điều này. Nó lấy mã lỗi và một bộ đệm được cấp phát trước làm các tham số của mình. Bộ đệm phải có độ dài 256 byte. Nếu tham số này là NULL, OpenSSL viết chuỗi ký tự vào một bộ đệm tĩnh có độ dài 256 byte và trả về một con trỏ đến bộ đệm đó. Nếu không, nó sẽ trả lại con trỏ mà bạn đã cung cấp. Nếu bạn chọn tùy chọn bộ đệm tĩnh, bộ đệm đó sẽ bị ghi đè bằng cuộc gọi tiếp theo đến ERR_error_string.

Liệt kê 15. Lấy ra một chuỗi ký tự lỗi được định dạng sẵn
printf("%s\n", ERR_error_string(ERR_get_error(), NULL));

Bạn cũng có thể kết xuất toàn bộ hàng đợi lỗi vào một tệp hoặc BIO. Điều này đạt được thông qua ERR_print_errors hoặc ERR_print_errors_fp. Hàng đợi được kết xuất theo một định dạng dễ đọc. Bước đầu tiên gửi hàng đợi đến một BIO, trong khi bước thứ hai gửi nó đến một FILE. Chuỗi ký tự được định dạng theo cách sau (theo tài liệu hướng dẫn OpenSSL):

[pid]:error:[error code]:[library name]:[function name]:[reason string]:[file name]:[line]:[optional text message]

ở đây [pid] là ID quy trình, [error code] (mã lỗi) là mã hệ đếm 16 có 8 chữ số, [file name] (tên tệp) là tệp mã nguồn trong thư viện OpenSSL và [line] là số dòng trong tệp nguồn đó.

Liệt kê 16. Kết xuất hàng đợi lỗi
ERR_print_errors_fp(FILE *);
ERR_print_errors(BIO *);

Bắt đầu

Việc tạo một kết nối cơ bản với OpenSSL không khó, nhưng tài liệu hướng dẫn có thể làm nản lòng người dùng một chút khi cố gắng tìm hiểu cách để làm điều đó. Bài này đã giới thiệu cho bạn những điều cơ bản, nhưng có khá ít về tính linh hoạt với OpenSSL được khám phá và các thiết lập nâng cao mà bạn có thể cần đến để thực hiện đầy đủ chức năng SSL cho dự án của mình.

Có hai mẫu ví dụ kèm theo bài này. Một mẫu hiển thị một kết nối không bảo mật đến http://www.verisign.com/, trong khi mẫu khác hiển thị một kết nối SSL có bảo mật đến https://www.verisign.com/. Cả hai đều kết nối đến máy chủ và tải trang chủ. Không có bất kỳ các cách kiểm tra bảo mật nào và tất cả các thiết lập trong thư viện đều là mặc định -- Chỉ nên sử dụng nó cho mục đích giáo dục như một phần của bài này.

Mã nguồn nên dễ biên dịch trên bất kỳ hệ thống được hỗ trợ nào, nhưng điều quan trọng là bạn có phiên bản OpenSSL mới nhất. Tại thời điểm viết bài này, phiên bản mới nhất là 0.9.7d.


Tải về

Mô tảTênKích thước
Source code for this articleintro-openssl.zip9KB

Tài nguyên

Học tập

Lấy sản phẩm và công nghệ

Bình luận

developerWorks: Đăng nhập

Các trường được đánh dấu hoa thị là bắt buộc (*).


Bạn cần một ID của IBM?
Bạn quên định danh?


Bạn quên mật khẩu?
Đổi mật khẩu

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Ở lần bạn đăng nhập đầu tiên vào trang developerWorks, một hồ sơ cá nhân của bạn được tạo ra. Thông tin trong bản hồ sơ này (tên bạn, nước/vùng lãnh thổ, và tên cơ quan) sẽ được trưng ra cho mọi người và sẽ đi cùng các nội dung mà bạn đăng, trừ khi bạn chọn việc ẩn tên cơ quan của bạn. Bạn có thể cập nhật tài khoản trên trang IBM bất cứ khi nào.

Thông tin gửi đi được đảm bảo an toàn.

Chọn tên hiển thị của bạn



Lần đầu tiên bạn đăng nhập vào trang developerWorks, một bản trích ngang được tạo ra cho bạn, bạn cần phải chọn một tên để hiển thị. Tên hiển thị của bạn sẽ đi kèm theo các nội dung mà bạn đăng tải trên developerWorks.

Tên hiển thị cần có từ 3 đến 30 ký tự. Tên xuất hiện của bạn phải là duy nhất trên trang Cộng đồng developerWorks và vì lí do an ninh nó không phải là địa chỉ email của bạn.

Các trường được đánh dấu hoa thị là bắt buộc (*).

(Tên hiển thị cần có từ 3 đến 30 ký tự)

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Thông tin gửi đi được đảm bảo an toàn.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=70
Zone=Nguồn mở
ArticleID=832200
ArticleTitle=Lập trình bảo mật với OpenSSL API, Phần 1: Tổng quan về API
publish-date=06282012