2

Amazon provides documentation for verifying that SNS messages routed to an HTTP/HTTPS endpoint are valid. Using Vapor and Swift on the server, along with swift-certificates and swift-crypto, I implemented the verification logic:

func messageHasValidSignature<Message: VerifiableMessage>(_ message: Message) async throws -> Bool {
    guard let certificateBytes = try await client.get(.init(message.signingCertificateURL)).body else {
        logger.error("Failed to get signing certificate body")
        throw Abort(.badRequest)
    }

    let certificateString = String(buffer: certificateBytes)
    let certificate = try Certificate(pemEncoded: certificateString)
    let publicKey = try _RSA.Signing.PublicKey(derRepresentation: certificate.publicKey.subjectPublicKeyInfoBytes)
    guard let signatureData = Data(base64Encoded: message.signature) else {
        logger.error("Failed to decode signature")
        throw Abort(.badRequest)
    }

    let signature = _RSA.Signing.RSASignature(rawRepresentation: signatureData)

    guard let dataToSign = message.stringToSign.data(using: .utf8) else {
        logger.error("Failed to convert string to sign to data")
        throw Abort(.badRequest)
    }

    let digest: any Digest
    switch message.signatureVersion {
    case ._1:
        digest = Insecure.SHA1.hash(data: dataToSign)
    case ._2:
        digest = SHA256.hash(data: dataToSign)
    }

    return publicKey.isValidSignature(signature, for: digest)
}

I am constructing the string to sign as follows:

extension SubscriptionConfirmationMessage: VerifiableMessage {
    var stringToSign: String {
        """
        Message
        \(message)
        MessageId
        \(messageID)
        SubscribeURL
        \(subscribeURL)
        Timestamp
        \(timestamp)
        Token
        \(token)
        TopicArn
        \(topicARN)
        Type
        SubscriptionConfirmation

        """
    }
}

But isValidSignature always returns false. How can I properly verify the message?

1 Answer 1

2

The problem was with the padding passed to isValidSignature. The default is PSS ("PSS padding using MGF1"). By checking the source of the C# implementation I found that it should be insecurePKCS1v1_5 ("PKCS#1 v1.5").

So the correct way to verify the signature is:

return publicKey.isValidSignature(signature, for: digest,  padding: .insecurePKCS1v1_5)
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.