requestManipulator = new RequestManipulator(); $this->signer = new RequestSigner($this->requestManipulator); } #[Test] public function it_can_sign_a_request_with_hmac_sha256(): void { $key = SigningKey::createHmac('test-key', 'my-secret-key-that-is-long-enough-for-security'); $request = new HttpRequest( method: Method::GET, path: '/api/test', headers: new Headers(['Host' => 'example.com', 'Date' => 'Thu, 05 Jan 2023 21:31:40 GMT']) ); $signature = $this->signer->sign($request, $key); $this->assertEquals('test-key', $signature->keyId); $this->assertEquals(SigningAlgorithm::HMAC_SHA256, $signature->algorithm); $this->assertNotEmpty($signature->signature); $this->assertEquals(['(request-target)', 'host', 'date'], $signature->headers); } #[Test] public function it_can_sign_request_with_custom_headers(): void { $key = SigningKey::createHmac('test-key', 'my-secret-key-that-is-long-enough-for-security'); $request = new HttpRequest( method: Method::POST, path: '/api/test', headers: new Headers([ 'Host' => 'example.com', 'Date' => 'Thu, 05 Jan 2023 21:31:40 GMT', 'Content-Type' => 'application/json', ]), body: '{"test": "data"}' ); $customHeaders = ['(request-target)', 'host', 'date', 'content-type']; $signature = $this->signer->sign($request, $key, $customHeaders); $this->assertEquals($customHeaders, $signature->headers); } #[Test] public function it_can_create_digest_for_request_body(): void { $body = '{"test": "data"}'; $digest = $this->signer->createDigest($body); $expectedHash = base64_encode(hash('sha256', $body, true)); $this->assertEquals("SHA256={$expectedHash}", $digest); } #[Test] public function it_can_sign_complete_request_with_body(): void { $key = SigningKey::createHmac('test-key', 'my-secret-key-that-is-long-enough-for-security'); $request = new HttpRequest( method: Method::POST, path: '/api/test', headers: new Headers(['Host' => 'example.com']), body: '{"test": "data"}' ); $signedRequest = $this->signer->signRequest($request, $key); // Should have added Date and Digest headers $this->assertNotNull($signedRequest->headers->getFirst('Date')); $this->assertNotNull($signedRequest->headers->getFirst('Digest')); $this->assertNotNull($signedRequest->headers->getFirst('Signature')); // Digest should be correct $expectedHash = base64_encode(hash('sha256', $request->body, true)); $this->assertEquals("SHA256={$expectedHash}", $signedRequest->headers->getFirst('Digest')); } #[Test] public function it_throws_exception_for_invalid_key(): void { $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Signing key is not valid'); $key = SigningKey::createHmac('expired-key', 'secret-that-is-long-enough-for-security', expiresAt: new \DateTimeImmutable('2020-01-01')); $request = new HttpRequest(method: Method::GET, path: '/test'); $this->signer->sign($request, $key); } #[Test] public function it_can_generate_rsa_signature(): void { // Generate test RSA key pair $keyPair = openssl_pkey_new([ 'digest_alg' => 'sha256', 'private_key_bits' => 2048, 'private_key_type' => OPENSSL_KEYTYPE_RSA, ]); openssl_pkey_export($keyPair, $privateKey); $key = SigningKey::createRsa('rsa-test-key', $privateKey); $request = new HttpRequest( method: Method::GET, path: '/api/test', headers: new Headers(['Host' => 'example.com', 'Date' => 'Thu, 05 Jan 2023 21:31:40 GMT']) ); $signature = $this->signer->sign($request, $key); $this->assertEquals('rsa-test-key', $signature->keyId); $this->assertEquals(SigningAlgorithm::RSA_SHA256, $signature->algorithm); $this->assertNotEmpty($signature->signature); } }