def rsa_decrypt(encrypted_data, pem, password=None): """ rsa 解密 :param encrypted_data: 待解密 bytes :param pem: RSA private key 内容/binary :param password: RSA private key pass phrase :return: 解密后的 binary """ from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import padding encrypted_data = to_binary(encrypted_data) pem = to_binary(pem) private_key = serialization.load_pem_private_key(pem, password, backend=default_backend()) data = private_key.decrypt( encrypted_data, padding=padding.OAEP( mgf=padding.MGF1(hashes.SHA1()), algorithm=hashes.SHA1(), label=None, ) ) return data
def rsa_decrypt(encrypted_data, pem, password=None): """ rsa 解密 :param encrypted_data: 待解密 bytes :param pem: RSA private key 内容/binary :param password: RSA private key pass phrase :return: 解密后的 binary """ from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import padding encrypted_data = to_binary(encrypted_data) pem = to_binary(pem) private_key = serialization.load_pem_private_key(pem, password, backend=default_backend()) data = private_key.decrypt( encrypted_data, padding=padding.OAEP( mgf=padding.MGF1(hashes.SHA1()), algorithm=hashes.SHA1(), label=None, ), ) return data
def rsa_encrypt(data, pem, b64_encode=True): """ rsa 加密 :param data: 待加密字符串/binary :param pem: RSA public key 内容/binary :param b64_encode: 是否对输出进行 base64 encode :return: 如果 b64_encode=True 的话,返回加密并 base64 处理后的 string;否则返回加密后的 binary """ from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import padding encoded_data = to_binary(data) pem = to_binary(pem) public_key = serialization.load_pem_public_key(pem) encrypted_data = public_key.encrypt( encoded_data, padding=padding.OAEP( mgf=padding.MGF1(hashes.SHA1()), algorithm=hashes.SHA1(), label=None, ), ) if b64_encode: encrypted_data = base64.b64encode(encrypted_data).decode("utf-8") return encrypted_data
def rsa_encrypt(data, pem, b64_encode=True): """ rsa 加密 :param data: 待加密字符串/binary :param pem: RSA public key 内容/binary :param b64_encode: 是否对输出进行 base64 encode :return: 如果 b64_encode=True 的话,返回加密并 base64 处理后的 string;否则返回加密后的 binary """ from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import padding encoded_data = to_binary(data) pem = to_binary(pem) public_key = serialization.load_pem_public_key(pem, backend=default_backend()) encrypted_data = public_key.encrypt( encoded_data, padding=padding.OAEP( mgf=padding.MGF1(hashes.SHA1()), algorithm=hashes.SHA1(), label=None, ) ) if b64_encode: encrypted_data = base64.b64encode(encrypted_data).decode('utf-8') return encrypted_data
def format_url(params, api_key=None): data = [ to_binary('{0}={1}'.format(k, params[k])) for k in sorted(params) if params[k] ] if api_key: data.append(to_binary('key={0}'.format(api_key))) return b"&".join(data)
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 _fetch_access_token(self, url, params): """ 替代 requests 版本 _fetch_access_token """ http_client = AsyncHTTPClient() params = urlencode(dict((k, to_binary(v)) for k, v in params.items())) _url = '{0}?{1}'.format(url, params) req = HTTPRequest(url=_url, method="GET", request_timeout=self.timeout) res = yield http_client.fetch(req) if res.error is not None: raise WeChatClientException(errcode=None, errmsg=None, client=self, request=req, response=res) result = self._decode_result(res) if 'errcode' in result and result['errcode'] != 0: raise WeChatClientException(result['errcode'], result['errmsg'], client=self, request=res.request, response=res) expires_in = 7200 if 'expires_in' in result: expires_in = result['expires_in'] self.session.set(self.access_token_key, result['access_token'], expires_in) self.expires_at = int(time.time()) + expires_in raise Return(result)
def __repr__(self): _repr = "{klass}({msg})".format(klass=self.__class__.__name__, msg=repr(self._data)) if six.PY2: return to_binary(_repr) else: return to_text(_repr)
def __str__(self): _repr = 'Error code: {code}, message: {msg}'.format(code=self.errcode, msg=self.errmsg) if six.PY2: return to_binary(_repr) else: return to_text(_repr)
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
def __repr__(self): _repr = '{klass}({name})'.format(klass=self.__class__.__name__, name=repr(self.name)) if six.PY2: return to_binary(_repr) else: return to_text(_repr)
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 __repr__(self): _repr = '{klass}({code}, {msg})'.format(klass=self.__class__.__name__, code=self.errcode, msg=self.errmsg) if six.PY2: return to_binary(_repr) else: return to_text(_repr)
def __str__(self): _repr = 'Error code: {code}, message: {msg}'.format( code=self.errcode, msg=self.errmsg ) if six.PY2: return to_binary(_repr) else: return to_text(_repr)
def __repr__(self): _repr = "{klass}({msg})".format( klass=self.__class__.__name__, msg=repr(self._data) ) if six.PY2: return to_binary(_repr) else: return to_text(_repr)
def __repr__(self): _repr = '{klass}({name})'.format( klass=self.__class__.__name__, name=repr(self.name) ) if six.PY2: return to_binary(_repr) else: return to_text(_repr)
def __repr__(self): _repr = '{klass}({code}, {msg})'.format( klass=self.__class__.__name__, code=self.errcode, msg=self.errmsg ) if six.PY2: return to_binary(_repr) else: return to_text(_repr)
def __str__(self): if six.PY2: return to_binary('Error code: {code}, message: {msg}'.format( code=self.return_code, msg=self.return_msg )) else: return to_text('Error code: {code}, message: {msg}'.format( code=self.return_code, msg=self.return_msg ))
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
def _request(self, method, url_or_endpoint, **kwargs): http_client = AsyncHTTPClient() if not url_or_endpoint.startswith(('http://', 'https://')): api_base_url = kwargs.pop('api_base_url', self.API_BASE_URL) url = '{base}{endpoint}'.format(base=api_base_url, endpoint=url_or_endpoint) else: url = url_or_endpoint headers = {} params = kwargs.pop('params', {}) if 'access_token' not in params: params['access_token'] = self.access_token params = urlencode(dict((k, to_binary(v)) for k, v in params.items())) url = '{0}?{1}'.format(url, params) data = kwargs.get('data') files = kwargs.get('files') if files: from requests.models import RequestEncodingMixin from requests.utils import super_len body, content_type = RequestEncodingMixin._encode_files( files, data) headers['Content-Type'] = content_type headers['Content-Length'] = super_len(body) else: if isinstance(data, dict): body = json.dumps(data, ensure_ascii=False) body = body.encode('utf-8') else: body = data result_processor = kwargs.pop('result_processor', None) timeout = kwargs.get('timeout', self.timeout) req = HTTPRequest(url=url, method=method.upper(), headers=headers, body=body, request_timeout=timeout) res = yield http_client.fetch(req) if res.error is not None: raise WeChatClientException(errcode=None, errmsg=None, client=self, request=req, response=res) result = self._handle_result(res, method, url, result_processor, **kwargs) raise Return(result)
def _request(self, method, url_or_endpoint, **kwargs): http_client = AsyncHTTPClient() if not url_or_endpoint.startswith(('http://', 'https://')): api_base_url = kwargs.pop('api_base_url', self.API_BASE_URL) url = '{base}{endpoint}'.format( base=api_base_url, endpoint=url_or_endpoint ) else: url = url_or_endpoint headers = {} params = kwargs.pop('params', {}) params = urlencode(dict((k, to_binary(v)) for k, v in params.items())) url = '{0}?{1}'.format(url, params) data = kwargs.get('data') if isinstance(data, dict): data = optionaldict(data) if 'mchid' not in data: # F**k Tencent data.setdefault('mch_id', self.mch_id) data.setdefault('sub_mch_id', self.sub_mch_id) data.setdefault('nonce_str', random_string(32)) sign = calculate_signature(data, self.api_key) body = dict_to_xml(data, sign) body = body.encode('utf-8') else: body = data req = HTTPRequest( url=url, method=method.upper(), headers=headers, body=body ) res = yield http_client.fetch(req) if res.error is not None: raise WeChatClientException( errcode=None, errmsg=None, client=self, request=req, response=res ) result = self._handle_result(res) raise Return(result)
def delete_account(self, account): """ 删除客服账号 详情请参考 http://mp.weixin.qq.com/wiki/1/70a29afed17f56d537c833f89be979c9.html :param account: 完整客服账号,格式为:账号前缀@公众号微信号 :return: 返回的 JSON 数据包 """ params_data = [ 'access_token={0}'.format(quote(self.access_token)), 'kf_account={0}'.format(quote(to_binary(account), safe=b'/@')), ] params = '&'.join(params_data) return self._get( 'https://api.weixin.qq.com/customservice/kfaccount/del', params=params)
def get_qrcode_url(self, ticket, data=None): """ 通过 ticket 换取二维码地址 详情请参考 http://iot.weixin.qq.com/document-2_5.html :param ticket: 二维码 ticket :param data: 额外数据 :return: 二维码地址 """ url = 'http://we.qq.com/d/{ticket}'.format(ticket=ticket) if data: if isinstance(data, (dict, tuple, list)): data = urllib.urlencode(data) data = to_text(base64.b64encode(to_binary(data))) url = '{base}#{data}'.format(base=url, data=data) return url
def get_qrcode_url(self, ticket, data=None): """ 通过 ticket 换取二维码地址 详情请参考 http://iot.weixin.qq.com/wiki/new/index.html?page=3-4-4 :param ticket: 二维码 ticket :param data: 额外数据 :return: 二维码地址 """ url = 'http://we.qq.com/d/{ticket}'.format(ticket=ticket) if data: if isinstance(data, (dict, tuple, list)): data = urllib.urlencode(data) data = to_text(base64.b64encode(to_binary(data))) url = '{base}#{data}'.format(base=url, data=data) return url
def get_qrcode_url(self, ticket, data=None): """ 通过 ticket 换取二维码地址 详情请参考 https://iot.weixin.qq.com/wiki/new/index.html?page=3-4-4 :param ticket: 二维码 ticket :param data: 额外数据 :return: 二维码地址 """ url = f"https://we.qq.com/d/{ticket}" if data: if isinstance(data, (dict, tuple, list)): data = urllib.parse.urlencode(data) data = to_text(base64.b64encode(to_binary(data))) url = f"{url}#{data}" return url
def update_account(self, account, nickname, password): """ 修改客服账号 详情请参考 https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Service_Center_messages.html#%E4%BF%AE%E6%94%B9%E5%AE%A2%E6%9C%8D%E5%B8%90%E5%8F%B7 :param account: 完整客服账号,格式为:账号前缀@公众号微信号 :param nickname: 客服昵称,最长6个汉字或12个英文字符 :param password: 客服账号登录密码 :return: 返回的 JSON 数据包 """ password = to_binary(password) password = hashlib.md5(password).hexdigest() return self._post( "kfaccount/update", data={"kf_account": account, "nickname": nickname, "password": password}, )
def state_imgs(request, user, msg, data, step): with transaction.atomic(): tweets = TaskTweet.objects.select_for_update(skip_locked=True).filter( task__owner=user.user, new=True) if tweets.exists(): ids = tweets.values_list('tweet__images__id', flat=True) content = to_binary( settings.WECHAT_TOKEN + ','.join(map(lambda x: '' if x is None else str(x), ids))) cipher_text = views.imgs_cipher.encrypt( PKCS7Encoder.encode(content)) encoded = to_text(base64.b64encode(cipher_text)) url = request.build_absolute_uri( reverse('wechat_imgs', kwargs={'imgs': encoded})) tweets.update(new=False) return TextReply(content=url, message=msg), None else: return TextReply(content='还没有获取到照片,请稍后再试', message=msg), None
def delete_account(self, account): """ 删除客服账号 详情请参考 http://mp.weixin.qq.com/wiki/1/70a29afed17f56d537c833f89be979c9.html :param account: 完整客服账号,格式为:账号前缀@公众号微信号 :return: 返回的 JSON 数据包 """ params_data = [ 'access_token={0}'.format(quote(self.access_token)), 'kf_account={0}'.format(quote(to_binary(account), safe=b'/@')), ] params = '&'.join(params_data) return self._get( 'https://api.weixin.qq.com/customservice/kfaccount/del', params=params )
def _fetch_access_token(self, url, params): """ 替代 requests 版本 _fetch_access_token """ http_client = AsyncHTTPClient() params = urlencode(dict((k, to_binary(v)) for k, v in params.items())) _url = '{0}?{1}'.format(url, params) req = HTTPRequest( url=_url, method="GET", request_timeout=self.timeout ) res = yield http_client.fetch(req) if res.error is not None: raise WeChatClientException( errcode=None, errmsg=None, client=self, request=req, response=res ) result = self._decode_result(res) if 'errcode' in result and result['errcode'] != 0: raise WeChatClientException( result['errcode'], result['errmsg'], client=self, request=res.request, response=res ) expires_in = 7200 if 'expires_in' in result: expires_in = result['expires_in'] self.session.set( self.access_token_key, result['access_token'], expires_in ) self.expires_at = int(time.time()) + expires_in raise Return(result)
def add_account(self, account, nickname, password): """ 添加客服账号 详情请参考 http://mp.weixin.qq.com/wiki/1/70a29afed17f56d537c833f89be979c9.html :param account: 完整客服账号,格式为:账号前缀@公众号微信号 :param nickname: 客服昵称,最长6个汉字或12个英文字符 :param password: 客服账号登录密码 :return: 返回的 JSON 数据包 """ password = to_binary(password) password = hashlib.md5(password).hexdigest() return self._post('customservice/kfaccount/add', data={ 'kf_account': account, 'nickname': nickname, 'password': password })
def send_message(self, device_type, device_id, user_id, content): """ 主动发送消息给设备 详情请参考 http://iot.weixin.qq.com/wiki/new/index.html?page=3-4-3 :param device_type: 设备类型,目前为“公众账号原始ID” :param device_id: 设备ID :param user_id: 微信用户账号的openid :param content: 消息内容,BASE64编码 :return: 返回的 JSON 数据包 """ content = to_text(base64.b64encode(to_binary(content))) return self._post('transmsg', data={ 'device_type': device_type, 'device_id': device_id, 'openid': user_id, 'content': content })
def update_account(self, account, nickname, password): """ 更新客服账号 详情请参考 http://mp.weixin.qq.com/wiki/1/70a29afed17f56d537c833f89be979c9.html :param account: 完整客服账号,格式为:账号前缀@公众号微信号 :param nickname: 客服昵称,最长6个汉字或12个英文字符 :param password: 客服账号登录密码 :return: 返回的 JSON 数据包 """ password = to_binary(password) password = hashlib.md5(password).hexdigest() return self._post( 'https://api.weixin.qq.com/customservice/kfaccount/update', data={ 'kf_account': account, 'nickname': nickname, 'password': password } )
def update_account(self, account, nickname, password): """ 更新客服账号 详情请参考 http://mp.weixin.qq.com/wiki/1/70a29afed17f56d537c833f89be979c9.html :param account: 完整客服账号,格式为:账号前缀@公众号微信号 :param nickname: 客服昵称,最长6个汉字或12个英文字符 :param password: 客服账号登录密码 :return: 返回的 JSON 数据包 """ password = to_binary(password) password = hashlib.md5(password).hexdigest() return self._post( "https://api.weixin.qq.com/customservice/kfaccount/update", data={ "kf_account": account, "nickname": nickname, "password": password }, )
def send_message(self, device_type, device_id, user_id, content): """ 主动发送消息给设备 详情请参考 http://iot.weixin.qq.com/wiki/new/index.html?page=3-4-3 :param device_type: 设备类型,目前为“公众账号原始ID” :param device_id: 设备ID :param user_id: 微信用户账号的openid :param content: 消息内容,BASE64编码 :return: 返回的 JSON 数据包 """ content = to_text(base64.b64encode(to_binary(content))) return self._post( 'transmsg', data={ 'device_type': device_type, 'device_id': device_id, 'openid': user_id, 'content': content } )
def __init__(self, key): self.key = to_binary(hashlib.md5(to_binary(key)).hexdigest()) assert len(self.key) == 32
def format_url(params, api_key=None): data = [to_binary('{0}={1}'.format(k, params[k])) for k in sorted(params) if params[k]] if api_key: data.append(to_binary('key={0}'.format(api_key))) return b"&".join(data)
def _encrypt(self, text): text = to_binary(text) text = PKCS7Encoder.encode(text) ciphertext = to_binary(self.cipher.encrypt(text)) return base64.b64encode(ciphertext)
def __str__(self): if six.PY2: return to_binary(self.render()) else: return to_text(self.render())
def __base64_decode(self, text): return to_text(base64.b64decode(to_binary(text)))
def _request(self, method, url_or_endpoint, **kwargs): http_client = AsyncHTTPClient() if not url_or_endpoint.startswith(('http://', 'https://')): api_base_url = kwargs.pop('api_base_url', self.API_BASE_URL) url = '{base}{endpoint}'.format( base=api_base_url, endpoint=url_or_endpoint ) else: url = url_or_endpoint headers = {} params = kwargs.pop('params', {}) if 'access_token' not in params: params['access_token'] = self.access_token params = urlencode(dict((k, to_binary(v)) for k, v in params.items())) url = '{0}?{1}'.format(url, params) data = kwargs.get('data') files = kwargs.get('files') if files: from requests.models import RequestEncodingMixin from requests.utils import super_len body, content_type = RequestEncodingMixin._encode_files( files, data ) headers['Content-Type'] = content_type headers['Content-Length'] = super_len(body) else: if isinstance(data, dict): body = json.dumps(data, ensure_ascii=False) body = body.encode('utf-8') else: body = data result_processor = kwargs.pop('result_processor', None) timeout = kwargs.get('timeout', self.timeout) req = HTTPRequest( url=url, method=method.upper(), headers=headers, body=body, request_timeout=timeout ) res = yield http_client.fetch(req) if res.error is not None: raise WeChatClientException( errcode=None, errmsg=None, client=self, request=req, response=res ) result = self._handle_result( res, method, url, result_processor, **kwargs ) raise Return(result)
def _decrypt(self, text, exception=None): text = to_binary(text) plain_text = self.cipher.decrypt(base64.b64decode(text)) padding = byte2int(plain_text[-1]) content = plain_text[:-padding] return content
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 transMsg(deviceType, deviceId, user, content): content = to_text(base64.b64encode(to_binary(content))) return POST(DEVICE_BASE_URL, "transmsg", {"device_type":deviceType,"device_id":deviceId,"openid":user,"content":content}, {"access_token": getToken()})
def format_url(params, api_key=None): data = [to_binary(f"{k}={params[k]}") for k in sorted(params) if params[k]] if api_key: data.append(to_binary(f"key={api_key}")) return b"&".join(data)