def _encrypt_message(self, msg, nonce, timestamp=None): """ 将公众号回复用户的消息加密打包 :param msg: 待回复用户的消息,xml格式的字符串 :param nonce: 随机串,可以自己生成,也可以用URL参数的nonce :param timestamp: 时间戳,可以自己生成,也可以用URL参数的timestamp,如为None则自动用当前时间 :return: 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串 """ xml = """<xml> <Encrypt><![CDATA[{encrypt}]]></Encrypt> <MsgSignature><![CDATA[{signature}]]></MsgSignature> <TimeStamp>{timestamp}</TimeStamp> <Nonce><![CDATA[{nonce}]]></Nonce> </xml>""" nonce = to_binary(nonce) timestamp = to_binary(timestamp) or to_binary(int(time.time())) encrypt = self.__pc.encrypt(to_text(msg), self.__id) # 生成安全签名 signature = get_sha1_signature(self.__token, timestamp, nonce, encrypt) return to_text( xml.format(encrypt=to_text(encrypt), signature=to_text(signature), timestamp=to_text(timestamp), nonce=to_text(nonce)))
def __init__(self, token, encoding_aes_key, _id): """构造函数 :param token: 公众平台上,开发者设置的Token :param encoding_aes_key: 公众平台上,开发者设置的EncodingAESKey :param _id: 公众号的 appid 或企业号的 corpid """ self.__key = base64.b64decode( to_binary(encoding_aes_key) + to_binary('=')) if len(self.__key) != 32: raise ValidateAESKeyError(encoding_aes_key) self.__token = to_binary(token) self.__id = to_binary(_id) self.__pc = BaseCrypto(self.__key)
def __str__(self): if six.PY2: return to_binary('{code}: {msg}'.format(code=self.errcode, msg=self.errmsg)) else: return to_text('{code}: {msg}'.format(code=self.errcode, msg=self.errmsg))
def get_sha1_signature(token, timestamp, nonce, encrypt): """ 用 SHA1 算法生成安全签名 @param token: 票据 @param timestamp: 时间戳 @param nonce: 随机字符串 @param encrypt: 密文 @return: 安全签名 """ try: sortlist = [token, timestamp, nonce, to_binary(encrypt)] sortlist.sort() sha = hashlib.sha1() sha.update(to_binary("").join(sortlist)) return sha.hexdigest() except Exception as e: raise CryptoComputeSignatureError(e)
def _decrypt_message(self, msg, msg_signature, timestamp, nonce): """ 检验消息的真实性,并且获取解密后的明文 :param msg: 密文,对应POST请求的数据 :param msg_signature: 签名串,对应URL参数的msg_signature :param timestamp: 时间戳,对应URL参数的timestamp :param nonce: 随机串,对应URL参数的nonce :return: 解密后的原文 """ timestamp = to_binary(timestamp) nonce = to_binary(nonce) if isinstance(msg, six.string_types): try: msg = xmltodict.parse(to_text(msg))['xml'] except Exception as e: raise ParseError(e) encrypt = msg['Encrypt'] signature = get_sha1_signature(self.__token, timestamp, nonce, encrypt) if signature != msg_signature: raise ValidateSignatureError() return self.__pc.decrypt(encrypt, self.__id)
def encode(cls, text): """ 对需要加密的明文进行填充补位 @param text: 需要进行填充补位操作的明文 @return: 补齐明文字符串 """ text_length = len(text) # 计算需要填充的位数 amount_to_pad = cls.block_size - (text_length % cls.block_size) if amount_to_pad == 0: amount_to_pad = cls.block_size # 获得补位所用的字符 pad = to_binary(chr(amount_to_pad)) return text + pad * amount_to_pad
def encrypt(self, text, appid): """ 对明文进行加密 @param text: 需要加密的明文 @return: 加密得到的字符串 """ # 16位随机字符串添加到明文开头 text = self.get_random_str() + struct.pack("I", socket.htonl(len(text))) + \ to_binary(text) + appid # 使用自定义的填充方式对明文进行补位填充 pkcs7 = PKCS7Encoder() text = pkcs7.encode(text) # 加密 cryptor = AES.new(self.key, self.mode, self.key[:16]) try: ciphertext = cryptor.encrypt(text) # 使用BASE64对加密后的字符串进行编码 return base64.b64encode(ciphertext) except Exception as e: raise EncryptAESError(e)
def __str__(self): if six.PY2: return to_binary(self.message) else: return to_text(self.message)