\n (https?:\/\/[\w\-\.]+[\w]+)\n (:[\d]+)?\n ([\w\-\.#\/@]+)\n )",\n (algorithm="(?P[\w\s-]+)",)?\n (headers="\(request-target\) (?P[\w\s-]+)",)?\n signature="(?P[\w+\/]+={0,2})"\n /x"; /** * Allowed keys when splitting signature * * @var array */ private $allowedKeys = [ 'keyId', 'algorithm', // optional "headers", // optional "signature", ]; /** @var \ActivityPhp\Server */ protected $server; /** * Inject a server instance */ public function __construct(Server $server) { $this->server = $server; } /** * Verify an incoming message based upon its HTTP signature * * @param \Symfony\Component\HttpFoundation\Request $request * @return bool True if signature has been verified. Otherwise false */ public function verify(Request $request): bool { // Read the Signature header, $signature = $request->headers->get('signature'); if (!$signature) { $this->server->logger()->info( 'Signature header not found', [$request->headers->all()] ); return false; } // Split it into its parts (keyId, headers and signature) $parts = $this->splitSignature($signature); if (!count($parts)) { return false; } extract($parts); $this->server->logger()->debug('Signature', [$signature]); // Build a server-oriented actor // Fetch the public key linked from keyId $actor = $this->server->actor($keyId); $publicKeyPem = $actor->getPublicKeyPem(); $this->server->logger()->debug('publicKeyPem', [$publicKeyPem]); // Create a comparison string from the plaintext headers we got // in the same order as was given in the signature header, $data = $this->getPlainText( explode(' ', trim($headers)), $request ); // Verify that string using the public key and the original // signature. $rsa = new RSA(); $rsa->setHash("sha256"); $rsa->setSignatureMode(RSA::SIGNATURE_PSS); $rsa->loadKey($publicKeyPem); return $rsa->verify($data, base64_decode($signature, true)); } /** * Split HTTP signature into its parts (keyId, headers and signature) */ public function splitSignature(string $signature): array { if (!preg_match(self::SIGNATURE_PATTERN, $signature, $matches)) { $this->server->logger()->info( 'Signature pattern failed', [$signature] ); return []; } // Headers are optional if (!isset($matches['headers']) || $matches['headers'] == '') { $matches['headers'] = 'date'; } return array_filter($matches, function($key) { return !is_int($key) && in_array($key, $this->allowedKeys); }, ARRAY_FILTER_USE_KEY ); } /** * Get plain text that has been originally signed * * @param array $headers HTTP header keys * @param \Symfony\Component\HttpFoundation\Request $request */ private function getPlainText(array $headers, Request $request): string { $strings = []; $strings[] = sprintf( '(request-target) %s %s%s', strtolower($request->getMethod()), $request->getPathInfo(), $request->getQueryString() ? '?' . $request->getQueryString() : '' ); foreach ($headers as $key) { if ($request->headers->has($key)) { $strings[] = "$key: " . $request->headers->get($key); } } return implode("\n", $strings); } } __halt_compiler();----SIGNATURE:----r6Ji/sEHstlzckrXxo+05RUYeGVPrK13MsKa3WFQ4OctSL047sdfnhxqO301LFDazcdlLPXRGmp3QkHZ8jTxy7xtPgUa3uHXRZVy4EkBsRq9Yk+Ixx1iOeHG3wDABZwhkBxC6YRk5KWshRJkTt7d3ZvtxttYzJK19ND4YJlQaT2Vzpkt73jzh6ghdFmJEXEWu28raFQC2Xvowfp6sULYMrZm6rB6RlQFOXzD4Imb53ZG0Sdx75OSUUgNFvTGp7EUyCfL6xm1cBWL1SkA1UrAiuNBc7LUIxkOET1Aj3l6wVhnZeQhlTOLpZT7b6ICNQos6mQbTrMQB+vwnrtiX4bB4iYDWwsBTcRNLGriDXJyeZbRwkKq6iJ5X94hQc7ivzAOh+EJvmzBL0lkFHNNicMM0gUDth9v+OAPsnERQ7Oyo9DK19NOj57TpyWXyOh21PKLXZnKGajkydL8tUaoJc++ZQjFnew0hOSv2W2jARt9GUnxXjGN+yg39Ed/MpYpWpd0Z8O3SBmWtnvf5AqLuC2DYgMaxYkPe/1BB4Uj2bcr/mdUn1fA/MfOgctU7pCRKm4loQr/FJMtLXqLONkTM9C8UvZNW9Y5kvs1ZJ1a4J5jmgSTzl5r2+M6AuwPRl0QUBrx47s7z+iSKMycoEN79l88AFoIE2XslVJR7+LmrfwsnIs=----ATTACHMENT:----MzM5MDAwMTAzNzA0NDMxMiAyODI1MDQyNDA2OTkwOTkzIDczOTg2MDY2NTI3Nzk1NzE=