Предыдущая статья.
В продолжение темы WeChat. В данной статье мы покажем как сериализуются и десериализуются объекты, а так же зашифруем сообщение и получим СМС с кодом подтверждения. Кроме того мы приведем весь необходимый код на PHP, чтобы вы могли попробовать и убедиться что все работает
В приложении используется библиотека ProtoBuf.
Существует базовый объект WXPBGeneratedMessage, все остальные наследуются от него.
Изначально Protobuf умеет сериализовать только простые типы(строки и числа). Чтобы сериализовать сложные объекты, для каждого объекта создается поле, которое мы назвали classInfo. В нем хранятся имена и описания всех полей.
Для запроса СМС необходимо создать объект BindOpMobileRequest.
Как можно увидеть, classInfo состоит из двух массивов(имена полей и их описания). ObjectDefinition состоит из пяти чисел и строки:
Объект сериализуется рекурсивно по алгоритму прямого обхода дерева в глубину. Перед каждым полем сначала ставиться тег, который вычисляется (c использованием словаря) следующим образом:
Если поле является объектом, то после тега ставиться его длинна.
Создаем объект BindOpMobileRequest, заполняем поля и сериализуем:
В результате получаем:

Теперь осталось, составить заголовок, зашифровать данные и отправить на сервер.
Если все сделано правильно, сервер пришлет нам ответ, который нужно расшифровать AES-ключом переданным в запросе и десериализовать:
Десериализовать необходимо в объект BindOpMobileResponse.
Здесь приведен код на php, реализующий все выше сказанное. Дерзайте…
PS. SMS не приходят на номера одной «жужжащей» компании. Не понятно почему…
В продолжение темы WeChat. В данной статье мы покажем как сериализуются и десериализуются объекты, а так же зашифруем сообщение и получим СМС с кодом подтверждения. Кроме того мы приведем весь необходимый код на PHP, чтобы вы могли попробовать и убедиться что все работает
В приложении используется библиотека ProtoBuf.
Существует базовый объект WXPBGeneratedMessage, все остальные наследуются от него.
Изначально Protobuf умеет сериализовать только простые типы(строки и числа). Чтобы сериализовать сложные объекты, для каждого объекта создается поле, которое мы назвали classInfo. В нем хранятся имена и описания всех полей.
Для запроса СМС необходимо создать объект BindOpMobileRequest.
public function __construct() { $this->classInfo = new \wechat\PBClassInfo(); $this->classInfo->nameProperty[] = 'baseRequest'; $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0x1, 0x2, 0xB, 0x0, 0x0, 'BaseRequest'); $this->classInfo->nameProperty[] = 'userName'; $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0x2, 0x1, 0x9, 0x0, 0x0, ''); $this->classInfo->nameProperty[] = 'mobile'; $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0x3, 0x1, 0x9, 0x0, 0x0, ''); $this->classInfo->nameProperty[] = 'opcode'; $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0x4, 0x2, 0x5, 0x0, 0x0, ''); $this->classInfo->nameProperty[] = 'verifycode'; $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0x5, 0x1, 0x9, 0x0, 0x0, ''); $this->classInfo->nameProperty[] = 'dialFlag'; $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0x6, 0x1, 0x5, 0x0, 0x0, ''); $this->classInfo->nameProperty[] = 'dialLang'; $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0x7, 0x1, 0x9, 0x0, 0x0, ''); $this->classInfo->nameProperty[] = 'authTicket'; $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0x8, 0x1, 0x9, 0x0, 0x0, ''); $this->classInfo->nameProperty[] = 'forceReg'; $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0x9, 0x1, 0xD, 0x0, 0x0, ''); $this->classInfo->nameProperty[] = 'safeDeviceName'; $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0xA, 0x1, 0x9, 0x0, 0x0, ''); $this->classInfo->nameProperty[] = 'safeDeviceType'; $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0xB, 0x1, 0x9, 0x0, 0x0, ''); $this->classInfo->nameProperty[] = 'randomEncryKey'; $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0xC, 0x1, 0xB, 0x0, 0x0, 'SKBuiltinBuffer_t'); $this->classInfo->nameProperty[] = 'language'; $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0xD, 0x1, 0x9, 0x0, 0x0, ''); $this->classInfo->nameProperty[] = 'inputMobileRetrys'; $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0xE, 0x1, 0xD, 0x0, 0x0, ''); $this->classInfo->nameProperty[] = 'adjustRet'; $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0xF, 0x1, 0xD, 0x0, 0x0, ''); $this->classInfo->nameProperty[] = 'clientSeqId'; $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0x10, 0x1, 0x9, 0x0, 0x0, ''); parent::__construct(); }
Как можно увидеть, classInfo состоит из двух массивов(имена полей и их описания). ObjectDefinition состоит из пяти чисел и строки:
- Порядковый номер поля
- Идентификатор массива. Ставится равным трем, если поле является массивом. В остальных случаях ни на что не влияет
- Тип поля (например 0x9 — string, 0x8 — bool, 0x7 — unsigned int, 0xB — объект итд )
- Используется при сериализации массивов вместе со вторым пунктом, в остальных случаях равен нулю
- Назначение этого числа осталось загадкой (оно всегда равно нулю и нигде не применяется)
- В случае если поле является объектом, указывается тип объекта, в противном случае указывается пустая строка.
Объект сериализуется рекурсивно по алгоритму прямого обхода дерева в глубину. Перед каждым полем сначала ставиться тег, который вычисляется (c использованием словаря) следующим образом:
$FieldTypeDictionary = array(-1, 1, 5, 0, 0, 0, 1, 5, 0, 2, 3, 2, 2, 0, 0, 5, 1, 0, 0); $fieldType = $FieldTypeDictionary[$fieldType]; $tag = $fieldNumber << 3 | $fieldType;
Если поле является объектом, то после тега ставиться его длинна.
Готовим сообщение для отправки на сервер
Создаем объект BindOpMobileRequest, заполняем поля и сериализуем:
public function getVerifyCode() { $bindOpMobileRequest = new \wechat\Request\BindOpMobileRequest(); $baseRequest = $this->createBaseRequest(); $this->aesKey = random_bytes(0x10); $baseRequest->sessionKey = ''; $baseRequest->scene = 0; $bindOpMobileRequest->baseRequest = $baseRequest; $bindOpMobileRequest->mobile = $this->phoneNumber; $bindOpMobileRequest->opcode = 14; $bindOpMobileRequest->safeDeviceName = $this->deviceName; $bindOpMobileRequest->safeDeviceType = 'iPhone'; $bindOpMobileRequest->randomEncryKey = new \wechat\Object\SKBuiltinBuffer_t(); $bindOpMobileRequest->randomEncryKey->iLen = strlen($this->aesKey); $bindOpMobileRequest->randomEncryKey->buffer = $this->aesKey; $bindOpMobileRequest->language = $this->language; $bindOpMobileRequest->inputMobileRetrys = 5; $bindOpMobileRequest->adjustRet = 0; $bindOpMobileRequest->clientSeqId = $this->clientSeqId; $serializedData = $bindOpMobileRequest->serializedData(); }
В результате получаем:

Теперь осталось, составить заголовок, зашифровать данные и отправить на сервер.
$header = $this->computeHeader($serializedData, 0x91, 2); $dataToSend = $header . $this->client->RSAEncrypt($serializedData); $response = $this->client->request($this, $dataToSend, 'bindopmobileforreg');
Если все сделано правильно, сервер пришлет нам ответ, который нужно расшифровать AES-ключом переданным в запросе и десериализовать:
$response = deleteHeaderFromResponse($response); $response = $this->client->AESDecrypt($response, $this->aesKey); $bindOpMobileResponse = new \wechat\Response\BindOpMobileResponse(); $bindOpMobileResponse->mergeFromData($response);
Десериализовать необходимо в объект BindOpMobileResponse.
Здесь приведен код на php, реализующий все выше сказанное. Дерзайте…
PS. SMS не приходят на номера одной «жужжащей» компании. Не понятно почему…
