從本期開始,記錄一些在使用 OpenSSL 過程中碰到的問題及解決辦法
在 Linux 下需要生成 pkcs12 文件,立即想到 OpenSSL。鍵入如下命令
~ # openssl pkcs12 -export -inkey clientkey.pem -in client.crt -out client.p12 No certificate matches private key ~ # openssl version OpenSSL 0.9.8j 07 Jan 2009
奇怪,明明 clientkey.pem 和 client.crt 是剛生成的配套文件,其中前者保存私鑰,后者則是用戶證書(包含公鑰),怎么會出錯?
於是切換到 Windows 平台再驗證一番,雖然版本不同,但仍然報錯,只不過顯示的錯誤信息不一樣
d:\>openssl pkcs12 -export -inkey clientkey.pem -in client.crt -out client.p12 WARNING: can't open config file: /usr/local/ssl/openssl.cnf Loading 'screen' into random state - done unable to load certificates d:\>openssl version WARNING: can't open config file: /usr/local/ssl/openssl.cnf OpenSSL 1.0.1f 6 Jan 2014
沒辦法,只得祭出調試器,到源碼內部看個究竟。既然在 Windows 平台,就用順手的 Visual Studio。打開(以前建立的)OpenSSL 解決方案(OpenSSL 版本號 1.0.1f),切換到 Debug 配置,確保該配置實際包含【-Zi】調試選項。
准備下斷點,該在哪里下呢?
由於錯誤信息為 unable to load certificates ,其中過程肯定涉及到證書加載函數,所以應該是 load_cert(不要問我為什么知道,因為我熟悉^_^)。如果不熟悉,可以在 Source Insight 中用 F7 調出【Browse Project Symbols】對話框,輸入猜測的關鍵字,比如 load/certificate 等,也能找到 load_cert 函數。
反正不論如何,找到了懷疑函數 load_cert。在 Visual Studio 中新建此函數的斷點,並在 IDE 中配置好命令參數和工作目錄。F5 啟動,果然命中,說明成功了一半。繼續跟蹤,最后得到出錯的位置如下(位於文件 crypto\pem\pem_lib.c 中)
695 696 buf[254]='\0'; 697 for (;;) 698 { 699 i=BIO_gets(bp,buf,254); 700 701 if (i <= 0) 702 { 703 PEMerr(PEM_F_PEM_READ_BIO,PEM_R_NO_START_LINE); 704 goto err; // <-- 【在此處出錯,並跳出】 705 } 706
出錯的調用棧為
openssl.exe!main
openssl.exe!do_cmd
openssl.exe!pkcs12_main
openssl.exe!load_certs
openssl.exe!load_certs_crls
libeay32.dll!PEM_X509_INFO_read_bio
libeay32.dll!PEM_read_bio
自底向上逐個切換調用棧,查看是哪里引發了問題,結果在 pkcs12_main 函數(文件 apps\pkcs12.c)中,發現如下一處調用
461 462 /* Load in all certs in input file */ 463 if(!(options & NOCERTS)) 464 { 465 certs = load_certs(bio_err, infile, FORMAT_PEM, NULL, e, 466 "certificates"); // <-- 【以 PEM 格式加載證書】 467 if (!certs) 468 goto export_end; 469
原來證書要求以 PEM 格式加載,回過頭用文本編輯器打開 client.crt。果然,此文件是 DER 格式的。
名不副實,自然是要出問題的。
原因知道了,解決辦法也立即有了。使用如下命令將證書轉為 PEM 格式
d:\>openssl x509 -in client.crt -inform der -out client.pem
然后再執行一次 pkcs12 命令
d:\>openssl pkcs12 -export -inkey clientkey.pem -in client.pem -out client.p12
問題得到解決。
自然想到,難道 pkcs12 命令就不提供 -inform 的格式轉換選項?
運行命令【openssl pkcs12 ?】,可以看到確實沒有提供 -inform 選項,看來還是有改進的空間。