diff --git a/services-custom/sns-message-manager/pom.xml b/services-custom/sns-message-manager/pom.xml index fb5619d5e5c..9fa4f195f6d 100644 --- a/services-custom/sns-message-manager/pom.xml +++ b/services-custom/sns-message-manager/pom.xml @@ -68,6 +68,11 @@ sdk-core ${project.version} + + org.junit.jupiter + junit-jupiter + test + software.amazon.awssdk regions @@ -93,11 +98,6 @@ httpclient5 ${httpcomponents.client5.version} - - org.junit.jupiter - junit-jupiter - test - org.assertj assertj-core diff --git a/services-custom/sns-message-manager/src/main/java/software/amazon/awssdk/messagemanager/sns/internal/SignatureValidator.java b/services-custom/sns-message-manager/src/main/java/software/amazon/awssdk/messagemanager/sns/internal/SignatureValidator.java new file mode 100644 index 00000000000..963e6901d93 --- /dev/null +++ b/services-custom/sns-message-manager/src/main/java/software/amazon/awssdk/messagemanager/sns/internal/SignatureValidator.java @@ -0,0 +1,171 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.awssdk.messagemanager.sns.internal; + +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.Signature; +import java.security.SignatureException; +import java.util.StringJoiner; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.core.SdkBytes; +import software.amazon.awssdk.core.exception.SdkClientException; +import software.amazon.awssdk.messagemanager.sns.model.SignatureVersion; +import software.amazon.awssdk.messagemanager.sns.model.SnsMessage; +import software.amazon.awssdk.messagemanager.sns.model.SnsNotification; +import software.amazon.awssdk.messagemanager.sns.model.SnsSubscriptionConfirmation; +import software.amazon.awssdk.messagemanager.sns.model.SnsUnsubscribeConfirmation; +import software.amazon.awssdk.utils.Logger; +import software.amazon.awssdk.utils.Validate; + +/** + * See + * + * The official documentation. + */ +@SdkInternalApi +public final class SignatureValidator { + private static final Logger LOG = Logger.loggerFor(SignatureValidator.class); + + private static final String MESSAGE = "Message"; + private static final String MESSAGE_ID = "MessageId"; + private static final String SUBJECT = "Subject"; + private static final String SUBSCRIBE_URL = "SubscribeURL"; + private static final String TIMESTAMP = "Timestamp"; + private static final String TOKEN = "Token"; + private static final String TOPIC_ARN = "TopicArn"; + private static final String TYPE = "Type"; + + private static final String NEWLINE = "\n"; + + public void validateSignature(SnsMessage message, PublicKey publicKey) { + Validate.paramNotNull(message, "message"); + Validate.paramNotNull(publicKey, "publicKey"); + + SdkBytes messageSignature = message.signature(); + if (messageSignature == null) { + throw SdkClientException.create("Message signature cannot be null"); + } + + SignatureVersion signatureVersion = message.signatureVersion(); + if (signatureVersion == null) { + throw SdkClientException.create("Message signature version cannot be null"); + } + + if (message.timestamp() == null) { + throw SdkClientException.create("Message timestamp cannot be null"); + } + + String canonicalMessage = buildCanonicalMessage(message); + LOG.debug(() -> String.format("Canonical message: %s%n", canonicalMessage)); + + Signature signature = getSignature(signatureVersion); + + verifySignature(canonicalMessage, messageSignature, publicKey, signature); + } + + private static String buildCanonicalMessage(SnsMessage message) { + switch (message.type()) { + case NOTIFICATION: + return buildCanonicalMessage((SnsNotification) message); + case SUBSCRIPTION_CONFIRMATION: + return buildCanonicalMessage((SnsSubscriptionConfirmation) message); + case UNSUBSCRIBE_CONFIRMATION: + return buildCanonicalMessage((SnsUnsubscribeConfirmation) message); + default: + throw new IllegalStateException(String.format("Unsupported SNS message type: %s", message.type())); + } + } + + private static String buildCanonicalMessage(SnsNotification notification) { + StringJoiner joiner = new StringJoiner(NEWLINE, "", NEWLINE); + joiner.add(MESSAGE).add(notification.message()); + joiner.add(MESSAGE_ID).add(notification.messageId()); + + if (notification.subject() != null) { + joiner.add(SUBJECT).add(notification.subject()); + } + + joiner.add(TIMESTAMP).add(notification.timestamp().toString()); + joiner.add(TOPIC_ARN).add(notification.topicArn()); + joiner.add(TYPE).add(notification.type().toString()); + + return joiner.toString(); + } + + // Message, MessageId, SubscribeURL, Timestamp, Token, TopicArn, and Type. + private static String buildCanonicalMessage(SnsSubscriptionConfirmation message) { + StringJoiner joiner = new StringJoiner(NEWLINE, "", NEWLINE); + joiner.add(MESSAGE).add(message.message()); + joiner.add(MESSAGE_ID).add(message.messageId()); + joiner.add(SUBSCRIBE_URL).add(message.subscribeUrl().toString()); + joiner.add(TIMESTAMP).add(message.timestamp().toString()); + joiner.add(TOKEN).add(message.token()); + joiner.add(TOPIC_ARN).add(message.topicArn()); + joiner.add(TYPE).add(message.type().toString()); + + return joiner.toString(); + } + + private static String buildCanonicalMessage(SnsUnsubscribeConfirmation message) { + StringJoiner joiner = new StringJoiner(NEWLINE, "", NEWLINE); + joiner.add(MESSAGE).add(message.message()); + joiner.add(MESSAGE_ID).add(message.messageId()); + joiner.add(SUBSCRIBE_URL).add(message.subscribeUrl().toString()); + joiner.add(TIMESTAMP).add(message.timestamp().toString()); + joiner.add(TOKEN).add(message.token()); + joiner.add(TOPIC_ARN).add(message.topicArn()); + joiner.add(TYPE).add(message.type().toString()); + + return joiner.toString(); + } + + private static void verifySignature(String canonicalMessage, SdkBytes messageSignature, PublicKey publicKey, + Signature signature) { + + try { + signature.initVerify(publicKey); + signature.update(canonicalMessage.getBytes(StandardCharsets.UTF_8)); + + boolean isValid = signature.verify(messageSignature.asByteArray()); + + if (!isValid) { + throw SdkClientException.create("The computed signature did not match the expected signature"); + } + } catch (InvalidKeyException e) { + throw SdkClientException.create("The public key is invalid", e); + } catch (SignatureException e) { + throw SdkClientException.create("The signature is invalid", e); + } + } + + private static Signature getSignature(SignatureVersion signatureVersion) { + try { + switch (signatureVersion) { + case VERSION_1: + return Signature.getInstance("SHA1withRSA"); + case VERSION_2: + return Signature.getInstance("SHA256withRSA"); + default: + throw new IllegalArgumentException("Unsupported signature version: " + signatureVersion); + } + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("Unable to create Signature for " + signatureVersion, e); + } + } +} \ No newline at end of file diff --git a/services-custom/sns-message-manager/src/test/java/software/amazon/awssdk/messagemanager/sns/internal/SignatureValidatorTest.java b/services-custom/sns-message-manager/src/test/java/software/amazon/awssdk/messagemanager/sns/internal/SignatureValidatorTest.java new file mode 100644 index 00000000000..fe30a03d16a --- /dev/null +++ b/services-custom/sns-message-manager/src/test/java/software/amazon/awssdk/messagemanager/sns/internal/SignatureValidatorTest.java @@ -0,0 +1,199 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.awssdk.messagemanager.sns.internal; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.InputStream; +import java.net.URI; +import java.security.PublicKey; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.time.Instant; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import software.amazon.awssdk.core.SdkBytes; +import software.amazon.awssdk.core.exception.SdkClientException; +import software.amazon.awssdk.messagemanager.sns.model.SignatureVersion; +import software.amazon.awssdk.messagemanager.sns.model.SnsMessage; +import software.amazon.awssdk.messagemanager.sns.model.SnsNotification; + +class SignatureValidatorTest { + private static final String RESOURCE_ROOT = "/software/amazon/awssdk/messagemanager/sns/internal/"; + private static final String SIGNING_CERT_RESOURCE = "SimpleNotificationService-7506a1e35b36ef5a444dd1a8e7cc3ed8.pem"; + private static final SignatureValidator VALIDATOR = new SignatureValidator(); + private static X509Certificate signingCertificate; + + @BeforeAll + static void setup() throws CertificateException { + InputStream is = resourceAsStream(SIGNING_CERT_RESOURCE); + CertificateFactory factory = CertificateFactory.getInstance("X.509"); + signingCertificate = (X509Certificate) factory.generateCertificate(is); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("validMessages") + void validateSignature_signatureValid_doesNotThrow(TestCase tc) { + SnsMessageUnmarshaller unmarshaller = new SnsMessageUnmarshaller(); + SnsMessage msg = unmarshaller.unmarshall(resourceAsStream(tc.messageJsonResource)); + VALIDATOR.validateSignature(msg, signingCertificate.getPublicKey()); + } + + @Test + void validateSignature_signatureMismatch_throws() { + SnsNotification notification = SnsNotification.builder() + .message("hello world") + .messageId("message-id") + .timestamp(Instant.now()) + .signature(SdkBytes.fromByteArray(new byte[256])) + .signatureVersion(SignatureVersion.VERSION_1) + .build(); + + assertThatThrownBy(() -> VALIDATOR.validateSignature(notification, signingCertificate.getPublicKey())) + .isInstanceOf(SdkClientException.class) + .hasMessageContaining("The computed signature did not match the expected signature"); + } + + @Test + void validateSignature_signatureMissing_throws() { + SnsNotification notification = SnsNotification.builder() + .subject("hello world") + .message("hello world") + .messageId("message-id") + .timestamp(Instant.now()) + .unsubscribeUrl(URI.create("https://my-test-service.amazonaws.com")) + .signingCertUrl(URI.create("https://my-test-service.amazonaws.com/cert" + + ".pem")) + .signatureVersion(SignatureVersion.VERSION_1) + .build(); + + assertThatThrownBy(() -> VALIDATOR.validateSignature(notification, signingCertificate.getPublicKey())) + .isInstanceOf(SdkClientException.class) + .hasMessage("Message signature cannot be null"); + } + + @Test + void validateSignature_timestampMissing_throws() { + SnsNotification notification = SnsNotification.builder() + .subject("hello world") + .message("hello world") + .messageId("message-id") + .signature(SdkBytes.fromByteArray(new byte[256])) + .unsubscribeUrl(URI.create("https://my-test-service.amazonaws.com")) + .signingCertUrl(URI.create("https://my-test-service.amazonaws.com/cert" + + ".pem")) + .signatureVersion(SignatureVersion.VERSION_1) + .build(); + + assertThatThrownBy(() -> VALIDATOR.validateSignature(notification, signingCertificate.getPublicKey())) + .isInstanceOf(SdkClientException.class) + .hasMessage("Message timestamp cannot be null"); + } + + @Test + void validateSignature_signatureVersionMissing_throws() { + SnsNotification notification = SnsNotification.builder() + .subject("hello world") + .message("hello world") + .messageId("message-id") + .signature(SdkBytes.fromByteArray(new byte[256])) + .timestamp(Instant.now()) + .unsubscribeUrl(URI.create("https://my-test-service.amazonaws.com")) + .signingCertUrl(URI.create("https://my-test-service.amazonaws.com/cert" + + ".pem")) + .build(); + + + assertThatThrownBy(() -> VALIDATOR.validateSignature(notification, signingCertificate.getPublicKey())) + .isInstanceOf(SdkClientException.class) + .hasMessage("Message signature version cannot be null"); + } + + @Test + void validateSignature_certInvalid_throws() throws CertificateException { + SnsNotification notification = SnsNotification.builder() + .signature(SdkBytes.fromByteArray(new byte[1])) + .signatureVersion(SignatureVersion.VERSION_1) + .timestamp(Instant.now()) + .build(); + + PublicKey badKey = mock(PublicKey.class); + when(badKey.getFormat()).thenReturn("X.509"); + when(badKey.getAlgorithm()).thenReturn("RSA"); + when(badKey.getEncoded()).thenReturn(new byte[1]); + + assertThatThrownBy(() -> VALIDATOR.validateSignature(notification, badKey)) + .isInstanceOf(SdkClientException.class) + .hasMessage("The public key is invalid"); + } + + @Test + void validateSignature_signatureInvalid_throws() throws CertificateException { + SnsNotification notification = SnsNotification.builder() + .subject("hello world") + .message("hello world") + .messageId("message-id") + .signature(SdkBytes.fromByteArray(new byte[1])) + .signatureVersion(SignatureVersion.VERSION_1) + .timestamp(Instant.now()) + .unsubscribeUrl(URI.create("https://my-test-service.amazonaws.com")) + .signingCertUrl(URI.create("https://my-test-service.amazonaws.com/cert" + + ".pem")) + .build(); + + assertThatThrownBy(() -> VALIDATOR.validateSignature(notification, signingCertificate.getPublicKey())) + .isInstanceOf(SdkClientException.class) + .hasMessage("The signature is invalid"); + } + + private static List validMessages() { + return Stream.of( + new TestCase("Notification - No Subject", "test-notification-no-subject.json"), + new TestCase("Notification - Version 2 signature", "test-notification-signature-v2.json"), + new TestCase("Notification with subject", "test-notification-with-subject.json"), + new TestCase("Subscription confirmation", "test-subscription-confirmation.json"), + new TestCase("Unsubscribe confirmation", "test-unsubscribe-confirmation.json") + ) + .collect(Collectors.toList()); + } + + private static InputStream resourceAsStream(String resourceName) { + return SignatureValidatorTest.class.getResourceAsStream(RESOURCE_ROOT + resourceName); + } + + private static class TestCase { + private String desription; + private String messageJsonResource; + + public TestCase(String desription, String messageJsonResource) { + this.desription = desription; + this.messageJsonResource = messageJsonResource; + } + + @Override + public String toString() { + return desription; + } + } +} \ No newline at end of file diff --git a/services-custom/sns-message-manager/src/test/resources/software/amazon/awssdk/messagemanager/sns/internal/SimpleNotificationService-7506a1e35b36ef5a444dd1a8e7cc3ed8.pem b/services-custom/sns-message-manager/src/test/resources/software/amazon/awssdk/messagemanager/sns/internal/SimpleNotificationService-7506a1e35b36ef5a444dd1a8e7cc3ed8.pem new file mode 100644 index 00000000000..f5f615db34a --- /dev/null +++ b/services-custom/sns-message-manager/src/test/resources/software/amazon/awssdk/messagemanager/sns/internal/SimpleNotificationService-7506a1e35b36ef5a444dd1a8e7cc3ed8.pem @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFxTCCBK2gAwIBAgIQBkyRNHAyygLkObkxH5ju+zANBgkqhkiG9w0BAQsFADA8 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRwwGgYDVQQDExNBbWF6b24g +UlNBIDIwNDggTTAxMB4XDTI1MTExMDAwMDAwMFoXDTI2MTAxNDIzNTk1OVowHDEa +MBgGA1UEAxMRc25zLmFtYXpvbmF3cy5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQChbVNQ+DEaT5WN+l+bg0iEeF666Z6LDFc1KJr4lNl0odcXQSsk +KsZYCMVdp4I/Ikk9zWyckRTMkmQGYimpD+bTsMCP5C/sJIU0E9Y2OXjjl5Videkq +qnRL9VdE5N41Rvks7S/5MpYcSmCsmaHfwcv82Gvx2zm9CrA5Xu7K2ZqhzRQPCRSP +mHTHQr4ED63ujE+qTpPgQZ0TvgmcrAc08Z75Z7Zzs17aTNc3YhAtQx9WowN8uiHk +kgc+ONNdtwKvWZNmnbmxMHYtYc291WCP1K1Nh4TV5XNyjIebzQKDrqTCN0ItAr1x +e6Bl3BYS3SG9XHAy0q3ekITzQsKypcuaK6dTAgMBAAGjggLhMIIC3TAfBgNVHSME +GDAWgBSBuA5jiokSGOX6OztQlZ/m5ZAThTAdBgNVHQ4EFgQUBQEUzzzuVJAKzAlR +d0Q//W6HvZUwHAYDVR0RBBUwE4IRc25zLmFtYXpvbmF3cy5jb20wEwYDVR0gBAww +CjAIBgZngQwBAgEwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMB +MDsGA1UdHwQ0MDIwMKAuoCyGKmh0dHA6Ly9jcmwucjJtMDEuYW1hem9udHJ1c3Qu +Y29tL3IybTAxLmNybDB1BggrBgEFBQcBAQRpMGcwLQYIKwYBBQUHMAGGIWh0dHA6 +Ly9vY3NwLnIybTAxLmFtYXpvbnRydXN0LmNvbTA2BggrBgEFBQcwAoYqaHR0cDov +L2NydC5yMm0wMS5hbWF6b250cnVzdC5jb20vcjJtMDEuY2VyMAwGA1UdEwEB/wQC +MAAwggF/BgorBgEEAdZ5AgQCBIIBbwSCAWsBaQB3ANdtfRDRp/V3wsfpX9cAv/mC +yTNaZeHQswFzF8DIxWl3AAABmnAb33MAAAQDAEgwRgIhANXAgMEZWU326cqxqwnf +ntD0XcBjRbmvVMOp3hZ8boPXAiEAkI3NTMXcdLCChHak1ipOae7K7r3spMGUMk6Q +cEUKqBEAdgDCMX5XRRmjRe5/ON6ykEHrx8IhWiK/f9W1rXaa2Q5SzQAAAZpwG9+u +AAAEAwBHMEUCIQD+Qyok8NYaFuQkccW+XIMmxHxj6Yka2wjtg6UN/tbU5wIgcp4z +8MyC/dUfeLRuHAt0owtVgEu7xXqrMBOYlbXrfC4AdgDLOPcViXyEoURfW8Hd+8lu +8ppZzUcKaQWFsMsUwxRY5wAAAZpwG9+yAAAEAwBHMEUCIQCmu1jQUG9A2br7OqwM +FvQXtBX10yIvvDa6a2Ov1Zgg6gIgKlqRvN6WhX0pSHAN2bKmjRwIUI3X9kjS/A/m +DYULaV4wDQYJKoZIhvcNAQELBQADggEBAIbTQWQhqrG6SzwLq79tKCpI0aL0xlD+ +/jrGPxpwTlK7rtAArHiEVh/bsmwLYRlIxPBiU+NE/kqeT0lhJA6/e3X3KRmnpJ6u +Z70PJGYwmZbSRsFom0vGFBMncX0qYx1iD0dooPeAkkSnM5j81rBh+FepV3HfF+tA +9gaglWLsU7Z/A0fdn9D32UPreVHloOut5EyEinvXRUAWZco9CRloyEXJr7Mxq9MZ +vYOK+5wuSPHnPij+fGlQsPxEgoTnCmU03PqNS49kL13jjEnYYdijqL+4rfHtuZbc +4hlVoZfttwg8wD3m5brhQ+GkBBNYN8HFtdVy/BnFi+SPJqv6BgB3Y00= +-----END CERTIFICATE----- diff --git a/services-custom/sns-message-manager/src/test/resources/software/amazon/awssdk/messagemanager/sns/internal/test-notification-no-subject.json b/services-custom/sns-message-manager/src/test/resources/software/amazon/awssdk/messagemanager/sns/internal/test-notification-no-subject.json new file mode 100644 index 00000000000..45b2e4a0121 --- /dev/null +++ b/services-custom/sns-message-manager/src/test/resources/software/amazon/awssdk/messagemanager/sns/internal/test-notification-no-subject.json @@ -0,0 +1,11 @@ +{ + "Type" : "Notification", + "MessageId" : "c0e5a280-53c9-5a98-93bb-c0a560b3d192", + "TopicArn" : "arn:aws:sns:us-west-2:131990247566:dongie-standard-topic", + "Message" : "This notification has no subject.", + "Timestamp" : "2026-03-06T19:55:38.903Z", + "SignatureVersion" : "1", + "Signature" : "h5/2kLNyPKvMH+ipOA1kueaA1GLFlcKbO/9o6GbH7BtKk/8lM8krPFDrV+Lwuq8yZDXTxeSXHR8pG1w9MkKQ3VlsHhMigy+5ejIsb+ZfpkDDO6dOACbkTg1twy9tLeKCyPfU01OokKTR6jlKJGCgXXDQm5Q1Fs4NfUz22xGqteycG1GIioqq2Hvd1PLsDkcoyUAhciugGFcZKIGjUbBtdhMTPFX1ed8iH+Y/p6o/eD0G2i614oRSXYRJyV2iNpl54LCCiCrAafAJAM5NXAJJq9CWyTjxHFC18FnPqSukW1/6Qa502oujhPJ/pyl+6mMBoF6LjnpbIyR8/Gmm+RY6fg==", + "SigningCertURL" : "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-7506a1e35b36ef5a444dd1a8e7cc3ed8.pem", + "UnsubscribeURL" : "https://sns.us-west-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-west-2:131990247566:dongie-standard-topic:b24e11a2-06bd-4e73-aa29-0ed2de3b41b5" +} \ No newline at end of file diff --git a/services-custom/sns-message-manager/src/test/resources/software/amazon/awssdk/messagemanager/sns/internal/test-notification-signature-v2.json b/services-custom/sns-message-manager/src/test/resources/software/amazon/awssdk/messagemanager/sns/internal/test-notification-signature-v2.json new file mode 100644 index 00000000000..2f946f54080 --- /dev/null +++ b/services-custom/sns-message-manager/src/test/resources/software/amazon/awssdk/messagemanager/sns/internal/test-notification-signature-v2.json @@ -0,0 +1,11 @@ +{ + "Type" : "Notification", + "MessageId" : "9d2c5580-b61d-550c-8583-37a582870907", + "TopicArn" : "arn:aws:sns:us-west-2:131990247566:dongie-standard-topic", + "Message" : "This message uses signature version 2.", + "Timestamp" : "2026-03-06T20:00:26.893Z", + "SignatureVersion" : "2", + "Signature" : "kGNJ7FIM5DJxRv9bdoSJBty2WHpADFWrJ0ZQU/A2sALy6tBE5hqfUvPRlklTJ+PACFMguoFOO7W6ac/Bs4gR+JBRFhx4598X8/HWzrmaLrtB2f7nzViHbpzYN+u+OewXhxjj2lNsu0bLKv6fogkACytDIOhjZylbfJoPphDYAB7T3Ila7u+K3m2wLHH4k1cPYKyBf5FI/kAoQAVsY6qFHeb6CWfHsdM98I2gwyEvwJSzW0iyYBfqckmthQwH3Ex53jrhQlZylXFDYmstSJAdvbLeGqIUxKyk+O25wYgBjbbbeR+2qE2UCGEQrk1Bq018wmTxdHZmDhoBjJXiGmFkfg==", + "SigningCertURL" : "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-7506a1e35b36ef5a444dd1a8e7cc3ed8.pem", + "UnsubscribeURL" : "https://sns.us-west-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-west-2:131990247566:dongie-standard-topic:b24e11a2-06bd-4e73-aa29-0ed2de3b41b5" +} \ No newline at end of file diff --git a/services-custom/sns-message-manager/src/test/resources/software/amazon/awssdk/messagemanager/sns/internal/test-notification-with-subject.json b/services-custom/sns-message-manager/src/test/resources/software/amazon/awssdk/messagemanager/sns/internal/test-notification-with-subject.json new file mode 100644 index 00000000000..50636ad7165 --- /dev/null +++ b/services-custom/sns-message-manager/src/test/resources/software/amazon/awssdk/messagemanager/sns/internal/test-notification-with-subject.json @@ -0,0 +1,12 @@ +{ + "Type" : "Notification", + "MessageId" : "26858270-545e-5848-82a2-e05566af3dcf", + "TopicArn" : "arn:aws:sns:us-west-2:131990247566:dongie-standard-topic", + "Subject" : "My message subject", + "Message" : "Hello! This message has a subject.", + "Timestamp" : "2026-03-06T19:53:21.424Z", + "SignatureVersion" : "1", + "Signature" : "jvNSgHVOF9KUOKI3bmQZhURD0kDN6txhfjiyuvnV95Dw6b2SRa7nq3AhM8agP0KUCIrRgKHqUlN6K2XBA35Lzkx1eI5RT0oXIN00pvvzGxSz0ddnuSGmDJM7bd/b1vl2RlLjz+KvkhzEtspxLnxjLsGl2EpOmztt+um7owb4475sOLdGTx1QiFV/8HqB2J0DyjeWdRZvu7Xpsf1Yd2rgmijFoz8x89Vw8pIksjczr4q16pxdh/XKWjbN2uPoC+3EeYdljL8KUvLYH9ueHSs5E9qq2iLdze4+FtANnE73XRvvSGE0KI+FGOvz1igest6qon3g1/CVy4mq3n19TsCSmA==", + "SigningCertURL" : "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-7506a1e35b36ef5a444dd1a8e7cc3ed8.pem", + "UnsubscribeURL" : "https://sns.us-west-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-west-2:131990247566:dongie-standard-topic:b24e11a2-06bd-4e73-aa29-0ed2de3b41b5" +} \ No newline at end of file diff --git a/services-custom/sns-message-manager/src/test/resources/software/amazon/awssdk/messagemanager/sns/internal/test-subscription-confirmation.json b/services-custom/sns-message-manager/src/test/resources/software/amazon/awssdk/messagemanager/sns/internal/test-subscription-confirmation.json new file mode 100644 index 00000000000..bc1ea5de289 --- /dev/null +++ b/services-custom/sns-message-manager/src/test/resources/software/amazon/awssdk/messagemanager/sns/internal/test-subscription-confirmation.json @@ -0,0 +1,12 @@ +{ + "Type" : "SubscriptionConfirmation", + "MessageId" : "f3f2fc87-c426-4c2f-9044-b55890c0939c", + "Token" : "2336412f37fb687f5d51e6e2425929f52e8e353134fe72d55ef8b330217d82da14007cbccefffd35b695feb8f0294d01fd9644f41e1ddfad261519a2c0bac743ff375eeb64530147d2da57f3acc54739da14c8dbd48a9d58ce996eff0d498ea6b4b9f58720d8959b52b99b8de148081720d7da17434e376614a00c992e7823e4", + "TopicArn" : "arn:aws:sns:us-west-2:131990247566:dongie-standard-topic", + "Message" : "You have chosen to subscribe to the topic arn:aws:sns:us-west-2:131990247566:dongie-standard-topic.\nTo confirm the subscription, visit the SubscribeURL included in this message.", + "SubscribeURL" : "https://sns.us-west-2.amazonaws.com/?Action=ConfirmSubscription&TopicArn=arn:aws:sns:us-west-2:131990247566:dongie-standard-topic&Token=2336412f37fb687f5d51e6e2425929f52e8e353134fe72d55ef8b330217d82da14007cbccefffd35b695feb8f0294d01fd9644f41e1ddfad261519a2c0bac743ff375eeb64530147d2da57f3acc54739da14c8dbd48a9d58ce996eff0d498ea6b4b9f58720d8959b52b99b8de148081720d7da17434e376614a00c992e7823e4", + "Timestamp" : "2026-03-06T19:31:32.895Z", + "SignatureVersion" : "1", + "Signature" : "Ce2Ax8UbEq0RrVH7gsjP9WZ9bzif7dUkAY4OH99Pv7FSrvs8cK+uiH3a6ig54rXWUnZRUejk8K6KeK5Q80O9iJp8KbPRKdQ0PkJ3YASdabhfK0e7evC05YR2pFoS6nNaqNzT7HrR1d1Z9iLoFxluo2Dzb7kWVB7e9jp/y+ZBXRzRIaZgscM84kahBc6QgXOFJA1/PFA4cB5qaa6tBcUFtTE63o9vJ4wMmKk93W9MoszC+dNJeVFr8VHmt3toR+3JauBCZnGdgXgsDhni4PduSfWJJxEpuOK41iYf1hW+xFNFFsv46siTFiHdHiYDfI7/VlaY4spMgkq3EndRr7rHzQ==", + "SigningCertURL" : "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-7506a1e35b36ef5a444dd1a8e7cc3ed8.pem" +} \ No newline at end of file diff --git a/services-custom/sns-message-manager/src/test/resources/software/amazon/awssdk/messagemanager/sns/internal/test-unsubscribe-confirmation.json b/services-custom/sns-message-manager/src/test/resources/software/amazon/awssdk/messagemanager/sns/internal/test-unsubscribe-confirmation.json new file mode 100644 index 00000000000..378cb97be67 --- /dev/null +++ b/services-custom/sns-message-manager/src/test/resources/software/amazon/awssdk/messagemanager/sns/internal/test-unsubscribe-confirmation.json @@ -0,0 +1,12 @@ +{ + "Type" : "UnsubscribeConfirmation", + "MessageId" : "0b71e34e-42c3-486a-9e32-b1928ec994b5", + "Token" : "2336412f37fb687f5d51e6e2425929f52e8e353134fd0284eae5672847af082a982c7dd18215a1485e04e559716f8b42054594757f42dee2d63c46fd3a0fa59b4d688de03db956f8bf9f8cb3167587a28b74c5d325e6a7594d24a43ac534de2dfca614ca89cebd490342c721c30a76ea03def13673437a6a3e450642580119d0", + "TopicArn" : "arn:aws:sns:us-west-2:131990247566:dongie-standard-topic", + "Message" : "You have chosen to deactivate subscription arn:aws:sns:us-west-2:131990247566:dongie-standard-topic:b24e11a2-06bd-4e73-aa29-0ed2de3b41b5.\nTo cancel this operation and restore the subscription, visit the SubscribeURL included in this message.", + "SubscribeURL" : "https://sns.us-west-2.amazonaws.com/?Action=ConfirmSubscription&TopicArn=arn:aws:sns:us-west-2:131990247566:dongie-standard-topic&Token=2336412f37fb687f5d51e6e2425929f52e8e353134fd0284eae5672847af082a982c7dd18215a1485e04e559716f8b42054594757f42dee2d63c46fd3a0fa59b4d688de03db956f8bf9f8cb3167587a28b74c5d325e6a7594d24a43ac534de2dfca614ca89cebd490342c721c30a76ea03def13673437a6a3e450642580119d0", + "Timestamp" : "2026-03-06T20:02:25.038Z", + "SignatureVersion" : "2", + "Signature" : "RLW/cGWZAdY96iEcvXGmAZQ851DUyWehJIUMHT3Qa0/kX7Wu2QimI44TfE8d9hTqyYFreA6ln4AS67m225DuySpBeiUBtzB/ibiU7jdZmfSiT/I/RTEU3eD0lHHZIYW0M7HAlhHyaSXdcSAbtfnhMBMION93IVl6UOR4kWYdJaJ17/u3by3zlSRs8wCIpj06S6BzuzxrINZPJl+NKZYCVdMlrjuT4etX/UfZkC0Tjchpmu/CQcuimAgswlesm8cVue0WqNPXkOr8h7HmsiGVXPN1jjkYKf2/tVJyBxur/lSksMopER8wtAIsrqY1caAZSI8YJkIndg/b+7eCwkjTBA==", + "SigningCertURL" : "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-7506a1e35b36ef5a444dd1a8e7cc3ed8.pem" +} \ No newline at end of file