2.2.13 在Linux下编译安装OpenSSL 1.0.2

打开官网下载源码。OpenSSL的官网地址是https://www.openssl.org。这里使用的版本是1.0.2m,不求最新,但求稳定,这是一线开发者的原则。另外要注意的是,OpenSSL官方现在已停止对0.9.8和1.0.0两个版本的升级维护。这里下载下来的是一个压缩文件:openssl-1.0.2m.tar。

1.卸载当前已有的版本

刚下载下来不能马上安装,先要看看现在的操作系统是否已经安装OpenSSL了,可以用以下命令进行查看:

[root@localhost ~]# rpm -ql openssl

或者直接查询OpenSSL版本:

[root@localhost ~]# openssl version
openssl 1.0.1e-fips 11 feb 2013

可以看出,在笔者的CentOS 7上已经预先安装了OpenSSL 1.0.1e版本,如果要查看这个版本更为详细的信息,可以输入命令:

其实,也就是加了-a选项。如果要查看OpenSSL所在的路径,可以使用whereis openssl命令,比如:

[root@localhost bin]# whereis openssl
openssl: /usr/bin/openssl /usr/lib64/openssl /usr/include/openssl /usr/share/man/man1/openssl.1ssl.gz

其中,/usr/bin/下的openssl是一个程序;/usr/lib64/openssl是一个目录;/usr/include/openssl也是一个目录,里面存放的是开发所用的头文件。

因为我们要用OpenSSL 1.0.2m,所以要先卸载这个自带的旧版本,卸载命令如下:

[root@localhost soft]# rpm -e --nodeps openssl

然后再次查看:

[root@localhost soft]# rpm -qa openssl
[root@localhost soft]#

或者再次查看其版本:

[root@localhost 桌面]# openssl version
bash: /usr/bin/openssl: 没有那个文件或目录

可以看到/usr/bin下的程序OpenSSL没有了,说明卸载成功了。但要注意,有些目录并没有删除,我们可以用whereis查看一下:

[root@localhost openssl-1.0.2m]# whereis openssl
openssl: /usr/lib64/openssl /usr/include/openssl

我们可以进入/usr/include/openssl/下查看,头文件依旧存在,当我们用ll命令查看时,可以发现是2015年生成的:

注意,我们后面装新版的OpenSSL时,是不会覆盖这些文件的。另外,/usr/lib64下的共享库依旧存在libcrypto.so.1.0.1e:

这里,我们可以直接把目录/usr/include/openssl、动态库文件/usr/lib64/libcrypto.so.1.0.1e和符号链接文件/usr/lib64/libcrypto.so删除。当然,以后这3样都要在安装新版本OpenSSL时手工恢复成新版本OpenSSL对应的内容。为了怕大家遗忘,这里先不删,在下一节安装后再删除也可以。

当然,用到1.0.2m的例子,其实用1.0.1e也是可以的。这里主要是为了让大家学会卸载和重新安装。

2.不指定安装目录安装OpenSSL

假设旧版本已经卸载。把下载下来的压缩文件放到Linux中,这里存放的路径是/root/soft,大家可以自定义路径,然后进入这个路径后解压缩:

[root@localhost ~]# cd /root/soft
[root@localhost soft]# tar zxf openssl-1.0.2m.tar.gz

进入解压后的文件夹,开始配置、编译和安装:

[root@localhost soft]# cd openssl-1.0.2m/
[root@localhost openssl-1.0.2m]# ./config shared zlib

shared表示除了生成静态库外,还要生成共享库,如果仅仅想生成静态库,可以不用这个选项,或者使用no-shared;zlib表示编译时使用zlib这个压缩库。更多配置选项可以参考源码目录下的configure文件。

下面开始编译:

[root@localhost openssl-1.0.2m]# make

稍等片刻,编译结束。编译完成并不会复制新的文件到默认目录,我们可以使用whereis看一下:

[root@localhost openssl-1.0.2m]# whereis openssl
openssl: /usr/lib64/openssl /usr/include/openssl

依旧是这两个目录,我们进入/usr/include/openssl/看看里面的文件有没有被更新:

可以看出,没有被更新。而且我们用make install安装新版本的OpenSSL后,也不会被更新。这一点要注意,开发时不要去引用这个目录下的头文件。

[root@localhost openssl-1.0.2m]# make install

稍等片刻,安装完成。通过查看make install的过程可以发现新建了几个目录,如图2-37所示。

图2-37

从图2-37可以看出,安装程序创建了目录/usr/local/ssl,这个目录就是不指定安装目录时安装程序所采用的默认安装目录。我们可以进入这个目录下查看:

其中,子目录bin存放OpenSSL程序,该程序可以在命令行下使用OpenSSL功能;include子目录存放开发所需的头文件;lib子目录存放开发所需的静态库和共享库。值得注意的是,/usr/include/openssl/下的头文件依然是旧的,如图2-38所示。

图2-38

但要注意的是,/usr/lib64/下依然有libcrypto.so.1.0.1e:

[root@localhost openssl-1.0.2m]# find / -name  libcrypto.so.1.0.1e
/usr/lib64/libcrypto.so.1.0.1e

我们开发时不需要引用这个目录下的头文件,而要引用/usr/local/ssl/include下的头文件。为了防止以后误用,我们可以直接删除旧的头文件,包含目录/usr/include/openssl/:

[root@localhost include]# rm -rf /usr/include/openssl

再创建新的头文件,包含目录的软链接:

ln -s /usr/local/ssl/include/openssl /usr/include/openssl

下面再创建可执行文件的软链接,这样就可以在命令行下使用openssl命令了:

ln -s /usr/local/ssl/bin/openssl /usr/bin/openssl

这样执行/usr/bin下的OpenSSL实际就是执行/usr/local/ssl/bin下的OpenSSL程序。引用/usr/include/openssl下的头文件就是引用/usr/local/ssl/include/openssl下的头文件。不放心的话,我们可以到/usr/include/openssl下查看一下:

终于不是2015年的了。最后添加动态库路径到动态库配置文件并更新:

echo "/usr/local/ssl/lib" >> /etc/ld.so.conf
ldconfig -v

至此,升级安装工作完成了。我们可以看一下现在OpenSSL的版本号:

版本升级成功了。如果要以命令方式使用OpenSSL,可以在终端下输入openssl,然后就会出现OpenSSL提示,如图2-39所示。

图2-39

具体的OpenSSL命令我们会在后面的章节讲述,这里暂且不表。

值得注意的是,/usr/local/ssl/bin/下的程序OpenSSL依赖于共享库libcrypto.so.1.0.0。例如把/usr/local/ssl/lib目录改个名字,再运行OpenSSL,可以发现出错了:

这也说明,openssl程序和/usr/lib64/libcrypto.so.1.0.1e没什么关系。但我们依旧需要删除/usr/lib64/libcrypto.so.1.0.1e,因为编译自己写的C/C++程序的时候,需要到/usr/lib64下找libcrypto.so,而/usr/lib64/下有一个libcrypto.so是一个软链接,它测试指向的是/usr/lib64/libcrypto.so.1.0.1e这个共享库,如果此时编译我们的程序,那么使用的共享库是/usr/lib64/libcrypto.so.1.0.1e,而不是新版的OpenSSL的共享库。

为了让自己的C/C++程序能链接到新版OpenSSL的共享库libcrypto.so.1.0.0,我们需要重新做一个软链接。先删除旧的共享库/usr/lib64/libcrypto.so.1.0.1e:

rm -f /usr/lib64/libcrypto.so.1.0.1e

如果此时我们写一个C++程序,比如例2.11,然后在命令行下编译:

g++ test.cpp -o test  -lcrypto

就会发现报错了:

这说明我们把旧版共享库删除后,虽然软链接依旧存在,但还是无法编译成功。下面我们需要把/usr/lib64/下的软链接libcrypto.so指向新版OpenSSL的共享库/usr/local/ssl/lib/libcrypto.so.1.0.0。因为原来已经有软链接,需要先删除才能再创建:

[root@localhost lib64]# ln -s /usr/local/ssl/lib/libcrypto.so.1.0.0 /usr/lib64/libcrypto.so
ln: 无法创建符号链接"/usr/lib64/libcrypto.so": 文件已存在
[root@localhost lib64]# rm /usr/lib64/libcrypto.so
rm:是否删除符号链接 "/usr/lib64/libcrypto.so"?y
[root@localhost lib64]# ln -s /usr/local/ssl/lib/libcrypto.so.1.0.0 /usr/lib64/libcrypto.so

此时如果编译我们的程序,会发现可以编译,但运行报错:

我们需要把libcrypto.so.1.0.0复制一份到/usr/lib64/。

[root@localhost ex]# cp /usr/local/ssl/lib/libcrypto.so.1.0.0 /usr/lib64

此时如果运行test,会发现可以运行了:

[root@localhost ex]# ./test
Hello, OpenSSL!

有朋友说了,既然/usr/lib64下有libcrypto.so.1.0.0了,那么是否可以让符号链接libcrypto.so指向同目录下的libcrypto.so.1.0.0?这样完全可以,而且做法和原来旧版本的情况是一样的,旧版本时的libcrypto.so就是指向同目录下的libcrypto.so.1.0.1e。下面先删除符号链接,再新建:

[root@localhost ex]# cd /usr/lib64
[root@localhost lib64]# rm -f libcrypto.so
[root@localhost lib64]#  ln -s libcrypto.so.1.0.0 libcrypto.so

此时,我们编译test.cpp,然后运行:

[root@localhost ex]#  g++ test.cpp -o test  -lcrypto
[root@localhost ex]# ./test
Hello, OpenSSL!

一气呵成!而且此时链接的动态库是新的OpenSSL的动态库,不信可以用ldd命令查看一下:

我们可以看到粗体部分就是新的共享库。

是不是感觉有点麻烦?升级就是这样的,不彻底把旧的删除,那以后用了许久,说不定使用的还是旧版的共享库。

顺便说一句,如果不想复制共享库也可以,只要在/usr/lib64下做一个符号链接,指向/usr/local/ssl/lib/libcrypto.so.1.0.0,比如:

ln -s /usr/local/ssl/lib/libcrypto.so.1.0.0 /usr/lib64/

这样也可以运行test。反正一句话,/usr/lib64下要有libcrypto.so和libcrypto.so.1.0.0,无论是符号链接还是真正的共享库。

之所以讲这些,就是为了让大家知道运行下面的例子时背后的故事,别编译运行了半天,链接的还是旧版的共享库。下面我们详细说明自己的OpenSSL程序的建立过程。

【例2.11】第一个OpenSSL的C++程序

(1)在Windows下打开UltraEdit或其他编辑软件,输入代码如下:

代码很简单,就调用了一个OpenSSL的库函数openssl_add_all_algorithms,该函数的作用是载入所有SSL算法,我们这里调用就是看看能否调用得起来。

evp.h的路径是/usr/local/ssl/include/openssl/evp.h,它包含常用密码算法的声明。

(2)保存为test.cpp,上传到Linux,在命令行下编译运行:

[root@localhost test]# g++ test.cpp -o test  -lcrypto
[root@localhost test]# ./test
Hello, OpenSSL!

运行成功了。编译的时候要注意链接OpenSSL的动态库crypto,这个库文件位于/usr/lib64/libcrypto.so,是一个符号链接,我们前面让它指向了/usr/local/ssl/lib下的共享库/usr/local/ssl/lib/libcrypto.so.1.0.0。

有读者或许会问,evp.h的存放路径是/usr/local/ssl/include/openssl/evp.h,编译的时候为何不用-I包含头文件的路径呢?答案是双引号包含头文件时,如果当前工作目录没有找到所需的头文件,就到-I所包含的路径下去找;如果编译时没有用-I指定包含目录,就去/usr/local/include下找;如果/usr/local/include下也没有,再到/usr/include下去找,再找不到就报错了。而/usr/include下是有OpenSSL的,因为前面我们做了软链接,软链接指向的实际目录是/usr/local/ssl/include/openssl/,因此我们使用的evp.h就是/usr/local/ssl/include/openssl/evp.h。

3.在指定安装目录安装OpenSSL

前面因为要讲不少原理,所以比较啰唆,这里将进行简化,直接用步骤阐述。

(1)卸载旧版OpenSSL

这一步前面的章节已经讲过,这里不再赘述。

(2)解压和编译

把下载下来的压缩文件放到Linux中,这里存放的路径是/root/soft,大家可以自定义路径,然后进入这个路径后解压缩:

[root@localhost ~]# cd /root/soft
[root@localhost soft]# tar zxf openssl-1.0.2m.tar.gz

进入解压后的文件夹,开始配置、编译和安装:

[root@localhost soft]# cd openssl-1.0.2m/
[root@localhost openssl-1.0.2m]#./config --prefix=/usr/local/openssl shared

其中,--prefix表示安装到指定的目录中,这里的指定目录是/usr/local/openssl,这个目录不必手工预先建立,安装(make install)的过程会自动新建;shared表示除了生成静态库外,还要生成共享库,如果仅仅想生成静态库,可以不用这个选项,或者用no-shared。

下面开始编译:

[root@localhost openssl-1.0.2m]# make

此时,如果到/usr/local下查看,发现并没有openssl文件夹,这说明还没建立。而且/usr/include/openssl下的头文件依旧是老版本OpenSSL遗留下来的。

(3)安装OpenSSL

[root@localhost openssl-1.0.2m]# make install

细心的朋友可以看到,安装过程中有如图2-40所示的这几步。

图2-40

created directory表示目录创建完成,所以/usr/local/openssl建立了。

稍等片刻,安装完成。此时如果到/usr/local下查看,发现有openssl文件夹了,而且在该目录下可以看到其子文件夹,如图2-41所示。

图2-41

其中,bin里面存放OpenSSL命令程序,include存放开发所需要的头文件,lib存放静态库文件,ssl存放配置文件等。

(4)更新头文件包含的目录和命令程序

删除旧的头文件包含的目录/usr/include/openssl/:

[root@localhost include]# rm -rf /usr/include/openssl/openssl

再创建新的头文件包含目录的软链接:

ln -s /usr/local/openssl/include/openssl /usr/include/openssl

下面再创建可执行文件的软链接,这样就可以在命令行下使用openssl命令:

ln -s /usr/local/openssl/bin/openssl /usr/bin/openssl

这样执行/usr/bin下的openssl实际就是执行/usr/local/openssl/bin下的openssl程序。引用/usr/include/openssl下的头文件就是引用/usr/local/openssl/include/openssl下的头文件。此时,我们可以在任意目录下运行openssl命令。我们可以看一下现在openssl的版本号:

[root@localhost bin]# openssl version
openssl 1.0.2m  2 nov 2017

如果要以命令方式使用OpenSSL,可以在终端下输入openssl,然后就会出现OpenSSL提示,如图2-42所示。

图2-42

(5)更新共享库

删除旧的共享库/usr/lib64/libcrypto.so.1.0.1e:

rm -f /usr/lib64/libcrypto.so.1.0.1e

我们需要把libcrypto.so.1.0.0复制一份到/usr/lib64/:

[root@localhost ex]# cp /usr/local/openssl/lib/libcrypto.so.1.0.0 /usr/lib64

(6)更新符号链接

删除旧的符号链接才能再创建新的:

[root@localhost lib64]# rm -f /usr/lib64/libcrypto.so
[root@localhost lib64]# ln -s /usr/local/openssl/lib/libcrypto.so.1.0.0 /usr/lib64/libcrypto.so

(7)验证

我们对上例的test.cpp进行编译,然后运行:

[root@localhost ex]# g++ test.cpp -o test -lcrypto
[root@localhost ex]# ./test
Hello, OpenSSL!

一气呵成!而且此时连接的动态库是新的OpenSSL的动态库,不信可以用ldd命令查看一下:

我们可以看到粗体部分就是新的共享库。