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