目录 |
网络是一个开放的世界,服务器可以被不法节点冒名顶替,传输的数据可以被整个通路上的任何一个节点监听。要保证客户机和服务器在网络中通信的安全,客户机首先要检查数字签名以验明服务器正身,然后还要加密要传输的数据,该数据到达服务器后再进行解密。
通信密码学里,非对称密码技术RSA已经在计算机网络中得到广泛的应用,密钥是成对出现的,一个是私钥一个公钥。它们既可以用以数字签名,又可以用作加密解密。
RSA是MIT的Rivest、Shamir和Adleman在1977年提出,他们以此申请了专利并成立了数家公司,最有名的当数RSA Security和VeriSign,它们分别被EMC和Symantec以21亿和12.8亿美元收购。1983年RSA算法申请了专利,直到2000年9月专利失效。由于RSA的广泛应用,他们被授予2002年度的图灵奖。
称a、b对N同余,记为a ≡ b (mod N),当且仅当a、b除以N之后的余数相等。
加法性质:若a1 ≡ b1 (mod N),a2 ≡ b2 (mod N),则(a1 + a2) ≡ (b1 + b2) (mod N)。
乘法性质:若a1 ≡ b1 (mod N),a2 ≡ b2 (mod N),则a1 * a2 ≡ b1 * b2 (mod N)。
模数性质:若a ≡ b (mod N1),a ≡ b (mod N2),则a ≡ b (mod lcm[N1, N2]),其中 lcm[N1, N2]为N1, N2的最小公倍数。
在小于等于N的正整数之中,与N构成互质关系(coprime)的整数的个数称为欧拉函数φ(N)。
质数的欧拉函数:显然,如果N是质数,则 φ(N)=N-1 。因为质数N与小于它的每一个数都构成互质关系。
质数乘积的欧拉函数:若p1和p2是质数,则 φ(p1*p2) = φ(p1)*φ(p2) = (p1-1)*(p2-1) 。
如果两个正整数a和N互质,则N的欧拉函数 φ(N) 可以让下面的等式成立:
a φ(N) ≡ 1 (mod N)
在N为质数的时候,任何小于N的整数a,都和N互质。因此
若N为质数,则:a N-1 ≡ 1 (mod N)
首先随机选取2个大质数p1和p2,p1≠p2,令N=p1*p2,由此可得φ(N)=(p1-1)*(p2-1)
随机取一个小于φ(N),且与φ(N)互质的整数e
计算e对φ(N)同余的逆元d,它满足ed ≡ 1 (mod φ(N))
把(N,d)妥善保管作为私钥,(N,e)公开作为公钥。对于任何信息m,我们有
m ≡ me * d (mod N)
证明如下:
因为ed ≡ 1 (mod (p1-1)*(p2-1)),所以存在整数k使ed = k*(p1-1)*(p2-1) + 1
因此me * d ≡ m * mk*(p1-1)*(p2-1) (mod N)
因p1和p2为质数,由费马小定理,mk*(p1-1)*(p2-1) ≡ 1 (mod p1),mk*(p1-1)*(p2-1) ≡ 1 (mod p2)
所以mk*(p1-1)*(p2-1) ≡ 1 (mod lcm[p1,p2]),即mk*(p1-1)*(p2-1) ≡ 1 (mod N)
根据同余乘法性质,me * d ≡ m (mod N)
证明毕。
发送方加密:以接受方公钥(N,e)对信息m计算 me (mod N)
接受方解密:以接受方私钥(N,d)还原信息m,计算 (me (mod N))d = me * d (mod N) = m
发送方签名:以发送方私钥(N,d)对信息m的散列函数h(m),计算 h(m)d (mod N) = h
接受方验证:用发送方公钥(N,e)计算 (hd (mod N))e = he * d (mod N) = h,比较其是否与h(m)值一致
基于RSA的网络通信过程在X.509规范的公钥体系(PKI - public key infrastructure)下得到完成,数据的加密解密由安全套接字层(SSL - secure socket layer)或传输层安全(TLS - transport layer security)实现。当客户机需要与某个服务器建立通信连接时,双方发生SSL握手过程:
客户机通过网络发送请求安全会话的消息(通常请求是HTTPS协议的形式)。服务器通过发送其X.509证书(包含公钥)进行响应。
客户机验证服务器证书的有效性,并检验该证书是否由可信任的证书认证机构(CA - certification authority)所签发。
当证书有效,客户机生成一次性的密钥,并用服务器的公钥对该密钥进行加密。然后,客户机把加密的会话密钥发送给服务器。
服务器用其私钥对其次进行解密,然后得到本次通讯的会话密钥。
客户机和服务器用其约定的会话密钥开始数据通信,直到一次会话结束。
由于编写密钥代码的复杂性,很多网站使用开源免费软件。OpenSSL就是这样的一套原码公开的C语言函数库,它最初由Eric和Tim在入职RSA公司前开发,其丰富的在线使用手册可以通过下列命令得到:
$ openssl -help
客户机、服务器和证书认证机构是涉足安全通信的三方。围绕证书这个技术关键,下面逐一细说三方通信过程中的协作。
基于Java技术的Web服务器通常以服务导向架构为蓝本,通过HTTPS协议将应用程序功能作为服务发送给客户机。通信传输协议的SSL握手过程中,服务器首先将其X.509证书发送给客户机,然后由客户机依据其存储的根CA证书验证该证书的真实性。Java内部维护一个密钥库(keystore),用以存储私钥及其对应的附有证书链的证书。Java为密钥库设置了一个密码,密钥库中的每一个私钥及其证书又有一个密码来控制存取,keytool是管理密钥库的工具软件。
$ keytool -help
若服务器FQDN是www.mysite.com,它在DNS中又有别名foo.mysite.com,创建JKS格式的初始密钥库可以用如下命令完成。
$ keytool -genkeypair -keystore mystore.jks -storepass mypass -alias www -keypass mypass -keyalg rsa -dname "CN=www.mysite.com" -ext "SAN=dns:foo.mysite.com" $ keytool -list -v -keystore mystore.jks -storepass mypass
可以看到,生成的密钥库mystore.jks里有一个私钥记录PrivateKeyEntry,记录中含有一个证书链(certificate chain),当然目前证书链里只包含一个证书Certificate[1]。我们来仔细研究一下私钥和证书的构成。
很难相信的一个事实是,keytool没有从密钥库中提取私钥的功能。因此要想得到私钥,只能把密钥库先转换成PKCS#12格式。
$ keytool -importkeystore -srckeystore mystore.jks -srcstorepass mypass -srcalias www -srckeypass mypass -destkeystore mystore.p12 -deststorepass mypass -destkeypass mypass -deststoretype pkcs12 $ keytool -list -v -keystore mystore.p12 -storepass mypass -storetype pkcs12 -alias www -keypass mypass
从密钥库mystore.p12中可以用OpenSSL工具提取出私钥:
$ openssl pkcs12 -nocerts -in mystore.p12 -passin pass:mypass -passout pass:mypass | awk "/--BEGIN/,/--END/" > xxx_private.key
得到的私钥受密码保护而存储在xxx_private.key文件中。可以进一步删除存储保护密码,从而得到一个PKCS#1格式的RSA私钥rsa_private.key。
$ openssl rsa -in xxx_private.key -passin pass:mypass > rsa_private.key
显示私钥rsa_private.key的内容
$ openssl rsa -text -noout -in rsa_private.key $ openssl asn1parse -in rsa_private.key
再进一步可以用OpenSSL工具,在RSA私钥的基础上加上版本号、算法标识等附加信息形成PKCS#8格式私钥,它是更一般格式的私钥形式,目的是为了包含RSA和其它一切格式的私钥。
$ openssl pkcs8 -topk8 -nocrypt -in rsa_private.key > private.key
显示私钥private.key的内容
$ openssl asn1parse -in private.key
用以下命令可以把PKCS#8格式的private.key中的RSA私钥提取出来。
$ openssl pkcs8 -nocrypt -in private.key $ openssl rsa -text -in private.key | awk "/--BEGIN/,/--END/"
从JKS格式的密钥库中提取自签名证书相对容易。
$ keytool -exportcert -rfc -alias www -keystore mystore.jks -storepass mypass > public.crt
也可以从PKCS#12格式的密钥库中提取。
$ openssl pkcs12 -in mystore.p12 -nokeys -passin pass:mypass | awk "/--BEGIN/,/--END/" > public.crt
分析证书public.crt的结构和具体内容
$ openssl x509 -text -in public.crt
可以看到证书包含Subject、Issuer、公钥和签名等信息。我们可以把其中的公钥单独提取出来
$ openssl x509 -pubkey -noout -in public.crt > public.key
我们还能从PKCS#8格式的private.key直接计算出其对应的公钥,以此可以与密钥库中提取出来的公钥进行比较。
$ openssl rsa -pubout -in private.key
显示公钥的内容
$ openssl rsa -text -noout -pubin -in public.key
鉴别私钥和证书是否成对,可以检查它们各自的模数。密钥对的私钥及其对应的公钥和证书,模数应该相同。
$ openssl rsa -modulus -noout -in private.key $ openssl rsa -modulus -noout -pubin -in public.key $ openssl x509 -modulus -noout -in public.crt
初始建立的密钥库,包含一个私钥和一个自签名的证书。下一步需要生成证书签名请求(CSR - certificate signing request),然后把证书签名请求提交认证机构CA,等待该机构依据X.509国际标准对服务器进行验证,合格者签发证书。
$ keytool -certreq -keystore mystore.jks -storepass mypass -alias www -keypass mypass -ext "SAN=dns:foo.mysite.com" -file www.csr
从证书签名请求www.csr可以提取出其间包含的公钥。还可以计算它的模数,与私钥公钥的模数进行比较。
$ openssl req -pubkey -noout -in www.csr $ openssl req -modulus -noout -in www.csr
显示证书签名请求的内容
$ openssl req -text -noout -in www.csr
目前全球最著名的证书认证机构有:
Comodo--占数字认证市场三分之一的份额
Symantec--占数字认证市场三分之一的份额
GoDaddy--占数字认证市场10%
GlobalSign--占数字认证市场10%
中国互联网络信息中心--拥有根证书的中国认证机构
香港邮政电子核证--拥有根证书的香港认证机构
澳门邮政eSignTrust--拥有根证书的澳门认证机构
通常证书认证机构有一个或若干根证书。作为演示,我们在这里生成这组根证书及其对应的私钥,根证书是以自签名的形式签发的。
$ mkdir root $ openssl req -newkey rsa:4096 -keyout root/xxx_private.key -out root/public.csr $ openssl pkcs8 -topk8 -nocrypt -in root/xxx_private.key > root/private.key $ openssl x509 -req -signkey root/private.key -in root/public.csr -out root/public.crt
机构还有一组中级认证:
$ mkdir intermediate $ openssl req -newkey rsa:2048 -keyout intermediate/xxx_private.key -out intermediate/public.csr $ openssl pkcs8 -topk8 -nocrypt -in intermediate/xxx_private.key > intermediate/private.key
中级认证证书是由根证书签发的。
touch root/database.txt echo 01 > root/serial.txt cat > root/openssl.cnf <<EOF [ ca ] default_ca = CA_default [ CA_default ] new_certs_dir = intermediate serial = root/serial.txt database = root/database.txt certificate = root/public.crt private_key = root/private.key default_days = 3652 default_crl_days = 30 default_md = md5 policy = policy_any [ policy_any ] countryName = supplied stateOrProvinceName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional EOF $ openssl ca -config root/openssl.cnf -in intermediate/public.csr -out intermediate/public.crt
有了中级证书,证书认证机构就具备了依据服务器的www.csr请求发送具有认证机构签名的服务证书的能力。
touch intermediate/database.txt echo 01 > intermediate/serial.txt cat > intermediate/openssl.cnf <<EOF [ ca ] default_ca = CA_default [ CA_default ] new_certs_dir = . serial = intermediate/serial.txt database = intermediate/database.txt certificate = intermediate/public.crt private_key = intermediate/private.key default_days = 365 default_crl_days = 30 default_md = md5 policy = policy_any [ policy_any ] countryName = supplied stateOrProvinceName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional EOF $ openssl ca -config intermediate/openssl.cnf -in www.csr -out www.crt
证书认证机构应答服务器的是具有数字签名的数据,其原理是这样的。认证机构把要发送的数据用私钥进行签名,然后把数据连同签名一并发送给服务器。
$ echo testing > 1.txt $ openssl dgst -sha256 -sign root/private.key 1.txt > 2.xxx
服务器可以获取的根证书,以此得到认证机构的公钥。
$ openssl x509 -pubkey -noout -in root/public.crt > ca0.key
接受到认证机构的数据及其签名信息后,依据认证机构的公钥对其进行签名验证
$ openssl dgst -sha256 -verify ca0.key -signature 2.xxx 1.txt
我们观察www.crt的结构和内容不难发现,生成的证书包含一个公钥和一个签名。因此服务器收到这样的证书,就可以完成签名验证。
$ openssl x509 -text -noout -in www.crt
证书认证机构向证书申请部门颁发PKCS#7格式的证书链:
$ cat www.crt intermediate/public.crt | awk "/--BEGIN/,/--END/" > chain.crt $ openssl crl2pkcs7 -nocrl -certfile chain.crt -out chain.p7b $ openssl pkcs7 -print_certs -in chain.p7b
Java存储国际认可的认证机构的根CA证书在其Truststore中$JAVA_HOME/jre/lib/security/cacerts,默认密码通常为changeit。本文为了模拟,把自己生成的CA根证书加入其中。
$ keytool -importcert -trustcacerts -file root/public.crt -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit -alias myCA
服务器得到chain.p7b后,先把证书链分割成若干个单独的证书文件:
$ openssl pkcs7 -print_certs -in chain.p7b | awk ‘BEGIN{i=0} /--BEGIN/{i=-i+1} {if(i>0) print>"ca" i ".crt"} /--END/{i=-i}‘
得到的签名证书,一般ca1.crt是和私钥配对的证书,而ca2.crt是CA中级证书。计算MD5值,以确定ca1.crt和自己的CSR请求一致。
$ openssl x509 -modulus -noout -in ca1.crt | openssl md5
确认正确即可以将它们安装到密钥库中,首先安装CA中级证书ca2.crt:
$ keytool -importcert -trustcacerts -file ca2.crt -keystore mystore.jks -storepass mypass -alias ca
然后再安装ca1.crt,以取代密钥库中原来的自签名证书。
$ keytool -importcert -trustcacerts -file ca1.crt -keystore mystore.jks -storepass mypass -alias www -keypass mypass
如果需要,可以把修改后的密钥库重新转化成PKCS#12格式:
$ rm mystore.p12 $ keytool -importkeystore -srckeystore mystore.jks -srcstorepass mypass -srcalias www -srckeypass mypass -destkeystore mystore.p12 -deststorepass mypass -destkeypass mypass -deststoretype pkcs12
最后,再验证一下密钥库就可以看到一个具有三个证书构成的证书链。证书链最后一个是已经存储在Truststore的CA根证书,以此表明整个证书链是可信任的。
$ keytool -list -v -keystore mystore.jks -storepass mypass -alias www -keypass mypass $ keytool -list -v -keystore mystore.p12 -storepass mypass -storetype pkcs12 -alias www -keypass mypass
【JBoss】把下列配置加入$CATALINA_HOME/conf/server.xml
<-- Define a SSL Coyote HTTP/1.1 Connector on port 8443 --> <Connector port="8443" minSpareThreads="5" maxSpareThreads="75" enableLookups="true" disableUploadTimeout="true" acceptCount="100" maxThreads="200" scheme="https" secure="true" SSLEnabled="true" keystoreFile="${user.home}/keystore.jks" keystorePass="mypass" clientAuth="false" sslProtocol="TLS"/>
【Tomcat】把下列配置加入$CATALINA_BASE/conf/server.xml
<-- Define a SSL Coyote HTTP/1.1 Connector on port 8443 --> <Connector protocol="org.apache.coyote.http11.Http11Protocol" port="8443" maxThreads="200" scheme="https" secure="true" SSLEnabled="true" keystoreFile="${user.home}/keystore.jks" keystorePass="mypass" clientAuth="false" sslProtocol="TLS"/>
通常服务器提供给客户机的证书都附有证书链,它是由一系列CA证书发出的证书序列,最终以根CA证书结束。客户机预 先存储了一组可信任的根CA证书,服务器证书链的终结根CA证书若在其列,客户机即可确认该服务器可信任。在Windows查阅PC中存储的根CA证书, 可以在命令行中键入
C:\> certmgr.msc
客户机查询服务器的证书、测试能否用/etc/ssl/certs目录下存储的根CA证书与服务器成功完成SSL握手,可以执行如下OpenSSL的命令
$ openssl s_client -showcerts -connect www.mysite.com:443 $ openssl s_client -CApath /etc/ssl/certs -connect www.mysite.com:443
客户机一旦取得了服务器的证书链ca1.crt和ca2.crt,可以用其存储的根CA证书ca.crt验证该的真实性。
$ openssl verify -CAfile ca.crt ca1.crt $ openssl verify -CAfile ca1.crt ca2.crt
客户机把要发送的数据有私钥进行签名
$ echo testing > 1.txt $ openssl rsautl -encrypt -inkey ca1.crt -certin -in 1.txt -out 2.xxx
服务器对接受到数据进行解密
$ openssl rsautl -decrypt -inkey private.key -in 2.xxx -out 3.txt
未完待续...
本文出自 “倒背如流 Linux - Unix” 博客,请务必保留此出处http://xnuil.blog.51cto.com/10714423/1698673
原文:http://xnuil.blog.51cto.com/10714423/1698673