· 7 years ago · Mar 27, 2018, 06:44 AM
1class AwsSesMailer(val accessKey: String, val secretKey: String, val securityToken: String) : Mailer {
2 override fun sendMail(m: Mail) {
3 val urlStr = "https://email.eu-west-1.amazonaws.com"
4 val amzDate = buildAmzDate()
5 val content = "Action=SendEmail" +
6 "&Source=${urlEncode(m.from)}" +
7 "&Destination.ToAddresses.member.1=${urlEncode(m.to)}" +
8 "&Message.Subject.Data=${urlEncode(m.subject)}" +
9 "&Message.Body.Text.Data=${urlEncode(m.content)}"
10 val amzContentSha256 = MessageDigest.getInstance("SHA-256").digest(content.toByteArray(Charsets.UTF_8)).toHex()
11 val signature = Signer.builder()
12 .awsCredentials(AwsCredentials(accessKey, secretKey))
13 .region("eu-west-1")
14 .header("Host", "email.eu-west-1.amazonaws.com")
15 .header("X-amz-date", amzDate)
16 .header("X-amz-security-token", securityToken)
17 .build(HttpRequest("POST", URI(urlStr)), "email", amzContentSha256)
18 .getSignature()
19
20 val httpConn = URL(urlStr).openConnection() as HttpURLConnection
21 httpConn.setDoOutput(true)
22 httpConn.setRequestMethod("POST")
23 httpConn.setRequestProperty("X-amz-date", amzDate)
24 httpConn.setRequestProperty("X-amz-content-sha256", amzContentSha256)
25 httpConn.setRequestProperty("X-amz-security-token", securityToken)
26 //httpConn.setRequestProperty("Content-type", "application/x-www-form-urlencoded")
27 httpConn.setRequestProperty("Authorization", signature)
28 httpConn.getOutputStream().use { outputStream ->
29 outputStream.write(content.toByteArray(Charsets.UTF_8))
30 }
31 val res = httpConn.getResponseMessage()
32 if (res != "OK") {
33 val resText = httpConn.errorStream?.bufferedReader()?.readText() ?: "No content"
34 throw Exception("SENDING EMAIL FAILED WITH $res and content $resText")
35 }
36 }
37
38 private fun buildAmzDate(): String {
39 val df = SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'", Locale.US)
40 df.timeZone = TimeZone.getTimeZone("UTC")
41 return df.format(Date())
42 }
43
44 private fun urlEncode(s: String) = URLEncoder.encode(s, "UTF-8")
45
46 private val HEX_CHARS = "0123456789ABCDEF".toCharArray()
47
48 private fun ByteArray.toHex() : String{
49 val result = StringBuffer()
50
51 forEach {
52 val octet = it.toInt()
53 val firstIndex = (octet and 0xF0).ushr(4)
54 val secondIndex = octet and 0x0F
55 result.append(HEX_CHARS[firstIndex])
56 result.append(HEX_CHARS[secondIndex])
57 }
58
59 return result.toString()
60 }
61
62}
63
64<ErrorResponse xmlns="http://ses.amazonaws.com/doc/2010-12-01/">
65 <Error>
66 <Type>Sender</Type>
67 <Code>SignatureDoesNotMatch</Code>
68 <Message>The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.</Message>
69 </Error>
70 <RequestId>722afabf-3189-11e8-b9ae-177fc93e21a2</RequestId>
71</ErrorResponse>