优化工单模块,增加企微模块
This commit is contained in:
120
sg_wechat_enterprise/we_api/crypto/__init__.py
Normal file
120
sg_wechat_enterprise/we_api/crypto/__init__.py
Normal file
@@ -0,0 +1,120 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
wechatpy.crypto
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
This module provides some crypto tools for WeChat and WeChat enterprise
|
||||
|
||||
:copyright: (c) 2014 by messense.
|
||||
:license: MIT, see LICENSE for more details.
|
||||
"""
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
import time
|
||||
import base64
|
||||
|
||||
from wechatpy.utils import to_text, to_binary, WeChatSigner
|
||||
from wechatpy.exceptions import (
|
||||
InvalidAppIdException,
|
||||
InvalidSignatureException
|
||||
)
|
||||
from wechatpy.crypto.base import BasePrpCrypto
|
||||
|
||||
|
||||
def _get_signature(token, timestamp, nonce, encrypt):
|
||||
signer = WeChatSigner()
|
||||
signer.add_data(token, timestamp, nonce, encrypt)
|
||||
return signer.signature
|
||||
|
||||
|
||||
class PrpCrypto(BasePrpCrypto):
|
||||
|
||||
def encrypt(self, text, app_id):
|
||||
return self._encrypt(text, app_id)
|
||||
|
||||
def decrypt(self, text, app_id):
|
||||
return self._decrypt(text, app_id, InvalidAppIdException)
|
||||
|
||||
|
||||
class BaseWeChatCrypto(object):
|
||||
|
||||
def __init__(self, token, encoding_aes_key, _id):
|
||||
encoding_aes_key = to_binary(encoding_aes_key + '=')
|
||||
self.key = base64.b64decode(encoding_aes_key)
|
||||
assert len(self.key) == 32
|
||||
self.token = token
|
||||
self._id = _id
|
||||
|
||||
def _check_signature(self,
|
||||
signature,
|
||||
timestamp,
|
||||
nonce,
|
||||
echo_str,
|
||||
crypto_class=None):
|
||||
_signature = _get_signature(self.token, timestamp, nonce, echo_str)
|
||||
if _signature != signature:
|
||||
raise InvalidSignatureException()
|
||||
pc = crypto_class(self.key)
|
||||
return pc.decrypt(echo_str, self._id)
|
||||
|
||||
def _encrypt_message(self,
|
||||
msg,
|
||||
nonce,
|
||||
timestamp=None,
|
||||
crypto_class=None):
|
||||
from wechatpy.replies import BaseReply
|
||||
|
||||
xml = """<xml>
|
||||
<Encrypt><![CDATA[{encrypt}]]></Encrypt>
|
||||
<MsgSignature><![CDATA[{signature}]]></MsgSignature>
|
||||
<TimeStamp>{timestamp}</TimeStamp>
|
||||
<Nonce><![CDATA[{nonce}]]></Nonce>
|
||||
</xml>"""
|
||||
if isinstance(msg, BaseReply):
|
||||
msg = msg.render()
|
||||
timestamp = timestamp or to_binary(int(time.time()))
|
||||
pc = crypto_class(self.key)
|
||||
encrypt = to_text(pc.encrypt(msg, self._id))
|
||||
signature = _get_signature(self.token, timestamp, nonce, encrypt)
|
||||
return to_text(xml.format(
|
||||
encrypt=encrypt,
|
||||
signature=signature,
|
||||
timestamp=timestamp,
|
||||
nonce=nonce
|
||||
))
|
||||
|
||||
def _decrypt_message(self,
|
||||
msg,
|
||||
signature,
|
||||
timestamp,
|
||||
nonce,
|
||||
crypto_class=None):
|
||||
if not isinstance(msg, dict):
|
||||
import xmltodict
|
||||
|
||||
msg = xmltodict.parse(to_text(msg))['xml']
|
||||
|
||||
encrypt = msg['Encrypt']
|
||||
_signature = _get_signature(self.token, timestamp, nonce, encrypt)
|
||||
if _signature != signature:
|
||||
raise InvalidSignatureException()
|
||||
pc = crypto_class(self.key)
|
||||
return pc.decrypt(encrypt, self._id)
|
||||
|
||||
|
||||
class WeChatCrypto(BaseWeChatCrypto):
|
||||
|
||||
def __init__(self, token, encoding_aes_key, app_id):
|
||||
super(WeChatCrypto, self).__init__(token, encoding_aes_key, app_id)
|
||||
self.app_id = app_id
|
||||
|
||||
def encrypt_message(self, msg, nonce, timestamp=None):
|
||||
return self._encrypt_message(msg, nonce, timestamp, PrpCrypto)
|
||||
|
||||
def decrypt_message(self, msg, signature, timestamp, nonce):
|
||||
return self._decrypt_message(
|
||||
msg,
|
||||
signature,
|
||||
timestamp,
|
||||
nonce,
|
||||
PrpCrypto
|
||||
)
|
||||
52
sg_wechat_enterprise/we_api/crypto/base.py
Normal file
52
sg_wechat_enterprise/we_api/crypto/base.py
Normal file
@@ -0,0 +1,52 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
import struct
|
||||
import socket
|
||||
import base64
|
||||
|
||||
from wechatpy.utils import to_text, to_binary, random_string, byte2int
|
||||
from wechatpy.crypto.pkcs7 import PKCS7Encoder
|
||||
try:
|
||||
from wechatpy.crypto.cryptography import WeChatCipher
|
||||
except ImportError:
|
||||
try:
|
||||
from wechatpy.crypto.pycrypto import WeChatCipher
|
||||
except ImportError:
|
||||
raise Exception('You must install either cryptography or PyCrypto!')
|
||||
|
||||
|
||||
class BasePrpCrypto(object):
|
||||
|
||||
def __init__(self, key):
|
||||
self.cipher = WeChatCipher(key)
|
||||
|
||||
def get_random_string(self):
|
||||
return random_string(16)
|
||||
|
||||
def _encrypt(self, text, _id):
|
||||
text = to_binary(text)
|
||||
tmp_list = []
|
||||
tmp_list.append(to_binary(self.get_random_string()))
|
||||
length = struct.pack(b'I', socket.htonl(len(text)))
|
||||
tmp_list.append(length)
|
||||
tmp_list.append(text)
|
||||
tmp_list.append(to_binary(_id))
|
||||
|
||||
text = b''.join(tmp_list)
|
||||
text = PKCS7Encoder.encode(text)
|
||||
|
||||
ciphertext = to_binary(self.cipher.encrypt(text))
|
||||
return base64.b64encode(ciphertext)
|
||||
|
||||
def _decrypt(self, text, _id, exception=None):
|
||||
text = to_binary(text)
|
||||
plain_text = self.cipher.decrypt(base64.b64decode(text))
|
||||
padding = byte2int(plain_text[-1])
|
||||
content = plain_text[16:-padding]
|
||||
xml_length = socket.ntohl(struct.unpack(b'I', content[:4])[0])
|
||||
xml_content = to_text(content[4:xml_length + 4])
|
||||
from_id = to_text(content[xml_length + 4:])
|
||||
if from_id != _id:
|
||||
exception = exception or Exception
|
||||
raise exception()
|
||||
return xml_content
|
||||
23
sg_wechat_enterprise/we_api/crypto/cryptography.py
Normal file
23
sg_wechat_enterprise/we_api/crypto/cryptography.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
|
||||
|
||||
class WeChatCipher(object):
|
||||
|
||||
def __init__(self, key):
|
||||
backend = default_backend()
|
||||
self.cipher = Cipher(
|
||||
algorithms.AES(key),
|
||||
modes.CBC(key[:16]),
|
||||
backend=backend
|
||||
)
|
||||
|
||||
def encrypt(self, plaintext):
|
||||
encryptor = self.cipher.encryptor()
|
||||
return encryptor.update(plaintext) + encryptor.finalize()
|
||||
|
||||
def decrypt(self, ciphertext):
|
||||
decryptor = self.cipher.decryptor()
|
||||
return decryptor.update(ciphertext) + decryptor.finalize()
|
||||
23
sg_wechat_enterprise/we_api/crypto/pkcs7.py
Normal file
23
sg_wechat_enterprise/we_api/crypto/pkcs7.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
from wechatpy.utils import to_binary, byte2int
|
||||
|
||||
|
||||
class PKCS7Encoder(object):
|
||||
block_size = 32
|
||||
|
||||
@classmethod
|
||||
def encode(cls, text):
|
||||
length = len(text)
|
||||
padding_count = cls.block_size - length % cls.block_size
|
||||
if padding_count == 0:
|
||||
padding_count = cls.block_size
|
||||
padding = to_binary(chr(padding_count))
|
||||
return text + padding * padding_count
|
||||
|
||||
@classmethod
|
||||
def decode(cls, decrypted):
|
||||
padding = byte2int(decrypted[-1])
|
||||
if padding < 1 or padding > 32:
|
||||
padding = 0
|
||||
return decrypted[:-padding]
|
||||
15
sg_wechat_enterprise/we_api/crypto/pycrypto.py
Normal file
15
sg_wechat_enterprise/we_api/crypto/pycrypto.py
Normal file
@@ -0,0 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
from Crypto.Cipher import AES
|
||||
|
||||
|
||||
class WeChatCipher(object):
|
||||
|
||||
def __init__(self, key):
|
||||
self.cipher = AES.new(key, AES.MODE_CBC, key[:16])
|
||||
|
||||
def encrypt(self, plaintext):
|
||||
return self.cipher.encrypt(plaintext)
|
||||
|
||||
def decrypt(self, ciphertext):
|
||||
return self.cipher.decrypt(ciphertext)
|
||||
Reference in New Issue
Block a user