token = $token; $this->encodingAesKey = $encodingAesKey; } /** * 解密钉钉事件回调消息 * * @param string $signature URL参数 signature * @param string $timestamp URL参数 timestamp * @param string $nonce URL参数 nonce * @param string $encrypt 消息体 encrypt * @return array|false */ public function decryptMsg(string $msgSignature, string $timestamp, string $nonce, string $encrypt) { // 验签 $tmpArr = [$this->token, $timestamp, $nonce, $encrypt]; sort($tmpArr, SORT_STRING); $tmpStr = implode('', $tmpArr); $hash = sha1($tmpStr); if ($hash !== $msgSignature) { return false; } // AES 解密 $aesKey = base64_decode($this->encodingAesKey . '='); // 补足 padding $iv = substr($aesKey, 0, 16); $cipherText = base64_decode($encrypt); $decrypted = openssl_decrypt($cipherText, 'AES-256-CBC', $aesKey, OPENSSL_ZERO_PADDING, $iv); if ($decrypted === false) return false; // 去掉 padding $decrypted = $this->pkcs7Unpad($decrypted); // 去掉前16位随机字符串 $content = substr($decrypted, 16); // 读取消息长度 $lenList = unpack("N", substr($content, 0, 4)); $jsonLen = $lenList[1]; // 获取 JSON $json = substr($content, 4, $jsonLen); return json_decode($json, true); } /** * 加密返回给钉钉的消息 * * @param string $replyMsg 明文消息 * @param string $timestamp * @param string $nonce * @return array */ public function encryptMsg(string $replyMsg, string $timestamp, string $nonce): array { $random16 = $this->getRandomStr(16); $msgLenBin = pack("N", strlen($replyMsg)); $corpId = ''; // 不需要corpId $plain = $random16 . $msgLenBin . $replyMsg . $corpId; // PKCS#7 padding $blockSize = 32; $pad = $blockSize - (strlen($plain) % $blockSize); $pad = $pad === 0 ? $blockSize : $pad; $plainPadded = $plain . str_repeat(chr($pad), $pad); // AES 加密 $aesKey = base64_decode($this->encodingAesKey); $iv = substr($aesKey, 0, 16); $encrypted = openssl_encrypt($plainPadded, 'AES-256-CBC', $aesKey, OPENSSL_ZERO_PADDING, $iv); $encryptBase64 = base64_encode($encrypted); // 计算 msg_signature $signatureStr = $this->token . $timestamp . $nonce . $encryptBase64; $msgSignature = sha1($signatureStr); return [ 'msg_signature' => $msgSignature, 'encrypt' => $encryptBase64 ]; } private function getRandomStr(int $length = 16): string { $chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; $str = ''; for ($i = 0; $i < $length; $i++) { $str .= $chars[random_int(0, strlen($chars) - 1)]; } return $str; } private function pkcs7Unpad(string $text) { $pad = ord(substr($text, -1)); if ($pad < 1 || $pad > 32) return false; return substr($text, 0, -$pad); } }