Java から (外部) SMTP サーバー経由で電子メールを送信する必要がありますが、このサーバーは JavaMail でサポートされていない CRAM-MD5 認証のみを受け入れます。
これらのメールを送信するにはどうすればよいでしょうか? (Java である必要があります。)
次のプロパティを追加する必要があることを示すスレッドがあります。
props.put("mail.smtp.auth.mechanisms", "CRAM-MD5")
また、Geronimo の実装にはCramMD5Authenticatorがあります。
この古い質問の解決に役立つことを願っています。
Java Mail 1.4.4 以降、smtp での使用に CRAM-MD5 がサポートされています。このパラメーターをプロパティに設定するだけで機能します。
props.put("mail.smtp.sasl.enable", "true");
import java.security.*;
class CRAMMD5Test
{
public static void main(String[] args) throws Exception
{
// This represents the BASE64 encoded timestamp sent by the POP server
String dataString = Base64Decoder.decode("PDAwMDAuMDAwMDAwMDAwMEBteDEuc2VydmVyLmNvbT4=");
byte[] data = dataString.getBytes();
// The password to access the account
byte[] key = new String("password").getBytes();
// The address of the e-mail account
String user = "client@server.com";
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.reset();
if (key.length > 64)
key = md5.digest(key);
byte[] k_ipad = new byte[64];
byte[] k_opad = new byte[64];
System.arraycopy(key, 0, k_ipad, 0, key.length);
System.arraycopy(key, 0, k_opad, 0, key.length);
for (int i=0; i<64; i++)
{
k_ipad[i] ^= 0x36;
k_opad[i] ^= 0x5c;
}
byte[] i_temp = new byte[k_ipad.length + data.length];
System.arraycopy(k_ipad, 0, i_temp, 0, k_ipad.length);
System.arraycopy(data, 0, i_temp, k_ipad.length, data.length);
i_temp = md5.digest(i_temp);
byte[] o_temp = new byte[k_opad.length + i_temp.length];
System.arraycopy(k_opad, 0, o_temp, 0, k_opad.length);
System.arraycopy(i_temp, 0, o_temp, k_opad.length, i_temp.length);
byte[] result = md5.digest(o_temp);
StringBuffer hexString = new StringBuffer();
for (int i=0;i < result.length; i++) {
hexString.append(Integer.toHexString((result[i] >>> 4) & 0x0F));
hexString.append(Integer.toHexString(0x0F & result[i]));
}
System.out.println(Base64Encoder.encode(user + " " + hexString.toString()));
}
}
これはおそらく役に立ちませんが、CRAM-MD5 と CRAM-SHA1 は、適切なライブラリ (md5/sha1) と、理想的には base64 エンコーディング ライブラリがあれば、かなり簡単に実装できます (ただし、base64 のものは自分で簡単に実装できます)。ピンチで)。
トランザクションは次のようになります。
C: AUTH CRAM-MD5
S: 334 BASE64(NONCE)
C: BASE64(USERNAME, " ", MD5((SECRET XOR opad),MD5((SECRET XOR ipad), NONCE)))
S: 235 Authentication succeeded
ここで、NONCE は 1 回限りのチャレンジ文字列、USERNAME は認証しようとしているユーザー名、SECRET は共有シークレット (「パスワード」)、opad は 0x5C、ipad は 0x36 です。
(CRAM-SHA1 は同じトランザクションですが、MD5() の代わりに SHA1() を使用してダイジェストを行います)
実際の CRAM-MD5 トランザクションの例を次に示します。
C: AUTH CRAM-MD5
S: 334 PDQ1MDMuMTIyMzU1Nzg2MkBtYWlsMDEuZXhhbXBsZS5jb20+
C: dXNlckBleGFtcGxlLmNvbSA4YjdjODA5YzQ0NTNjZTVhYTA5N2VhNWM4OTlmNGY4Nw==
S: 235 Authentication succeeded
プロセスをバックアップすると、次のステップが得られます。
S: 334 BASE64("<4503.1223557862@mail01.example.com>")
C: BASE64("user@example.com 8b7c809c4453ce5aa097ea5c899f4f87")
ダイジェストが計算される前にさらに 1 ステップ戻ると、次のようになります。
S: 334 BASE64("<4503.1223557862@mail01.example.com>")
C: BASE64("user@example.com ", MD5(("password" XOR opad),MD5(("password" XOR ipad), "<4503.1223557862@mail01.example.com>")))
書き出すとややこしいと思いますが、NTLM/SPA を手動で実行しようとする場合に比べれば、簡単です。やる気があれば、実際に実装するのは非常に簡単です。あるいは、メールクライアントとサーバーの内臓に手を当てて、それについて明確に考えるのに長い間費やしたのかもしれません...
これは直接的には役に立ちませんが、 boolean プロパティを に設定すると、JavaMail の IMAP 接続は SASL (したがって CRAM-MD5、 Java SASL のドキュメントを参照) をサポートします。mail.imap.sasl.enable
true
残念ながら、mail.smtp.sasl.enable
プロパティはなく、JavaMail の SMTP に対して SASL を有効にすることはできません。:-(
ただし、JavaMail のソース コードをダウンロードして、IMAP コードと同様の方法で SASL をサポートするように SMTP コードを編集してみることができます。幸運を!
実際のCRAM-MD5トランザクションの例と、RFC2195に記載されている例でコードを試しました。
16進文字列への変換が正しくないため、機能しません。たとえば、このコードを使用すると、「b913a602c7eda7a495b4e6e7334d3890」ではなく「b913a62c7eda7a495b4e6e7334d3890」が取得され、送信された認証文字列は正しくありません。
javaMailのソースコードをダウンロードすると、関数toHexがユニット「DigestMD5」に実装されていることがわかります。この変換を使用すると、機能します。
変化する:
for (int i=0; i<result.length; i++)
hexString.append(Integer.toHexString(0xFF & result[i]));
に:
for (int i=0;i < result.length; i++) {
hexString.append(Integer.toHexString((result[i] >>> 4) & 0x0F));
hexString.append(Integer.toHexString(0x0F & result[i]));
}