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 $signature, string $timestamp, string $nonce, string $encrypt) { // 1. 验签(官方:token + timestamp + nonce + encrypt) $tmpStr = $this->token . $timestamp . $nonce . $encrypt; $hash = sha1($tmpStr); if ($hash !== $signature) { return false; } // 2. 解密 AES-256-CBC $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. 去 padding $decrypted = $this->pkcs7Unpad($decrypted); // 4. 去掉前 16 位随机字符串 $content = substr($decrypted, 16); // 5. 读取消息长度 $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 $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); } }