🐛 Fix SNS signature verification field sets per AWS spec

Address critical code review findings:

- Remove 'Signature' field from string-to-sign (it's the output, not input)
- Differentiate V1 vs V2 field sets per AWS SNS documentation:
  - V1 Notification: Message, MessageId, Subject, Timestamp, TopicArn, Type
  - V1 SubscriptionConfirmation: adds Token, SubscribeURL (excludes SigningCertURL, SignatureVersion)
  - V2 Notification/Subscription: all fields except Signature
- Add 'Token' field to SubscriptionConfirmation (required by AWS spec)
- Add AWS documentation URL comments for future reference
- Improve error handling in fetch-certificate and verify-signature
- Add end-to-end signature verification tests with real key pairs
- Add test resources (certificate and private key) for signature tests

See: https://docs.aws.amazon.com/sns/latest/dg/sns-verify-signature-of-message.html

Closes #11092

AI-assisted-by: qwen3.7-plus
This commit is contained in:
Andrey Antukh 2026-08-05 16:00:09 +00:00 committed by alonso.torres
parent 94a30a4634
commit 874f681506
5 changed files with 263 additions and 78 deletions

View File

@ -78,7 +78,7 @@
org.clojure/data.csv {:mvn/version "1.1.1"} org.clojure/data.csv {:mvn/version "1.1.1"}
com.clojure-goes-fast/clj-async-profiler {:mvn/version "2.0.0-beta1"} com.clojure-goes-fast/clj-async-profiler {:mvn/version "2.0.0-beta1"}
mockery/mockery {:mvn/version "0.1.4"}} mockery/mockery {:mvn/version "0.1.4"}}
:extra-paths ["test" "dev"]} :extra-paths ["test" "test/resources" "dev"]}
:build :build
{:extra-deps {:extra-deps

View File

@ -35,7 +35,8 @@
(defn- valid-sns-url? (defn- valid-sns-url?
"Validates that a URL originates from an SNS endpoint. "Validates that a URL originates from an SNS endpoint.
Only accepts sns.<region>.amazonaws.com hosts." Only accepts sns.<region>.amazonaws.com hosts.
See: https://docs.aws.amazon.com/sns/latest/dg/sns-verify-signature-of-message.html"
[url] [url]
(when (string? url) (when (string? url)
(try (try
@ -49,21 +50,52 @@
(catch Exception _ (catch Exception _
false)))) false))))
(def ^:private notification-fields ;; AWS SNS Signature Verification Field Sets
["Message" "MessageId" "Subject" "Timestamp" ;; See: https://docs.aws.amazon.com/sns/latest/dg/sns-verify-signature-of-message.html
"TopicArn" "Type" "SigningCertURL" "SignatureVersion" "Signature"]) ;;
;; Signature Version 1 signs only specific fields (excludes SigningCertURL, SignatureVersion, Signature)
;; Signature Version 2 signs all fields except Signature
;;
;; IMPORTANT: The "Signature" field is NEVER part of the string-to-sign (it's the output, not input)
(def ^:private subscription-fields ;; V1 Notification: Message, MessageId, Subject (if present), Timestamp, TopicArn, Type
["Message" "MessageId" "SubscribeURL" "Timestamp" (def ^:private v1-notification-fields
"TopicArn" "Type" "SigningCertURL" "SignatureVersion" "Signature"]) ["Message" "MessageId" "Subject" "Timestamp" "TopicArn" "Type"])
;; V1 SubscriptionConfirmation: Message, MessageId, SubscribeURL, Timestamp, Token, TopicArn, Type
(def ^:private v1-subscription-fields
["Message" "MessageId" "SubscribeURL" "Timestamp" "Token" "TopicArn" "Type"])
;; V2 Notification: All fields except Signature
(def ^:private v2-notification-fields
["Message" "MessageId" "Subject" "Timestamp" "TopicArn" "Type"
"SigningCertURL" "SignatureVersion"])
;; V2 SubscriptionConfirmation: All fields except Signature (includes Token)
(def ^:private v2-subscription-fields
["Message" "MessageId" "SubscribeURL" "Timestamp" "Token" "TopicArn" "Type"
"SigningCertURL" "SignatureVersion"])
(defn- build-string-to-sign (defn- build-string-to-sign
"Builds the string-to-sign for AWS SNS signature verification. "Builds the string-to-sign for AWS SNS signature verification.
See: https://docs.aws.amazon.com/sns/latest/dg/sns-verify-signature-of-message.html" See: https://docs.aws.amazon.com/sns/latest/dg/sns-verify-signature-of-message.html"
[body] [body]
(let [fields (if (= "SubscriptionConfirmation" (get body "Type")) (let [sig-version (get body "SignatureVersion")
subscription-fields msg-type (get body "Type")
notification-fields)] fields (cond
(= "1" sig-version)
(if (= "SubscriptionConfirmation" msg-type)
v1-subscription-fields
v1-notification-fields)
(= "2" sig-version)
(if (= "SubscriptionConfirmation" msg-type)
v2-subscription-fields
v2-notification-fields)
:else
(throw (ex-info "Unsupported SNS signature version"
{:version sig-version})))]
(->> fields (->> fields
(filter #(contains? body %)) (filter #(contains? body %))
(map #(str % "\n" (get body %) "\n")) (map #(str % "\n" (get body %) "\n"))
@ -71,36 +103,52 @@
(defn- fetch-certificate (defn- fetch-certificate
"Fetches the X.509 certificate from the given URL. "Fetches the X.509 certificate from the given URL.
Returns an InputStream that must be closed by the caller." Returns an InputStream that must be closed by the caller.
See: https://docs.aws.amazon.com/sns/latest/dg/sns-verify-signature-of-message.html"
[cfg cert-url] [cfg cert-url]
(let [response (http/req cfg {:uri cert-url :method :get :timeout 10000} (let [response (http/req cfg {:uri cert-url :method :get :timeout 10000}
{:sync? true :response-type :input-stream})] {:sync? true :response-type :input-stream})]
(when (= 200 (:status response)) (when-not (= 200 (:status response))
(:body response)))) (l/wrn :hint "failed to fetch SNS signing certificate"
:action "sns-cert-fetch-failed"
:status (:status response)
:cert-url cert-url)
(ex/raise :type :internal :code :cert-fetch-failed))
(:body response)))
(defn- verify-signature (defn- verify-signature
"Verifies the RSA signature of the message." "Verifies the RSA signature of the message.
See: https://docs.aws.amazon.com/sns/latest/dg/sns-verify-signature-of-message.html"
[cfg body] [cfg body]
(let [cert-url (get body "SigningCertURL") (let [cert-url (get body "SigningCertURL")
signature (get body "Signature") signature (get body "Signature")
sig-version (get body "SignatureVersion") sig-version (get body "SignatureVersion")
string-sign (build-string-to-sign body)
algorithm (case sig-version algorithm (case sig-version
"1" "SHA1withRSA" "1" "SHA1withRSA"
"2" "SHA256withRSA" "2" "SHA256withRSA"
nil)] nil)]
(when-not algorithm (when-not algorithm
(throw (ex-info "Unsupported SNS signature version" (throw (ex-info "Unsupported SNS signature version"
{:version sig-version}))) {:type :validation :version sig-version})))
(when (and cert-url signature) (when (and cert-url signature)
(try (try
(with-open [cert-stream (fetch-certificate cfg cert-url)] (let [string-sign (build-string-to-sign body)]
(let [cf (CertificateFactory/getInstance "X.509") (with-open [cert-stream (fetch-certificate cfg cert-url)]
cert (.generateCertificate cf cert-stream) (let [cf (CertificateFactory/getInstance "X.509")
sig (Signature/getInstance algorithm)] cert (.generateCertificate cf cert-stream)
(.initVerify sig (.getPublicKey cert)) sig (Signature/getInstance algorithm)]
(.update sig (.getBytes string-sign java.nio.charset.StandardCharsets/UTF_8)) (.initVerify sig (.getPublicKey cert))
(.verify sig (.decode (Base64/getDecoder) signature)))) (.update sig (.getBytes string-sign java.nio.charset.StandardCharsets/UTF_8))
(.verify sig (.decode (Base64/getDecoder) signature)))))
(catch clojure.lang.ExceptionInfo e
(let [data (ex-data e)]
(if (= :validation (:type data))
(throw e)
(do
(l/wrn :hint "SNS signature verification exception"
:action "sns-signature-verification-exception"
:cause e)
false))))
(catch Exception e (catch Exception e
(l/wrn :hint "SNS signature verification exception" (l/wrn :hint "SNS signature verification exception"
:action "sns-signature-verification-exception" :action "sns-signature-verification-exception"
@ -109,7 +157,8 @@
(defn- verify-sns-message! (defn- verify-sns-message!
"Verifies the AWS SNS message signature and URL validity. "Verifies the AWS SNS message signature and URL validity.
Throws if verification fails." Throws if verification fails.
See: https://docs.aws.amazon.com/sns/latest/dg/sns-verify-signature-of-message.html"
[cfg body] [cfg body]
(let [cert-url (get body "SigningCertURL") (let [cert-url (get body "SigningCertURL")
subscribe-url (get body "SubscribeURL") subscribe-url (get body "SubscribeURL")

View File

@ -308,66 +308,130 @@
(t/is (true? (#'awsns/valid-sns-url? "https://sns.us-east-1.amazonaws.com/cert.pem"))) (t/is (true? (#'awsns/valid-sns-url? "https://sns.us-east-1.amazonaws.com/cert.pem")))
(t/is (true? (#'awsns/valid-sns-url? "https://sns.ap-southeast-1.amazonaws.com/cert.pem")))) (t/is (true? (#'awsns/valid-sns-url? "https://sns.ap-southeast-1.amazonaws.com/cert.pem"))))
(t/deftest test-verify-signature-version-1 ;; Helper to load test certificate and private key from resources
(let [keypair (java.security.KeyPairGenerator/getInstance "RSA") ;; See: https://docs.aws.amazon.com/sns/latest/dg/sns-verify-signature-of-message.html
_ (.initialize keypair 2048) (defn- load-test-cert-and-key
kp (.generateKeyPair keypair) "Loads the test certificate and private key from test resources."
private-key (.getPrivate kp) []
public-key (.getPublic kp) (let [cert-pem (slurp (clojure.java.io/resource "sns-test-cert.pem"))
key-pem (slurp (clojure.java.io/resource "sns-test-key.pem"))
;; Parse certificate
cert-bytes (.getBytes (-> cert-pem
(clojure.string/replace "-----BEGIN CERTIFICATE-----" "")
(clojure.string/replace "-----END CERTIFICATE-----" "")
(clojure.string/replace #"\s+" ""))
java.nio.charset.StandardCharsets/UTF_8)
cert-input (java.io.ByteArrayInputStream. (.decode (java.util.Base64/getDecoder) cert-bytes))
cf (java.security.cert.CertificateFactory/getInstance "X.509")
cert (.generateCertificate cf cert-input)
;; Parse private key
key-bytes (.getBytes (-> key-pem
(clojure.string/replace "-----BEGIN PRIVATE KEY-----" "")
(clojure.string/replace "-----END PRIVATE KEY-----" "")
(clojure.string/replace #"\s+" ""))
java.nio.charset.StandardCharsets/UTF_8)
key-spec (java.security.spec.PKCS8EncodedKeySpec. (.decode (java.util.Base64/getDecoder) key-bytes))
kf (java.security.KeyFactory/getInstance "RSA")
private-key (.generatePrivate kf key-spec)]
{:cert cert
:cert-bytes (.getEncoded cert)
:private-key private-key
:public-key (.getPublicKey cert)}))
;; Create a simple message (t/deftest test-verify-signature-end-to-end-v1
msg {"Type" "Notification" (let [{:keys [cert-bytes private-key]} (load-test-cert-and-key)
"MessageId" "test-msg-1"
"TopicArn" "arn:aws:sns:us-east-1:123:topic" msg {"Type" "Notification"
"Message" "test message" "MessageId" "test-msg-1"
"Timestamp" "2021-02-04T14:41:37.020Z" "TopicArn" "arn:aws:sns:us-east-1:123:topic"
"SigningCertURL" "https://sns.us-east-1.amazonaws.com/cert.pem" "Message" "test message"
"SignatureVersion" "1"} "Timestamp" "2021-02-04T14:41:37.020Z"
"SigningCertURL" "https://sns.us-east-1.amazonaws.com/cert.pem"
"SignatureVersion" "1"}
;; Build string to sign
string-to-sign (#'awsns/build-string-to-sign msg) string-to-sign (#'awsns/build-string-to-sign msg)
sig (java.security.Signature/getInstance "SHA1withRSA")
;; Sign with SHA1 _ (.initSign sig private-key)
sig (java.security.Signature/getInstance "SHA1withRSA") _ (.update sig (.getBytes string-to-sign java.nio.charset.StandardCharsets/UTF_8))
_ (.initSign sig private-key) signature (.encodeToString (java.util.Base64/getEncoder) (.sign sig))
_ (.update sig (.getBytes string-to-sign java.nio.charset.StandardCharsets/UTF_8))
signature (.encodeToString (java.util.Base64/getEncoder) (.sign sig))
msg-with-sig (assoc msg "Signature" signature)] msg-with-sig (assoc msg "Signature" signature)]
;; Test that signature verification logic would work (with-redefs [awsns/fetch-certificate (fn [_ _]
(t/is (string? string-to-sign)) (java.io.ByteArrayInputStream. cert-bytes))]
(t/is (string? signature)))) (t/is (true? (#'awsns/verify-signature {} msg-with-sig))))))
(t/deftest test-verify-signature-version-2 (t/deftest test-verify-signature-end-to-end-v2
(let [keypair (java.security.KeyPairGenerator/getInstance "RSA") (let [{:keys [cert-bytes private-key]} (load-test-cert-and-key)
_ (.initialize keypair 2048)
kp (.generateKeyPair keypair)
private-key (.getPrivate kp)
;; Create a simple message msg {"Type" "Notification"
msg {"Type" "Notification" "MessageId" "test-msg-2"
"MessageId" "test-msg-2" "TopicArn" "arn:aws:sns:us-east-1:123:topic"
"TopicArn" "arn:aws:sns:us-east-1:123:topic" "Message" "test message"
"Message" "test message" "Timestamp" "2021-02-04T14:41:37.020Z"
"Timestamp" "2021-02-04T14:41:37.020Z" "SigningCertURL" "https://sns.us-east-1.amazonaws.com/cert.pem"
"SigningCertURL" "https://sns.us-east-1.amazonaws.com/cert.pem" "SignatureVersion" "2"}
"SignatureVersion" "2"}
;; Build string to sign
string-to-sign (#'awsns/build-string-to-sign msg) string-to-sign (#'awsns/build-string-to-sign msg)
sig (java.security.Signature/getInstance "SHA256withRSA")
;; Sign with SHA256 _ (.initSign sig private-key)
sig (java.security.Signature/getInstance "SHA256withRSA") _ (.update sig (.getBytes string-to-sign java.nio.charset.StandardCharsets/UTF_8))
_ (.initSign sig private-key) signature (.encodeToString (java.util.Base64/getEncoder) (.sign sig))
_ (.update sig (.getBytes string-to-sign java.nio.charset.StandardCharsets/UTF_8))
signature (.encodeToString (java.util.Base64/getEncoder) (.sign sig))
msg-with-sig (assoc msg "Signature" signature)] msg-with-sig (assoc msg "Signature" signature)]
;; Test that signature verification logic would work (with-redefs [awsns/fetch-certificate (fn [_ _]
(t/is (string? string-to-sign)) (java.io.ByteArrayInputStream. cert-bytes))]
(t/is (string? signature)))) (t/is (true? (#'awsns/verify-signature {} msg-with-sig))))))
(t/deftest test-verify-signature-end-to-end-subscription-confirmation
(let [{:keys [cert-bytes private-key]} (load-test-cert-and-key)
msg {"Type" "SubscriptionConfirmation"
"MessageId" "test-msg-3"
"TopicArn" "arn:aws:sns:us-east-1:123:topic"
"Message" "You have chosen to subscribe"
"Timestamp" "2021-02-04T14:41:37.020Z"
"Token" "test-token-123"
"SubscribeURL" "https://sns.us-east-1.amazonaws.com/confirm"
"SigningCertURL" "https://sns.us-east-1.amazonaws.com/cert.pem"
"SignatureVersion" "1"}
string-to-sign (#'awsns/build-string-to-sign msg)
sig (java.security.Signature/getInstance "SHA1withRSA")
_ (.initSign sig private-key)
_ (.update sig (.getBytes string-to-sign java.nio.charset.StandardCharsets/UTF_8))
signature (.encodeToString (java.util.Base64/getEncoder) (.sign sig))
msg-with-sig (assoc msg "Signature" signature)]
(with-redefs [awsns/fetch-certificate (fn [_ _]
(java.io.ByteArrayInputStream. cert-bytes))]
(t/is (true? (#'awsns/verify-signature {} msg-with-sig))))))
(t/deftest test-verify-signature-rejects-wrong-key
(let [{:keys [cert-bytes]} (load-test-cert-and-key)
;; Generate a different keypair for signing
keypair-gen (java.security.KeyPairGenerator/getInstance "RSA")
_ (.initialize keypair-gen 2048)
kp (.generateKeyPair keypair-gen)
wrong-private-key (.getPrivate kp)
msg {"Type" "Notification"
"MessageId" "test-msg-4"
"TopicArn" "arn:aws:sns:us-east-1:123:topic"
"Message" "test message"
"Timestamp" "2021-02-04T14:41:37.020Z"
"SigningCertURL" "https://sns.us-east-1.amazonaws.com/cert.pem"
"SignatureVersion" "1"}
string-to-sign (#'awsns/build-string-to-sign msg)
sig (java.security.Signature/getInstance "SHA1withRSA")
_ (.initSign sig wrong-private-key)
_ (.update sig (.getBytes string-to-sign java.nio.charset.StandardCharsets/UTF_8))
signature (.encodeToString (java.util.Base64/getEncoder) (.sign sig))
msg-with-sig (assoc msg "Signature" signature)]
(with-redefs [awsns/fetch-certificate (fn [_ _]
(java.io.ByteArrayInputStream. cert-bytes))]
(t/is (false? (#'awsns/verify-signature {} msg-with-sig))))))
(t/deftest test-verify-signature-rejects-unsupported-version (t/deftest test-verify-signature-rejects-unsupported-version
(let [msg {"Type" "Notification" (let [msg {"Type" "Notification"
@ -379,11 +443,10 @@
"SignatureVersion" "3" "SignatureVersion" "3"
"Signature" "fake=="}] "Signature" "fake=="}]
;; Should throw exception for unsupported version
(t/is (thrown? clojure.lang.ExceptionInfo (t/is (thrown? clojure.lang.ExceptionInfo
(#'awsns/verify-signature {} msg))))) (#'awsns/verify-signature {} msg)))))
(t/deftest test-build-string-to-sign-notification (t/deftest test-build-string-to-sign-v1-notification
(let [msg {"Type" "Notification" (let [msg {"Type" "Notification"
"MessageId" "msg-123" "MessageId" "msg-123"
"TopicArn" "arn:aws:sns:eu-central-1:123:topic" "TopicArn" "arn:aws:sns:eu-central-1:123:topic"
@ -399,8 +462,30 @@
(t/is (.contains result "TopicArn")) (t/is (.contains result "TopicArn"))
(t/is (.contains result "Message")) (t/is (.contains result "Message"))
(t/is (.contains result "Timestamp")) (t/is (.contains result "Timestamp"))
;; V1 does NOT include SigningCertURL, SignatureVersion, or Signature
(t/is (not (.contains result "SigningCertURL")))
(t/is (not (.contains result "SignatureVersion")))
(t/is (not (.contains result "Signature")))))
(t/deftest test-build-string-to-sign-v2-notification
(let [msg {"Type" "Notification"
"MessageId" "msg-123"
"TopicArn" "arn:aws:sns:eu-central-1:123:topic"
"Message" "{\"notificationType\":\"Bounce\"}"
"Timestamp" "2021-02-04T14:41:37.020Z"
"SigningCertURL" "https://sns.eu-central-1.amazonaws.com/cert.pem"
"SignatureVersion" "2"
"Signature" "abc123=="}
result (#'awsns/build-string-to-sign msg)]
(t/is (string? result))
(t/is (.contains result "MessageId"))
(t/is (.contains result "TopicArn"))
;; V2 includes SigningCertURL and SignatureVersion but NOT Signature
(t/is (.contains result "SigningCertURL")) (t/is (.contains result "SigningCertURL"))
(t/is (.contains result "SignatureVersion")))) (t/is (.contains result "SignatureVersion"))
;; Check that "Signature\n" (the field name) is NOT in the result
;; Note: "SignatureVersion" contains "Signature" as substring, so we check for the exact field pattern
(t/is (not (.contains result "Signature\n")))))
(t/deftest test-build-string-to-sign-subscription-confirmation (t/deftest test-build-string-to-sign-subscription-confirmation
(let [msg {"Type" "SubscriptionConfirmation" (let [msg {"Type" "SubscriptionConfirmation"
@ -408,6 +493,7 @@
"TopicArn" "arn:aws:sns:eu-central-1:123:topic" "TopicArn" "arn:aws:sns:eu-central-1:123:topic"
"Message" "You have chosen to subscribe" "Message" "You have chosen to subscribe"
"Timestamp" "2021-02-04T14:41:37.020Z" "Timestamp" "2021-02-04T14:41:37.020Z"
"Token" "test-token-123"
"SigningCertURL" "https://sns.eu-central-1.amazonaws.com/cert.pem" "SigningCertURL" "https://sns.eu-central-1.amazonaws.com/cert.pem"
"SignatureVersion" "1" "SignatureVersion" "1"
"Signature" "xyz789==" "Signature" "xyz789=="
@ -415,7 +501,10 @@
result (#'awsns/build-string-to-sign msg)] result (#'awsns/build-string-to-sign msg)]
(t/is (string? result)) (t/is (string? result))
(t/is (.contains result "SubscribeURL")) (t/is (.contains result "SubscribeURL"))
(t/is (.contains result "https://sns.eu-central-1.amazonaws.com/confirm")))) (t/is (.contains result "https://sns.eu-central-1.amazonaws.com/confirm"))
;; Token must be included for SubscriptionConfirmation
(t/is (.contains result "Token"))
(t/is (.contains result "test-token-123"))))
(t/deftest test-handle-request-returns-4xx-for-invalid-signature (t/deftest test-handle-request-returns-4xx-for-invalid-signature
(let [body (j/write-str (let [body (j/write-str

View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDGDCCAgCgAwIBAgIJAMp8qfcfOi+AMA0GCSqGSIb3DQEBDAUAMDoxCzAJBgNV
BAYTAlVTMQ0wCwYDVQQHEwRUZXN0MQ0wCwYDVQQKEwRUZXN0MQ0wCwYDVQQDEwRU
ZXN0MB4XDTI2MDgwNTE1NTQxN1oXDTI3MDgwNTE1NTQxN1owOjELMAkGA1UEBhMC
VVMxDTALBgNVBAcTBFRlc3QxDTALBgNVBAoTBFRlc3QxDTALBgNVBAMTBFRlc3Qw
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDj18ep0Pm5Y8VdXOSH9kXv
7RWzmxfNvN/zD1Fp2scADGBXnuVUhHYoR09Pe8U26X2gmDRg+JS7h6XXcGaWGUyh
28HISH3J0XrnKQmDKO77kxWFOAeu5apP2NVv1Nndbv+VqQczZqNZ6Kq9juflHjfK
hnnYiQqKZZ118IMCnu0uX26ShTwZJe7rAtO4MB4UNnfmAeRdrBOdI+1fp7QItYRc
2ppK3pXo2MAh8WhFeUy2UrlW6w1FpRjG/8nMmr14mD/dP9aWW85vcht5sA19bU3I
sf8ZOxOO9SGAk8Tg5Ey8aCdL5BU05OF8PNClOuqhHdGKShul3n2Pgojb2yGBHTYB
AgMBAAGjITAfMB0GA1UdDgQWBBTQbRwLjgbx/S7j1S/QFNB5HakDEjANBgkqhkiG
9w0BAQwFAAOCAQEAklxu68LkEdTFthA/ASyzm7KNxiWw+k+9IaEcViuwVa3XR2Ly
cO3L00NznsctwtcbevIT6ZlaK454snnpc5qZWHZSTYjQP34svT+XUZkgT29JdiYe
r5so+cUdorJyngMAen/tzpAHXV2l9SbRsi5+fRkR8DjPv9Ctz+kmh/Oy5fF3x/nu
AVZiwO8Oq4M3r+ElEfFed+9MkIQ6OZ3+ezgRhJYlXzVzii1OYQYpndocGNB+Jgwy
1FqzP1/02dzT6Oz95Md3B9fxj0OSCpCCBMo3ihkOKuNixaqyQ2+n/06Y+qpvnfuM
YrXnhbdpH7hsJM0/xNjWJMQlP04+zr1CQ9V9tg==
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDj18ep0Pm5Y8Vd
XOSH9kXv7RWzmxfNvN/zD1Fp2scADGBXnuVUhHYoR09Pe8U26X2gmDRg+JS7h6XX
cGaWGUyh28HISH3J0XrnKQmDKO77kxWFOAeu5apP2NVv1Nndbv+VqQczZqNZ6Kq9
juflHjfKhnnYiQqKZZ118IMCnu0uX26ShTwZJe7rAtO4MB4UNnfmAeRdrBOdI+1f
p7QItYRc2ppK3pXo2MAh8WhFeUy2UrlW6w1FpRjG/8nMmr14mD/dP9aWW85vcht5
sA19bU3Isf8ZOxOO9SGAk8Tg5Ey8aCdL5BU05OF8PNClOuqhHdGKShul3n2Pgojb
2yGBHTYBAgMBAAECggEAIPku65wTL+nI+9iAOE8DUxQoIlyNJtixPl9WpG+lghPI
c5XK0Z7z7KNZToL2iRpkdHPijLAc8kDQ1uts5UcXCIuhsUcQcT8wPrj5J/KqF11z
bVqs/fo92h1i0jLnLr0sHvAd2yn89PuPjixa0hU79MLeamB21o2bKqDajOwMHjwq
bAFUED3rmQe0nLv1pJbCIUUqd4ZElx+/XV+Y0c329zfA0WzjJI2EkHCnsS4tQn50
KX77Mzcbm9ujVQf2xsr5pnRscsCk2LrzXmAW4nOw5jpeWxxudvVTzARhpv46mj0j
MN0EN00YSw9FAHXBwDrjzS6cvvkchmBTJCUpKcoEWQKBgQDmO23YlsHrtnpWZRKf
BTu/uIObLHXgNt1yXby0mFk2BSe6L6yJDxalJQ1jxdyay73+LLBUdJwfg2K/jRdw
PFDdOvSRpb42LxTJ0TQxNaXsr91FDmbkjj0rFk1ygZKd/79HZq7xKWsfHsyTfUI7
wgYz9QaapYYM1t1+diXlWxupJQKBgQD9V+T0EsuOTof8goKcBNw6C8zPnif8mKwy
qtFWVySWpcreq74Q9+qyhMzNKDQ2flb1Akc6J3CSSvZBR21IKuqk9Cgu3qMH5CrX
pe8smw8LpPaO5adnHvRgPTih7tJ0u7jUErWhmavkqroEq6DmvngmQ/6w9nhwTaC4
tWfJzPvIrQKBgQDA19qEZpJ7y1bhcruMMyf+yKCDo1QAwDPwjY94fXuMAflqvG/6
RYckQMrcXWkQx8OWWPxBYYM76iMWaynMutjI1Y7xSDDw1bLF8NOUvGkEvbHLG+sX
WgTmSEIKvXl/mi4vslSqb5TodjXI/Ew0HapwbrZfZnHH41mXiYLof83FeQKBgQDK
91T1ee1c6GuoEINFLdumIXgHyeStST+EJDgsXQpyKwd6F8vhWk3MkfpmTtRt6BAQ
oK+h1qEogygBKpFR5Rgx6W4cBsBEfTcZp9YTPXLzWEk0OKdCRZlxVPr/OQ+g+Bhe
x1J+0lfVjjYTsdDprCUkOwtciUn6ZybhdGxfT3tUzQKBgQC2BxPNlzPJvRkLvfMJ
fJXfM9g96Uy7to7a7+LcLB2+OEAt/5kQAMg6ishwwoNvxVMtdy5uLEYNpq74lnQ2
bCVc7qc834edVMykhjlK5CLu0GiK2NM4aQavBNRGBoLF7I1Ih+tcToZFUC6W8YEU
djPrILOfRncjBj+epHXo92Z0iA==
-----END PRIVATE KEY-----