UrsCryptoServiceTest.php 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. <?php
  2. namespace Tests\Unit\ThirdParty;
  3. use PHPUnit\Framework\TestCase;
  4. use ThirdParty\Urs\Util\CryptoService;
  5. /**
  6. * URS加密服务测试
  7. *
  8. * 测试URS包的CryptoService加密解密功能
  9. * 验证AES-256-CBC加密算法和SHA256签名验证
  10. */
  11. class UrsCryptoServiceTest extends TestCase
  12. {
  13. private string $testKey = 'test_key_32_characters_long_123456';
  14. private int $testTimeout = 300;
  15. /**
  16. * 测试CryptoService类是否存在
  17. */
  18. public function testCryptoServiceClassExists()
  19. {
  20. $this->assertTrue(
  21. class_exists(CryptoService::class),
  22. 'CryptoService类应该存在'
  23. );
  24. }
  25. /**
  26. * 测试CryptoService可以正常实例化
  27. */
  28. public function testCryptoServiceCanBeInstantiated()
  29. {
  30. $cryptoService = new CryptoService($this->testKey, $this->testTimeout);
  31. $this->assertInstanceOf(
  32. CryptoService::class,
  33. $cryptoService,
  34. 'CryptoService应该可以正常实例化'
  35. );
  36. }
  37. /**
  38. * 测试CryptoService有必需的方法
  39. */
  40. public function testCryptoServiceHasRequiredMethods()
  41. {
  42. $reflection = new \ReflectionClass(CryptoService::class);
  43. $expectedMethods = ['encrypt', 'decrypt'];
  44. foreach ($expectedMethods as $methodName) {
  45. $this->assertTrue(
  46. $reflection->hasMethod($methodName),
  47. "CryptoService应该有{$methodName}方法"
  48. );
  49. $method = $reflection->getMethod($methodName);
  50. $this->assertTrue(
  51. $method->isPublic(),
  52. "{$methodName}方法应该是public"
  53. );
  54. }
  55. }
  56. /**
  57. * 测试数据加密功能
  58. */
  59. public function testDataEncryption()
  60. {
  61. $cryptoService = new CryptoService($this->testKey, $this->testTimeout);
  62. $testData = [
  63. 'userKey' => 'test_user_key_123',
  64. 'userId' => 12345,
  65. 'level' => 1
  66. ];
  67. $encryptedData = $cryptoService->encrypt($testData);
  68. // 验证加密结果结构
  69. $this->assertIsArray($encryptedData, '加密结果应该是数组');
  70. $expectedKeys = ['data', 'iv', 'timestamp', 'sign'];
  71. foreach ($expectedKeys as $key) {
  72. $this->assertArrayHasKey(
  73. $key,
  74. $encryptedData,
  75. "加密结果应该包含{$key}字段"
  76. );
  77. }
  78. // 验证数据格式
  79. $this->assertIsString($encryptedData['data'], 'data字段应该是字符串');
  80. $this->assertIsString($encryptedData['iv'], 'iv字段应该是字符串');
  81. $this->assertIsInt($encryptedData['timestamp'], 'timestamp字段应该是整数');
  82. $this->assertIsString($encryptedData['sign'], 'sign字段应该是字符串');
  83. // 验证Base64编码
  84. $this->assertNotFalse(
  85. base64_decode($encryptedData['data'], true),
  86. 'data字段应该是有效的Base64编码'
  87. );
  88. $this->assertNotFalse(
  89. base64_decode($encryptedData['iv'], true),
  90. 'iv字段应该是有效的Base64编码'
  91. );
  92. // 验证IV长度(解码后应该是16字节)
  93. $decodedIv = base64_decode($encryptedData['iv']);
  94. $this->assertEquals(16, strlen($decodedIv), 'IV应该是16字节');
  95. // 验证签名长度(SHA256应该是64个字符)
  96. $this->assertEquals(64, strlen($encryptedData['sign']), 'SHA256签名应该是64个字符');
  97. }
  98. /**
  99. * 测试数据解密功能
  100. */
  101. public function testDataDecryption()
  102. {
  103. $cryptoService = new CryptoService($this->testKey, $this->testTimeout);
  104. $originalData = [
  105. 'userKey' => 'test_user_key_123',
  106. 'userId' => 12345,
  107. 'level' => 1
  108. ];
  109. // 先加密
  110. $encryptedData = $cryptoService->encrypt($originalData);
  111. // 再解密
  112. $decryptedData = $cryptoService->decrypt($encryptedData);
  113. // 验证解密结果
  114. $this->assertIsArray($decryptedData, '解密结果应该是数组');
  115. $this->assertEquals($originalData, $decryptedData, '解密后的数据应该与原始数据相同');
  116. }
  117. /**
  118. * 测试加密解密的完整流程
  119. */
  120. public function testEncryptDecryptRoundTrip()
  121. {
  122. $cryptoService = new CryptoService($this->testKey, $this->testTimeout);
  123. $testCases = [
  124. // 简单数据
  125. ['test' => 'value'],
  126. // 复杂数据
  127. [
  128. 'userKey' => '$2y$10$i.h97m13olfIaU.ZTYiyeeXFl8xqn48w2bFiAhcoQsJdU6K3w.Lgu',
  129. 'userId' => 12345,
  130. 'level' => 3,
  131. 'nested' => [
  132. 'array' => [1, 2, 3],
  133. 'string' => 'test'
  134. ]
  135. ],
  136. // 中文数据
  137. [
  138. 'name' => '测试用户',
  139. 'message' => '这是一个测试消息'
  140. ],
  141. // 空数据
  142. [],
  143. // 特殊字符
  144. [
  145. 'special' => '!@#$%^&*()_+-=[]{}|;:,.<>?'
  146. ]
  147. ];
  148. foreach ($testCases as $index => $testData) {
  149. $encrypted = $cryptoService->encrypt($testData);
  150. $decrypted = $cryptoService->decrypt($encrypted);
  151. $this->assertEquals(
  152. $testData,
  153. $decrypted,
  154. "测试案例{$index}:解密后的数据应该与原始数据相同"
  155. );
  156. }
  157. }
  158. /**
  159. * 测试时间戳过期验证
  160. */
  161. public function testTimestampExpiration()
  162. {
  163. $shortTimeoutService = new CryptoService($this->testKey, 1); // 1秒超时
  164. $testData = ['test' => 'value'];
  165. $encryptedData = $shortTimeoutService->encrypt($testData);
  166. // 等待超过超时时间
  167. sleep(2);
  168. // 尝试解密应该失败
  169. $this->expectException(\UCore\Exception\LogicException::class);
  170. $this->expectExceptionMessage('请求已过期');
  171. $shortTimeoutService->decrypt($encryptedData);
  172. }
  173. /**
  174. * 测试签名验证
  175. */
  176. public function testSignatureVerification()
  177. {
  178. $cryptoService = new CryptoService($this->testKey, $this->testTimeout);
  179. $testData = ['test' => 'value'];
  180. $encryptedData = $cryptoService->encrypt($testData);
  181. // 篡改签名
  182. $encryptedData['sign'] = 'invalid_signature';
  183. // 尝试解密应该失败
  184. $this->expectException(\UCore\Exception\LogicException::class);
  185. $this->expectExceptionMessage('签名验证失败');
  186. $cryptoService->decrypt($encryptedData);
  187. }
  188. /**
  189. * 测试数据篡改检测
  190. */
  191. public function testDataTamperingDetection()
  192. {
  193. $cryptoService = new CryptoService($this->testKey, $this->testTimeout);
  194. $testData = ['test' => 'value'];
  195. $encryptedData = $cryptoService->encrypt($testData);
  196. // 篡改加密数据
  197. $encryptedData['data'] = base64_encode('tampered_data');
  198. // 尝试解密应该失败(签名验证会失败)
  199. $this->expectException(\UCore\Exception\LogicException::class);
  200. $this->expectExceptionMessage('签名验证失败');
  201. $cryptoService->decrypt($encryptedData);
  202. }
  203. /**
  204. * 测试IV篡改检测
  205. */
  206. public function testIvTamperingDetection()
  207. {
  208. $cryptoService = new CryptoService($this->testKey, $this->testTimeout);
  209. $testData = ['test' => 'value'];
  210. $encryptedData = $cryptoService->encrypt($testData);
  211. // 篡改IV
  212. $encryptedData['iv'] = base64_encode('tampered_iv_16byte');
  213. // 尝试解密应该失败(签名验证会失败)
  214. $this->expectException(\UCore\Exception\LogicException::class);
  215. $this->expectExceptionMessage('签名验证失败');
  216. $cryptoService->decrypt($encryptedData);
  217. }
  218. /**
  219. * 测试不同密钥的加密数据无法解密
  220. */
  221. public function testDifferentKeyCannotDecrypt()
  222. {
  223. $cryptoService1 = new CryptoService('key1_32_characters_long_12345678', $this->testTimeout);
  224. $cryptoService2 = new CryptoService('key2_32_characters_long_87654321', $this->testTimeout);
  225. $testData = ['test' => 'value'];
  226. $encryptedData = $cryptoService1->encrypt($testData);
  227. // 使用不同密钥尝试解密应该失败
  228. $this->expectException(\UCore\Exception\LogicException::class);
  229. $this->expectExceptionMessage('签名验证失败');
  230. $cryptoService2->decrypt($encryptedData);
  231. }
  232. /**
  233. * 测试无效JSON数据处理
  234. */
  235. public function testInvalidJsonHandling()
  236. {
  237. $cryptoService = new CryptoService($this->testKey, $this->testTimeout);
  238. // 手动构造一个包含无效JSON的加密数据
  239. $iv = random_bytes(16);
  240. $timestamp = time();
  241. $invalidJson = 'invalid json data';
  242. $encrypted = openssl_encrypt(
  243. $invalidJson,
  244. 'AES-256-CBC',
  245. $this->testKey,
  246. OPENSSL_RAW_DATA,
  247. $iv
  248. );
  249. $dataBase64 = base64_encode($encrypted);
  250. $ivBase64 = base64_encode($iv);
  251. $sign = hash('sha256', $dataBase64 . $ivBase64 . $timestamp . $this->testKey);
  252. $encryptedData = [
  253. 'data' => $dataBase64,
  254. 'iv' => $ivBase64,
  255. 'timestamp' => $timestamp,
  256. 'sign' => $sign
  257. ];
  258. // 尝试解密应该失败
  259. $this->expectException(\UCore\Exception\LogicException::class);
  260. $this->expectExceptionMessage('JSON解析失败');
  261. $cryptoService->decrypt($encryptedData);
  262. }
  263. }