AWS SES SMTP インターフェイスを使用したメール送信のSMTP認証パスワードを生成する方法

# はじめに
業務でAWS SESを使用してメール送信処理を実装する機会があった。
実装中にSMTP認証のパスワードが違うというエラーが出て少し詰まったのでその解決方法をご紹介します。

## STMP(Simple Mail Transfer Protocol)とは
メール送信で使用されるプロトコルらしい。
```
SMTP（Simple Mail Transfer Protocol）は、インターネット上で電子メールを送信するためのプロトコルです。
SMTPは主にメールの送信に使用されるのに対し、メールの受信にはPOP3（Post Office Protocol 3）やIMAP（Internet Message Access Protocol）といった他のプロトコルが使用されます。

SMTPの主な特徴や役割は以下の通りです：

メールサーバ間の通信: SMTPは、メールを受け取るサーバ（受信サーバ）からメールを送り出すサーバ（送信サーバ）へと、メールを転送するためのメカニズムを提供します。

送信要求の処理: ユーザが電子メールを送信する際、その要求はSMTPを使用してメールサーバに転送されます。

ポート番号: 通常、SMTPはTCPのポート番号25を使用して通信します。ただし、TLS/SSLによる安全なメール送信のためにポート番号465や587も利用されることがあります。

コマンド: SMTPは、MAIL、RCPT、DATAなどのコマンドを使用して通信します。これにより、メールの送信元、宛先、本文などの情報が転送されます。

SMTPの動作にはいくつかの段階があり、各段階で特定のコマンドとレスポンスが交わされます。これによって、メールの送信が適切に行われることが保証されます。
```
## Amazon SES(Simple Email Service)とは
簡潔に説明するとawsが用意するメールサーバーを借りて簡単にメールを送信できますよ〜〜〜って感じのサービス。
```
Amazon SES（Simple Email Service）は、Amazon Web Services（AWS）が提供するクラウドベースの電子メール送信サービスです。
Amazon SESを使用すると、アプリケーションから電子メールを送信できるようになります。
これは、大量のトランザクションメール（例：注文確認、パスワードリセット）やプロモーションメールなどを、高可用性と高スケーラビリティで送信するためのサービスです。

Amazon SESの主な特点は以下の通りです：

高い配信率: Amazon SESは、メールがスパムフォルダに振り分けられるのを防ぐためのさまざまなベストプラクティスを実装しています。

スケーラビリティ: 大量のメールを短時間で送信するニーズにも柔軟に対応できます。

セキュリティ: SESは、メールの内容を暗号化し、送信中のデータを保護します。また、Amazon SESを使用して送信されるすべてのメールは、DKIM (DomainKeys Identified Mail) とSPF (Sender Policy Framework) を使用して署名され、これによってメールの真正性が確保されます。

統合: Amazon SESは、AWSの他のサービス、例えばAmazon S3やAWS Lambdaと簡単に統合できます。

モニタリング: SESは詳細なログや監視ツールを提供し、送信されたメールの配信率、バウンス率、苦情率などのメトリクスを確認できます。

コスト効果: 使った分だけの料金がかかる従量課金制を採用しており、特に大量にメールを送信する場合にコスト効果が高いです。

開発者や企業は、Amazon SESを使用して、大量のメールを信頼性高く、効率的に、また低コストで送信することができます。
```
### SESはこんな感じの仕組みらしい
https://docs.aws.amazon.com/ja_jp/ses/latest/dg/send-email-concepts-process.html

# 詰まった箇所
前提知識をなんとな〜〜く把握した上で実装に取り掛かりました。
aws系のサービスはだいたい使用したいサービスのアクセスが許可されたIAMのアクセスキーとシークレットアクセスキーを使用して認証するイメージがあったので今回もその2つのキーを使用して認証できるであろうと思っていた。

何度やってもうまくいかないのでドキュメントを読んでみるとしっかりと書いてあった。

>  SMTP パスワードは、AWS シークレットアクセスキーとは異なります。

https://docs.aws.amazon.com/ja_jp/ses/latest/dg/smtp-credentials.html

## SMTPパスワードを生成する
ドキュメントの手順に沿ってシークレットアクセスキーからSMTPパスワードを生成する。

> IAM ユーザーが SES SMTP インターフェイスを使用してメールを送信するためには、次の手順を行ってください。
> - ユーザーの SMTP 認証情報を AWS 認証情報から派生させるには、このセクションで示すアルゴリズムを使用します。AWS 認証情報から開始するため、SMTP ユーザー名は AWS アクセスキー ID と同じです。したがって、**作成する必要があるのは SMTP パスワードのみです。**

**SESのSMTP認証情報は各リージョンに固有**なのでAWS シークレットアクセスキーとSESのリージョン情報を合わせて以下awsが公開しているアルゴリズムのpythonファイルを実行して生成します。

東京リージョン: `ap-northeast-1`
SESへのアクセスが許可されたシークレットアクセスキー: `xxxxxx`

```python
#!/usr/bin/env python3

import hmac
import hashlib
import base64
import argparse

SMTP_REGIONS = [
    'us-east-2',       # US East (Ohio)
    'us-east-1',       # US East (N. Virginia)
    'us-west-2',       # US West (Oregon)
    'ap-south-1',      # Asia Pacific (Mumbai)
    'ap-northeast-2',  # Asia Pacific (Seoul)
    'ap-southeast-1',  # Asia Pacific (Singapore)
    'ap-southeast-2',  # Asia Pacific (Sydney)
    'ap-northeast-1',  # Asia Pacific (Tokyo)
    'ca-central-1',    # Canada (Central)
    'eu-central-1',    # Europe (Frankfurt)
    'eu-west-1',       # Europe (Ireland)
    'eu-west-2',       # Europe (London)
    'sa-east-1',       # South America (Sao Paulo)
    'us-gov-west-1',   # AWS GovCloud (US)
]

# These values are required to calculate the signature. Do not change them.
DATE = "11111111"
SERVICE = "ses"
MESSAGE = "SendRawEmail"
TERMINAL = "aws4_request"
VERSION = 0x04


def sign(key, msg):
    return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()


def calculate_key(secret_access_key, region):
    if region not in SMTP_REGIONS:
        raise ValueError(f"The {region} Region doesn't have an SMTP endpoint.")

    signature = sign(("AWS4" + secret_access_key).encode('utf-8'), DATE)
    signature = sign(signature, region)
    signature = sign(signature, SERVICE)
    signature = sign(signature, TERMINAL)
    signature = sign(signature, MESSAGE)
    signature_and_version = bytes([VERSION]) + signature
    smtp_password = base64.b64encode(signature_and_version)
    return smtp_password.decode('utf-8')


def main():
    parser = argparse.ArgumentParser(
        description='Convert a Secret Access Key to an SMTP password.')
    parser.add_argument(
        'secret', help='The Secret Access Key to convert.')
    parser.add_argument(
        'region',
        help='The AWS Region where the SMTP password will be used.',
        choices=SMTP_REGIONS)
    args = parser.parse_args()
    print(calculate_key(args.secret, args.region))


if __name__ == '__main__':
    main()
```

上記PythonファイルをAWS シークレットアクセスキーとSESのリージョン情報を引数につけて実行。
```terminal
> python3 smtp_credentials_generate.py <シークレットアクセスキー> ap-northeast-1
> SMTP認証パスワード
```

# さいごに
無事メール送信が行えてよかったです。
ちなみに、今回はSES SMTP インターフェイスを使用してメールを送信するが以下3つのやりかたがあるみたです。

- Amazon SES API
- Amazon SES SMTP インターフェイス
- Amazon SES コンソール

https://docs.aws.amazon.com/ja_jp/ses/latest/dg/send-email-concepts-credentials.html



# 参考
https://docs.aws.amazon.com/ja_jp/ses/latest/dg/send-email-concepts-credentials.html

https://docs.aws.amazon.com/ja_jp/ses/latest/dg/smtp-credentials.html