msn email google-talk twitter tumblr flickr

HTTPS/SSL原理及Ruby实现

话说这还是去年的心得体会,一直没整理.最近我们'伟-大-的-防-火-墙'太给力了,整理出来给有能力的童鞋作参考:

What

SSL (Secure Socket Layer)为Netscape所研发,用以保障在Internet上数据传输之安全,利用数据加密(Encryption)技术,可确保数据在网络 上之传输过程中不会被截取及窃听。目前一般通用之规格为40 bit之安全标准,美国则已推出128 bit之更高安全 标准,但限制出境。只要3.0版本以上之I.E.或Netscape浏览器即可支持SSL。 当前版本为3.0。它已被广泛地用于Web浏览器与服务器之间的身份认证和加密数据传输。

说到SSL就不得不说HTTPS,全称:Hypertext Transfer Protocol over Secure Socket Layer,是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容请看SSL。

SSL协议位于TCP/IP协议与各种应用层协议之间,为数据通讯提供安全支持。SSL协议可分为两层: SSL记录协议(SSL Record Protocol):它建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。 SSL握手协议(SSL Handshake Protocol):它建立在SSL记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等。HTTPS使用端口443,而不是象HTTP那样使用端口80来和TCP/IP进行通信。

SSL协议提供的服务主要有:

1)认证用户和服务器,确保数据发送到正确的客户机和服务器;

2)加密数据以防止数据中途被窃取;

3)维护数据的完整性,确保数据在传输过程中不被改变。

Why

此前我一直对https/ssl如何保护数据不被窃听有点疑问,因为服务器的证书是公开的,只能实行上行方向的数据加密,下行数据的加密我一直认为是浏览器会自动生成一个客户端的密钥对并将公钥发给服务器。仔细研究了https/ssl后发现其实并不像我想的那样,这里面既有非对称加密,又因为性能原因使用了对称加密。

基本的加解密算法类型也就这两种:

对称加密 :密钥只有一个,加密解密为同一个密码,且加解密速度快,典型的对称加密算法有DES、AES等;

非对称加密 :密钥成对出现(且根据公钥无法推知私钥,根据私钥也无法推知公钥),加密解密使用不同密钥(公钥加密需要私钥解密,私钥加密需要公钥解密),相对对称加密速度较慢,典型的非对称加密算法有RSA、DSA等。

明白了这两种加密类型,具体的认证过程就容易理解了:

  1. 客户端浏览器连接到https/ssl服务器,并发送ssl版本号等信息到服务器,协商此次连接使用的版本和参数。

  2. 服务器根据客户端发来的协商数据和自身支持的特性返回客户端协商参数,并且将服务器的证书发送给客户端,服务器的证书里包括用于非对称加密的服务器的公钥。

  3. 客户端收到服务器的证书,可以用于鉴别服务器身份,防止假冒的服务器。但最重要的用处是将一段由客户端浏览器随机生成的数据pre-master secret用服务器证书里的公钥进行加密,发给服务器。注意,因为这段加密的数据只由用服务器的私钥才能解密,所以pre-master secret不会被人监听到。

  4. 服务器收到加密后的pre-master secret数据后,用自己的私钥解密得到原始的pre-master secret,并使用一定算法得到对称加密的密钥master secret。

  5. 客户端也根据同样的算法得到master secret。

  6. 至此,客户端和服务器之间的上/下行数据传送使用对称加/解密,初始密钥为master secret。常用的对称加密算法有RC4,AES等。

经过这些步骤就可以保证https/ssl上下行数据不被监听并且加/解密速度也可以接受。

文字看起来特别枯燥吧?别急,有图:

https/ssl

以上是单向认证的过程,也就是服务端身份验证,https/ssl还支持客户端身份验证,也就是双向验证,需要将客户端证书上传到服务器,但这个证书并不用来加密下行数据。比如银行那些高价卖给你的'x盾'之类,就是属于客户端的证书。

简单来说,非对称加因为速度问题只用来认证(验证?)。而通信内容的安全及完整性是通过对称加密完成的。

How

其实我们设计自己的加密通信协议时完全可以照搬https/ssl这种方式,当然,有时我们也可以适当简化认证步骤,下面两段代码就是使用Ruby使用RSA非对成加密认证过程的简单实现。

过程简要描述:

1.服务端使用RSA初始化一个key-pair

2.客户端请求服务端的public-key

3.客户端使用public-key加密传输内容 发送至服务端

4.服务端使用private-key解密内容并执行

传输内容的有效性验证使用SHA1加密串与正文内容一并加密发送 服务端使用同样算法来验证内容是否被篡改(当然 这个过程中我们还可以加salt)

客户端代码:

   1      require 'socket'
   2      require 'digest/sha1'
   3      require "openssl"
   4  
   5      begin
   6        print "Starting client......"
   7        client = TCPSocket.new('localhost', 8888)
   8  
   9        puts "connected!\n\n"
  10  
  11        temp = ""
  12        #get server public encryption key
  13        5.times do
  14          temp << client.gets
  15        end
  16  
  17        puts "Received public 1024 RSA key!\n\n"
  18        public_key = OpenSSL::PKey::RSA.new(temp)
  19  
  20        p public_key
  21        #Construct message to send
  22        msg = 'open*open*/'
  23        sha1 = Digest::SHA1.hexdigest(msg)
  24  
  25        command = public_key.public_encrypt("#{sha1}*#{msg}")
  26        print "Sending the command...."
  27  
  28        #send the message
  29        client.send(command,0)
  30  
  31        puts "sent!"
  32      rescue => e
  33        puts "Something terrible happened..."
  34        puts e
  35        retry
  36      end
  37  
  38      client.close

服务端代码:

   1      require 'socket'
   2      require 'digest/sha1'
   3      require "openssl"
   4  
   5      priv_key = OpenSSL::PKey::RSA.new(1024)
   6      pub_key = priv_key.public_key
   7  
   8      host = ARGV[0] || 'localhost'
   9      port = (ARGV[1] || 8888).to_i
  10  
  11      #Start a listening TCP server on the ip address of host and port
  12      server = TCPServer.new(host, port)
  13  
  14      #If a connection is madge, send the public key and wait for data
  15      while session = server.accept
  16        begin
  17          puts "Connection made...sending public key.\n\n"
  18          puts pub_key
  19          session.print pub_key
  20          puts "Public key sent, waiting on data...\n\n"
  21  
  22          temp = session.recv(10000)
  23          puts "Received data..."
  24  
  25          msg = priv_key.private_decrypt(temp)
  26        rescue => e
  27          puts "Something terrible happened while receiving and decrypting."
  28          puts e
  29        end
  30  
  31        #Split the message sent by the client
  32        command = msg.split("*")
  33  
  34        serv_hash = command[0]
  35        nix_app = command[1]
  36        win_app = command[2]
  37        file = command[3]
  38  
  39        #Execute message from client based on the server platform
  40        if Digest::SHA1.hexdigest("#{nix_app}*#{win_app}*#{file}")==serv_hash
  41          puts "Message integrity confirmed..."
  42          if RUBY_PLATFORM.include?('mswin32')
  43            puts "Executing windows command: #{win_app} #{file}"
  44            `#{win_app} #{file}`
  45            exit
  46          else
  47            puts "Executing Linux command: #{nix_app} #{file}"
  48            `#{nix_app} #{file}`
  49            exit
  50          end
  51        else
  52          puts "The message could not be validated!"
  53        end
  54        exit
  55      end

给出Ruby的实现,主要是因为ruby容易读。但无论哪种语言实现,要想凹/凸墙加密就是必须的,有兴趣的童鞋建议Erlang或者node.js来实现服务端。

=====================

附上ruby安装缺少openssl的trouble shooting:

报错内容no such file to load -- openssl

解决方法:

cd /ruby_source_code_dir/ext/openssl
ruby extconf.rb --with-openssl-include=/usr/local/ssl/include/ --with-openssl-lib=/usr/local/ssl/lib
make
make install