web-dev-qa-db-ja.com

JavamailNTLM認証の失敗

JavaMailでNTLMを使用してExchangeサーバーに接続しようとしています。 SMTPに接続できますが、IMAPには接続できません。同一のホスト/ユーザー名/パスワード、アカウントタイプ= "IMAP"、ポート143、ssl = false、authentication = NTLM、ドメイン名= ""を使用してOSXMail.appアプリケーションを介して認証することもできます。

接続コード:

import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.Store;
import Java.util.Properties;

    public class NTLMTest {
        public static void main(String[] args) throws Exception {
            final String Host = "example.com";
            final String user = "bob";
            final String password = "password";

            final Properties properties = new Properties();
            Session session = Session.getDefaultInstance(properties);
            session.setDebug(true);

            // SMTP CONNECT
            final Transport transport = session.getTransport("smtp");
            transport.connect(Host, user, password);
            System.out.println("SMTP Connect successful");

            // IMAP CONNECT
            final Store store = session.getStore("imap");
            store.connect(Host, user, password);
            System.out.println("IMAP Connect Successful");

        }
    }

出力:

DEBUG: setDebug: JavaMail version 1.4.3
DEBUG: getProvider() returning javax.mail.Provider[TRANSPORT,smtp,com.Sun.mail.smtp.SMTPTransport,Sun Microsystems, Inc]
DEBUG SMTP: useEhlo true, useAuth false
DEBUG SMTP: trying to connect to Host "example.com", port 25, isSSL false
220 server18.example.com ESMTP Sendmail 8.14.3/8.14.3/Debian-5+lenny1; Thu, 2 Dec 2010 18:05:30 +0100; (No UCE/UBE) logging access from: xxx.xxx.xxx.xxx
DEBUG SMTP: connected to Host "example.com", port: 25

EHLO 192.168.1.107
250-server18.example.com Hello c-xxxx [xxx.xxx.xxx.xxx], pleased to meet you
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-8BITMIME
250-SIZE 20971520
250-DSN
250-ETRN
250-AUTH DIGEST-MD5 CRAM-MD5 LOGIN PLAIN
250-STARTTLS
250-DELIVERBY
250 HELP
DEBUG SMTP: Found extension "ENHANCEDSTATUSCODES", arg ""
DEBUG SMTP: Found extension "PIPELINING", arg ""
DEBUG SMTP: Found extension "8BITMIME", arg ""
DEBUG SMTP: Found extension "SIZE", arg "20971520"
DEBUG SMTP: Found extension "DSN", arg ""
DEBUG SMTP: Found extension "ETRN", arg ""
DEBUG SMTP: Found extension "AUTH", arg "DIGEST-MD5 CRAM-MD5 LOGIN PLAIN"
DEBUG SMTP: Found extension "STARTTLS", arg ""
DEBUG SMTP: Found extension "DELIVERBY", arg ""
DEBUG SMTP: Found extension "HELP", arg ""
DEBUG SMTP: Attempt to authenticate
DEBUG SMTP: check mechanisms: LOGIN PLAIN DIGEST-MD5 NTLM 
AUTH LOGIN
334 VXNlcm5hbWU6
YWR2aWVzZW5raWVzMDU=
334 UGFzc3dvcmQ6
ZGlja2hvbmluZw==
235 2.0.0 OK Authenticated
SMTP Connect successful
DEBUG: getProvider() returning javax.mail.Provider[STORE,imap,com.Sun.mail.imap.IMAPStore,Sun Microsystems, Inc]
DEBUG: mail.imap.fetchsize: 16384
DEBUG: mail.imap.statuscachetimeout: 1000
DEBUG: mail.imap.appendbuffersize: -1
DEBUG: mail.imap.minidletime: 10
DEBUG: trying to connect to Host "example.com", port 143, isSSL false
* OK server18.example.com Cyrus IMAP4 v2.1.18-IPv6-Debian-2.1.18-5.1 server ready
A0 CAPABILITY
* CAPABILITY IMAP4 IMAP4rev1 ACL QUOTA LITERAL+ MAILBOX-REFERRALS NAMESPACE UIDPLUS ID NO_ATOMIC_RENAME UNSELECT CHILDREN MULTIAPPEND SORT THREAD=ORDEREDSUBJECT THREAD=REFERENCES IDLE AUTH=DIGEST-MD5 AUTH=NTLM AUTH=CRAM-MD5 ANNOTATEMORE
A0 OK Completed
IMAP DEBUG: AUTH: DIGEST-MD5
IMAP DEBUG: AUTH: NTLM
IMAP DEBUG: AUTH: CRAM-MD5
DEBUG: protocolConnect login, Host=example.com, user=bob, password=<non-null>
DEBUG NTLM: type 1 message: Type1Message[suppliedDomain=,suppliedWorkstation=192.168.1.107,flags=0x00000201]
DEBUG NTLM: type 1 message length: 45
A1 AUTHENTICATE NTLM
+ 
TlRMTVNTUAABAAAAASIAAAAAAAAAAAAADQANACAAAAAxOTIuMTY4LjEuMTA3
+ TlRMTVNTUAACAAAAAAAAADAAAAABIgAApdhJrA6NzmwAAAAAAAAAAAAAAAAAAAAA
TlRMTVNTUAADAAAAGAAYAEAAAAAwADAAWAAAAAAAAAAAAAAAHAAcAIgAAAAaABoApAAAAAAAAAAAAAAAAQIAALV6mIutJKdZSH4IZGmvNqNFxJafzInd0yJDR4J3oe3LyBls0Y75UuwBAQAAAAAAANAS9yNDkssBVbH5v087iUIAAAAAAAAAAGEAZAB2AGkAZQBzAGUAbgBrAGkAZQBzADAANQAxADkAMgAuADEANgA4AC4AMQAuADEAMAA3AA==
A1 NO authentication failure
Exception in thread "main" javax.mail.AuthenticationFailedException: authentication failure
    at com.Sun.mail.imap.IMAPStore.protocolConnect(IMAPStore.Java:613)
    at javax.mail.Service.connect(Service.Java:291)
    at javax.mail.Service.connect(Service.Java:172)
    at com.prosc.emailplugin.NTLMTest.main(NTLMTest.Java:25)
Disconnected from the target VM, address: '127.0.0.1:56125', transport: 'socket'

Process finished with exit code 1

http://www.Oracle.com/technetwork/Java/faq-135477.html#Exchange-login に従って、ユーザー名をバックスラッシュでラップしようとしました。次のエラーが発生します。

Exception in thread "main" javax.mail.AuthenticationFailedException: One time use of a plaintext password will enable requested mechanism for user

SMTP接続部分のユーザー名を円記号で囲むと、失敗します。 「1回限りの使用」エラーが正しい方向への一歩であるかどうかはわかりません。

12
Sam Barnum

SMTP経由でNTLM認証が古いバージョンのjavax.mail(1.4.1まで)では機能しなかったが、バージョン1.4.5では機能するようになったことに気づきました。また、指定するユーザー名は「domain\username」の形式でした。同じ効果(javax.mailのバージョンの違い)がIMAPにも適用される可能性があります。

8
FelixJongleur42

NTLMについて思い出したことと、NTLMデバッグメッセージから、次の情報を収集できます。

  • NTLMはシングルサインオン用に設計されているため、実行中のWindowsマシンから資格情報を取得します。特にNTLMのJDK実装です。
    • NTLMには2つのバージョンがあり、正確には3つです。 NTLM v1、NTLMv2、および現時点では思い出せない別のバージョン。 NTLM v1にはセキュリティホールがあり、ユーザー名とパスワードを実際に使用して、NTLMプロトコルを使用して接続できます。 NTLM v2で修正され、ログインしたWindowsマシンからパスワード(ハッシュパス)を取得するように実装が強制されました。
    • あなたの場合のNTLMプロトコルは、Exchangeサーバーによって送信された最初のメッセージの後で停止しているようです。使用されているNTLMのタイプを宣言するフラグと、暗号化などの他のフラグがあることに注意してください。

JDKがクライアントのWindowsマシンから資格情報(u/p)を自動的に取得する方法をたどってみることをお勧めします。

2
Asaf Mesika

ExhangeSMTPコネクタを介して電子メールを送信するときに同じ問題が発生しました。 javamailがNTLMv2認証をサポートしていないことを知った後、私はJCIFSライブラリを必要とする回避策を考え出しました。

Javamail apiソースコード( https://Java.net/projects/javamail/pages/Home )をダウンロードし、クラスcom.Sun.mail.auth.Ntlmを編集して、不足しているサポートを追加しました。 JCIFSライブラリサポートを使用するNTLMv2( http://jcifs.samba.org )。

ファイルNtlm.Javaの最初の変更はメソッドinit0にあり、欠落しているフラグNTLMSSP_NEGOTIATE_NTLM2を追加することで構成されていました。

private void init0() {
// ANDREA LUCIANO:
//    turn on the NTLMv2 negotiation flag in TYPE1 messages: 
//    NTLMSSP_NEGOTIATE_NTLM2   (0x00080000) 
//  See also http://davenport.sourceforge.net/ntlm.html#type1MessageExample

    type1 = new byte[256];
    type3 = new byte[256];
    System.arraycopy(new byte[] {'N','T','L','M','S','S','P',0,1}, 0,
            type1, 0, 9);
    type1[12] = (byte) 3;
    type1[13] = (byte) 0xb2;
    type1[14] = (byte) 0x08;  // ANDREA LUCIANO - added
// ...

2番目の変更は、メソッドgenerateType3Msgを次のように置き換えることでした。

public String generateType3Msg(String challenge) {
    /* First decode the type2 message */
    byte[] type2 = null;
    try {
       type2 = BASE64DecoderStream.decode(challenge.getBytes("us-ascii"));
       logger.fine("type 2 message: " + toHex(type2)); // ANDREA LUCIANO - added
    } catch (UnsupportedEncodingException ex) {
       // should never happen
       assert false;
    }
    jcifs.ntlmssp.Type2Message t2m;
    try {
          t2m = new jcifs.ntlmssp.Type2Message(type2);
    } catch (IOException ex) {
          logger.log(Level.FINE, "Invalid Type 2 message", ex);
          return "";   // will fail later
    }

    final int type2Flags = t2m.getFlags();
    final int type3Flags = type2Flags & (0xffffffff ^ (jcifs.ntlmssp.NtlmFlags.NTLMSSP_TARGET_TYPE_DOMAIN | jcifs.ntlmssp.NtlmFlags.NTLMSSP_TARGET_TYPE_SERVER));

    jcifs.ntlmssp.Type3Message t3m = new jcifs.ntlmssp.Type3Message(t2m, password, ntdomain, username, hostname, type3Flags);
       return jcifs.util.Base64.encode(t3m.toByteArray());
}

ライブラリにパッチを適用するために私が見つけた最も簡単な方法は、クラスをコンパイルしてライブラリのjarファイルを更新することです。

"c:\Program Files\Java\jdk1.5.0_22\bin\javac.exe" -cp jcifs-1.3.17.jar;javax.mail-1.5.2.jar -d . Ntlm.Java 
 jar uvf javax.mail-1.5.2.jar com\Sun\mail\auth\Ntlm.class

可能な限りデバッグを有効にするために、テストクラスのmainメソッドで次のコードを使用しました。

    final InputStream inputStream = Main.class.getResourceAsStream("/logging.properties");
    LogManager.getLogManager().readConfiguration(inputStream);

    Properties props = new Properties();

    props.put("mail.debug", "true");
    props.put("mail.debug.auth", "true");

このlogging.propertiesで:

   # Logging
   handlers = Java.util.logging.ConsoleHandler

  .level = INFO

  # Console Logging
  Java.util.logging.ConsoleHandler.level = INFO

ExchangeサーバーでNTLMv2認証が必要だったため、パッチを適用する前に、タイプ1メッセージを送信した後にテストがスタックしました。パッチの後、認証は正常に行われました。

別の解決策は、Microsoftによってリリースおよび管理されている## EWS Java API ##を使用して、## Exchange Webサービス##別名EWSを介して電子メールを送信することです( https:// github .com/OfficeDev/ews-Java-api )、次の例のように:

public class Main {

/**
 * @param args
 */
public static void main(String[] args) throws Exception {

       ExchangeService exchangeService = new ExchangeService(ExchangeVersion.Exchange2007_SP1);

        ExchangeCredentials credentials = new WebCredentials("myusername","mypassword", "mydomain");

        exchangeService.setCredentials(credentials);
        exchangeService.setUrl(new URI("https://myhostname/EWS/Exchange.asmx"));

        exchangeService.setTraceEnabled(true);

        EmailMessage msg;
        msg = new EmailMessage(exchangeService);
        msg.setFrom(new EmailAddress("[email protected]"));
        msg.setSubject("Test Mail");
        msg.getToRecipients().add("[email protected]");
        msg.setBody(MessageBody.getMessageBodyFromText("Email sent by Miscrosoft Java EWS API."));
        msg.getAttachments().addFileAttachment("c:\\temp\\myattachement.pdf");
        msg.send();

}

}

ただし、この投稿への回答に示されているように、EwsJCIFSNTLMScheme.Javaの内部クラスNTLMに適用してNTLMv2を有効にするパッチがあります。

JavaでExchange Webサービス接続にLDAP認証を使用する方法は?

あれは:

private class NTLM {
/** Character encoding */
public static final String DEFAULT_CHARSET = "ASCII";

/**
* The character was used by 3.x's NTLM to encode the username and
* password. Apparently, this is not needed in when passing username,
* password from NTCredentials to the JCIFS library
*/
private String credentialCharset = DEFAULT_CHARSET;

void setCredentialCharset(String credentialCharset) {
       this.credentialCharset = credentialCharset;
}

private static final int TYPE_1_FLAGS = NtlmFlags.NTLMSSP_NEGOTIATE_NTLM
             | NtlmFlags.NTLMSSP_NEGOTIATE_UNICODE
             | NtlmFlags.NTLMSSP_NEGOTIATE_NTLM2;

private String generateType1Msg(String Host, String domain) {
       jcifs.ntlmssp.Type1Message t1m = new jcifs.ntlmssp.Type1Message(
                    TYPE_1_FLAGS, domain, Host);
       return jcifs.util.Base64.encode(t1m.toByteArray());
}

private String generateType3Msg(String username, String password,
             String Host, String domain, String challenge) {
       jcifs.ntlmssp.Type2Message t2m;
       try {
             t2m = new jcifs.ntlmssp.Type2Message(
                           jcifs.util.Base64.decode(challenge));
       } catch (IOException e) {
             throw new RuntimeException("Invalid Type2 message", e);
       }

       final int type2Flags = t2m.getFlags();
       final int type3Flags = type2Flags
                    & (0xffffffff ^ (NtlmFlags.NTLMSSP_TARGET_TYPE_DOMAIN | NtlmFlags.NTLMSSP_TARGET_TYPE_SERVER));

       jcifs.ntlmssp.Type3Message t3m = new jcifs.ntlmssp.Type3Message(
                    t2m, password, domain, username, Host, type3Flags);
       return jcifs.util.Base64.encode(t3m.toByteArray());
}

}

私は試しましたが、うまくいきました。

1
Andrea Luciano

これが私の完全な実用的な解決策です:

NetBeansを使用して、新しいJavaアプリケーションプロジェクトを作成します。次のコードをそこに配置します:

package javaapplication4;
import Java.util.Date;
import Java.util.Properties;

import javax.mail.Message;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.MimeMessage;


public class JavaApplication4 {
    public static void main(String[] args) throws Exception {
           new JavaApplication4().send_email();
    }
    private void send_email() throws Exception
    {
        Properties props = new Properties();
        props.put("mail.smtp.Host", "smtp.yourserver.net");
        props.put("mail.from", "[email protected]");
        props.put("mail.smtp.starttls.enable", "true");
        props.put("mail.smtp.ssl.enable", "false");
        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.port", "587");

        Authenticator authenticator = new Authenticator();
        props.setProperty("mail.smtp.submitter", authenticator.getPasswordAuthentication().getUserName());

        Session session = Session.getInstance(props, authenticator);
        MimeMessage msg = new MimeMessage(session);
        msg.setFrom();
        msg.setRecipients(Message.RecipientType.TO, "[email protected]");  
            // also tried @gmail.com
        msg.setSubject("JavaMail ssl test");
        msg.setSentDate(new Date());
        msg.setText("Hello, world!\n");

        Transport transport;
        transport = session.getTransport("smtp");
        transport.connect();
        msg.saveChanges(); 
        transport.sendMessage(msg, msg.getAllRecipients());
        transport.close();
    }
    private class Authenticator extends javax.mail.Authenticator {
       private PasswordAuthentication authentication;
       public Authenticator() {
           String username = "[email protected]";
           String password = "yourpassword";
           authentication = new PasswordAuthentication(username, password);
       }
       protected PasswordAuthentication getPasswordAuthentication() {
           return authentication;
       }
   }
}

ユーザー名、パスワード、ポート、およびプロパティを特定の設定に変更します。

javamail-1.4.7を取得し、mail.jarを( http://www.Oracle.com/technetwork/Java/index-138643.html )からにロードする必要があります。事業。

パラメータがどうあるべきかを明らかにするために行ったことの1つは、Exchangeサーバーに関する情報を自動検出してすべての設定が正しいことを確認できるThunderbirdメールクライアントをダウンロードすることです。 Thunderbirdに接続するように説得できない場合、このコードも同様に機能するとは期待できません。

1
Eric Leschinski