记录一下Android客户端通过HTTPS连接Nodejs构建的服务端。
1,了解一下数字证书和数字签名,转一篇文章http://netsecurity.51cto.com/art/201108/287971.htm
数字签名是什么? 1. 鲍勃有两把钥匙,一把是公钥,另一把是私钥。 2. 鲍勃把公钥送给他的朋友们----帕蒂、道格、苏珊----每人一把。 3. 苏珊给鲍勃写信,写完后用鲍勃的公钥加密,达到保密的效果。 4. 鲍勃收信后,用私钥解密,看到信件内容。 5. 鲍勃给苏珊回信,写完后用Hash函数,生成信件的摘要(digest)。 6. 然后,鲍勃使用私钥,对这个摘要加密,生成"数字签名"(signature)。 7. 鲍勃将这个签名,附在信件下面,一起发给苏珊。 8. 苏珊收信后,取下数字签名,用鲍勃的公钥解密,得到信件的摘要。由此证明,这封信确实是鲍勃发出的。 9. 苏珊再对信件本身使用Hash函数,将得到的结果,与上一步得到的摘要进行对比。如果两者一致,就证明这封信未被修改过。 10. 复杂的情况出现了。道格想欺骗苏珊,他偷偷使用了苏珊的电脑,用自己的公钥换走了鲍勃的公钥。因此,他就可以冒充鲍勃,写信给苏珊。 11. 苏珊发现,自己无法确定公钥是否真的属于鲍勃。她想到了一个办法,要求鲍勃去找"证书中心"(certificate authority,简称CA),为公钥做认证。证书中心用自己的私钥,对鲍勃的公钥和一些相关信息一起加密,生成"数字证书 "(Digital Certificate)。 12. 鲍勃拿到数字证书以后,就可以放心了。以后再给苏珊写信,只要在签名的同时,再附上数字证书就行了。 13. 苏珊收信后,用CA的公钥解开数字证书,就可以拿到鲍勃真实的公钥了,然后就能证明"数字签名"是否真的是鲍勃签的。 14. 下面,我们看一个应用"数字证书"的实例:https协议。这个协议主要用于网页加密。 15. 首先,客户端向服务器发出加密请求。 16. 服务器用自己的私钥加密网页以后,连同本身的数字证书,一起发送给客户端。 17. 客户端(浏览器)的"证书管理器",有"受信任的根证书颁发机构"列表。客户端会根据这张列表,查看解开数字证书的公钥是否在列表之内。 18. 如果数字证书记载的网址,与你正在浏览的网址不一致,就说明这张证书可能被冒用,浏览器会发出警告。 19. 如果这张数字证书不是由受信任的机构颁发的,浏览器会发出另一种警告。 20. 数字证书如果是可靠的,客户端就可以使用证书中的服务器公钥,对信息进行加密,然后与服务器交换加密信息。 |
2,了解一下SSL,以下转自http://www.cnblogs.com/jeriffe/articles/2804190.html
SSL协议详解背景介绍 最近在看《密码学与网络安全》相关的书籍,这篇文章主要详细介绍一下著名的网络安全协议SSL。 在开始SSl介绍之前,先给大家介绍几个密码学的概念和相关的知识。 1、密码学的相关概念
2、相关的加密算法介绍 DES算法即数据加密标准,也称为数据加密算法。加密过程如下: 在SSL中会用到分组DES、三重DES算法等加密算法对数据进行加密。当然可以选用其他非DES加密算法,视情况而定,后面会详细介绍。 3、密钥交换算法 使用对称加密算法时,密钥交换是个大难题,所以Diffie和Hellman提出了著名的Diffie-Hellman密钥交换算法。 Diffie-Hellman密钥交换算法原理: RSA加密算法是基于这样的数学事实:两个大素数相乘容易,而对得到的乘积求因子则很难。加密过程如下: (1)选择两个大素数P、Q (2)计算N=P*Q (3)选择一个公钥(加密密钥)E,使其不是(P-1)与(Q-1)的因子 (4)选择私钥(解密密钥)D,满足如下条件: (D*E) mod (P-1)(Q-1)=1 (5)加密时,明文PT计算密文CT如下: CT=PTE mod N (6)解密时,从密文CT计算明文PT如下: PT=CTDmodN 这也是SSL中会用一种密钥交换算法。 3、散列算法: 主要用于验证数据的完整性,即保证时消息在发送之后和接收之前没有被篡改对于SSL中使用到的散列算法有MD5、SHA-1。 4、数字证书: 数字证书其实就是一个小的计算机文件,其作用类似于我们的身份证、护照,用于证明身份,在SSL中,使用数字证书来证明自己的身份,而不是伪造的。 5、简单的总结: 在SSL中会使用密钥交换算法交换密钥;使用密钥对数据进行加密;使用散列算法对数据的完整性进行验证,使用数字证书证明自己的身份。好了,下面开始介绍SSL协议。 SSL介绍: 安全套接字(Secure Socket Layer,SSL)协议是Web浏览器与Web服务器之间安全交换信息的协议,提供两个基本的安全服务:鉴别与保密。 SSL是Netscape于1994年开发的,后来成为了世界上最著名的web安全机制,所有主要的浏览器都支持SSL协议。 目前有三个版本:2、3、3.1,最常用的是第3版,是1995年发布的。 SSL协议的三个特性 ① 保密:在握手协议中定义了会话密钥后,所有的消息都被加密。 ② 鉴别:可选的客户端认证,和强制的服务器端认证。 ③ 完整性:传送的消息包括消息完整性检查(使用MAC)。 SSL的位置 SSL介于应用层和TCP层之间。应用层数据不再直接传递给传输层,而是传递给SSL层,SSL层对从应用层收到的数据进行加密,并增加自己的SSL头。 SSL的工作原理 握手协议(Handshake protocol) 记录协议(Record protocol) 警报协议(Alert protocol) 1、握手协议 握手协议是客户机和服务器用SSL连接通信时使用的第一个子协议,握手协议包括客户机与服务器之间的一系列消息。 SSL中最复杂的协议就是握手协议。该协议允许服务器和客户机相互验证,协商加密和MAC算法以及保密密钥,用来保护在SSL记录中发送的数据。握手协议 是在应用程序的数据传输之前使用的。 每个握手协议包含以下3个字段 (1)Type:表示10种消息类型之一 (2)Length:表示消息长度字节数 (3)Content:与消息相关的参数 握手协议的4个阶段 1.1 建立安全能力 SSL握手的第一阶段启动逻辑连接,建立这个连接的安全能力。首先客户机向服务器发出client hello消息并等待服务器响应,随后服务器向客户机返回server hello消息,对client hello消息中的信息进行确认。 Client hello消息包括Version,Random,Session id,Cipher suite,Compression method等信息。 ClientHello 客户发送CilentHello信息,包含如下内容: (1)客户端可以支持的SSL最高版本号 (2)一个用于生成主秘密的32字节的随机数。(等会介绍主秘密是什么) (3)一个确定会话的会话ID。 (4)一个客户端可以支持的密码套件列表。 密码套件格式:每个套件都以“SSL”开头,紧跟着的是密钥交换算法。用“With”这个词把密钥交换算法、加密算法、散列算法分开,例 如:SSL_DHE_RSA_WITH_DES_CBC_SHA, 表示把DHE_RSA(带有RSA数字签名的暂时Diffie-HellMan)定义为密钥交换算法;把DES_CBC定义为加密算法;把SHA定义为散 列算法。 (5)一个客户端可以支持的压缩算法列表。 ServerHello服务器用ServerHello信息应答客户,包括下列内容 (1)一个SSL版本号。取客户端支持的最高版本号和服务端支持的最高版本号中的较低者。 (2)一个用于生成主秘密的32字节的随机数。(客户端一个、服务端一个) (3)会话ID (4)从客户端的密码套件列表中选择的一个密码套件 (5)从客户端的压缩方法的列表中选择的压缩方法 这个阶段之后,客户端服务端知道了下列内容: (1)SSL版本 (2)密钥交换、信息验证和加密算法 (3)压缩方法 (4)有关密钥生成的两个随机数。 1.2 服务器鉴别与密钥交换 服务器启动SSL握手第2阶段,是本阶段所有消息的唯一发送方,客户机是所有消息的唯一接收方。该阶段分为4步: (a)证书:服务器将数字证书和到根CA整个链发给客户端,使客户端能用服务器证书中的服务器公钥认证服务器。 (b)服务器密钥交换(可选):这里视密钥交换算法而定 (c)证书请求:服务端可能会要求客户自身进行验证。 (d)服务器握手完成:第二阶段的结束,第三阶段开始的信号 这里重点介绍一下服务端的验证和密钥交换。这个阶段的前面的(a)证书 和(b)服务器密钥交换是基于密钥交换方法的。而在SSL中密钥交换算法有6种:无效(没有密钥交换)、RSA、匿名Diffie-Hellman、暂时 Diffie-Hellman、固定Diffie-Hellman、Fortezza。 在阶段1过程客户端与服务端协商的过程中已经确定使哪种密钥交换算法。 如果协商过程中确定使用RSA交换密钥,那么过程如下图: 这个方法中,服务器在它的第一个信息中,发送了RSA加密/解密公钥证书。不过,因为预备主秘密是由客户端在下一个 阶段生成并发送的,所以第二个信息是空的。注意,公钥证书会进行从服务器到客户端的验证。当服务器收到预备主秘密时,它使用私钥进行解密。服务端拥有私钥 是一个证据,可以证明服务器是一个它在第一个信息发送的公钥证书中要求的实体。 其他的几种密钥交换算法这里就不介绍了。可以参考Behrouz A.Forouzan著的《密码学与网络安全》。 1.3 客户机鉴别与密钥交换: 客户机启动SSL握手第3阶段,是本阶段所有消息的唯一发送方,服务器是所有消息的唯一接收方。该阶段分为3步: (a)证书(可选):为了对服务器证明自身,客户要发送一个证书信息,这是可选的,在IIS中可以配置强制客户端证书认证。 (b)客户机密钥交换(Pre-master-secret):这里客户端将预备主密钥发送给服务端,注意这里会使用服务端的公钥进行加密。 (c)证书验证(可选),对预备秘密和随机数进行签名,证明拥有(a)证书的公钥。 下面也重点介绍一下RSA方式的客户端验证和密钥交换。 这种情况,除非服务器在阶段II明确请求,否则没有证书信息。客户端密钥交换方法包括阶段II收到的由RSA公钥加密的预备主密钥。 阶段III之后,客户要有服务器进行验证,客户和服务器都知道预备主密钥。 1.4 完成 客户机启动SSL握手第4阶段,使服务器结束。该阶段分为4步,前2个消息来自客户机,后2个消息来自服务器。 1.5 密钥生成的过程 这样握手协议完成,下面看下什么是预备主密钥,主密钥是怎么生成的。为了保证信息的完整性和机密性,SSL需要有六 个加密秘密:四个密钥和两个IV。为了信息的可信性,客户端需要一个密钥(HMAC),为了加密要有一个密钥,为了分组加密要一个IV,服务也是如此。 SSL需要的密钥是单向的,不同于那些在其他方向的密钥。如果在一个方向上有攻击,这种攻击在其他方向是没影响的。生成过程如下: 2、记录协议 记录协议在客户机和服务器握手成功后使用,即客户机和服务器鉴别对方和确定安全信息交换使用的算法后,进入SSL记录协议,记录协议向SSL连接提供两个服务: (1)保密性:使用握手协议定义的秘密密钥实现 (2)完整性:握手协议定义了MAC,用于保证消息完整性 记录协议的过程: 3、警报协议 客户机和服务器发现错误时,向对方发送一个警报消息。如果是致命错误,则算法立即关闭SSL连接,双方还会先删除相关的会话号,秘密和密钥。每 个警报消息共2个字节,第1个字节表示错误类型,如果是警报,则值为1,如果是致命错误,则值为2;第2个字节制定实际错误类型。 总结 SSL中,使用握手协议协商加密和MAC算法以及保密密钥 ,使用握手协议对交换的数据进行加密和签名,使用警报协议定义数据传输过程中,出现问题如何去解决。 整个过程比较复杂,如果大家有不理解和我叙述不周的地方,欢迎大家指正出来! |
3,SSL与TLS的区别与介绍 , 转自http://blog.csdn.net/jackie03/article/details/7323569
SSL:(Secure Socket Layer,安全套接字层),位于可靠的面向连接的网络层协议和应用层协议之间的一种协议层。SSL通过互相认证、使用数字签名确保完整性、使用加密确保 私密性,以实现客户端和服务器之间的安全通讯。该协议由两层组成:SSL记录协议和SSL握手协议。 TLS:(Transport Layer Security,传输层安全协议),用于两个应用程序之间提供保密性和数据完整性。该协议由两层组成:TLS记录协议和TLS握手协议。 SSL是Netscape开发的专门用户保护Web通讯的,目前版本为3.0。最新版 本的TLS 1.0是IETF(工程任务组)制定的一种新的协议,它建立在SSL 3.0协议规范之上,是SSL 3.0的后续版本。两者差别极小,可以理解为SSL 3.1,它是写入了RFC的。
关系就是。。。。并列关系 |
4,HTTPS简介,转自http://blog.leezhong.com/tech/2011/02/19/https-workflow.html
HTTPS(全称:Hypertext Transfer P rotocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容请看SSL。
具体是如何进行加密,解密,验证的,且看下图。
4,建立https服务器端
OK, 了解了一些安全方面的知识,现在尝试一下构建服务器端,用nodejs建立一个简单的https服务器,
https.js
var https = require('https'); var fs = require('fs'); var options = { key: fs.readFileSync('server.key'), cert: fs.readFileSync('server.crt') }; https.createServer(options, function (req, res) { res.writeHead(200); res.end("hello world\n"); }).listen(8180);
nodejs文档:
-
key
: Private key to use for SSL. Defaultnull
. -
cert
: Public x509 certificate to use. Defaultnull
.
服务器端需要一个私钥和一个X509证书,我们用openssl工具生成。(我曾尝试使用jdk提供的keytool工具,发现这个工具似乎无法导出私钥,对于nodejs还不太好用,而它生成的密钥仓库对于tomcat配置起来倒是非常方便)
在生成服务器端私钥和证书前 ,我们先生成CA证书,
1,首先生成CA私钥
openssl genrsa -out ca.key 1024
2,然后用CA私钥生成自签名证书
openssl req -new -x509 -days 365 -key ca.key -out ca.crt
没有-x509参数,只是生成证书请求,加上参数生成自签名证书。
这样CA根证书就生成了,以后所有的用户证书都用根证书进行签名
3,生成服务器端私钥
openssl genrsa -out server.key 1024
4,生成服务器端证书请求
openssl req -new -key server.key -out server.csr
5,用CA根证书进行签名生成服务器端证书
openssl ca -in server.csr -out server.crt -cert ca.crt -keyfile ca.key
这样我们就有了构建服务器端需要的两个文件server.key和server.crt
node https.js 启动服务器
在浏览器输入https://localhost:8180测试,成功!
这里介绍的只是单向认证,也就是服务器端需要证书,客户端不需要。双向认证更为复杂,这里暂且不提。
5,Android客户端通过https连接服务器
上面我们测试了用浏览器连接服务器端是没有问题的,接下来我们尝试用Android客户端去连接服务器。
Android客户端通过https连接服务器需要特别注意的是,Android只支持BKS类型的证书库,所以我们用jdk提供的keytool工具导入CA根证书生成bks证书库文件:
keytool -importcert -keystore cacerts.bks -storetype BKS -file ca.crt -provider org.bouncycastle.jce.provider.BouncyCastleProvider
你执行到这里可能会出错,因为jdk下的keytool默认没有BKS的类库。
我们需要下载bcprov-jdk15on-1.46.jar,放入jdk1.6.0_10\jre\lib\ext下面。同时要修改jdk1.6.0_10\jre\lib\security\java.security
找到security.provider.1=sun.security.provider.Sun这行,这里有好几行,在最后一行加上 security.provider.10=org.bouncycastle.jce.provider.BouncyCastleProvider
这样我们就有了包含CA根证书的证书库文件cacerts.bks,怎样使用它呢,首先将cacerts.bks放入android项目下的res/raw文件夹下面,然后改动部分代码,我这里用的httpclient,加入对https的支持
SchemeRegistry schReg = new SchemeRegistry();
KeyStore trustStore = KeyStore.getInstance("BKS");
trustStore.load(context.getResources().openRawResource(R.raw.cacerts), "password".toCharArray());
SSLSocketFactory socketFactory = new SSLSocketFactory(trustStore);
//允许访问的hostname与证书中的hostname不同,严格模式下必须一样
HostnameVerifier hostnameVerifier = SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
socketFactory.setHostnameVerifier((X509HostnameVerifier)hostnameVerifier);
Scheme sch = new Scheme("https", socketFactory, 8180);
schReg.register(sch);
测试一下,总是出现错误"Wrong version of key store ",先怀疑是bcprov-jdk15on-1.46.jar包的版本问题,但是换了多个版本后还是不行,只好用portecle工具来生成cacerts.bks,这次OK了。
portecle下载地址:http://sourceforge.net/projects/portecle/
至此整个SSL单向认证android客户端连接Nodejs服务器端测试成功。
6,客户端通过SSL socket连接服务端
服务端:
var tls = require('tls'); var fs = require('fs'); var options = { key: fs.readFileSync('server.key'), cert: fs.readFileSync('server.crt'), // This is necessary only if using the client certificate authentication. //requestCert: true, // This is necessary only if the client uses the self-signed certificate. //ca: [ fs.readFileSync('client-cert.pem') ] }; var server = tls.createServer(options, function(cleartextStream) { console.log('server connected', cleartextStream.authorized ? 'authorized' : 'unauthorized'); cleartextStream.write("welcome!\n"); cleartextStream.setEncoding('utf8'); cleartextStream.pipe(cleartextStream); }); server.listen(8180, function() { console.log('server bound'); });
客户端与以上https连接类似