token = $token; $this->encodingAesKey = $encodingAesKey; } /** * 解密钉钉事件回调消息 * * @param string $msgSignature URL参数 msg_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) { // 1. 验签 (官方要求 token + timestamp + nonce + encrypt 顺序拼接) $hash = sha1($this->token . $timestamp . $nonce . $encrypt); if ($hash !== $msgSignature) { return false; } // 2. AES 解密 $aesKey = base64_decode($this->encodingAesKey); $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; // 3. 去掉 PKCS#7 padding $decrypted = $this->pkcs7Unpad($decrypted); // 4. 去掉前16位随机字符串 $content = substr($decrypted, 16); // 5. 读取消息长度(4字节 network order) $lenList = unpack("N", substr($content, 0, 4)); $jsonLen = $lenList[1]; // 6. 获取 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 $msgSignature = sha1($this->token . $timestamp . $nonce . $encryptBase64); 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); } }