因为项目的原因,最近一直在跟IPsec和SSL/TLS打交道。公司的产品是基于Openssl开源代码库来实现的,加之我一直对这一块比较感兴趣,所以趁着这个机会决心好好了解一下Openssl和基于Openssl的编程。Openssl是一个很好的开源代码项目,但是它最大的缺点在于文档不完善。虽然国外有一些专门介绍openssl编程的书籍,但是几乎没有国内的出版社加以翻译引进,所以对于中国的开发者而言了解openssl是一项颇为头疼的工作。我想知识是用于分享的,趁着自己尚有热情,利用博客的形式来给大家分享一下我自己学习途中所遇到的困难和收获。如能乐己而助人,不亦乐乎!本着分享的原则,如有转载,请注明出处!
本篇先从Openssl的安装开始,逐步了解如何生成一个自签名的CA,并用这个CA来签发两个简单证书,从而对openssl有一个初步的了解。在后继的博文中,我将逐步展开对SSL/TLS协议的讨论和探索利用openssl库来编写简单的ssl程序。
因为我几乎不与windows编程打交道,所以以下都是以linux(Ubuntu或者Fedora)作为编程环境。但是大同小异,触类旁通,相信了解了linux下的openssl,你也可以很容易的转移到windows的开发。安装openssl有两种方式,第一种是利用linux发行版的软件仓库,比如Ubuntu的aptitude或者Fedora的yum来安装;第二种是从openssl.org下载openssl的最新代码,自己编译安装。两种方法都不困难,但是我推荐使用软件仓库,因为每个不同版本的linux中的软件可能只和某个特定版本的openssl兼容。从openssl.org上下载的最近代码,可能会导致某些软件的兼容错误。
OpenSSL的安装
因为是做开发,所以我们选择安装openssl和libssl-dev,其中openssl是一个程序组,帮助我们生成CA和证书等,libssl-dev包含了openssl的include文件和代码库,是我们开发到必备组件。安装过程如下:
Ubuntu:
首先,确认系统中openssl的信息
# aptitude show openssl
如果没有安装,运行以下命令安装
# aptitude install openssl
完成后,在shell中输入openssl,如果出现openssl命令行控制台就说明已经安装成功
# openssl
OpenSSL>
安装libssl-dev到过程跟上面类似:
# aptitude show libssl-dev
# aptitude install libssl-dev
# whereis openssl
这时应该可以看到openssl所在的bin目录和头文件所在的include目录。
到现在,openssl的安装就告一段落了,下面将演示如何修改openssl的配置文件,已经如何部署自签名的CA和证书。
生成自签名的CA证书
CA在网络安全中扮演着警察局的角色。它能够对每个网络实体的证书进行验证,从而保证该实体是可信的。一个根CA可以对次级CA进行签名,保证它的身份可靠,次级CA可以对用户证书进行签名,保证用户身份可靠。这样,根CA --> 次级CA --> 用户证书形成了一条证书链。当我们通过https连接到一台远程服务器,服务器会给我们发送自己的证书,通过证书链我们可以找到它到根CA,从而可以验证服务器身份的有效性。
在Openssl中,我们可以自己生成一个自签名的CA证书,然后用它来签发用户证书。在讨论证书生成之前,先来了解以下openssl的配置文件。在ubuntu中,ssl的配置文件一般位于/etc/ssl/目录下,名字是openssl.cnf。在Fedora中,ssl到配置文件一般在/etc/pki/tls目录下。Openssl的配置文件结构并不复杂。这里,我们重点关注其中的[ CA_default ]配置项和[ req_distinguished_name ]两项。[ CA_default ]是默认的CA配置,一般来说不必修改,但是必须要按照它的配置来创建必须的目录和文件,构建CA的目录结构。而[ req_distinguished_name ]则配置了一个CA证书请求中的一些默认值,比如国家名、组织名等。我们当然可以不配置这些,等到创建CA请求时在手动输入,但是事先配置好一些默认值可以省去一些不必要的输入。配置的方法在网上有很多文章有详细的描述,这里就不再赘述了。
接下来,我们就可以来生成一个CA证书了:
首先,创建一个用DES3加密的RSA私钥,密钥长度是1024。插一句,如果不了解每个openssl命令的用途,既可以到openssl.org上去找它的文档,也可以用openssl genrsa -h之类的方式让该命令输出它的帮助选项。
# openssl genrsa -des3 -out cakey.pem 1024
有了私钥之后,就可以用它来生成自签名的证书。一般说来,生成一个证书需要两个步骤。第一步,由用户生成一个证书请求;第二步,由CA对这个证书进行签名。但是CA自己的证书比较特殊,因为它是自己对自己签名,所以openssl提供了简单的方式,一个命令就可以生成自签名的证书:
# openssl req -new -x509 -days 3650 -key cakey.pem -out cacert.pem
这里的‘-x509’选项提示req命令生存自签名的CA,‘-key‘选项指定了私钥,‘-out’选项指定了输出的CA证书名字。
完成了以上步骤,还需要把CA证书和私钥拷贝到openssl.cnf的[ CA_default ]所指定的目录下。比如,我的openssl.cnf配置如下:
[ CA_default ]
dir = /etc/ssl/CA # Where everything is kept
certificate = $dir/cacert.pem # The CA certificate
private_key = $dir/private/cakey.pem # The private key
那么,我需要把生成的cacert.pem拷贝到/etc/ssl/CA/目录下,把cakey.pem拷贝到/etc/ssl/CA/private/目录下。
完成后,我们可以检查以下证书的内容:
# openssl x509 -noout -text -in cacert.pem
签发用户证书
既然已经生成了CA证书,我们就可以用它来签发用户证书了。签发用户证书比CA证书多一个创建CA请求的过程:
# openssl genrsa -des3 -out clientkey.pem 1024
# openssl req -new -key clientkey.pem -out client.req
这样就生成了一个名为client.req的用户证书请求,然后用CA来给它签名。
# openssl ca -out client.pem -infiles client.req
那么,如何验证签发的证书是否正确呢?可以用openssl的verify命令。
# openssl verify -CAfile /etc/ssl/CA/cacert.pem client.pem
这里的'-CAfile'或者'-CApath'选项很有意思,如果不指定,很可能会出现“X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY”这个错误。原因在于,openssl.cnf中的CA路径跟系统查找CA证书的路径是两回事。在Ubuntu中,CA证书的管理是用ca-certificates软件包来完成的。所以,如果想不指定'-CAfile'或者'-CApath',就需要把自签名的CA安装到系统中,如下:
# whereis ca-certificates
ca-certificates: /etc/ca-certificates /etc/ca-certificates.conf /usr/share/ca-certificates
这里会显示三个目录,/etc/ca-certificates.conf配置了需要被加入的证书,所有的证书都必须以.crt为后缀,否则ca-certificates包不认识。/usr/share/ca-certificates目录则保存着需要被安装的CA证书。以下步骤演示如何安装一个CA证书:
# mkdir /usr/share/ca-certificates/myCA.org
# cp /etc/ssl/CA/cacert.pem /usr/share/ca-certificates/myCA.org/cacert.crt
# vi /etc/ca-certificates.conf
并加入以下代码:
myCA.org/cacert.crt
然后执行以下命令:
# dpkg-reconfigure ca-certificates
现在再尝试:
# openssl verify client.pem
就不会再报错了。
生成CA自签名证书和签发证书的过程大概就说完了。但是还有很多的选项这里都没有涉及到,大家可以参考openssl.org详细了解每一个选项的作用,生成满足自己需求的CA证书和用户证书。最后,还有一点需要提及一下。有些应用程序可能需要把私钥和证书放在同一个文件里,怎么做呢?
其实私钥文件就是一个纯文本文件,它的形式大概如下:
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,B109C0ED98E4EF80
v2lapOrxeU+oqx0mL2y+iN6YhgtNsKVK2pQi0NfJcoeHR77BocpagF6WNSeuhBqE
H14GLJWsqMy/DliWpuiSNMWG5Z3moT1N0LlmpjOquLLH0oGBWiCjFNW6oq0xFjEh
ssXezPzt3evd+sCHEEWN5qN+kesZjQXuPuwnmUhMNX8rv/eDuGQI3wNAOL9UwJDD
CGeFwwLFniZ2p73rxG41f4/kSacHiBGl88yxX5Uugy2Z1RtNUDAdV2xLUtegv7bN
wr2Vt7NsDPcRuJF35YWpLnmt9p99OTkkUpy46ojrZEp6+iA1A9ToudnQBvMz3roB
vOHuud0F6OzNNvbYcf8U/F90QGigF55xkibSvGHhoBnUMRsBn81LeZPB9uv8Og77
gDf4/JHSX0ayesB4rr8XrWr8AP5g1vb+cO9WxSxfOIVKNA7svcxx7rbWki510ZBs
Q9c7TnnxzO/jTP4nR3FfbYeVWfDxf/N1avPSSmkmC8n9K2dMi33X154Ma5K/Bcza
eMjvBGnFdn9SNc2l4uvNcjbQnoAanB+c4K4bH1LMvu+N69k8pvwNU8MoYxkQxni7
XvbVGBe7iOsr8LxeC8y7Yo5SKOox3q9ZUXRMCoEWAYKLS7PMWKH/vAR8oTiP81nu
tsRz74H8RVp/x+ApNW6/A0E2Oos5xpl/7I1aKTUDikVH/2E/l+EYyLT5AsTrV146
BU3iClvVjIY/ZHpHlLTnFfPcOMIrCJHmQVBDblfExg9dWHEHnStKKTgaH6wNK5vI
qbkHOfIavhKLhX5qcmIeliw0g7rX67XE1jzRNvuEDHNiGEfKnFlZVg==
-----END RSA PRIVATE KEY-----
用户证书也类似,只不过用CERTIFICATE代替了RSA PRIVATE KEY,并且可能还包含着证书的明文信息。所以,合并两个文件,只需要把以上两个BEGIN-END文本段拷贝到同一个文件里即可,比如,以下是我的一个证书文件:
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,B109C0ED98E4EF80
v2lapOrxeU+oqx0mL2y+iN6YhgtNsKVK2pQi0NfJcoeHR77BocpagF6WNSeuhBqE
H14GLJWsqMy/DliWpuiSNMWG5Z3moT1N0LlmpjOquLLH0oGBWiCjFNW6oq0xFjEh
ssXezPzt3evd+sCHEEWN5qN+kesZjQXuPuwnmUhMNX8rv/eDuGQI3wNAOL9UwJDD
CGeFwwLFniZ2p73rxG41f4/kSacHiBGl88yxX5Uugy2Z1RtNUDAdV2xLUtegv7bN
wr2Vt7NsDPcRuJF35YWpLnmt9p99OTkkUpy46ojrZEp6+iA1A9ToudnQBvMz3roB
vOHuud0F6OzNNvbYcf8U/F90QGigF55xkibSvGHhoBnUMRsBn81LeZPB9uv8Og77
gDf4/JHSX0ayesB4rr8XrWr8AP5g1vb+cO9WxSxfOIVKNA7svcxx7rbWki510ZBs
Q9c7TnnxzO/jTP4nR3FfbYeVWfDxf/N1avPSSmkmC8n9K2dMi33X154Ma5K/Bcza
eMjvBGnFdn9SNc2l4uvNcjbQnoAanB+c4K4bH1LMvu+N69k8pvwNU8MoYxkQxni7
XvbVGBe7iOsr8LxeC8y7Yo5SKOox3q9ZUXRMCoEWAYKLS7PMWKH/vAR8oTiP81nu
tsRz74H8RVp/x+ApNW6/A0E2Oos5xpl/7I1aKTUDikVH/2E/l+EYyLT5AsTrV146
BU3iClvVjIY/ZHpHlLTnFfPcOMIrCJHmQVBDblfExg9dWHEHnStKKTgaH6wNK5vI
qbkHOfIavhKLhX5qcmIeliw0g7rX67XE1jzRNvuEDHNiGEfKnFlZVg==
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIDFDCCAn2gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBnDELMAkGA1UEBhMCQ04x
EDAOBgNVBAgTB0JlaWppbmcxEDAOBgNVBAcTB0JlaWppbmcxFjAUBgNVBAoTDUpp
YW5nd2xlZSBMdGQxHDAaBgNVBAsTE1NvZnR3YXJlIERlcGFydG1lbnQxETAPBgNV
BAMTCEJydWNlIExpMSAwHgYJKoZIhvcNAQkBFhFqaWFuZ3dsZWVAMTYzLmNvbTAe
Fw0xMjA3MDYxNTA3NTJaFw0xMzA3MDYxNTA3NTJaMIGFMQswCQYDVQQGEwJDTjEQ
MA4GA1UECBMHQmVpamluZzEWMBQGA1UEChMNSmlhbmd3bGVlIEx0ZDEcMBoGA1UE
CxMTU29mdHdhcmUgRGVwYXJ0bWVudDEPMA0GA1UEAxMGY2xpZW50MR0wGwYJKoZI
hvcNAQkBFg5jbGllbnRAMTYzLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
gYEA0NYf+2570AIPg4cbwUrYKsFfxiEValFeIYLz4PS9FpWENdN3MJ2Tp2+SeMcy
yeMl9qjRUbLPDaxL17NoYvSpRCMcN/6nIslbvteDOHSPvTUERvXxgu6a3g9dWBJo
FxLTlExkD8/8l1D0o3vWOu6kFNWlsS5coOrJuOU0qh0lTtUCAwEAAaN7MHkwCQYD
VR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlm
aWNhdGUwHQYDVR0OBBYEFMZnGWoBn1S3ibkVkMGeFLeccmsZMB8GA1UdIwQYMBaA
FJT4t3vrh5M6jZEsRI1h7zGNfttMMA0GCSqGSIb3DQEBBQUAA4GBAIzzebnfs2gN
u8lZx+n6/HCFGA2UtopkWIoiaOo1MBTEBAPva8xE9JVmaMfG9+CUEh7+UvY8xfJM
9KZdbseHtWncQ7P1/L/q3Af4j+RZ8QW4TLzk6CK1roqnsnDmC6eYgdOxD06z79fD
LdBF2U2bFqpBUhQuBvmQzAt9vSkPo7C6
-----END CERTIFICATE-----
下一篇,我将从一个简单的例子开始,探索用openssl编程的方法。