Crypto++虽好,但功能不如OpenSSL。一线开发中,用得更多的是OpenSSL。虽然OpenSSL是用C语言写的,但在C++程序中使用完全没有问题。何况,OpenSSL很多地方利用了面向对象的设计方法与多态来支持多种加密算法。所以,学好OpenSSL,甚至分析其源码,对我们提高面向对象的设计能力大有帮助。很多著名的开源软件,比如内核XFRM框架、VPN软件StrongSwan等都是用C语言来实现面向对象设计的。因此,我们会对OpenSSL叙述的更为详细些,因为一线实践开发中,经常会碰到这个库的使用(很多C#开发的软件,底层的安全连接也会用VC封装OpenSSL为控件后供C#界面使用,更不要说Linux的一线开发了),希望大家能预先掌握好。
随着Internet的迅速发展和广泛应用,网络与信息安全的重要性和紧迫性日益突出。Netscape公司提出了安全套接层协议(Secure Socket Layer,SSL),该协议基于公开密钥技术,可保证两个实体间通信的保密性和可靠性,是目前Internet上保密通信的工业标准。
Eric A.Young和Tim J. Hudson自1995年开始编写后来具有巨大影响力的OpenSSL软件包,这是一个没有太多限制的开放源代码的软件包,可以利用这个软件包做很多事情。1998年,OpenSSL项目组接管了OpenSSL的开发工作,并推出了OpenSSL的0.9.1版,到目前为止,OpenSSL的算法已经非常完善,对SSL 2.0、SSL 3.0以及TLS 1.0都支持。OpenSSL目前新的版本是1.1.1版。
OpenSSL采用C语言作为开发语言,使得OpenSSL具有优秀的跨平台性能,可以在不同的平台使用。OpenSSL支持Linux、Windows、BSD、Mac等平台,OpenSSL具有广泛的适用性。OpenSSL实现了8种对称加密算法,如AES、DES、Blowfish、CAST、IDEA、RC2、RC4、RC5,实现了4种非对称加密算法,如DH、RSA、DSA和ECC,实现了5种信息摘要算法,如MD2、MD5、MDC2、SHA1和RIPEMD。此外,OpenSSL还实现了密钥和证书的管理。
OpenSSL的License(许可证)是SSLeay License和OpenSSL License的结合,这两种许可证实际上都是BSD类型的许可证,依照许可证里面的说明,OpenSSL可以被用作各种商业、非商业的用途,但是需要相应地遵守一些协定,其实这都是为了保护自由软件作者及其作品的权利。
OpenSSL整个软件包大概可以分成三个主要的功能部分:密码算法库、SSL协议库以及应用程序。OpenSSL的目录结构也是围绕这三个功能部分进行规划的,具体可见表2-1。
表2-1 OpenSSL的目录结构及功能
OpenSSL的算法目录Crypto目录包含OpenSSL密码算法库的所有源代码文件,是OpenSSL中重要的目录之一。OpenSSL的密码算法库包含OpenSSL中所有密码算法、密钥管理和证书管理相关标准的实现。
OpenSSL是全开放的和开放源代码的工具包,实现安全套接层协议(SSL v2/v3)和传输层安全协议(TLS v1)以及形成一个功能完整的、通用目的的加密库SSLeay。应用程序可以通过三种方式调用SSLeay,如图2-1所示。
图2-1
一是直接调用,二是通过OpenSSL加密库接口调用,三是通过Engine平台和OpenSSL对象调用。除了SSLeay外,用户还可以通过Engine安全平台访问CSP。
使用Engine技术的OpenSSL已经不仅仅是一个密码算法库,而是一个提供通用加解密接口的安全框架,在使用时只要加载了用户的Engine模块,应用程序中所调用的OpenSSL加解密函数就会自动调用用户自己开发的加解密函数来完成实际的加解密工作。这种方法将底层硬件的复杂多样性与上层应用分隔开,大大降低了应用开发的难度。
OpenSSL一共提供了8种对称加密算法,其中7种是分组加密算法,仅有一种流加密算法是RC4。这7种分组加密算法分别是AES、DES、Blowfish、CAST、IDEA、RC2、RC5,都支持电子密码本模式(ECB)、加密分组链接模式(CBC)、加密反馈模式(CFB)和输出反馈模式(OFB)4种常用的分组密码加密模式。其中,AES使用的加密反馈模式(CFB)和输出反馈模式(OFB)分组长度是128位,其他算法使用的则是64位。事实上,DES算法里面不仅仅是常用的DES算法,还支持三个密钥和两个密钥3DES算法。OpenSSL还使用EVP封装了所有的对称加密算法,使得各种对称加密算法能够使用统一的API接口EVP_Encrypt和EVP_Decrypt进行数据的加密和解密,大大提高了代码的可重用性能。
OpenSSL实现了4种非对称加密算法,包括DH算法、RSA算法、DSA算法和ECC算法。DH算法一般用于密钥交换。RSA算法和ECC算法既可以用于密钥交换,又可以用于数字签名,当然,如果你能够忍受其缓慢的速度,那么也可以用于数据加解密。DSA算法则一般只用于数字签名。
跟对称加密算法相似,OpenSSL也使用EVP技术对不同功能的非对称加密算法进行封装,提供了统一的API接口。若使用非对称加密算法进行密钥交换或者密钥加密,则使用EVPSeal和EVPOpen进行加密和解密;若使用非对称加密算法进行数字签名,则使用EVP_Sign和EVP_Verify进行签名和验证。
OpenSSL实现了5种信息摘要算法,分别是MD2、MD5、MDC2、SHA(SHA1)和RIPEMD。SHA算法事实上包括SHA和SHA1两种信息摘要算法,此外,OpenSSL还实现了DSS标准中规定的两种信息摘要算法:DSS和DSS1。
OpenSSL采用EVPDigest接口作为信息摘要算法统一的EVP接口,对所有信息摘要算法进行了封装,提供了代码的重用性。当然,跟对称加密算法和非对称加密算法不一样,信息摘要算法是不可逆的,不需要一个解密的逆函数。
OpenSSL实现了ASN.1的证书和密钥相关标准,提供了对证书、公钥、私钥、证书请求以及CRL等数据对象的DER、PEM和BASE64的编解码功能。OpenSSL提供了产生各种公开密钥对和对称密钥的方法、函数和应用程序,同时提供了对公钥和私钥的DER编解码功能,并实现了私钥的PKCS#12和PKCS#8的编解码功能。OpenSSL在标准中提供了对私钥的加密保护功能,使得密钥可以安全地进行存储和分发。
在此基础上,OpenSSL实现了对证书的X.509标准编解码、PKCS#12格式的编解码以及PKCS#7格式的编解码功能,并提供了一种文本数据库,支持证书的管理功能,包括证书密钥产生、请求产生、证书签发、吊销和验证等功能。
事实上,OpenSSL提供的CA应用程序就是一个小型的证书管理中心(CA),实现了证书签发的整个流程和证书管理的大部分机制。
OpenSSL支持常见的密码算法。OpenSSL成功地运用了面向对象的方法与技术,才使得它能支持众多算法并能实现SSL协议。OpenSSL的可贵之处在于它利用面向过程的C语言去实现面向对象的思想。
面向对象方法是一种运用对象、类、继承、封装、聚合、消息传递、多态性等概念来构造系统的软件开发方法。
面向对象方法与技术起源于面向对象的编程语言(Object-Oriented Programming Language,OOPL)。但是,面向对象不仅是一些具体的软件开发技术与策略,而且是一整套关于如何看待软件系统与现实世界的关系、以什么观点来研究问题并进行求解,以及如何进行系统构造的软件方法学。概括地说,面向对象方法的基本思想是,从现实世界中客观存在的事物(对象)出发来构造软件系统,并在系统构造中尽可能运用人类的自然思维方式。面向对象方法强调直接以问题域(现实世界)中的事物为中心来思考问题、认识问题,并根据这些事物的本质特征,把它们抽象地表示为系统中的对象,作为系统的基本构成单位。这可以使系统直接地映像问题域,保持问题域中的事物及其相互关系的本来面貌。
结构化方法采用了许多符合人类思维习惯的原则与策略(如自顶向下、逐步求精)。面向对象方法则更加强调运用人类在日常的逻辑思维中经常采用的思想方法与原则,例如抽象、分类、继承、聚合、封装等。这使得软件开发者能够更有效地思考问题,并以其他人也能看得懂的方式把自己的认识表达出来。具体地讲,面向对象方法有如下一些主要特点:
(1)从问题域中客观存在的事物出发来构造软件系统,用对象作为这些事物的抽象表示,并以此作为系统的基本构成单位。
(2)事物的静态特征(可以用一些数据来表达的特征)用对象的属性表示,事物的动态特征(事物的行为)用对象的服务表示。
(3)对象的属性与服务结合成一体,成为一个独立的实体,对外屏蔽其内部细节(称作封装)。
(4)对事物进行分类。把具有相同属性和相同服务的对象归为一类,类是这些对象的抽象描述,每个对象是它的类的一个实例。
(5)通过在不同程度上运用抽象的原则(较多或较少地忽略事物之间的差异),可以得到较一般的类和较特殊的类。子类继承超类的属性与服务,面向对象方法支持对这种继承关系的描述与实现,从而简化系统的构造过程及其文档。
(6)复杂的对象可以用简单的对象作为其构成部分(称作聚合)。
(7)对象之间通过消息进行通信,以实现对象之间的动态联系。
(8)通过关联表达对象之间的静态关系。
概括以上几点可以看到,在使用面向对象方法开发的系统中,以类的形式进行描述并通过对类的引用而创建的对象是系统的基本构成单位。这些对象对应着问题域中的各个事物,它们内部的属性与服务刻画了事物的静态特征和动态特征。对象类之间的继承关系、聚合关系、消息和关联,如实地表达了问题域中事物之间实际存在的各种关系。因此,无论是系统的构成成分,还是通过这些成分之间的关系而体现的系统结构,都可以直接地映像问题域。
面向对象方法代表了一种贴近自然的思维方式,它强调运用人类在日常的逻辑思维中经常采用的思想方法与原则。面向对象方法中的抽象、分类、继承、聚合、封装等思维方法和分析手段,能有效地反映客观世界中事物的特点和相互的关系。而面向对象方法中的继承、多态等特点可以提高过程模型的灵活性、可重用性。因此,应用面向对象的方法将降低工作流分析和建模的复杂性,并使工作流模型具有较好的灵活性,可以较好地反映客观事物。
在OpenSSL源代码中,将文件及网络操作封装成BIO。BIO几乎封装了除了证书处理外的OpenSSL所有的功能,包括加密库以及SSL/TLS协议。当然,它们都只是在OpenSSL其他功能之上封装搭建起来的,但却方便了不少。OpenSSL对各种加密算法封装,就可以使用相同的代码但采用不同的加密算法进行数据的加密和解密。
在OpenSSL源代码中,I/O操作主要有网络操作和磁盘操作。为了方便调用者实现其I/O操作,OpenSSL源代码中将所有的与I/O操作有关的函数进行统一封装,即无论是网络还是磁盘操作,其接口是一样的。对于函数调用者来说,以统一的接口函数去实现其真正的I/O操作。
为了达到此目的,OpenSSL采用BIO抽象接口。BIO是在底层覆盖了许多类型I/O接口细节的一种应用接口,如果在程序中使用BIO,就可以和SSL连接、非加密的网络连接以及文件I/O进行透明的连接。BIO接口的定义如下:
其中,BIO_METHOD结构体是各种函数的接口定义。如果是文件操作,此结构体如下:
以上定义了7个文件操作的接口函数的入口。这7个文件操作函数的具体实体与操作系统提供的API有关。BIO_METHOD结构体如果用于网络操作,其结构体如下:
它跟文件类型BIO在实现的动作上基本上是一样的。只不过是前缀名和类型字段的名称不一样。其实在像Linux这样的系统中,Socket类型跟fd类型是一样的,它们是可以通用的,但是,为什么要分开来实现呢?那是因为有些系统(如Windows系统)的Socket跟文件描述符是不一样的,所以,为了平台的兼容性,OpenSSL就将这两类分开来了。
EVP系列的函数定义包含在evp.h里面,这是一系列封装了OpenSSL加密库里面所有算法的函数。通过这样统一的封装,使得只需要在初始化参数的时候做很少的改变,就可以使用相同的代码但采用不同的加解密算法进行数据的加密和解密。
EVP系列函数主要封装了三大类型的算法,即公开密钥算法(也称非对称加密算法)、数字签名算法和对称加密算法(业内一般讲加密算法就是指加解密算法),要支持这些算法,需要调用OpenSSL_addall_algorithms函数。
函数名称:EVPSeal*...*、EVPOpen*...*。
功能描述:该系列函数封装提供了公开密钥算法的加密和解密功能,实现了电子信封的功能。
相关文件:p_seal、p_open.c。
函数名称:EVP_Sign*...*、EVP_Verify*...*。
功能描述:该系列函数封装提供了数字签名算法的功能。
相关文件:p_sign.c、p_verify.c。
函数名称:EVP_Encrypt*...*。
功能描述:该系列函数封装提供了对称加密算法的功能。
相关文件:evp_enc.c、p_enc.c、p_dec.c、e_*.c。
函数名称:EVPDigest*...*。
功能描述:该系列函数封装实现了多种信息摘要算法。
相关文件:digest.c、m_*.c。
函数名称:EVPEncode*...*。
功能描述:该系列函数封装实现了ASCII码与二进制码之间的转换函数的功能。
本书既要照顾老项目维护者,又要照顾新项目开发者。因此,笔者会选择目前新的版本和较老但使用较多且稳定的版本同时介绍,两个版本各有千秋,新版本会引进不少新技术和新算法,比如在新版本中加入了国密算法SM2/3/4,老版本主要用来兼容老项目,建议开发新项目还是用新版的OpenSSL。目前新的版本是OpenSSL 1.1.1b,它是在2019年2月26日发布的。我们选择的老版本是OpenSSL-1.0.2m。另外要注意的是,OpenSSL官方现在已停止对0.9.8和1.0.0两个版本的升级维护,所以大家选择的老版本也别太老了。
至于操作系统的选择,当前密码应用开发在Linux和Windows下都开展得如火如荼,因此也会介绍在这两个系统下的安装和使用。笔者选择的操作系统是Windows 7和CentOS 7。
OpenSSL是一个开源的第三方库,它实现了SSL(Secure Socket Layer,安全套接层)和TLS(Transport Layer Security,安全传输层)协议,被企业应用广泛采用。对于一般的开发人员而言,在Win32 OpenSSL上下载已经编译好的OpenSSL库是省力省事的好办法。对于高级的开发用户,可能需要适当地修改或者裁剪OpenSSL,那么编译它就成为一个关键问题。考虑到我们早晚要成为高级开发用户,所以掌握OpenSSL的编译是早晚的事。下面主要讲述如何在Windows上编译OpenSSL库。
前面讲了不少理论知识,虽然枯燥,但可以从宏观层面上对OpenSSL进行高屋建瓴地了解,这样以后走迷宫时不至于迷路。下面即将进入实战环节。废话不多说,打开官网下载源码。OpenSSL的官网地址是https://www.openssl.org。这里使用的是新版本OpenSSL 1.1.1,在学习的时候我们要勇于尝试新版本。另外要注意的是,OpenSSL官方现在已停止对0.9.8和1.0.0两个版本的升级维护,还在维护老代码的同志要注意了,升级是早晚的事情。这里会下载两个版本进行演示,为了照顾喜欢尝鲜的读者,先下载目前新的版本1.1.1,下载下来的压缩文件是openssl-1.1.1.tar.gz。后面会下载一个推荐用于实际开发的版本,即1.0.2m,下载下来的压缩文件是openssl-1.0.2m.tar.gz,不求最新,但求稳定,这是一线开发的原则。另外,本书也涉及一些CentOS 7下OpenSSL的使用,使用的版本是CentOS 7自带的,也是为了稳定。
因为编译OpenSSL源码的过程中会用到Perl解释器,所以在编译OpenSSL库之前,还需要下载一个Perl脚本解释器,这里选用大名鼎鼎的ActivePerl,我们可以从其官方网站(https://www.activestate.com/)下载。这里下载后的文件为ActivePerl-5.26.1.2601-MSWin32-x64-404865.exe。
下载完毕后,就可以开始安装了。直接双击即可开始安装,安装时间有点长,要有点耐心,最终会提示安装成功。安装完成的界面如图2-2所示。
图2-2
目前,OpenSSL 1.1.1b是新的版本,我们可以去官方网站下载,下载地址为https://www.openssl.org/source/。下载下来的文件名是openssl-1.1.1b.tar.gz。
下面开始我们的编译之旅。为什么要编译?因为编译后会生成库,这个库可以放到工程环境中使用。
在Windows平台上,用微软Visual Studio中的C编译器生成OpenSSL的静态库或动态库。我们要在VC命令行下编译,所以需要先安装VC,至少要VC 2008,本书采用的是VC 2017,建议大家使用这个版本,以后看书的过程中有问题方便联合作者一起排查。
安装完ActivePerl后,就可以正式编译安装OpenSSL了。我们下载下来的openssl-1.1.1.tar.gz是一个源码压缩包,需要对其进行解压,然后编译出开发所需要的静态库或动态库。
具体安装步骤如下:
(1)解压源码目录。把openssl-1.1.1b.tar.gz复制到某个目录下,比如D:,然后解压缩,解压后的目录为D:\openssl-1.1.1b,进入D:\openssl-1.1.1b,就可以看到各个子文件夹了。
(2)配置OpenSSL。打开VC 2017的开发者命令行提示窗口,单击“开始”→“Visual Studio 2017”→“Visual Studio Tools”→“VS 2017的开发人员命令提示符”,即可出现如图2-3所示的窗口。
图2-3
然后在图2-3所示的命令行窗口中输入cd d:\openssl-1.1.1b,然后输入命令:
perl Configure debug-VC-WIN32 no-shared no-asm --prefix="d:/openssl-1.1.1b/win32-debug" --openssldir="d:/openssl-1.1.1b/win32-debug/ssl"
其中,debug-VC-WIN32表示32位调试模式,no-asm表示不用汇编。
接着按回车键,然后开始自动配置,如图2-4所示。
图2-4
其中,VC-WIN32表示我们要编译出32位的版本,若要编译出Debug版本,则使用debug-VC-WIN32,若要使用Release版本的OpenSSL库,则不要加debug-。no-shared表示我们要编译出静态库,若要编译出动态库,则不要加no,用-shared即可,这个很好理解,静态库是不共享的,动态库是用来共享的。参数--prefix是OpenSSL编译完后所生成的命令程序、库、头文件等的存放路径。--openssldir是OpenSSL编译完后生成的配置文件的存放路径。
(3)编译OpenSSL。配置完成后,我们可以继续用nmake命令开始编译,在命令行窗口中输入nmake后按回车键即可开始编译,nmake程序是VC自带的命令行编译工具。这一步时间稍长,大家可以喝杯茶。编译成功后如图2-5所示。
图2-5
(4)测试编译。这一步不是必需的,但最好检查一下上一步的编译是否正确。在命令行窗口中输入命令:
nmake test
这一步时间也稍长,其实可以不用做,但笔者做了,测试全部成功,如图2-6所示。
图2-6
(5)安装。继续输入命令nmake install,执行成功后如图2-7所示。
图2-7
(6)清理。主要是删除一些中间文件。继续输入命令nmake clean。
此时,进入D:\openssl-1.1.1b\win32-debug,可以看到一些子文件夹,如图2-8所示。
图2-8
其中,bin目录下存放OpenSSL的命令行程序,利用该程序我们可以在命令行下执行一些加解密任务、证书操作任务。在lib目录下存放的就是我们编译出来的32位的静态库libcrypto.lib和libssl.lib,一般的加解密程序使用libcrypto.lib即可。include目录就是我们开发所需要的OpenSSL头文件。
现在,趁热打铁,马上来开发一个使用OpenSSL静态库的程序,以此检验我们生成的静态库是否正确。
【例2.1】使用32位OpenSSL 1.1.1的Debug静态库
(1)打开VC 2017,新建一个控制面板程序test。
(2)在test.cpp中输入代码如下:
(3)包含include目录。打开“test属性页”对话框,在界面左边选择“C/C++”→“常规”,在右边的“附加包含目录”旁输入OpenSSL头文件所在的路径D:\openssl-1.1.1b\win32-debug\include,如图2-9所示。
图2-9
然后单击“确定”按钮。这样我们的程序中包含openssl/evp.h的时候就不会出错了,因为include目录下有OpenSSL子目录。
顺便说一句,其实把include文件夹复制到自己的工程目录下也可以,但考虑到很多程序都要用到头文件,所以没必要每个工程都去复制一份include文件夹,建议放在一个公共路径下(比如D:\openssl-1.1.1b\win64-debug\include),各个开发者自己包含这个公共路径即可。
(4)添加静态库。在“test属性页”对话框中,在界面左边选择“链接器”→“常规”,在右边的“附加库目录”旁输入OpenSSL静态库所在的路径D:\openssl-1.1.1b\win32-debug\lib,如图2-10所示。
图2-10
然后单击“应用”按钮。接着展开界面左边的“链接器”→“输入”,在右边第一行“附加依赖项”右边的开头输入ws2_32.lib;Crypt32.lib;libcrypto.lib;,其中ws2_32.lib和Crypt32.lib是VC自带的库,分别实现网络功能和微软提供的加解密功能,加入这两个库的原因是libcrypto.lib依赖于它们。最后单击“确定”按钮。
(5)保存工程并运行,运行结果如图2-11所示。
图2-11
至此,说明32位的Debug版本的OpenSSL 1.1.1b静态库使用起来了。
(1)解压源码目录(若已经存在源码目录openssl-1.1.1b,则可以不必再解压)。把openssl-1.1.1b.tar.gz复制到某个目录下,比如D:,然后解压缩,解压后的目录为D:\openssl-1.1.1b,进入D:\openssl-1.1.1b,就可以看到各个子文件夹了。
(2)配置OpenSSL。打开VC 2017的开发者命令行提示窗口,单击“开始”→“Visual Studio 2017”→“Visual Studio Tools”→“VS 2017的开发人员命令提示符”,输入命令cd d:\openssl-1.1.1b,然后输入Perl命令如下:
perl Configure VC-WIN32 no-shared no-asm --prefix="d:/openssl-1.1.1b/win32-release" --openssldir="d:/openssl-1.1.1b/win32-release/ssl"
其实就是把no-shard改为shared,后面的步骤都一样,分别是:
nmake nmake test nmake install nmake clean
完毕后,我们到D:\openssl-1.1.1b\win32-release下去看,可以看到生成的各个子目录。
【例2.2】测试32位Release版本的库
(1)把例子2.1的工程复制一份,然后使用VC 2017打开工程。
(2)在工具栏选择解决方案配置为Release,如图2-12所示。
图2-12
(3)打开“test属性页”对话框,确保界面左上角的“配置”是Release,意思是我们要生成的程序是Release版本的程序,即程序中不带调试信息了。然后在界面左边选择“C/C++”→“常规”,在右边添加附加包含目录为D:\openssl-1.1.1b\win32-release\include。
(4)添加静态库。在“test属性页”对话框中,在界面左边选择“链接器”→“常规”,在右边的“附加库目录”旁输入OpenSSL静态库所在路径D:\openssl-1.1.1b\win32-release\lib,然后单击“应用”按钮。接着展开左边的“链接器”→“输入”,在右边第一行“附加依赖项”右边的开头输入ws2_32.lib;Crypt32.lib;libcrypto.lib;,其中ws2_32.lib和Crypt32.lib是VC自带的库,分别实现网络功能和微软提供的加解密功能,加入这两个库的原因是libcrypto.lib依赖于它们。最后单击“确定”按钮。
(5)保存工程并运行,运行结果如图2-13所示。
图2-13
至此,说明32位的Release版本的静态库使用起来了。
(1)解压源码目录(如果前面解压过了,就不必再解压)。把openssl-1.1.1b.tar.gz复制到某个目录下,比如D:,然后解压缩,解压后的目录为D:\openssl-1.1.1b,进入D:\openssl-1.1.1b,就可以看到各个子文件夹了。
(2)配置OpenSSL。打开VC 2017的开发者命令行提示窗口,单击“开始”→“Visual Studio 2017”→“Visual Studio Tools”→“VS 2017的开发人员命令提示符”,输入命令cd d:\openssl-1.1.1b,然后输入Perl命令如下:
perl Configure debug-VC-WIN32 shared no-asm --prefix="d:/openssl-1.1.1b/win32-shared-debug" --openssldir="d:/openssl-1.1.1b/win32-shared-debug/ssl"
后面的步骤都一样,分别是:
nmake nmake test nmake install nmake clean
完毕后,我们到D:\openssl-1.1.1b\win32-shared-debug下查看,可以看到生成的各个子目录。
【例2.3】使用32位的Debug版本的动态库
(1)打开VC 2017,新建一个控制面板程序test。
(2)在test.cpp中输入代码如下:
(3)包含include目录。打开“test属性页”对话框,在界面左边选择“C/C++”→“常规”,在右边的“附加包含目录”旁输入OpenSSL头文件所在的路径D:\openssl-1.1.1b\win32-shared-debug\include。
(4)添加链接符号库。在“test属性页”对话框中,在界面左边选择“链接器”→“常规”,在右边的“附加库目录”旁输入OpenSSL引用库(注意虽然名字和静态库一样,但动态库中叫引用库,用于编译时的符号引用)所在的路径D:\openssl-1.1.1b\win32-shared-debug\lib,然后单击“应用”按钮。接着展开界面左边的“链接器”→“输入”,在右边第一行“附加依赖项”右边的开头输入ws2_32.lib;Crypt32.lib;libcrypto.lib;,其中ws2_32.lib和Crypt32.lib是VC自带的库,分别实现网络功能和微软提供的加解密功能,加入这两个库的原因是libcrypto.lib依赖于它们。最后单击“确定”按钮。
(5)把D:\openssl-1.1.1b\win32-shared-debug\bin下的libcrypto-1_1.dll复制到我们的解决方案的debug目录下,即和test.exe同一个目录下。
(6)保存工程并运行,运行结果如图2-14所示。
图2-14
(1)解压源码目录。把openssl-1.1.1b.tar.gz复制到某个目录下,比如D:,然后解压缩,解压后的目录为D:\openssl-1.1.1b,进入D:\openssl-1.1.1b,就可以看到各个子文件夹了。
(2)配置OpenSSL。打开VC 2017的开发者命令行提示窗口,单击“开始”→“Visual Studio 2017”→“Visual Studio Tools”→“VS 2017开发人员命令提示符”,输入命令cd d:\openssl-1.1.1b,然后输入Perl命令如下:
perl Configure VC-WIN32 shared no-asm --prefix="d:/openssl-1.1.1b/win32-shared-release" --openssldir="d:/openssl-1.1.1b/win32-shared-release/ssl"
其实就是把no-shard改为shared,后面的步骤都一样,分别是:
nmake nmake test nmake install nmake clean
完毕后,我们到D:\openssl-1.1.1b\win32-shared-release下去看,可以看到生成的各个子目录。
【例2.4】使用32位的release版本的动态库
(1)打开VC 2017,新建一个控制面板程序test。
(2)在test.cpp中输入代码如下:
(3)将界面左上角工具栏上的选择解决方案配置为Release,意思是我们要生成的程序是Release版本的程序,即程序中不带调试信息了。在界面左边选择“C/C++”→“常规”,在右边的“附加包含目录”旁输入OpenSSL头文件所在的路径D:\openssl-1.1.1b\win32-shared-release\include。
(4)添加链接符号库。在“test属性页”对话框中,在界面左边选择“链接器”→“常规”,在右边的“附加库目录”旁输入OpenSSL引用库所在的路径D:\openssl-1.1.1b\win32-shared-release\lib,然后单击“应用”按钮。接着展开左边的“链接器”→“输入”,在右边第一行“附加依赖项”右边的开头输入ws2_32.lib;Crypt32.lib;libcrypto.lib;,其中ws2_32.lib和Crypt32.lib是VC自带的库,分别实现网络功能和微软提供的加解密功能,加入这两个库的原因是libcrypto.lib依赖于它们。最后单击“确定”按钮。
(5)把D:\openssl-1.1.1b\win32-shared-release\bin下的libcrypto-1_1.dll复制到解决方案的release目录下,即和test.exe同一个目录下。
(6)在工具栏切换解决方案配置为Release,保存工程并运行,运行结果如图2-15所示。
图2-15
(1)解压源码目录。把openssl-1.1.1b.tar.gz复制到某个目录下,比如D:,然后解压缩,解压后的目录为D:\openssl-1.1.1b,进入D:\openssl-1.1.1b,就可以看到各个子文件夹了。
(2)配置OpenSSL。打开VC 2017的开发者命令行提示窗口,单击“开始”→“Visual Studio 2017”→“Visual Studio Tools”→“VS 2017的开发人员命令提示符”,输入命令cd d:\openssl-1.1.1b,然后输入Perl命令如下:
perl Configure debug-VC-WIN64A no-shared no-asm --prefix="d:/openssl-1.1.1b/win64-debug" --openssldir="d:/openssl-1.1.1b/win64-debug/ssl"
debug-VC-WIN64A表示64位调试模式。
后面的步骤都一样,分别是:
nmake nmake test nmake install nmake clean
完毕后,我们到D:\openssl-1.1.1b\win64-debug下去看,可以看到生成的各个子目录,如果我们进入lib子目录下去看,可以发现libcrypto.lib的文件尺寸比32位的版本大了2MB多,如图2-16所示。
图2-16
【例2.5】验证64位Debug版本的OpenSSL静态库
(1)打开VC 2017,新建一个控制面板程序test。
(2)在test.cpp中输入代码如下:
(3)在工具栏上的解决方案平台选择“x64”,意思是我们要生成的程序是64位的程序,如图2-17所示。
图2-17
打开“test属性页”对话框,在左边选择“C/C++”→“常规”,在右边的“附加包含目录”旁输入OpenSSL头文件所在的路径D:\openssl-1.1.1b\win64-debug\include。
(4)添加链接符号库。在“test属性页”对话框中,在左边选择“链接器”→“常规”,在右边的“附加库目录”旁输入OpenSSL静态库所在的路径D:\openssl-1.1.1b\win64-debug\lib,然后单击“应用”按钮。接着展开左边的“链接器”→“输入”,在右边第一行“附加依赖项”右边的开头输入ws2_32.lib;Crypt32.lib;libcrypto.lib;,其中ws2_32.lib和Crypt32.lib是VC自带的库,分别实现网络功能和微软提供的加解密功能,加入这两个库的原因是libcrypto.lib依赖于它们。最后单击“确定”按钮。
(5)保存工程并运行,运行结果如图2-18所示。
图2-18
(1)解压源码目录。把openssl-1.1.1b.tar.gz复制到某个目录下,比如D:,然后解压缩,解压后的目录为D:\openssl-1.1.1b,进入D:\openssl-1.1.1b,就可以看到各个子文件夹了。
(2)配置OpenSSL。打开VC 2017的开发者命令行提示窗口,单击“开始”→“Visual Studio 2017”→“Visual Studio Tools”→“VS 2017的开发人员命令提示符”,输入命令cd d:\openssl-1.1.1b,然后输入Perl命令如下:
perl Configure VC-WIN64A no-shared no-asm --prefix="d:/openssl-1.1.1b/win64-release" --openssldir="d:/openssl-1.1.1b/win64-release/ssl"
后面的步骤都一样,分别是:
nmake nmake test nmake install nmake clean
完毕后,我们到D:\openssl-1.1.1b\win64-release下去看,可以看到生成的各个子目录。
【例2.6】验证64位Release版本的OpenSSL静态库
(1)打开VC 2017,新建一个控制面板程序test。
(2)在test.cpp中输入代码如下:
#pragma comment表示以代码方式引用库。这样就不用在工程属性中设置了。
(3)打开“test属性页”对话框,新建一个x64平台,在工程属性中切换到Release模式,然后添加头文件包含路径:D:\openssl-1.1.1b\win64-release\include,以及静态库路径:D:\openssl-1.1.1b\win64-release\lib。这里讲的简略了,路径具体在哪个位置添加,前面已经介绍过了。
(4)保存工程,然后在工具栏上选择解决方案平台为x64,解决方案配置为Release,然后运行工程,运行结果如图2-19所示。
图2-19
(1)解压源码目录。把openssl-1.1.1b.tar.gz复制到某个目录下,比如D:,然后解压缩,解压后的目录为D:\openssl-1.1.1b,进入D:\openssl-1.1.1b,就可以看到各个子文件夹了。
(2)配置OpenSSL。打开VC 2017的开发者命令行提示窗口,单击“开始”→“Visual Studio 2017”→“Visual Studio Tools”→“VS 2017的开发人员命令提示符”(注意是x64本机工具命令提示,不要选择其他的),输入命令cd d:\openssl-1.1.1b,然后输入Perl命令如下:
perl Configure debug-VC-WIN64A shared no-asm --prefix="d:/openssl-1.1.1b/win64-shared-debug" --openssldir="d:/openssl-1.1.1b/win64-shared-debug/ssl"
后面的步骤都一样,分别是:
nmake nmake test nmake install nmake clean
完毕后,我们到D:\openssl-1.1.1b\win64-shared-debug下去看,可以看到生成的各个子目录。
【例2.7】验证64位Debug版本的OpenSSL动态库
(1)打开VC 2017,新建一个控制面板程序test。
(2)在test.cpp中输入代码如下:
#pragma comment表示以代码方式引用符号库。这样就不用在工程属性中设置了。
(3)打开“test属性页”对话框,新建一个x64平台,然后添加头文件包含路径:D:\openssl-1.1.1b\win64-shared-debug\include,以及符号库路径:D:\openssl-1.1.1b\win64-shared-debug\lib。这里讲的简略了,路径具体在哪个位置添加,前面已经介绍过了。
把D:\openssl-1.1.1b\win64-shared-debug\bin下的libcrypto-1_1-x64.dll复制到解决方案路径下的x64文件夹下的Debug子目录下。
(4)保存工程,然后在工具栏上选择解决方案平台为x64,然后运行工程,运行结果如图2-20所示。
图2-20
(1)解压源码目录。把openssl-1.1.1b.tar.gz复制到某个目录下,比如D:,然后解压缩,解压后的目录为D:\openssl-1.1.1b,进入D:\openssl-1.1.1b,就可以看到各个子文件夹了。
(2)配置OpenSSL。打开VC 2017的开发者命令行提示窗口,单击“开始”→“Visual Studio 2017”→“Visual Studio Tools”→“VS 2017的开发人员命令提示符”(注意是x64本机工具命令提示,不要选择其他的),输入命令cd d:\openssl-1.1.1b,然后输入Perl命令如下:
perl Configure VC-WIN64A shared no-asm --prefix="d:/openssl-1.1.1b/win64-shared-release" --openssldir="d:/openssl-1.1.1b/win64-shared-release/ssl"
后面的步骤都一样,分别是:
nmake nmake test nmake install nmake clean
完毕后,我们到D:\openssl-1.1.1b\win64-shared-release下去看,可以看到生成的各个子目录。
【例2.8】验证64位Release版本的OpenSSL动态库
(1)打开VC 2017,新建一个控制面板程序test。
(2)在test.cpp中输入代码如下:
#pragma comment表示以代码方式引用符号库。这样就不用在工程属性中设置了。
(3)打开“test属性页”对话框,新建一个x64平台,在工程属性中切换到Release模式,然后添加头文件包含路径:D:\openssl-1.1.1b\win64-shared-release\include,以及静态库路径:D:\openssl-1.1.1b\win64-shared-release\lib。这里讲的简略了,路径具体在哪个位置添加,前面已经介绍过了。
(4)保存工程,然后在工具栏上选择解决方案平台为x64,解决方案配置为Release,并把D:\openssl-1.1.1b\win64-shared-release\bin下的libcrypto-1_1-x64.dll复制到解决方案的x64文件夹下的Release文件夹下,然后运行工程,运行结果如图2-21所示。
图2-21
以上我们对新版本的OpenSSL的各种库都进行了编译和测试,虽然略显烦琐,但也是必要的,尤其是在实际项目中使用之前,建议大家都测试一下,库好才用。
这个1.0.2版本属于当前主流使用的版本,无论是维护老项目,还是开发新项目,这个版本都用得比较多,因为其成熟、稳定。尤其对于信息安全相关的项目,建议大家不要直接使用很新的算法库,因为可能有潜在的Bug没有被发现。该版本下载地址:https://www.openssl.org/source/old/。
这里我们下载openssl-1.0.2m.tar.gz,把它复制到C:(也可以是其他目录),然后按照下面的步骤开始编译和安装。这里我们编译32位的Debug版本的动态库。
(1)安装ActivePerl。这个软件我们前面已经介绍过了,这里不再赘述。
(2)安装NASM。可以到https://www.nasm.us/下载新版安装包,这里下载的是nasm-2.14-installer-x64.exe,下载下来后直接双击安装。安装完毕后,要在系统变量Path中配置NASM程序所在路径,这里采用默认安装路径,所以NASM的路径是:C:\Program Files\NASM,把它添加到Path系统变量中,如图2-22所示。
图2-22
单击“确定”按钮。然后打开一个命令行窗口,输入命令nasm,此时界面显示如图2-23所示。
图2-23
这说明NASM安装并配置成功了。好了,准备工作完成,可以正式开始编译OpenSSL了。为了让大家知道不指定目录OpenSSL会把生成的文件放在哪里,我们先在不指定路径的情况下进行编译。
(1)解压OpenSSL源码目录。把openssl-1.0.2m.tar.gz复制到某个目录下,比如C:,然后解压缩,解压后的目录为C:\openssl-1.0.2m,进入C:\openssl-1.0.2m,就可以看到各个子文件夹了。
(2)配置OpenSSL。打开VC 2017的“VS 2017的开发人员命令提示符”提示窗口,单击“开始”→“所有程序”→“Visual Studio 2017”→“Visual Studio Tools”→“VS 2017的开发人员命令提示符”,输入命令如下:
cd C:\openssl-1.0.2m perl Configure VC-WIN32 ms\do_nasm nmake -f ms\ntdll.mak
VC-WIN32表示生成Release版本的32位的库,如果需要Debug版本,就使用debug-VC-WIN32。ntdll.mak表示即将生成动态链接库。执行完毕后,我们可以在C:\openssl-1.0.2m\out32dll\下看到生成的动态链接库,比如libeay32.dll,如图2-24所示。
图2-24
该文件夹除了包括动态库外,相关的导入库文件(比如libeay32.lib和ssleay32.lib)和一些可执行的工具(.exe)程序也在该目录下。导入库文件在开发中需要引用,所以我们需要知道它的路径。
头文件文件夹所在的路径是C:\openssl-1.0.2m\inc32\,开发的时候我们可以把inc32下的OpenSSL文件夹复制到工程目录,再在VC工程设置中添加引用,就可以使用头文件了。当然,不复制到工程目录也可以,只要在VC中引用到这里的路径即可。稍后我们会通过实例来演示如何使用这里编译出来的动态库。如果大家觉得在OpenSSL目录下去找这个子目录很麻烦,也可以执行安装命令:nmake -f ms\ntdll.mak install,执行该命令后,将会把include文件夹、lib文件夹、和bin文件夹复制到在C:\usr\local\ssl下,有兴趣的读者可以试试。
重要提示:如果编译过程中出错,建议把C:\openssl-1.0.2m这个文件夹删除,然后重新解压,再按上面的步骤执行。
好了,下面我们进行指定生成目录下的编译。一般这种方式用得多,这样可以和OpenSSL源码目录分离开来。
(1)如果C盘已经有openssl-1.0.2m文件夹,就解压OpenSSL源码目录。把openssl-1.0.2m.tar.gz复制到C:,然后解压缩,解压后的目录为C:\openssl-1.0.2m,进入C:\openssl-1.0.2m,就可以看到各个子文件夹了。如果C盘已经有openssl-1.0.2m文件夹,可以不用再解压。
(2)配置OpenSSL。打开VC 2017的“VS 2017的开发人员命令提示符”提示窗口,单击“开始”→“所有程序”→“Visual Studio 2017”→“Visual Studio Tools”→“VS 2017的开发人员命令提示符”,输入命令如下:
cd C:\openssl-1.0.2m perl Configure VC-WIN32 --prefix=c:/myOpensllout ms\do_nasm nmake -f ms\ntdll.mak
--prefix用于指定安装目录,就是生成的文件存放的目录;VC-WIN32表示生成Release版本的32位的库,如果需要Debug版本,就使用debug-VC-WIN32。稍等片刻,编译完成,如图2-25所示。
图2-25
此时可以看到C盘下并没有myOpensslout,因为我们还没有执行安装命令,但可以在C:\openssl-1.0.2m\out32dll\下看到生成的动态链接库,比如libeay32.dll。头文件文件夹OpenSSL所在的路径为C:\openssl-1.0.2m\inc32\。下面执行安装命令:
nmake -f ms\ntdll.mak install
执行完毕后,我们看到C盘下有myOpensslout了,如图2-26所示。
图2-26
如果喜欢干净,可以用nmake -f ms\ntdll.mak clean命令清理一下。
至此,32位动态库编译安装完成。下面进入验证环节。
【例2.9】验证32位动态库
(1)新建一个控制面板工程test。
(2)打开test.cpp,输入代码如下:
(3)打开工程属性对话框,然后添加头文件包含路径:C:\myopensslout\include,以及导入库路径:C:\myopensslout\lib。如果此时运行程序,系统干净的朋友是无法运行的,会提示缺少动态库,如图2-27所示。
图2-27
但有些朋友发现可以直接运行,难道上面生成的lib文件是静态库,而不是导入库。其实是导入库,我们可以验证一下。打开VC 2017的“VS 2017的开发人员命令提示符”提示窗口,即单击“开始”→“所有程序”→“Visual Studio 2017”→“Visual Studio Tools”→“VS 2017的开发人员命令提示符”,输入命令如下:
lib /list C:\myopensslout\lib\libeay32.lib
如果输出的是LIBEAY32.dll,就说明libeay32.lib是一个导入库,如果输出的是.obj,就说明是静态库。既然不是静态库,为何能运行起来呢?说明系统路径肯定存在libeay32.dll。大家可以去C:\Windows\SysWOW64或C:\Windows\System32等常见系统路径下搜索,如果删掉或重命名后还能运行test.exe,就说明安装了某些软件导致test.exe依然能找到libeay32.dll,比如安装了ice3.7.2这个通信库。或许有朋友到这里有点怀疑test.exe是否真的依赖libeay32.dll,大家可以验证一下,如果有Dependency Walker工具就查看一下依赖项,如图2-28所示。
图2-28
如果没有Dependency Walker工具,也可以使用VC 2017的dumpbin程序,把test.exe复制到C盘下,然后VC 2017的“VS 2017的开发人员命令提示符”提示窗口,然后输入命令dumpbin /dependents c:\test.exe,如图2-29所示。
图2-29
可以看到的确依赖libeay32.dll。另外,可以发现把test.exe放到一个干净的操作系统上,就运行不起来了。如果一定要知道libeay32.dll在哪里,也不是没有可能。大招就是使用Dependency Walker,这个依赖项查看工具自VC 6开始就自带了,后来高版本的VC虽然不带它了,但可以从官网(www.dependencywalker.com)上下载。这里还是使用VC 6自带的1.0版。我们把test.exe拖进Dependency walker工具,然后单击工具栏上的c:\按钮,它用于显示全路径,如图2-30所示。
图2-30
可以看到,test.exe依赖的libeay32.dll位于system32下,终于找到元凶了,把它删除再运行test.exe会发现无法运行。
绕了一大圈,让系统干净的朋友久等了,继续把C:\myopensslout\bin下的libeay32.dll复制到解决方案路径下的Debug子目录下,即和test.exe同一文件夹下。
(4)保存工程并运行,运行结果如图2-31所示。
图2-31
这里测试工程test用了Debug模式,而库libeay32.dll是Release版本的,这是没问题的。
首先把C盘下的openssl-1.0.2m文件夹删除(如果有的话),然后按照下面的步骤进行:
(1)解压OpenSSL源码目录。把openssl-1.0.2m.tar.gz复制到某个目录下,比如C:,然后解压缩,解压后的目录为C:\openssl-1.0.2m,进入C:\openssl-1.0.2m,就可以看到各个子文件夹了。
(2)配置OpenSSL。单击“开始”→“Visual Studio 2017”→“Visual Studio Tools”→“VS 2017的开发人员命令提示符”,打开VC 2017的“VS 2017的开发人员命令提示符”窗口,输入命令如下:
cd C:\openssl-1.0.2m perl Configure VC-WIN64A no-asm --prefix=c:/myopensslstout64 ms\do_win64A nmake -f ms\ntdll.mak
--prefix用于指定安装目录,就是生成的文件存放的目录。VC-WIN64A表示生成Release版本的64位的库,如果需要Debug版本,就使用debug-VC-WIN64A。稍等片刻,编译完成,如图2-32所示。
图2-32
此时我们看到C盘下并没有文件夹myopensslout64,这是因为还没有执行安装命令,但可以在C:\openssl-1.0.2m\out32dll\下看到生成的动态链接库,比如libeay32.dll。头文件文件夹OpenSSL所在的路径为C:\openssl-1.0.2m\inc32\,有些多疑的读者可能会疑惑,为何64位的.dll文件会生成在名字是out32dll的文件夹下,看名字out32dll像是存放32位的库。笔者认为这是OpenSSL官方偷懒的地方,这样的文件夹名字的确容易引起歧义,为了消除读者的疑惑,我们可以验证一下生成的libeay32.dll到底是32位还是64位的,方法有多种:
(1)在“VS 2017的开发人员命令提示符”窗口的提示符下输入命令:
dumpbin /headers c:\openssl-1.0.2m\out32dll\libeay32.dll
如果出现machine(x64)字样,就说明该库是64位库,如图2-33所示。
图2-33
(2)如果安装的是VC 6,可以用VC 6自带的Dependency Walker工具来查看,因为VC 6自带的该工具(版本是1.0)只能查看32位的动态库,所以64位的库拖进去是看不到信息的,如图2-34所示。
图2-34
当然,现在高版本的Dependency Walker工具已经可以同时查看32位和64位的库了。不过,VC 2017不自带这个小工具,如果读者需要的话可以去官网(http://www.dependencywalker.com/)下载。
只有执行了安装命令才会把生成的库、头文件等放到我们指定的目录myOpenSSLout64下。下面执行安装命令:
nmake -f ms\ntdll.mak install
执行完毕后,如图2-35所示。
图2-35
仔细看图2-35,我们发现其实就是把out32dll下的内容复制到C:/myopensslout64下。此时我们看到C盘下有myopensslout64了。至此,64位的动态库编译安装完成。下面进入验证阶段。
【例2.10】验证64位动态库
(1)新建一个控制面板工程test。
(2)打开test.cpp,输入代码如下:
(3)打开工程属性对话框,新建一个x64平台,在工程属性中切换到Release模式,然后添加头文件包含路径:C:\myopensslout64\include,以及导入库路径:C:\myopensslout64\lib。
(4)保存工程,然后在工具栏上选择解决方案平台为x64,解决方案配置为Release,并把C:\myopensslout64\bin下的libeay32.dll复制到解决方案的x64文件夹下的Release文件夹下,然后运行工程,运行结果如图2-36所示。
图2-36
本来编译完动态库想结束本节的讲解,但考虑有朋友喜欢静态库,所以笔者再演示一下静态库的编译过程。首先把C盘下的openssl-1.0.2m文件夹删除(如果有的话),然后按下面的步骤进行:
(1)解压OpenSSL源码目录。把openssl-1.0.2m.tar.gz复制到某个目录下,比如C:,然后解压缩,解压后的目录为C:\openssl-1.0.2m,进入C:\openssl-1.0.2m,就可以看到各个子文件夹了。
(2)配置OpenSSL。单击“开始”→“所有程序”→“Visual Studio 2017”→“Visual Studio Tools”→“VS 2017的开发人员命令提示符”,打开VC 2017的“VS 2017的开发人员命令提示符”窗口,输入命令如下:
cd C:\openssl-1.0.2m perl Configure debug-VC-WIN64A --prefix=c:/myopensslout64 ms\do_win64A nmake -f ms\ntdll.mak
--prefix用于指定安装目录,就是生成的文件存放的目录。VC-WIN64A表示生成Release版本的64位的库,如果需要Debug版本,就使用debug-VC-WIN64A。稍等片刻,编译完成。
打开官网下载源码。OpenSSL的官网地址是https://www.openssl.org。这里使用的版本是1.0.2m,不求最新,但求稳定,这是一线开发者的原则。另外要注意的是,OpenSSL官方现在已停止对0.9.8和1.0.0两个版本的升级维护。这里下载下来的是一个压缩文件:openssl-1.0.2m.tar。
刚下载下来不能马上安装,先要看看现在的操作系统是否已经安装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也是可以的。这里主要是为了让大家学会卸载和重新安装。
假设旧版本已经卸载。把下载下来的压缩文件放到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。
前面因为要讲不少原理,所以比较啰唆,这里将进行简化,直接用步骤阐述。
(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命令查看一下:
我们可以看到粗体部分就是新的共享库。
OpenSSL的命令行程序为openssl.exe。本节的命令用32位的1.1.1b版本的openssl.exe来阐述。其他版本的openssl.exe的用法类似。openssl命令程序位于apps目录下,编译这些源码最终会生成一个可执行程序,在Linux下为opessl,在Windows下为openssl.exe,生成的openssl.exe位于D:\openssl-1.1.1b\win32-debug\bin。用户可运行openssl命令来进行各种操作。
打开操作系统的命令行窗口,然后进入D:\openssl-1.1.1b\win32-debug\bin\,输入openssl.exe,按回车键运行。虽然也可以在Windows资源管理器中双击openssl.exe,但此时出现的OpenSSL命令行窗口中居然不能粘贴,这对于懒惰的“码农”来说是不可接受的。但很幸运,可以从操作系统的命令行窗口中启动openssl.exe。
在OpenSSL命令行提示符后输入version可以查看版本号,如图2-43所示。
图2-43
这是我们学到的OpenSSL的第一个命令。如果要查看详细的版本信息,可以加-a,如图2-44所示。
图2-44
定位到bin文件夹路径,然后输入命令:openssl enc –ciphers,如图2-45所示。
图2-45
支持好多算法,最激动的是支持我们国产算法了,比如SM4。我们可以往下拖曳滚动条,可以看到SM4了,如图2-46所示。
图2-46
查看某个命令的帮助信息使用命令-help。比如我们要查看version命令的帮助信息,如图2-47所示。
图2-47
通过几个简单命令的使用,我们知道安装成功了。