client = $client ?: new Client(); } /** * Validates a message from SNS to ensure that it was delivered by AWS * * @param Message $message The message to validate * * @throws CannotGetPublicKeyFromCertificateException If the certificate cannot be retrieved * @throws CertificateFromUnrecognizedSourceException If the certificate's source cannot be verified * @throws InvalidMessageSignatureException If the message's signature is invalid */ public function validate(Message $message) { // Get the cert's URL and ensure it is from AWS $certUrl = Url::factory($message->get('SigningCertURL')); if ('.amazonaws.com' != substr($certUrl->getHost(), -14)) { throw new CertificateFromUnrecognizedSourceException(); } // Get the cert itself and extract the public key $certificate = $this->client->get((string) $certUrl)->send()->getBody(); $publicKey = openssl_get_publickey($certificate); if (!$publicKey) { throw new CannotGetPublicKeyFromCertificateException(); } // Verify the signature of the message $stringToSign = $message->getStringToSign(); $incomingSignature = base64_decode($message->get('Signature')); if (!openssl_verify($stringToSign, $incomingSignature, $publicKey, OPENSSL_ALGO_SHA1)) { throw new InvalidMessageSignatureException(); } } /** * Determines if a message is valid and that is was delivered by AWS. This method does not throw exceptions and * returns a simple boolean value. * * @param Message $message The message to validate * * @return bool */ public function isValid(Message $message) { try { $this->validate($message); return true; } catch (SnsMessageValidatorException $e) { return false; } } }