assertTrue( class_exists(CryptoService::class), 'CryptoService类应该存在' ); } /** * 测试CryptoService可以正常实例化 */ public function testCryptoServiceCanBeInstantiated() { $cryptoService = new CryptoService($this->testKey, $this->testTimeout); $this->assertInstanceOf( CryptoService::class, $cryptoService, 'CryptoService应该可以正常实例化' ); } /** * 测试CryptoService有必需的方法 */ public function testCryptoServiceHasRequiredMethods() { $reflection = new \ReflectionClass(CryptoService::class); $expectedMethods = ['encrypt', 'decrypt']; foreach ($expectedMethods as $methodName) { $this->assertTrue( $reflection->hasMethod($methodName), "CryptoService应该有{$methodName}方法" ); $method = $reflection->getMethod($methodName); $this->assertTrue( $method->isPublic(), "{$methodName}方法应该是public" ); } } /** * 测试数据加密功能 */ public function testDataEncryption() { $cryptoService = new CryptoService($this->testKey, $this->testTimeout); $testData = [ 'userKey' => 'test_user_key_123', 'userId' => 12345, 'level' => 1 ]; $encryptedData = $cryptoService->encrypt($testData); // 验证加密结果结构 $this->assertIsArray($encryptedData, '加密结果应该是数组'); $expectedKeys = ['data', 'iv', 'timestamp', 'sign']; foreach ($expectedKeys as $key) { $this->assertArrayHasKey( $key, $encryptedData, "加密结果应该包含{$key}字段" ); } // 验证数据格式 $this->assertIsString($encryptedData['data'], 'data字段应该是字符串'); $this->assertIsString($encryptedData['iv'], 'iv字段应该是字符串'); $this->assertIsInt($encryptedData['timestamp'], 'timestamp字段应该是整数'); $this->assertIsString($encryptedData['sign'], 'sign字段应该是字符串'); // 验证Base64编码 $this->assertNotFalse( base64_decode($encryptedData['data'], true), 'data字段应该是有效的Base64编码' ); $this->assertNotFalse( base64_decode($encryptedData['iv'], true), 'iv字段应该是有效的Base64编码' ); // 验证IV长度(解码后应该是16字节) $decodedIv = base64_decode($encryptedData['iv']); $this->assertEquals(16, strlen($decodedIv), 'IV应该是16字节'); // 验证签名长度(SHA256应该是64个字符) $this->assertEquals(64, strlen($encryptedData['sign']), 'SHA256签名应该是64个字符'); } /** * 测试数据解密功能 */ public function testDataDecryption() { $cryptoService = new CryptoService($this->testKey, $this->testTimeout); $originalData = [ 'userKey' => 'test_user_key_123', 'userId' => 12345, 'level' => 1 ]; // 先加密 $encryptedData = $cryptoService->encrypt($originalData); // 再解密 $decryptedData = $cryptoService->decrypt($encryptedData); // 验证解密结果 $this->assertIsArray($decryptedData, '解密结果应该是数组'); $this->assertEquals($originalData, $decryptedData, '解密后的数据应该与原始数据相同'); } /** * 测试加密解密的完整流程 */ public function testEncryptDecryptRoundTrip() { $cryptoService = new CryptoService($this->testKey, $this->testTimeout); $testCases = [ // 简单数据 ['test' => 'value'], // 复杂数据 [ 'userKey' => '$2y$10$i.h97m13olfIaU.ZTYiyeeXFl8xqn48w2bFiAhcoQsJdU6K3w.Lgu', 'userId' => 12345, 'level' => 3, 'nested' => [ 'array' => [1, 2, 3], 'string' => 'test' ] ], // 中文数据 [ 'name' => '测试用户', 'message' => '这是一个测试消息' ], // 空数据 [], // 特殊字符 [ 'special' => '!@#$%^&*()_+-=[]{}|;:,.<>?' ] ]; foreach ($testCases as $index => $testData) { $encrypted = $cryptoService->encrypt($testData); $decrypted = $cryptoService->decrypt($encrypted); $this->assertEquals( $testData, $decrypted, "测试案例{$index}:解密后的数据应该与原始数据相同" ); } } /** * 测试时间戳过期验证 */ public function testTimestampExpiration() { $shortTimeoutService = new CryptoService($this->testKey, 1); // 1秒超时 $testData = ['test' => 'value']; $encryptedData = $shortTimeoutService->encrypt($testData); // 等待超过超时时间 sleep(2); // 尝试解密应该失败 $this->expectException(\UCore\Exception\LogicException::class); $this->expectExceptionMessage('请求已过期'); $shortTimeoutService->decrypt($encryptedData); } /** * 测试签名验证 */ public function testSignatureVerification() { $cryptoService = new CryptoService($this->testKey, $this->testTimeout); $testData = ['test' => 'value']; $encryptedData = $cryptoService->encrypt($testData); // 篡改签名 $encryptedData['sign'] = 'invalid_signature'; // 尝试解密应该失败 $this->expectException(\UCore\Exception\LogicException::class); $this->expectExceptionMessage('签名验证失败'); $cryptoService->decrypt($encryptedData); } /** * 测试数据篡改检测 */ public function testDataTamperingDetection() { $cryptoService = new CryptoService($this->testKey, $this->testTimeout); $testData = ['test' => 'value']; $encryptedData = $cryptoService->encrypt($testData); // 篡改加密数据 $encryptedData['data'] = base64_encode('tampered_data'); // 尝试解密应该失败(签名验证会失败) $this->expectException(\UCore\Exception\LogicException::class); $this->expectExceptionMessage('签名验证失败'); $cryptoService->decrypt($encryptedData); } /** * 测试IV篡改检测 */ public function testIvTamperingDetection() { $cryptoService = new CryptoService($this->testKey, $this->testTimeout); $testData = ['test' => 'value']; $encryptedData = $cryptoService->encrypt($testData); // 篡改IV $encryptedData['iv'] = base64_encode('tampered_iv_16byte'); // 尝试解密应该失败(签名验证会失败) $this->expectException(\UCore\Exception\LogicException::class); $this->expectExceptionMessage('签名验证失败'); $cryptoService->decrypt($encryptedData); } /** * 测试不同密钥的加密数据无法解密 */ public function testDifferentKeyCannotDecrypt() { $cryptoService1 = new CryptoService('key1_32_characters_long_12345678', $this->testTimeout); $cryptoService2 = new CryptoService('key2_32_characters_long_87654321', $this->testTimeout); $testData = ['test' => 'value']; $encryptedData = $cryptoService1->encrypt($testData); // 使用不同密钥尝试解密应该失败 $this->expectException(\UCore\Exception\LogicException::class); $this->expectExceptionMessage('签名验证失败'); $cryptoService2->decrypt($encryptedData); } /** * 测试无效JSON数据处理 */ public function testInvalidJsonHandling() { $cryptoService = new CryptoService($this->testKey, $this->testTimeout); // 手动构造一个包含无效JSON的加密数据 $iv = random_bytes(16); $timestamp = time(); $invalidJson = 'invalid json data'; $encrypted = openssl_encrypt( $invalidJson, 'AES-256-CBC', $this->testKey, OPENSSL_RAW_DATA, $iv ); $dataBase64 = base64_encode($encrypted); $ivBase64 = base64_encode($iv); $sign = hash('sha256', $dataBase64 . $ivBase64 . $timestamp . $this->testKey); $encryptedData = [ 'data' => $dataBase64, 'iv' => $ivBase64, 'timestamp' => $timestamp, 'sign' => $sign ]; // 尝试解密应该失败 $this->expectException(\UCore\Exception\LogicException::class); $this->expectExceptionMessage('JSON解析失败'); $cryptoService->decrypt($encryptedData); } }