openssl源碼分析
1. 如何選擇OpenSSL源碼的版本
nginx一般我們使用的是openssl靜態載入的。也就是你系統安裝openssl的rpm包。所以你要查看openssl版本,只要看你系統安裝的openssl版本就可以了,操作如下: rpm -aq | grep openssl
2. 如何將openssl aes源碼移植到嵌入式
移植步驟如下:
1、首先將openssl 源碼download下來;
2、在你的嵌入式開發環境中,交叉編譯openssl源碼,生成庫文件;
3、在你的嵌入式程序中,添加對交叉編譯後的openssl庫文件的支持,並在socket通信中調用ssl相關的函數。
3. 求問怎麼編譯OpenSSL,得到libssl.a 和libcrypto.a,跪求
如何在Windows下編譯OpenSSL (VS2010使用VC10的cl編譯器)
1、安裝ActivePerl//初始化的時候,需要用到perl解釋器
2、使用VS2010下的Visual Studio 2010 Command Prompt進入控制台模式(這個模式會自動設置各種環境變數)
3、解壓縮openssl的包,進入openssl的目錄
4、perl configure VC-WIN32
盡量在這個目錄下執行該命令,否則找不到Configure文件,或者指定完整的Configure文件路徑。
5、ms\do_ms.bat
在解壓目錄下執行ms\do_ms.bat命令
6、nmake -f ms\ntdll.mak
7、nmake -f ms\nt.mak
編譯後
在openssl解壓目錄下執行,完成編譯後。輸出的文件在out32dll (6), out32 (7)裡面,包括應用程序的可執行文件、lib文件和dll文件
注意:在運行第五步時,cl編譯器會抱怨說.\crypto\des\enc_read.c文件的read是The POSIX name for this item is deprecated(不被推薦的),建議使用_read。呵呵,我可不想將OpenSSL中的所有的read函數修改為_read。再看cl的錯誤代碼error C2220,於是上MSDN上查找:
warning treated as error - no object file generated
/WX tells the compiler to treat all warnings as errors. Since an error occurred, no object or executable file was generated.
是由於設置了/WX選項,將所有的警告都作為錯誤對待,所以。。。
於是打開OpenSSL目錄下的MS目錄下的ntdll.mak文件,將CFLAG的/WX選項去掉,存檔。。。
繼續執行nmake -f ms\ntdll.mak
=================================
一、編譯並安裝OpenSSL
1、按照標准步驟從源代碼編譯安裝OpenSSL
在編譯OpenSSL前,需要正確安裝Perl,因為在編譯OpenSSL時需要使用到該程序。
下載最新版本的Perl然後安裝之。
下載最新版本的OpenSSL
然後將源碼解壓縮到某個目錄(如 C:\openssl-0.9.8j)中。
進入openssl源碼目錄。
cd c:\openssl-1.0.1e
以下為參照該目錄下的文件INSTALL.W32的執行過程:
運行configure:
perl Configure VC-WIN32
創建Makefile文件:
ms\do_ms.bat
編譯動態庫:
nmake -f ms\ntdll.mak
編譯靜態庫:
nmake -f ms\nt.mak
測試動態庫:
nmake -f ms\ntdll.mak test
測試靜態庫:
nmake -f ms\nt.mak test
安裝動態庫:
nmake -f ms\ntdll.mak install
安裝靜態庫:
nmake -f ms\nt.mak install
清除上次動態庫的編譯,以便重新編譯:
nmake -f ms\ntdll.mak clean
清除上次靜態庫的編譯,以便重新編譯:
nmake -f ms\nt.mak clean
2、如果嫌麻煩,不想編譯,可以直接用別人做好的windows OpenSSL 安裝包(我用的是0.9.8j版),
可以下載 OpenSSL for Windows,直接安裝。
P.S. OpenSSL for Windows 的源代碼有一些數據類型和VC6的編譯器不兼容,我發現的不兼容的數據類型如下:
在OpenSSL安裝目錄的下的include/bn.h文件中,將
#define BN_ULLONG unsigned long long
#define BN_ULONG unsigned long long
#define BN_LONG long long
分別修改為:
#define BN_ULLONG ULONGLONG
#define BN_ULONG ULONGLONG
#define BN_LONG LONGLONG
否則,會出現編譯錯誤。
二、使用OpenSSL
在VC中配置使用以上的函數庫:
點擊菜單:Tools -> Options,彈出對話框"Options",在該對話框中選擇"Directories"標簽。
在"Show directories for:"的"Include files"選項中新增目錄"C:\openssl\include";
"Library files"選擇中新增目錄"C:\openssl\lib"。
然後在需要鏈接OpenSSL函數庫的工程中加入如下兩句:
#pragma comment(lib, "ssleay32.lib")
#pragma comment(lib, "libeay32.lib")
其作用是將OpenSSL所需的庫導入工程中。
三、問題
我在鏈接OpenSSL的靜態函數庫時遇到類似以下的問題:
Linking...
msvcrt.lib(MSVCRT.dll) : error LNK2005: _strchr already defined in libcmtd.lib(strchr.obj)
...
這是由於OpenSSL的靜態函數庫使用的是了VC的多線程DLL的Release版本,而我的程序使用了多線程靜態鏈接的Release版本。
調整OpenSSL的靜態函數庫使用的庫函數版本即可,調整過程如下:
編輯文件 ms\nt.mak,將該文件第19行
"CFLAG= /MD /Ox /O2 /Ob2 /W3 /WX /Gs0 /GF /Gy /nologo -DOPENSSL_SYSNAME_WIN32 -DWIN32_LEAN_AND_MEAN -DL_ENDIAN -DDSO_WIN32 -D_CRT_SECURE_NO_DEPRECATE -
D_CRT_NONSTDC_NO_DEPRECATE /Fdout32 -DOPENSSL_NO_CAMELLIA -DOPENSSL_NO_SEED -DOPENSSL_NO_RC5 -DOPENSSL_NO_MDC2 -DOPENSSL_NO_TLSEXT -DOPENSSL_NO_KRB5 -
DOPENSSL_NO_DYNAMIC_ENGINE"
中的"/MD"修改為"/MT"。然後重新編譯安裝OpenSSL即可。
四、附錄:在VC中對C/C++ 運行時庫不同版本編譯指令說明
《在VC中對C/C++ 運行時庫不同版本編譯指令說明》一文中詳細介紹了連接不同版本庫的編譯指令如下:
C Runtime Library:
/MD MSVCRT.LIB 多線程DLL的Release版本
/MDd MSVCRTD.LIB 多線程DLL的Debug版本
/MT LIBCMT.LIB 多線程靜態鏈接的Release版本
/MTd LIBCMTD.LIB 多線程靜態鏈接的Debug版本
/clr MSVCMRT.LIB 託管代碼和非託管代碼混合
/clr:pure MSVCURT.LIB 純託管代碼
C++ Standard Library:
/MD MSVCPRT.LIB 多線程DLL的Release版本
/MDd MSVCPRTD.LIB 多線程DLL的Debug版本
/MT LIBCPMT.LIB 多線程靜態鏈接的Release版本
/MTd LIBCPMTD.LIB 多線程靜態鏈接的Debug版本
===============================================
一 配置編譯參數
配置編譯參數是進行OpenSSL編譯的第一步,這一步可以確定系統的環境,使用什麼編譯器,默認安裝路徑以及其他一些選項.步驟如下:
1.安裝perl:下載ActivePerl-5.8.8.822-MSWin32-x86-280952.msi,然後點擊msi文件進行安裝!
2..配置編譯參數:下載openssl-1.0.1e.tar.gz,解壓.
vc:首先在C:\Program Files\Microsoft Visual Studio .NET 2010\VC10\bin\目錄下執行vcvars32.bat,然後在解壓後的openssl-1.0.1e目錄,執行命令配置編譯參數:perl Configure VC-WIN32
bc:在解壓後的openssl-0.9.8g目錄下執行:perl Configure BC-32
二 生成批處理文件
在使用configure腳本配置好的編譯參數後,就可以使用批處理命令來生成編譯腳本.生成編譯腳本根據採用編譯器的不同通常使用不同的批處理文件.就目前來說,使用vc編譯的時候有三種選擇:do_ms,do_masm和do_nasm來創建一系列編譯腳本文件,即.mak腳本.步驟如下:
vc:在openssl-1.0.1e目錄下,執行命令來批處理文件:do_ms,do_masm和do_nasm
bc:1.下載nsm09839.zip微軟匯編編譯器,解壓,拷貝到c:/windows目錄下,修改名稱為nasmw.exe;2.在openssl-1.0.1e目錄下,執行命令來批處理文件:ms\do_nasm
三 代碼編譯
vc:
完成上面步驟後,可以看到兩個關鍵腳本文件:nt.mak和ntdll.mak.如果我們需要編譯後的OpenSSL庫是支持動態DLL形式的,那麼應該使用ntddll.mak文件進行編譯,這樣編譯完成我們會得到四個與OpenSSL的API庫有關文件:ssleay32.lib,libeay32.lib,ssleay32.dll和libeay32.dll.執行的編譯命令形式如下:nmake -f ms\ntdll.mak
如果不希望以動態庫的形式使用OpenSSL,那麼可以使用nt.mak文件進行編譯.這樣編譯後使用OpenSSL的時候,回直接將代碼鏈接進我們的程序裡面.執行命令如下:nmake -f ms\nt.mak
bc:執行命令來完成代碼編譯:make -f ms\bcb.mak
四 ELSE
1)
測試動態庫:
nmake -f ms\ntdll.mak test
測試靜態庫:
nmake -f ms\nt.mak test
安裝動態庫:
nmake -f ms\ntdll.mak install
安裝靜態庫:
nmake -f ms\nt.mak install
清除上次動態庫的編譯,以便重新編譯:
nmake -f ms\ntdll.mak clean
清除上次靜態庫的編譯,以便重新編譯:
nmake -f ms\nt.mak clean
2)
使用OpenSSL
在VC中配置使用以上的函數庫:
點擊菜單:Tools -> Options,彈出對話框"Options",在該對話框中選擇"Directories"標簽。
在"Show directories for:"的"Include files"選項中新增目錄"C:\openssl\include";"Library files"選擇中新增目錄"C:\openssl\lib"。
然後在需要鏈接OpenSSL函數庫的工程中編譯時加入"libeay32.lib"就可以了。
4. php的openssl_sign($data,$sign,$private_key)這個函數的源碼是什麼
這個是打開https的訪問所用的到的函數
5. openssl 如何解析pfx
pfx就是我們通常所說的p12文件,openssl關於p12的操作在crypto/pkcs12 目錄下,有具體實現的源碼,您可以參考一下。
p12_add.c:處理 PKCS12_SAFEBAG,PKCS12_SAFEBAG 用於存放證書和私鑰相關的信息;
p12_attr.c :屬性處理;
p12_crt:生成一個完整的 pkcs12 ;
p12_init.c:構造一個 pkcs12 數據結構;
p12_kiss.c:解析 pkcs12 結構,獲取證書和私鑰等信息;
p12_npas :設置新口令;
p12_p8e.c:加密處理用戶私鑰(pkcs8 格式);
p12_p8d.c:解密出用戶私鑰;
pk12err.c :錯誤處理;
p12_asn.c:pkcs12 各個數據結構的 DER 編解碼實現;
p12_crpt.c:pkcs12 的pbe( 基於口令的加密)函數;
p12_decr.c.c:pkcs12 的pbe 解密;
p12_key.c:根據用戶口令生成對稱密鑰;
p12_mutl.c :pkcs12 的MAC信息處理;
p12_utl.c :一些通用的函數。
6. 求一個基於openssl寫的ecc曲線的源代碼
下面的例子生成兩對ECC密鑰,並用它做簽名和驗簽,並生成共享密鑰。
#include <string.h>
#include <stdio.h>
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/objects.h>
#include <openssl/err.h>
int main()
{
EC_KEY *key1,*key2;
EC_POINT *pubkey1,*pubkey2;
EC_GROUP *group1,*group2;
int ret,nid,size,i,sig_len;
unsigned char*signature,digest[20];
BIO *berr;
EC_builtin_curve *curves;
int crv_len;
char shareKey1[128],shareKey2[128];
int len1,len2;
/* 構造EC_KEY數據結構 */
key1=EC_KEY_new();
if(key1==NULL)
{
printf("EC_KEY_new err!\n");
return -1;
}
key2=EC_KEY_new();
if(key2==NULL)
{
printf("EC_KEY_new err!\n");
return -1;
}
/* 獲取實現的橢圓曲線個數 */
crv_len = EC_get_builtin_curves(NULL, 0);
curves = (EC_builtin_curve *)malloc(sizeof(EC_builtin_curve) * crv_len);
/* 獲取橢圓曲線列表 */
EC_get_builtin_curves(curves, crv_len);
/*
nid=curves[0].nid;會有錯誤,原因是密鑰太短
*/
/* 選取一種橢圓曲線 */
nid=curves[25].nid;
/* 根據選擇的橢圓曲線生成密鑰參數group */
group1=EC_GROUP_new_by_curve_name(nid);
if(group1==NULL)
{
printf("EC_GROUP_new_by_curve_name err!\n");
return -1;
}
group2=EC_GROUP_new_by_curve_name(nid);
if(group1==NULL)
{
printf("EC_GROUP_new_by_curve_name err!\n");
return -1;
}
/* 設置密鑰參數 */
ret=EC_KEY_set_group(key1,group1);
if(ret!=1)
{
printf("EC_KEY_set_group err.\n");
return -1;
}
ret=EC_KEY_set_group(key2,group2);
if(ret!=1)
{
printf("EC_KEY_set_group err.\n");
return -1;
}
/* 生成密鑰 */
ret=EC_KEY_generate_key(key1);
if(ret!=1)
{
printf("EC_KEY_generate_key err.\n");
return -1;
}
ret=EC_KEY_generate_key(key2);
if(ret!=1)
{
printf("EC_KEY_generate_key err.\n");
return -1;
}
/* 檢查密鑰 */
ret=EC_KEY_check_key(key1);
if(ret!=1)
{
printf("check key err.\n");
return -1;
}
/* 獲取密鑰大小 */
size=ECDSA_size(key1);
printf("size %d \n",size);
for(i=0;i<20;i++)
memset(&digest[i],i+1,1);
signature=malloc(size);
ERR_load_crypto_strings();
berr=BIO_new(BIO_s_file());
BIO_set_fp(berr,stdout,BIO_NOCLOSE);
/* 簽名數據,本例未做摘要,可將digest中的數據看作是sha1摘要結果 */
ret=ECDSA_sign(0,digest,20,signature,&sig_len,key1);
if(ret!=1)
{
ERR_print_errors(berr);
printf("sign err!\n");
return -1;
}
/* 驗證簽名 */
ret=ECDSA_verify(0,digest,20,signature,sig_len,key1);
if(ret!=1)
{
ERR_print_errors(berr);
printf("ECDSA_verify err!\n");
return -1;
}
/* 獲取對方公鑰,不能直接引用 */
pubkey2 = EC_KEY_get0_public_key(key2);
/* 生成一方的共享密鑰 */
len1=ECDH_compute_key(shareKey1, 128, pubkey2, key1, NULL);
pubkey1 = EC_KEY_get0_public_key(key1);
/* 生成另一方共享密鑰 */
len2=ECDH_compute_key(shareKey2, 128, pubkey1, key2, NULL);
if(len1!=len2)
{
printf("err\n");
}
else
{
ret=memcmp(shareKey1,shareKey2,len1);
if(ret==0)
printf("生成共享密鑰成功\n");
else
printf("生成共享密鑰失敗\n");
}
printf("test ok!\n");
BIO_free(berr);
EC_KEY_free(key1);
EC_KEY_free(key2);
free(signature);
free(curves);
return 0;
}
7. 如何在Windows下編譯OpenSSL
1、使用VS2005下的Visual Studio 2005 Command Prompt進入控制台模式(這個模式會自動設置各種環境變數) 2、解壓縮openssl的包,進入openssl的目錄 3、perl configure VC-WIN32 盡量在這個目錄下執行該命令,否則找不到Configure文件,或者指定完整的Configure文件路徑。 4、ms\do_ms 在解壓目錄下執行ms\do_ms命令 5、nmake -f ms\ntdll.mak編譯後在openssl解壓目錄下執行,完成編譯後。輸出的文件在out32dll裡面,包括應用程序的可執行文件、lib文件和dll文件
8. linux下使用openssl檢測PE文件數字簽名的證書是否有效
第一個坑: 有效期
windows在判斷證書是否有效時不檢測證書的有效期, 即使該證書超過有效期好幾年了, 只要沒有被吊銷, 微軟仍然認為它是有效的. 但在 openssl 提供的 X509_verify_cert 函數會驗證證書的有效期, 因此需要注釋掉驗證有效期的那部分代碼並重新編譯 openssl...
OK, 從 openssl 官網 上下載最新的版本, 好吧, 現在還是剛剛修復 Heartbleed 漏洞的 1.0.1g 版本...
下載, 解壓, 看下 INSTALL 文檔, 先試試可以編譯不:
./config
make
運氣不錯, 不用安裝什麼依賴直接編譯成功. 將代碼根目錄產生的 libcrypto.a 添加到項目中測試下, OK, 可以使用, 下面開始折騰了~
在 crypto/x509/x509_vfy.c 的 153 行找到 X509_verify_cert 函數(在線查看), 局部變數 ok 緩存每一步驗證是否通過, 它依次調用了:
check_issued
check_chain_extensions
check_name_constraints
check_trust
check_revocation
internal_verify
check_policy
其中 internal_verify (在線查看)驗證了證書的有效期, 進入這個函數, 在 1654 行找到這個代碼:
ok = check_cert_time(ctx, xs);
if (!ok)
goto end;
看看 check_cert_time 函數, 確認是檢查 notBefore 和 notAfter, 因此將上面三行代碼注釋掉, 驗證證書時就不會檢測有效期了.
然後就是重新編譯 openssl, 將 libcrypto.a 集成到項目里了~
第二個坑: unhandled critical extension
搜索了下, 在 openssl 官網上找到這個:
-ignore_critical
Normally if an unhandled critical extension is present which is not supported by OpenSSL the certificate is rejected (as required by RFC5280). If this option is set critical extensions are ignored.
原來是當openssl遇到證書中有它不支持的 未處理的關鍵擴展(unhandled critical extension ?) 時, 它會拒絕載入該證書.
再搜索下 -ignore_critical, 在 verify.c 中找到如下代碼片段:
else if (strcmp(*argv,"-ignore_critical") == 0)
vflags |= X509_V_FLAG_IGNORE_CRITICAL;
然後再使用 X509_STORE_set_flags 函數設置標志位:
X509_STORE *ctx;
...
X509_STORE_set_flags(ctx, vflags);
即可.
第三個坑: certificate signature failure
這個坑填不上了, openssl 說:
7 X509_V_ERR_CERT_SIGNATURE_FAILURE: certificate signature failure
the signature of the certificate is invalid.
在windows下導出證書文件, 直接用 openssl 驗證, 在載入證書就會出錯, PEM_read_bio_X509 返回為空....
第四個坑: A certificate was explicitly revoked by its issuer.
A certificate was explicitly revoked by its issuer. 是 Sysinternals 提供的工具sigcheck.exe 的檢測結果, 把文件拎出來一看, 證書真的被撤銷了...
OK, 只好根據證書上的 CRL Distribution Point(CRL 分發點) 提供的 URL 下載 撤銷證書列表 文件, 然後在調用 X509_verify_cert 驗證證書鏈之前, 設置填充被撤銷的證書列表:
X509_CRL *d2i_X509_CRL_fp(FILE *fp, X509_CRL **crl); // 讀取被撤銷的證書列表
STACK_OF(X509_CRL) *sk_X509_CRL_new_null();
#define sk_X509_CRL_push(st, val) SKM_sk_push(X509_CRL, (st), (val)); // sk_X509_CRL_push(STACK_OF(X509_CRL) *crls, X509_CRL *crl);
void X509_STORE_CTX_set0_crls(X509_STORE_CTX *c, STACK_OF(X509_CRL) *sk); // 設置被撤銷的證書列表
同時, 也要設置檢查被撤銷證書列表的標志位 X509_V_FLAG_CRL_CHECK, 然後再調用X509_verify_cert 驗證證書鏈即可.
填了第四個坑後又引起了第五個坑(如何獲取撤銷證書列表)和第六個坑(設置檢測撤銷證書列表的標識位後, 如果該證書沒有撤銷證書列表則直接報錯)...
第五個坑: 獲取撤銷證書列表文件
證書上的 CRL Distribution Point(CRL 分發點) 屬於擴展屬性, 在 PKCS #7: Cryptographic Message Syntax V1.5 上沒有相關介紹.
在 StackOverflow 上找到這個問答 Openssl - How to check if a certificate is revoked or not, 其中第二個回答說 CRL 是在 RFC 5280 中定義的, 除了證書中附帶被撤銷的證書列表以外還有使用 OCSP 協議的, 即使證書撤銷列表也分為使用 URL分發點和 LDAP DNs(???)提供的, 目前先考慮使用 URL 作為 CRL分發點 的情況吧.
然而 openssl 沒有提供直接獲取 CRL 分發點 URL 的API, 那個回答說 Apache 的 mod_ssl 模塊有本地 CRL 和 OCSP 檢測的實現代碼, 但沒有說明哪裡有檢測使用 URL 作為 CRL分發點 的實現方法.
然後又在 frank4dd.com上找到這個代碼 certextensions.c, 他給出了一個如何使用 openssl 從 X.509v3 版本的證書文件中提取擴展內容的示常式序, 太感謝 Frank4DD 這位仁兄了~~~
到這里後, 可以直接使用他的示常式序, 根據關鍵字 Full Name 和 URI 定位 CRL 分發點 的 URL, 也可以看看 openssl 是如何提取這個 URL 的, 然後自己實現一個介面.
如果自作孽使用第二種方法的話, 就編譯個 debug 版的 openssl 庫, 然後調試跟進X509V3_EXT_print 函數, 一步一步的向下走, 直到走到 GENERAL_NAME_print 函數, 這里就是終點了...然後就知道了 CRL 分發點 的 URL 的編號為 6, 也就是 GEN_URI, 直接取結果吧.
第六個坑: CRL有效期
在windows環境下每次查看PE文件的數字簽名時, windows 都會從 CRL分發點 下載吊銷證書列表做驗證,一般來說, 每個 CRL的有效期是非常短的,大概只有5~20 天的有效期吧, 然而我們不可能像 windows 一樣每次查看數字簽名時就從CRL分發點下載最新的吊銷列表.
另外, windows 遇到過期的 CRL 時不會產生證書鏈無效的結果, 但 openssl 在遇到過期的 CRL 時就會導致證書鏈驗證失敗, 因此在載入和驗證 CRL 時, 要忽略 CRL 的有效期.
分析 openssl 源代碼, X509_verify_cert 調用 check_revocation , 之後調用 check_cert , 然後再調用 check_crl , 在這個函數里有檢測 CRL 有效期的代碼:
if (!(ctx->current_crl_score & CRL_SCORE_TIME))
{
ok = check_crl_time(ctx, crl, 1);
if (!ok)
goto err;
}
將其注釋掉即可忽略檢測 CRL 有效期.
第七個坑: CRL 列表為空導致 openssl 認為沒有載入 CRL
9 初始化順序
10 證書名: key_id