openssl 使用指南 (轉)
• 介紹
• 編譯
• 執行 OpenSSL.exe
• 演算法程式設計 API
• 介紹
OpenSSL 是使用非常廣泛的 SSL 的開源實現。由於其中實現了為 SSL 所用的各種加密演算法,因此 OpenSSL 也是被廣泛使用的加密函式庫。
1.1 SSL
SSL(Secure Socket Layer) 安全協議是由 Netscape 公司首先提出,最初用在保護 Navigator 瀏覽器和 Web 伺服器之間的 HTTP 通訊 ( 即 HTTPS) 。後來 SSL 協議成為傳輸層安全通訊事實上的標準,並被 IETF 吸收改進為 TLS(Transport Layer Security) 協議。 SSL/TLS 協議位於 TCP 協議和應用層協議之間,為傳輸雙方提供認證、加密和完整性保護等安全服務。 SSL 作為一個協議框架,通訊雙方可以選用合適的對稱演算法、公鑰演算法、 MAC 演算法等密碼演算法實現安全服務。
1.2 OpenSSL
OpenSSL 是著名的 SSL 的開源實現,是用 C 語言實現的。
OpenSSL 的前身是 SSLeay ,一個由 Eric Young 開發的 SSL 的開源實現,支援 SSLv2/v3 和 TLSv1 。
伴隨著 SSL 協議的普及應用, OpenSSL 被廣泛應用在基於 TCP/Socket 的網路程式中,尤其是 OpenSSL 和 Apache 相結合,是很多電子商務網站伺服器的典型配置。
• 編譯和安裝 OpenSSL
OpenSSL 開放原始碼,這對學習、分析 SSL 和各種密碼演算法提供了機會,也便於在上面進一步開發。
2.1 獲得 OpenSSL
到 OpenSSL 的網站 http://www.openssl.org/source/ 即可下載當前版本的 OpenSSL 原始碼壓縮包。 當前版本 openssl- 1.0.0 beta3 。其中 crypto 子目錄中是眾多密碼演算法實現, ssl 子目錄中是 SSL 協議的實現。
在 Linux 中解壓縮:
$tar zxf openssl-1.0.0-beta3.tar.gz, 在 Windows 中可以使用 winzip 或 winrar 。
2.2 編譯工具
編譯 OpenSSL 需要 Perl 和 C 編譯器。在 Windows 下如果要用加密演算法的彙編程式碼實現,還需要 masm 或 nasm 彙編器
Perl 在 Windows 下推薦使用 Active Perl 。
C 編譯器可以使用 gcc 。在 W indows 下可以使用 Visual C 編譯器。
彙編器推薦使用 nasm 。
這些工具所在目錄必須加入到 PATH 環境變數中去。
2.3 編譯和安裝步驟
檢視 readme 是個好習慣。從 readme 瞭解到需要進一步檢視 INSTALL 和 INSTALL.W32 檔案。
在 Windows 中:
>perl Configure VC-WIN32
>ms"do_nasm ( 如果不使用匯編程式碼實現,則可 >ms"do_ms)
>nmake -f ms"ntdll.mak
>cd out32dll
>.."ms"test
編譯結果得到標頭檔案、連結庫、執行庫和 openssl.exe 工具。
標頭檔案位於 ./inc32 或者 ./inculde 目錄,
有一個 openssl 子目錄,內有幾十個 .h 檔案。
連結庫即 ./out32dll 目錄中的 libeay32.lib 和 ssleay32.lib ,分別是密碼演算法相關的和 ssl 協議相關的。
執行庫是 ./out32dll 目錄中的 libeay32.dll 和 ssleay32.dll ,和連結庫相對應。
在 ./out32dll 中還有一個工具 openssl.exe ,可以直接用來測試效能、產生 RSA 金鑰、加解密檔案,甚至可以用來維護一個測試用的
CA 。
在 Linux 中的編譯和安裝步驟較簡單 :
$./config
$make
$make test
$make install
在 Linux 下,標頭檔案、庫檔案、工具都已被安裝放到了合適的位置。庫檔案是 .a 或 .so 格式。
• 使用 OpenSSL.exe
使用 OpenSSL.exe(Linux 中可執行檔名是 openssl) 可以做很多工作,是一個很好的測試或除錯工具。
3.1 版本和編譯引數
顯示版本和編譯引數: >openssl version -a
3.2 支援的子命令、密碼演算法
檢視支援的子命令: >openssl ?
SSL 密碼組合列表: >openssl ciphers
3.3 測試密碼演算法速度
測試所有演算法速度: >openssl speed
測試 RSA 速度: >openssl speed rsa
測試 DES 速度: >openssl speed des
3.4 RSA 金鑰操作
產生 RSA 金鑰對: >openssl genrsa -out 1.key 1024
取出 RSA 公鑰: >openssl rsa -in 1.key -pubout -out 1.pubkey
3.5 加密檔案
加密檔案: >openssl enc -e -rc4 -in 1.key -out 1.key.enc
解密檔案: >openssl enc -d -rc4 -in 1.key.enc -out 1.key.dec
3.6 計算 Hash 值
計算檔案的 MD5 值: >openssl md5 < 1.key
計算檔案的 SHA1 值: >openssl sha1 < 1.key
• 演算法程式設計 API
OpenSSL 中支援眾多的密碼演算法,並提供了很好的封裝和介面。密碼演算法主要分為如下幾類:對稱演算法、公鑰演算法、雜湊演算法、隨機數產生演算法等。
OpenSSL 的目標是實現安全協議。其中相關協議和標準包括: SSL/TLS 、 PKCS#1 、 PCKS#10 、 X.509 、 PEM 、 OCSP 等。
4.1 對稱演算法介面
OpenSSL 中實現的對稱演算法太多,舉三個例子: DES 、 AES 、 RC4 。
4.1.1 DES
DES 加密演算法是分組演算法。 DES 的基本操作是把 64 位元明文在 56 位元金鑰指引下加密成 64 位元密文。在實際使用中把金鑰看作 64 位元可以更方便。
DES ( IN , KEY ) = OUT
(1) DES ECB 模式
在 OpenSSL 中 ECB 操作模式對應的函式是 DES_ecb_encrypt() ,該函式把一個 8 位元組明文分組 input 加密成為一個 8 位元組密文分組 output 。引數中金鑰結構 ks 是用函式 DES_set_key() 準備好的,而金鑰 key 是用隨機數演算法產生的 64 個隨機位元。引數 enc 指示是加密還是解密。該函式每次只加密一個分組,因此用來加密很多資料時不方便使用。
void DES_ecb_encrypt(const_DES_cblock *input,DES_cblock *output, DES_key_schedule *ks,int enc);
int DES_set_key(const_DES_cblock *key,DES_key_schedule *schedule);
(2) DES CBC 模式
DES 演算法 CBC 操作模式加解密函式是 DES_ncbc_encrypt() 。引數 length 指示輸入位元組長度。如果長度不是 8 位元組的倍數,則會被用 0 填充到 8 位元組倍數。因此,輸出可能比 length 長,而且必然是 8 位元組的倍數。
void DES_ncbc_encrypt(const unsigned char *input,unsigned char *output, long length, DES_key_schedule *schedule, DES_cblock *ivec, int enc);
(3) DES CFB 模式
DES 演算法 CFB 操作模式加解密函式是 DES_cfb_encrypt() 。引數 length 指示輸入位元組長度。引數 numbits 則指示了 CFB 每次迴圈加密多少明文位元,也即密文反饋的位元數目。 ivec 是初始向量,被看做第 0 個密文分組,是不用保密但應隨機取值的 8 個位元組。如果在一次會話中數次呼叫 DES_cfb_encrypt() ,則應該記憶 ivec 。由於 CFB 模式中每次 DES 基本操作只加密 numbits 位元明文,因此如果 numbits 太小則效率太低。
void DES_cfb_encrypt(const unsigned char *in, unsigned char *out, int numbits, long length, DES_key_schedule *schedule, DES_cblock *ivec, int enc);
另有一個 numbit 是 64 位元的版本,既高效又沒有填充的麻煩,推薦使用。 num 中的返回值指示了 ivec 中的狀態,是和下次呼叫銜接的。
void DES_cfb64_encrypt(const unsigned char *in, unsigned char *out, long length, DES_key_schedule *schedule, DES_cblock *ivec, int *num, int enc) ;
(4) DES OFB 模式
OFB 和 CFB 類似,也有兩個函式,用法一樣。
void DES_ofb_encrypt(const unsigned char *in,unsigned char *out,int numbits,long length,DES_key_schedule *schedule,DES_cblock *ivec);
void DES_ofb64_encrypt(const unsigned char *in,unsigned char *out,long length,DES_key_schedule *schedule,DES_cblock *ivec,int *num);
(5) DES 函式示例程式
見附件 A.1 。
4.1.2 AES
AES 加密演算法是分組演算法。典型引數的 AES 的基本操作是把 128 位元明文在 128 位元金鑰指引下加密成 128 位元密文。
AES ( IN , KEY ) = OUT
OpenSSL 中關於 AES 的函式名和引數介面和 DES 的雷同。相關函式名如下 ( 引數略 ) 。
int AES_set_encrypt_key();
int AES_set_decrypt_key();
void AES_ecb_encrypt();
void AES_cbc_encrypt();
void AES_cfb128_encrypt();
void AES_ofb128_encrypt();
AES 示例程式見附件 A.2 。
4.1.3 RC4
RC4 密碼演算法是流演算法,也叫序列演算法。流演算法是從金鑰作為種子產生金鑰流,明文位元流和金鑰流異或即加密。 RC4 演算法由於演算法簡潔,速度極快,金鑰長度可變,而且也沒有填充的麻煩,因此在很多場合值得大力推薦。
OpenSSL 中 RC4 演算法有兩個函式 : RC4_set_key() 設定金鑰, RC4() 加解密。可以把 RC4 看作異或,因此加密兩次即解密。
void RC4_set_key(RC4_KEY *key, int len, const unsigned char *data);
void RC4(RC4_KEY *key, unsigned long len, const unsigned char *indata, unsigned char *outdata);
RC4 示例程式見附件 A.3 。
例子 A.3.(1) 是利用 OpenSSL 動態庫函式。例子 A.3.(2) 是把 RC4 的實現程式碼從 OpenSSL 中分離出來的。例子 A.3.(3) 是另一個演示實現。
4.2 公鑰演算法
OpenSSL 中實現了 RSA 、 DSA 、 ECDSA 等公鑰演算法。
4.2.1 RSA
RSA 是分組演算法,典型的金鑰模長度 1024 位元時,分組即是 1024 位元,即 128 位元組。
(1) RSA 金鑰
RSA 金鑰產生函式 RSA_generate_key() ,需要指定模長位元數 bits 和公鑰指數 e 。另外兩個引數為 NULL 即可。
RSA * RSA_generate_key(int bits, unsigned long e, void (*callback) (int,int,void *),void *cb_arg);
如果從檔案中讀取金鑰,可使用函式 PEM_read_bio_PrivateKey()/ PEM_read_bio_PUBKEY(); EVP_PKEY 中包含一個 RSA 結構,可以引用。
EVP_PKEY *PEM_read_bio_PrivateKey(BIO *bp, EVP_PKEY **x, pem_password_cb *cb, void *u);
(2) RSA 加密解密
RSA 加密函式 RSA_public_encrypt() 使用公鑰部分,解密函式 RSA_private_decrypt() 使用私鑰。填充方式常用的有兩種 RSA_PKCS1_PADDING 和 RSA_PKCS1_OAEP_PADDING 。出錯時返回 -1 。輸入必須比 RSA 鑰模長短至少 11 個位元組(在 RSA_PKCS1_PADDING 時?)。輸出長度等於 RSA 鑰的模長。
int RSA_public_encrypt(int flen, const unsigned char *from,unsigned char *to, RSA *rsa,int padding);
int RSA_private_decrypt(int flen, const unsigned char *from,unsigned char *to, RSA *rsa,int padding);
(3) 簽名和驗證
簽名使用私鑰,驗證使用公鑰。 RSA 簽名是把被簽署訊息的雜湊值編碼後用私鑰加密,因此函式中引數 type 用來指示雜湊函式的型別,一般是 NID_md5 或 NID_sha1 。正確情況下返回 0 。
int RSA_sign(int type, const unsigned char *m, unsigned int m_length, unsigned char *sigret, unsigned int *siglen, RSA *rsa);
int RSA_verify(int type, const unsigned char *m, unsigned int m_length, unsigned char *sigbuf, unsigned int siglen, RSA *rsa);
(4) RSA 函式示例程式
RSA 示例程式見附件 A.4 。
例子 A.4.(1) 是加密解密例子。例子 A.4.(2) 是簽名驗證例子。
4.2.2 DSA
( TOBE )
4.2.2 ECDSA
( or NOT TOBE )
4.3 Hash 演算法
Hash 演算法舉 MD5 和 SHA1 兩個例子。 Hash 演算法重複接收使用者輸入,直到最後一次結束時輸出雜湊結果。
4.3.1 MD5
MD5 演算法輸出的雜湊值是 16 位元組。
int MD5_Init(MD5_CTX *c);
int MD5_Update(MD5_CTX *c, const void *data, size_t len);
int MD5_Final(unsigned char *md, MD5_CTX *c);
4.3.2 SHA1
SHA1 演算法輸出的雜湊值是 20 位元組。
int SHA1_Init(SHA_CTX *c);
int SHA1_Update(SHA_CTX *c, const void *data, size_t len);
int SHA1_Final(unsigned char *md, SHA_CTX *c);
4.3.3 MD5 例子
MD5 示例程式見附件 A.5 。
md5sum 這是一個實用小工具,可以計算一個檔案的 MD5 值。
4.4 隨機數演算法
隨機性是密碼安全的基石。為了產生安全的偽隨機數,必須有好的隨機因素作為種子。 OpenSSL 在內部做了努力,但是仍建議在實用隨機數產生函式之前新增隨機因素。
函式 RAND_add() 可以新增隨機因素到內部狀態中去。然後,即可以使用 RAND_bytes() 獲得隨機數。
void RAND_add(const void *buf,int num,double entropy);
int RAND_bytes(unsigned char *buf,int num);
• 參考網址
SSL 3.0 Specification
Transp ort Layer Security (tls) Charter
OpenSSL: The Open Source toolkit for SSL/TLS
SSLeay
OpenSSL 中文論壇
Perl
NASM