python實現aes加密解密,RSA簽名和驗籤,RSA加密解密,並呼叫介面

G8bao7發表於2014-09-29
文章來自:http://blog.csdn.net/kevin6216/article/details/7573753

用python實現呼叫介面的示例程式碼,過程涉及到很多的加密演算法,值得分享一下。

首先公鑰和私鑰如何生成,並且能相容java平臺,嘗試了很多方法。最終決定用openssl命令
前提,需要安裝openssl,Crypto庫
生成公鑰私鑰對過程:
生成私鑰:

  1. openssl genrsa -out rsa_private_key.pem 1024
根據私鑰生成公鑰:

  1. openssl rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout
這時候的私鑰還不能直接被使用,需要進行PKCS#8編碼


  1. openssl pkcs8 -topk8 -in rsa_private_key.pem -out pkcs8_rsa_private_key.pem -nocrypt

命令中指明瞭輸入私鑰檔案為rsa_private_key.pem,輸出私鑰檔案為pkcs8_rsa_private_key.pem,不採用任何二次加密(-nocrypt)
這時候就獲得了一對公鑰和私鑰,只要拿到對方的公鑰,用自己的公鑰的格式替換就可以使用啦~~ 

我們最好把全域性變數給提出來,便於管理。這樣子就不用改程式碼都改一遍了


檔案Gl.py


  1. #!-*- coding:utf--*- 
  2. ''
  3. Created on 2013-6-15 
  4.   
  5. @author: shangwei 
  6. ''' 
  7. ''
  8. 全域性變數 
  9. ''' 
  10. from Crypto.PublicKey import RSA 
  11. ''
  12. publickey為對方的公鑰 
  13. privatekey為商戶自己的私鑰 
  14. ''' 
  15. publickey = RSA.importKey(open('rsa_public_key.pem','r').read()) 
  16. privatekey=RSA.importKey(open('pkcs8_rsa_private_key.pem','r').read()) 
  17. merchantaccount='YB010000000xx' 
  18. URL='xxx.xxx.com'


  1. #!-*- coding:utf--*- 
  2.     ''
  3.     Created on 2013-5-24 
  4.       
  5.     @author: shangwei 
  6.     ''' 
  7.     from Crypto import Random 
  8.     from Crypto.Cipher import PKCS1_v1_5 
  9.     from Crypto.Hash import SHA 
  10.     from hashlib import sha1 
  11.     from rsa import key, common, encrypt 
  12.     from urllib import urlencode 
  13.     import base64 
  14.     import hmac 
  15.     from Crypto.PublicKey import RSA 
  16.     import urllib 
  17.     import urllib2 
  18.     import time 
  19.     import json 
  20.     from Crypto.Signature import PKCS1_v1_5 as pk 
  21.     import Gl 
  22.     class MerchantAPI: 
  23.           
  24.         def doPost(self,url,values): 
  25.             ''
  26.             post請求 
  27.             引數URL 
  28.             字典型別的引數 
  29.             ''' 
  30.             req = urllib2.Request(url) 
  31.             data = urllib.urlencode(values) 
  32.             res = urllib2.urlopen(req, data) 
  33.             ret = res.read() 
  34.             return ret 
  35.       
  36.           
  37.         def doGet(self,url,values): 
  38.             ''
  39.             get請求 
  40.             引數URL 
  41.             字典型別的引數 
  42.             ''' 
  43.             REQUEST = url + "?" + urllib.urlencode(values) 
  44.             ret = urllib2.urlopen(REQUEST).read() 
  45.             return ret 
  46.             
  47.         @staticmethod 
  48.         def _pkcs7padding(data): 
  49.             ""
  50.             對齊塊 
  51.             size 16 
  52.             999999999=>9999999997777777 
  53.             """ 
  54.             size = AES.block_size 
  55.             count = size - len(data)%size 
  56.             if count: 
  57.                 data+=(chr(count)*count) 
  58.             return data 
  59.           
  60.           
  61.       
  62.           
  63.           
  64.         @staticmethod 
  65.         def _depkcs7padding(data): 
  66.             ""
  67.             反對齊 
  68.             """ 
  69.             newdata = '' 
  70.             for c in data: 
  71.                 if ord(c) > AES.block_size: 
  72.                     newdata+=
  73.             return newdata 
  74.           
  75.           
  76.         ''
  77.         aes加密base64編碼 
  78.         ''' 
  79.         def aes_base64_encrypt(self,data,key): 
  80.               
  81.             ""
  82.             @summary: 
  83.                 1. pkcs7padding 
  84.                 2. aes encrypt 
  85.                 3. base64 encrypt 
  86.             @return: 
  87.                 string 
  88.             """ 
  89.             cipher = AES.new(key) 
  90.             return base64.b64encode(cipher.encrypt(self._pkcs7padding(data))) 
  91.       
  92.       
  93.         def base64_aes_decrypt(self,data,key): 
  94.             ""
  95.             1. base64 decode 
  96.             2. aes decode 
  97.             3. dpkcs7padding 
  98.             """ 
  99.             cipher = AES.new(key) 
  100.             return self._depkcs7padding(cipher.decrypt(base64.b64decode(data))) 
  101.               
  102.         ''
  103.         rsa加密 
  104.         ''' 
  105.         def rsa_base64_encrypt(self,data,key): 
  106.             ''
  107.             1. rsa encrypt 
  108.             2. base64 encrypt 
  109.             ''' 
  110.             cipher = PKCS1_v1_5.new(key) 
  111.             return base64.b64encode(cipher.encrypt(data)) 
  112.           
  113.         ''
  114.         rsa解密 
  115.         ''' 
  116.         def rsa_base64_decrypt(self,data,key): 
  117.             ''
  118.             1. base64 decrypt 
  119.             2. rsa decrypt 
  120.             示例程式碼 
  121.               
  122.            key = RSA.importKey(open('privkey.der').read()) 
  123.             >>> 
  124.             >>> dsize = SHA.digest_size 
  125.             >>> sentinel = Random.new().read(15+dsize) # Let's assume that average data length is 15 
  126.             >>> 
  127.             >>> cipher = PKCS1_v1_5.new(key) 
  128.             >>> message = cipher.decrypt(ciphertext, sentinel) 
  129.             >>> 
  130.             >>> digest = SHA.new(message[:-dsize]).digest() 
  131.             >>> if digest==message[-dsize:]: # Note how we DO NOT look for the sentinel 
  132.             >>> print "Encryption was correct." 
  133.             >>> else: 
  134.             >>> print "Encryption was not correct." 
  135.             ''
  136.             cipher = PKCS1_v1_5.new(key) 
  137.             return cipher.decrypt(base64.b64decode(data), Random.new().read(15+SHA.digest_size)) 
  138.               
  139.         ''' 
  140.         RSA簽名 
  141.         ''
  142.         def sign(self,signdata): 
  143.             ''' 
  144.             @param signdata: 需要簽名的字串 
  145.             ''
  146.               
  147.             h=SHA.new(signdata) 
  148.             signer = pk.new(Gl.privatekey) 
  149.             signn=signer.sign(h) 
  150.             signn=base64.b64encode(signn) 
  151.             return signn 
  152.              
  153.         ''' 
  154.         RSA驗籤 
  155.         結果:如果驗籤透過,則返回The signature is authentic 
  156.              如果驗籤不透過,則返回"The signature is not authentic." 
  157.         ''
  158.         def checksign(self,rdata): 
  159.               
  160.             signn=base64.b64decode(rdata.pop('sign')) 
  161.             signdata=self.sort(rdata) 
  162.             verifier = pk.new(Gl.publickey) 
  163.             if verifier.verify(SHA.new(signdata), signn): 
  164.                 print "The signature is authentic." 
  165.             else: 
  166.                 print "The signature is not authentic." 
  167.                
  168.           
  169.               
  170.               
  171.           
  172.         def sort(self,mes): 
  173.             ''' 
  174.             作用類似與java的treemap, 
  175.             取出key值,按照字母排序後將value拼接起來 
  176.             返回字串 
  177.             ''
  178.             _par = [] 
  179.              
  180.             keys=mes.keys() 
  181.             keys.sort() 
  182.             for v in keys: 
  183.                 _par.append(str(mes[v])) 
  184.             sep='
  185.             message=sep.join(_par) 
  186.             return message 
  187.           
  188.         ''' 
  189.         請求介面前的加密過程 
  190.         ''
  191.         def requestprocess(self,mesdata): 
  192.             ''' 
  193.             加密過程: 
  194.             1、將需要的引數mes取出key排序後取出value拼成字串signdata 
  195.             2、用signdata對商戶私鑰進行rsa簽名,生成簽名signn,並轉base64格式 
  196.             3、將簽名signn插入到mesdata的最後生成新的data 
  197.             4、用encryptkey16位常量對data進行AES加密後轉BASE64,生成機密後的data 
  198.             5、用對方公鑰publickey對encryptkey16位常量進行RSA加密BASE64編碼,生成加密後的encryptkey 
  199.             ''
  200.             signdata=self.sort(mesdata) 
  201.             print '需要簽名的排序後的字串為:'+signdata 
  202.             signn=self.sign(signdata) 
  203.       
  204.                 
  205.             mesdata['sign']=signn 
  206.             print mesdata 
  207.             encryptkey = '1234567890123456
  208.             data=self.aes_base64_encrypt(json.dumps(mesdata),encryptkey) 
  209.           
  210.             print '加密後的data='+data 
  211.             values={} 
  212.             values['merchantaccount']=Gl.merchantaccount 
  213.             values['data']=data 
  214.             values['encryptkey']=self.rsa_base64_encrypt(encryptkey,Gl.publickey) 
  215.             return values 
  216.           
  217.           
  218.         ''' 
  219.         對返回結果進行解密後輸出 
  220.         ''
  221.         def result_decrypt(self,result): 
  222.             ''' 
  223.             1、返回的結果json傳給data和encryptkey兩部分,都為加密後的 
  224.             2、用商戶私鑰對encryptkey進行RSA解密,生成解密後的encryptkey。參考方法:rsa_base64_decrypt 
  225.             3、用解密後的encryptkey對data進行AES解密。參考方法:base64_aes_decrypt 
  226.             ''
  227.             result=json.loads(result) 
  228.             kdata=result['data'] 
  229.             kencryptkey=result['encryptkey'] 
  230.             print '返回的加密後的data='+kdata 
  231.             print '返回的加密後的encryptkey='+kencryptkey 
  232.             cryptkey=self.rsa_base64_decrypt(kencryptkey,Gl.privatekey) 
  233.             print '解密後的encryptkey='+cryptkey 
  234.             rdata=self.base64_aes_decrypt(kdata,cryptkey) 
  235.             print '解密後的data='+rdata 
  236.             return rdata 
  237.          
  238.         def testCreditPayAsync(self): 
  239.             ''' 
  240.             生成公鑰私鑰對過程: 
  241.             生成私鑰:openssl genrsa -out rsa_private_key.pem 1024 
  242.             根據私鑰生成公鑰: openssl rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout 
  243.             這時候的私鑰還不能直接被使用,需要進行PKCS#8編碼: 
  244.             openssl pkcs8 -topk8 -in rsa_private_key.pem -out pkcs8_rsa_private_key.pem -nocrypt 
  245.             命令中指明瞭輸入私鑰檔案為rsa_private_key.pem,輸出私鑰檔案為pkcs8_rsa_private_key.pem,不採用任何二次加密(-nocrypt) 
  246.             加密過程: 
  247.             1、將需要的引數mes取出key排序後取出value拼成字串signdata 
  248.             2、用signdata對商戶私鑰進行rsa簽名,生成簽名signn,並轉base64格式 
  249.             3、將簽名signn插入到mes的最後生成新的data 
  250.             4、用encryptkey16位常量對data進行AES加密後轉BASE64,生成機密後的data 
  251.             5、用對方公鑰publickey對encryptkey16位常量進行RSA加密BASE64編碼,生成加密後的encryptkey 
  252.             6、將merchantaccount,第四部加密後的data,第五步加密後的encryptkey作為引數post請求給URL http://xxxx/xxx/api/xxx/xxx/xxx/xxx 
  253.             7、返回的結果json傳給data和encryptkey兩部分,都為加密後的 
  254.             8、用商戶私鑰對encryptkey進行RSA解密,生成解密後的encryptkey。參考方法:rsa_base64_decrypt 
  255.             9、用解密後的encryptkey對data進行AES解密。參考方法:base64_aes_decrypt 
  256.             ''
  257.             transtime=int(time.time()) 
  258.             od=str(random.randint(10, 100000)) 
  259.             mesdata={"merchantaccount":Gl.merchantaccount,"cardno":"xxxx758xxxx23xxxx","validthru":"04xx","cvv2":"200","phone":"1581xxxxxxx", 
  260.     "orderid":"33hhkssseef3u"+od,"transtime":transtime,"currency":156,"amount":2,"productcatalog":"1","productname":"","productdesc":"", 
  261.     "userip":"192.168.5.251","identityid":"ee","identitytype":6,"other":"","callbackurl":""} 
  262.             values=self.requestprocess(mesdata) 
  263.             url='http://'+Gl.URL+'/xxxx
  264.             print url 
  265.             result=self.doPost(url, values) 
  266.             print result 
  267.             rdata=json.loads(self.result_decrypt(result)) 
  268.             self.checksign(rdata) 
  269.     if __name__=='__main__

知識點:
除錯程式碼的時候也遇到了一些小問題和技巧
import的時候,如果有同名的類可以起個別名。不然會有報錯,告訴這個類找不到某個方法from Crypto.Cipher import PKCS1_v1_5,from Crypto.Signature import PKCS1_v1_5 as pk,這個需要注意一下
另外,如果想將字典內的單引號都變為雙引號,可以用json.dumps方法

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/26250550/viewspace-1284723/,如需轉載,請註明出處,否則將追究法律責任。

相關文章