作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net
微博:weibo.com/glinuxer
QQ技术群:4367710
github: https://github.com/gfreewind

      刚毕业时,一方面不知道自己的方向在哪,另一方面对什么都有兴趣,于是什么东西都学一下,当时各种脚本基本上都会写一点。后来可能做的事情越来越专了,基本上就只写C代码了,偶尔由于个人爱好,也会写一些C++。但脚本语言基本上忘光光了,比如Perl,TCL等。
       这段时间觉得Python有点意思,上手快,库丰富,可以快速做出功能,很适合搭建原型。所以有意识的让自己多写一些python代码。前几天给一个朋友,做个小功能,一个AES加解密服务。总共用了大概一天左右的时间,基本完成了第一版。
       因为我还是python代码的初学者,所以想把代码分享给大家,希望大家多提宝贵意见。下面是github的地址,方便大家阅读最新的代码(由于我可能会实时更新):https://github.com/gfreewind/py_aes_http_service
       为了方便大家,也在本文中贴出一份当前的代码:

  1. #!/usr/bin/python

  2. __author__ = "Feng Gao gfree.wind@gmail.com"
  3. __copyright__ = "Copyright (C) 2015 Feng Gao"
  4. __license__ = "GPL"
  5. __version__ = "1.0"

  6. import sys, socket, select, json
  7. import getopt, base64, binascii
  8. import commands

  9. from BaseHTTPServer import BaseHTTPRequestHandler
  10. from StringIO import StringIO
  11. from Crypto.Cipher import AES
  12. from Crypto import Random

  13. DEBUG = False
  14. USE_OPENSSL = False
  15. SAVE_DETAIL = False
  16. LISTEN_PORT = int(80)
  17. ENCRYPT_COUNT = int(1)
  18. AES_KEY = "1234567890abcdef"
  19. AES_BLOCK_SIZE = 16
  20. AES_MODE = AES.MODE_CBC
  21. AES_MODE_STR = "cbc"
  22. AES_IV_STR = ""
  23. OPENSSL_AES_MODE = "aes-256-cbc"
  24. OPENSSL_PASSWORD = "1234567890"


  25. AES_PAD = lambda s: s + (AES_BLOCK_SIZE - len(s) % AES_BLOCK_SIZE) * chr(AES_BLOCK_SIZE - len(s) % AES_BLOCK_SIZE)
  26. AES_UNPAD = lambda s : s[:-ord(s[len(s)-1:])]

  27. def ConstructErrorResponse(err):
  28.     lenth_str = "Content-Length: "+str(len(err))+"\r\n\r\n"
  29.     err_header = "HTTP/1.1 400 \r\nContent-Type: text/html\r\n"+lenth_str+err
  30.     return err_header
  31.     
  32. def ConstructResponse(payload):
  33.     lenth_str = "Content-Length: "+str(len(payload))+"\r\n\r\n"
  34.     response = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n"+lenth_str+payload
  35.     return response

  36. class AESCipher:
  37.     def __init__(self, key, bs):
  38.         self.key = key
  39.         self.bs = bs
  40.     
  41.     def encrypt(self, text):
  42.         if USE_OPENSSL:
  43.             cmd = "echo -n \""+text+"\" | openssl enc -" + OPENSSL_AES_MODE+" -pass pass:"+OPENSSL_PASSWORD+" | openssl base64"
  44.             if DEBUG:
  45.                 print cmd
  46.             ret, enc = commands.getstatusoutput(cmd)
  47.         else:
  48.             text = AES_PAD(text)
  49.             if (DEBUG):
  50.                 print "PAD text size is", len(text)
  51.                 print "PAD text is", text
  52.                 print "AES.block size is", AES.block_size
  53.             if (len(AES_IV_STR)):
  54.                 iv = AES_IV_STR
  55.             else:
  56.                 iv = Random.new().read(AES.block_size)
  57.             cipher = AES.new(self.key, AES_MODE, iv)
  58.             if (len(AES_IV_STR)):
  59.                 enc = base64.b64encode(cipher.encrypt(text))
  60.             else:
  61.                 enc = base64.b64encode(iv + cipher.encrypt(text))
  62.         if SAVE_DETAIL:
  63.             global ENCRYPT_COUNT
  64.             ENCRYPT_COUNT = ENCRYPT_COUNT+1
  65.             print str(ENCRYPT_COUNT)+". text: "+text+" ciper: "+enc
  66.         return enc

  67.     def decrypt(self, enc):
  68.         if (DEBUG):
  69.             print "enc is",enc
  70.         if USE_OPENSSL:
  71.             cmd = "echo \""+enc+ "\" | openssl base64 -d | openssl enc -"+ OPENSSL_AES_MODE+" -pass pass:"+OPENSSL_PASSWORD+" -d"
  72.             if DEBUG:
  73.                 print cmd
  74.             ret, text = commands.getstatusoutput(cmd)
  75.             return text
  76.         else:
  77.             enc = base64.b64decode(enc)
  78.             if (len(AES_IV_STR)):
  79.                 iv = AES_IV_STR
  80.                 cipher = AES.new(self.key, AES_MODE, iv)
  81.                 return AES_UNPAD(cipher.decrypt(enc))
  82.             else:
  83.                 iv = enc[:AES.block_size]
  84.                 cipher = AES.new(self.key, AES_MODE, iv)
  85.                 return AES_UNPAD(cipher.decrypt(enc[AES.block_size:]))

  86. class HTTPRequest(BaseHTTPRequestHandler):
  87.     def __init__(self, request_text):
  88.         self.rfile = StringIO(request_text)
  89.         self.raw_requestline = self.rfile.readline()
  90.         self.error_code = self.error_message = None
  91.         self.parse_request()

  92.     def send_error(self, code, message):
  93.         self.error_code = code
  94.         self.error_message = message
  95.     
  96.         
  97. class HTTPHeaders():
  98.     def __init__(self, data):
  99.         self.parsed_http = HTTPRequest(data)
  100.         self.uri = ""
  101.         self.params = ""
  102.         
  103.         if ('?' in self.parsed_http.path):
  104.             self.uri, param_str = self.parsed_http.path.split('?')
  105.             params = param_str.split('&')
  106.             self.params = {}
  107.             for str in params:
  108.                 name, value = str.split('=',1)
  109.                 self.params[name] = value
  110.         else:
  111.             self.uri = self.parsed_http.path
  112.         if (DEBUG):
  113.             self.show_headers()
  114.         
  115.     def show_headers(self):
  116.         parsed_http = self.parsed_http
  117.         headers = parsed_http.headers
  118.         print "Command:", parsed_http.command
  119.         print "Request_version:", parsed_http.request_version
  120.         print "Requestline:", parsed_http.requestline
  121.         print "Path:", parsed_http.path
  122.         print "URI:", self.uri
  123.         print "Params:", self.params
  124.         print "Headers:"
  125.         for (h,v) in parsed_http.headers.items():
  126.             print "\t", h, ": ", v
  127.     
  128.     def get_header_value(self, header_name):
  129.         headers = self.parsed_http.headers
  130.         if (not headers.has_key(header_name)):
  131.             return None
  132.         return headers[header_name]
  133.         
  134.     def get_param_value(self, param):
  135.         if param in self.params:
  136.             return self.params[param]
  137.         else:
  138.             return None            
  139.     
  140.         
  141. class HTTPClient():
  142.     def __init__(self, socket, addr, aes):
  143.         self.header = HTTPRequest("")
  144.         self.socket = socket
  145.         self.addr = addr
  146.         self.aes = aes
  147.         self.fileno = socket.fileno()
  148.         self.header_is_ok = False
  149.         self.request = ""
  150.         self.data_len = "0"
  151.             
  152.     def join_epoll(self, epoll):
  153.         if (DEBUG):
  154.             print self.addr, "is connected"
  155.         self.socket.setblocking(0)
  156.         epoll.register(self.fileno, select.EPOLLIN|select.EPOLLHUP)
  157.         self.epoll = epoll
  158.     
  159.     def leave_epoll(self):
  160.         if (DEBUG):
  161.             print self.addr, "is disconnected"
  162.         self.epoll.unregister(self.fileno)
  163.         self.socket.close();
  164.         
  165.     def get_fileno(self):
  166.         return self.fileno
  167.         
  168.     def parse_http_header(self):
  169.         if ("\r\n\r\n" in self.request):
  170.             sep_index = self.request.find("\r\n\r\n")
  171.             sep_index += len("\r\n\r\n")
  172.         elif ("\n\n" in self.request):
  173.             sep_index = self.request.find("\n\n")
  174.             sep_index += len("\n")
  175.         else:
  176.             print "Unexpected error"
  177.             exit(-1)
  178.         
  179.         header = self.request[:sep_index]
  180.         self.request = self.request[sep_index:]
  181.         self.http_headers = HTTPHeaders(header)
  182.         self.header_is_ok = True
  183.         
  184.     def read_data(self):
  185.         data = self.socket.recv(1024)
  186.         if (0 == len(data)):
  187.             self.leave_epoll()
  188.         else :
  189.             self.request += data
  190.             
  191.             if (not self.header_is_ok):
  192.                 if (self.header_is_completed()):
  193.                     self.parse_http_header()
  194.                     self.data_len = self.http_headers.get_header_value("Content-Length")
  195.                     if (None == self.data_len):
  196.                         self.data_len = "0"
  197.             
  198.             data_len = int(self.data_len)
  199.             if (self.header_is_ok and data_len <= len(self.request)):
  200.                 json_data = self.request[:data_len]
  201.                 self.request = self.request[data_len:]
  202.                 if (DEBUG):
  203.                     print "Payload:", json_data
  204.                 
  205.                 if (data_len):
  206.                     try:
  207.                         json_data = json.loads(json_data)
  208.                     except ValueError:
  209.                         print "Invalid json data", json_data
  210.                 
  211.                 # Get action
  212.                 action = self.http_headers.get_param_value("action")
  213.                 if ("enc" == action):
  214.                     text = self.http_headers.get_param_value("text")
  215.                     if (text):
  216.                         ciper = self.aes.encrypt(text)
  217.                         payload = {}
  218.                         payload["text"] = text
  219.                         payload["ciper"] = ciper
  220.                         jason_data = json.dumps(payload)
  221.                         response = ConstructResponse(jason_data)
  222.                     else:
  223.                         response = ConstructErrorResponse("No text param")
  224.                 elif ("dec" == action):
  225.                     ciper = self.http_headers.get_param_value("ciper")
  226.                     if (ciper):
  227.                         text = self.aes.decrypt(ciper)
  228.                         payload = {}
  229.                         payload["text"] = text
  230.                         payload["ciper"] = ciper
  231.                         jason_data = json.dumps(payload)
  232.                         response = ConstructResponse(jason_data)
  233.                     else:
  234.                         response = ConstructErrorResponse("No ciper param")
  235.                 else:
  236.                     if (DEBUG):
  237.                         print "No supported operation"
  238.                     response = ConstructErrorResponse("No supported aciton or no specify action")
  239.                     
  240.                 self.socket.send(response)
  241.                 self.leave_epoll()            
  242.             
  243.     def header_is_completed(self):
  244.         if (("\r\n\r\n" in self.request) or ("\n\n" in self.request)):
  245.             return True
  246.         else:
  247.             return False
  248.             
  249. def usage():
  250.     print "-h: Show the help"
  251.     print "-l: Specify listen port"
  252.     print "-k: Specify AES key"
  253.     print "-e: Specify AES encrypt mode. default is cbc"
  254.     print "-s: Specify AES block size"
  255.     print "-v: Specify AES IV. default is random string"
  256.     print "-o: Use openssl to encrypt or decrypt. Must specify password too"
  257.     print "-p: Specify password. Only used with openssl"
  258.     print "-m: Specify openssl enc mode. Only used with openssl"
  259.     print "-t: Save the encrypt count and record"
  260.     print "-d: Debug mode"

  261. if __name__ == "__main__":

  262.     try:
  263.         opts, args = getopt.getopt(sys.argv[1:], "hl:k:e:s:v:p:tdo")
  264.     except getopt.GetoptError as err:
  265.         print str(err)
  266.         usage()
  267.         sys.exit(1)
  268.     for o, a in opts:
  269.         if (o == "-h"):
  270.             usage()
  271.             sys.exit()
  272.         elif (o == "-l"):
  273.             LISTEN_PORT = int(a)
  274.         elif (o == "-k"):
  275.             AES_KEY = a
  276.             if (len(AES_KEY) != 16 and len(AES_KEY) != 24 and len(AES_KEY) != 32):
  277.                 print "AES key must be either 16, 24, or 32 bytes long"
  278.                 sys.exit(1)
  279.         elif (o == "-e"):
  280.             if ("ecb" == a):
  281.                 AES_MODE = AES.MODE_ECB
  282.             elif ("cbc" == a):
  283.                 AES_MODE = AES.MODE_CBC
  284.             elif ("cfb" == a):
  285.                 AES_MODE == AES.MODE_CFB
  286.             elif ("ofb" == a):
  287.                 AES_MODE == AES.MODE_OFB
  288.             elif ("ctr" == a):
  289.                 AES_MODE == AES.MODE_CTR
  290.             else:
  291.                 print "Only support (ecb|cbc|cfb|ofb|ctr)"
  292.                 sys.exit(1)
  293.             AES_MODE_STR = a
  294.         elif (o == "-s"):
  295.             AES_BLOCK_SIZE = int(a)
  296.         elif (o == "-v"):
  297.             AES_IV_STR = a
  298.             if (len(AES_IV_STR) != 16):
  299.                 print "AES IV size must be 16"
  300.                 sys.exit(1)
  301.         elif (o == "-p"):
  302.             OPENSSL_PASSWORD = a
  303.         elif (o == "-m"):
  304.             OPENSSL_AES_MODE = a
  305.         elif (o == "-o"):
  306.             USE_OPENSSL = True
  307.         elif (o == "-t"):
  308.             SAVE_DETAIL = True
  309.             ENCRYPT_COUNT = int(0)
  310.         elif (o == "-d"):
  311.             DEBUG = True
  312.         else:
  313.             assert False, "unhandled option"
  314.     
  315.     if (USE_OPENSSL and 0 == len(OPENSSL_PASSWORD)):
  316.         print "Must set password when use openssl"
  317.         sys.exit(1)

  318.     master_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  319.     master_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  320.     master_socket.bind(('', LISTEN_PORT))
  321.     master_socket.listen(5)
  322.     master_socket.setblocking(0)
  323.     
  324.     ass_ciper = AESCipher(AES_KEY, AES_BLOCK_SIZE)
  325.     
  326.     if not USE_OPENSSL:
  327.         if DEBUG:
  328.             print "Only support aes-128. Please use -o option to use openssl if you want to use other bits"
  329.     
  330.     if (DEBUG):
  331.         print "The AES server is listenning the", LISTEN_PORT, "port now"
  332.         print "The AES key is", AES_KEY, binascii.b2a_hex(AES_KEY)
  333.         print "The AES mode is", AES_MODE_STR
  334.         print "The AES block size is", AES_BLOCK_SIZE
  335.         if (len(AES_IV_STR)):
  336.             print "The AES iv is", AES_IV_STR, binascii.b2a_hex(AES_IV_STR)
  337.         else:
  338.             print "The AES iv uses random string"
  339.         if (USE_OPENSSL):
  340.             print "Use openssl to encrypt/decrypt"
  341.             print "password is", OPENSSL_PASSWORD
  342.             print "openssl aes mode is", OPENSSL_AES_MODE
  343.         if (SAVE_DETAIL):
  344.             print "Save details"

  345.     epoll = select.epoll()
  346.     epoll.register(master_socket.fileno(), select.EPOLLIN)

  347.     try:
  348.         http_clients = {}
  349.         
  350.         while (True):
  351.             request = ""
  352.             events = epoll.poll(3) # wait 3 seconds
  353.             for fileno, event in events:
  354.                 if fileno == master_socket.fileno():
  355.                     worker_socket, client_addr = master_socket.accept()
  356.                     http_client = HTTPClient(worker_socket, client_addr, ass_ciper)
  357.                     http_client.join_epoll(epoll)
  358.                     http_clients[http_client.get_fileno()] = http_client
  359.                 elif (event & (select.EPOLLIN|select.EPOLLHUP)):
  360.                     http_client = http_clients[fileno]
  361.                     http_client.read_data()
  362.                 else:
  363.                     print "Unhandled event", event
  364.     finally:
  365.         epoll.unregister(master_socket.fileno())
  366.         master_socket.close();





12-17 02:27