網絡服務器之HTTPS服務


import ssl, socket, time

if __name__ == "__main__":

    context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
    #context.load_cert_chain(certfile=‘key_pub.pem’, keyfile=‘key_priv.pem')   #可以分開定義公鑰和私鑰文件,也可以合並成一個文件
    context.load_cert_chain(certfile=’cert.pem')
    
    bindsocket = socket.socket()
    bindsocket.bind(('127.0.0.1', 443))
    bindsocket.listen(5)
    
    newsocket, fromaddr = bindsocket.accept()
    connstream = context.wrap_socket(newsocket, server_side=True)
    
    try:
        data = connstream.recv(1024)
        print(data)
        buf = 'Hi NN%f\n\n\n\n'%time.time()
        buf = buf.encode()
        connstream.send(buf)
    finally:
        connstream.shutdown(socket.SHUT_RDWR)
        connstream.close()
        bindsocket.close()

此例沒有使用socketserver框架,目的在於測試ssl模塊的用法。



繼續,用框架實現HTTPS服務

import socketserver, ssl, time
class MyHTTPSHandler_socket(socketserver.BaseRequestHandler):
    def handle(self):
        context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
        context.load_cert_chain(certfile="cert.pem")
        SSLSocket = context.wrap_socket(self.request, server_side=True)
        self.data = SSLSocket.recv(1024)
        print(self.data)
        buf = 'test HTTPS Server Handler<br>%f'%time.time()
        buf = buf.encode()
        SSLSocket.send(buf)

if __name__ == "__main__":
    port = 443
    httpd = socketserver.TCPServer(('localhost‘, port), MyHTTPSHandler_socket)
    print(’https serving at port', port)
    httpd.serve_forever()

說明:handle()函數負責所有與客戶端的通信。客戶端連接過來之后,ssl模塊載入證書,並用SSLSocket對socket進行封裝,屏蔽底層的加密通信細節。


下面再給出HTTPS文件服務器代碼,文件訪問功能由SimpleHTTPRequestHandler實現,數據加密傳輸由ssl實現。

import socketserver, ssl, time, http.server
class MyHTTPS_SimpleHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
    def setup(self):
        print('setup')
        context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
        context.load_cert_chain(certfile=‘cert.pem’)
        SSLSocket = context.wrap_socket(self.request, server_side=True)
        self.rfile = SSLSocket.makefile('rb', self.rbufsize)
        self.wfile = SSLSocket.makefile('wb', self.wbufsize)
        
if __name__ == "__main__":
    port = 443
    httpd = socketserver.TCPServer(("localhost", port), MyHTTPS_SimpleHTTPRequestHandler)
    print('https serving at port', port)
    httpd.serve_forever()

最后,要指出的是setup()和handle()都是在客戶端開始連接之后才被調用,從順序上來說setup()先於handle()。

 


HTTPS Client in python with SSL Authentication

import ssl, socket

key = mykeyfile
cert = mycertfile
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ssl_sock = ssl.wrap_socket(s,keyfile=key,certfile=cert,server_side=False)
ssl_sock.connect(("www.xxx.xxx", 443))
# This is where I call a method the server has exposed
ssl_sock.callServerMethod(arg1,arg2) 

 


How to do mutual certificate authentication with httplib2

up vote 4down voteaccepted

Here's the code my co-worker Dave St. Germain wrote to solve the problem:

import ssl import socket from httplib2 import has_timeout import httplib2 import socks class CertificateValidationError(httplib2.HttpLib2Error): pass def validating_sever_factory(ca_cert_file): # we need to define a closure here because we don't control # the arguments this class is instantiated with class ValidatingHTTPSConnection(httplib2.HTTPSConnectionWithTimeout): def connect(self): # begin copypasta from HTTPSConnectionWithTimeout "Connect to a host on a given (SSL) port." if self.proxy_info and self.proxy_info.isgood(): sock = socks.socksocket(socket.AF_INET, socket.SOCK_STREAM) sock.setproxy(*self.proxy_info.astuple()) else: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) if has_timeout(self.timeout): sock.settimeout(self.timeout) sock.connect((self.host, self.port)) # end copypasta try: self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, cert_reqs=ssl.CERT_REQUIRED, ca_certs=ca_cert_file ) except ssl.SSLError: # we have to capture the exception here and raise later because  # httplib2 tries to ignore exceptions on connect import sys self._exc_info = sys.exc_info() raise else: self._exc_info = None # this might be redundant server_cert = self.sock.getpeercert() if not server_cert: raise CertificateValidationError(repr(server_cert)) def getresponse(self): if not self._exc_info: return httplib2.HTTPSConnectionWithTimeout.getresponse(self) else: raise self._exc_info[1], None, self._exc_info[2] return ValidatingHTTPSConnection def do_request(url, method='GET', body=None, headers=None, keyfile=None, certfile=None, ca_certs=None, proxy_info=None, timeout=30): """ makes an http/https request, with optional client certificate and server certificate verification. returns response, content """ kwargs = {} h = httplib2.Http(proxy_info=proxy_info, timeout=timeout) is_ssl = url.startswith('https') if is_ssl and ca_certs: kwargs['connection_type'] = validating_sever_factory(ca_certs) if is_ssl and keyfile and certfile: h.add_certificate(keyfile, certfile, '') return h.request(url, method=method, body=body, headers=headers, **kwargs)
perhaps things have changed since your question, I am able to do mutual authentication withhttplib2 v0.7, as below:
import httplib2 h=httplib2.Http(ca_certs='ca.crt') h.add_certificate(key='client_private_key.pem', cert='cert_client.pem', domain='') try: resp, cont = h.request('https://mytest.com/cgi-bin/test.cgi') except Exception as e: print e

ssl certificate authentication in python

   s = socket.socket()  print "connecting..."  logging.debug("Connecting")  # Connect with SSL mutual authentication  # We only trust our server's CA, and it only trusts user certificates signed by it  c = ssl.wrap_socket(s, cert_reqs=ssl.CERT_REQUIRED,  ssl_version=ssl.PROTOCOL_SSLv3, ca_certs='ca.crt',  certfile='user.crt', keyfile='user.key')  c.connect((constants.server_addr, constants.port))
Generally, you've acquired the CA certificate via some out-of-band method, then saved it locally. Linux systems generally have a bundle of certificates for well-known, trusted CAs available under  or similar.
#!/usr/bin/env python

import httplib CERTFILE = '/home/robr/mycert' HOSTNAME = 'localhost' conn = httplib.HTTPSConnection( HOSTNAME, key_file = CERTFILE, cert_file = CERTFILE ) conn.putrequest('GET', '/ssltest/') conn.endheaders() response = conn.getresponse() print response.read()

"mycert" is a PEM formatted certificate file that includes both the public certificate and the private key. If you read the code, you will notice that you can keep the public and private in seperate files if you care to.

SSL authentication generally requires that you set up your own certificate authority. You want to make sure you are the only one giving out keys to your empire.

Apache needs to be set up to require SSL client authentication. In my httpd.conf file I have the following:

SSLCACertificatePath /etc/httpd/conf/ssl.crt SSLCACertificateFile /etc/httpd/conf/ssl.crt/myCA.crt SSLVerifyClient require SSLVerifyDepth 2 SSLRequireSSL

If you have SSLCACertificateFile defined elsewhere in your config file, you'll need to resolve the conflict. It seems that Apache cannot refer to more than one SSLCACertificateFile. Multiple CA certs can exist in one file, but you may not want everyone with certs from all of your accepted CAs access to all of your content.

So why use SSL client authentication? It's a convenient way to do client authentication between web-enabled applications. It's good for SOAP or XML-RPC implementations, or custom apps that communicate via HTTP/HTTPS.

References: http://httpd.apache.org/docs-2.0/ssl/ssl_howto.html http://www.pseudonym.org/ssl/ssl_cook.html

class HTTPSConnection( host[, port, key_file, cert_file]) A subclass of HTTPConnection that uses SSL for communication with secure servers. Default port is 443. key_file is the name of a PEM formatted file that contains your private key. cert_file is a PEM formatted certificate chain file.

Warning: This does not do any certificate verification! New in version 2.0.

If this doesn't do certificate verification, it seems to be very incomplete. Is there anyway to verify the certs or is this planned in a future release? The article/code snippet should state this defiency.


SSL Client Authentication over HTTPS (Python recipe)

A 16-line python application that demonstrates SSL client authentication over HTTPS. We also explain the basics of how to set up Apache to require SSL client authentication. This assumes at least Python-2.2 compiled with SSL support, and Apache with mod_ssl


On the server, I'm initializing the SSLContext with my private key, the certfile provided by the CA that I'm loading from the caroot.crt file. Now, when I initialize this with something like node, everything works fine (for example: Setting up SSL with node.js). My intentions were to set everything up the same way. My assumption is that during the handshake, the server is providing the client with a CA, just like my node server would. It seems like that's not the case. What am I doing wrong?

If ssl.CERT_REQUIRED isn't used, everything works perfectly, but I'm wanting to validate that the endpoint (server) is who they say they are.

# Server import socket import ssl context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) context.load_cert_chain(certfile='./path/to/certfile.crt', keyfile='./path/to/keyfile.pem') context.load_verify_locations('./path/to/caroot.crt') context.set_default_verify_paths() server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind(('', 23000)) server_socket.listen(socket.SOMAXCONN) def handle_client(ssl_socket): data = ssl_socket.read() while data: print("%s" % (str(data))) data = ssl_socket.read() while True: client_socket, addr = server_socket.accept() ssl_client_socket = context.wrap_socket(client_socket, server_side=True) handle_client(ssl_client_socket) # Client import socket import ssl context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) # I'm assuming this is not necessary, but I'd like to load the system provided CAs context.set_default_verify_paths() # Require CA validation to prevent MITM. context.verify_mode = ssl.CERT_REQUIRED client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ssl_client = context.wrap_socket(client_socket) ssl_client.connect(('', 23000)) ssl_client.send(bytes('hello, world!', 'UTF-8'))

As it turns out, my CA provided 3 different crt files. My solution was to append them in a specific order to generate the correct CA chain that was being passed to  For anyone using COMODO as a provider, here is a blog post:http://billpatrianakos.me/blog/2014/04/04/installing-comodo-positive-ssl-certs-on-apache-and-openssl/

Granted, that post is Apache specific, the premise is still the same. The command in question is:

cat COMODORSADomainValidationSecureServerCA.crt COMODORSAAddTrustCA.crt AddTrustExternalCARoot.crt > yourdomain.com.cer

You then do:

context.load_verify_locations('yourdomain.com.cer')

Everything now works as expected.

 

創建一個自簽名的 SSL 證書

  1. #### 使用 OpenSSL 創建自簽名證書


  2. ## 1.創建根證書的私鑰
  3. openssl genrsa -out ca.key 1024

  4. ## 2.使用私鑰創建根證書
  5. openssl req -new -x509 -days 36500 -key ca.key -out ca.crt -subj "/C=CN/ST=Fujian/L=Xiamen/O=Your Company Name/OU=Your Root CA"

  6. ## 3.創建服務器私鑰
  7. openssl genrsa -out server.key 1024

  8. ## 4.使用服務器私鑰創建證書請求文件
  9. openssl req -new -key server.key -out server.csr -subj "/C=CN/ST=Fujian/L=Xiamen/O=Your Company Name/OU=youwebsite.org/CN=yourwebsite.org"

  10. ## 5.准備工作
  11. mkdir -p demoCA/newcerts
  12. touch demoCA/index.txt
  13. echo '01' > demoCA/serial

  14. ## 6.創建服務器證書並使用ca根證書簽名
  15. openssl ca -in server.csr -out server.crt -cert ca.crt -keyfile ca.key


  16. ## ---查看不同格式文件的內容命令語法
  17. # openssl rsa -noout -text -in ca.key
  18. # openssl x509 -noout -text -in ca.crt
  19. # openssl rsa -noout -text -in server.key
  20. # openssl req -noout -text -in server.csr
  21. # openssl x509 -noout -text -in server.crt

  22. ## 創建證書最簡單方式
  23. # openssl req -new -x509 -days 365 -nodes -out cert.pem -keyout cert.key

 

python server

  1. import socket, ssl
  2. import time

  3. cacrtf="ca/ca.crt"
  4. crtf="ca/server.crt"
  5. keyf="ca/server.key"

  6. server_sc = socket.socket()
  7. server_sc.bind(('', 10023))
  8. server_sc.listen(5)

  9. newsocket, addr = server_sc.accept()
  10. sc = ssl.wrap_socket(newsocket,
  11.                      server_side=True,
  12.                      certfile=crtf,
  13.                      keyfile=keyf,
  14.                      ca_certs=cacrtf)

  15. data = sc.read()
  16. print data
  17. sc.write('Back time: ' + str(time.time()))

  18. sc.close()
  19. server_sc.close()

python client

  1. import socket, ssl, pprint
  2. import time

  3. cacrtf="ca/ca.crt"
  4. crtf="ca/server.crt"
  5. keyf="ca/server.key"

  6. socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  7. ssl_socket = ssl.wrap_socket(socket, ca_certs=cacrtf, cert_reqs=ssl.CERT_REQUIRED)
  8. ssl_socket.connect(('127.0.0.1', 10023))

  9. print repr(ssl_socket.getpeername())
  10. print ssl_socket.cipher()
  11. print pprint.pformat(ssl_socket.getpeercert())

  12. ssl_socket.write("Time: %s\r\n" % time.time())

  13. data = ssl_socket.read()
  14. print data

  15. ssl_socket.close()


python ssl socket 的使用(服務器+客戶端)


首先,使用如下命令生成證書和key:

openssl req -new -x509 -days 365 -nodes -out cert.pem -keyout key.pem

 

接下來實現服務器:

 

 

import socket, ssl,time

context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
context.load_cert_chain(certfile="cert.pem", keyfile="key.pem")

bindsocket = socket.socket()
bindsocket.bind(('191.8.1.235', 10023))
bindsocket.listen(5)

def do_something(connstream, data):
    #print("data length:",len(data))

    return True

def deal_with_client(connstream):
    t_recv=0
    t_send=0
    n = 0
    t1=time.clock()
    data = connstream.recv(1024)
    t2=time.clock()
    print("receive time:",t2-t1)
    # empty data means the client is finished with us
    while data:
        if not do_something(connstream, data):
            # we'll assume do_something returns False
            # when we're finished with client
            break
        n = n + 1
        t1=time.clock()
        connstream.send(b'b'*1024)
        t2=time.clock()
        t_send += t2-t1
        print("send time:",t2-t1)
        t1=time.clock()
        data = connstream.recv(1024)
        t2=time.clock()
        t_recv +=t2-t1
        print("receive time:",t2-t1)
    print("avg send time:",t_send/n,"avg receive time:",t_recv/n)
    # finished with client

while True:
    newsocket, fromaddr = bindsocket.accept()
    connstream = context.wrap_socket(newsocket, server_side=True)
    try:
        deal_with_client(connstream)
    finally:
        connstream.shutdown(socket.SHUT_RDWR)
        connstream.close()

 

客戶端:

import socket, ssl, pprint,time

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# require a certificate from the server
ssl_sock = ssl.wrap_socket(s,
                           ca_certs="cert.pem",
                           cert_reqs=ssl.CERT_REQUIRED)
ssl_sock.connect(('191.8.1.235', 10023))

pprint.pprint(ssl_sock.getpeercert())
# note that closing the SSLSocket will also close the underlying socket
n=0
t_send=0
t_recv=0
while n <1000:
    n = n+1
    t1=time.clock()
    ssl_sock.send(b'a'*100)
    t2=time.clock()
    t_send += t2-t1 
    print("send time:",t2-t1)
    t1=time.clock()
    data=ssl_sock.recv(1024)
    t2=time.clock()
    t_recv += t2-t1
    print("receive time:",t2-t1)
    #print(len(data))
print("avg send time:",t_send/n,"avg receive time:",t_recv/n)
#ssl_sock.send(b'')
ssl_sock.close()


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM