JAVAMAIL: ошибка авторизации NTLM

я пытаюсь отправить электронное письмо с java в локальной сети, используя сервер обмена Microsoft

есть мой код:

import java.io.UnsupportedEncodingException;
import java.util.Properties;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;


public class Main {


        public static void main(String[] args) {

            final String username = "[email protected]";
            final String password = "password";

            Properties props = new Properties();
            props.put("mail.smtp.auth", "true");
            props.put("mail.debug", "true");
            props.put("mail.smtp.host", "exchange_host.MyDomain.com");
            props.put("mail.smtp.port", "25");
            props.put("mail.smtp.auth.mechanisms","NTLM");
            props.put("mail.smtp.auth.ntlm.domain","MyDomain");


            Session session = Session.getInstance(props,new MyAuthenticator(username,password));

            try {

                Message message = new MimeMessage(session);
                message.setFrom(new InternetAddress("[email protected]"));
                message.setRecipients(Message.RecipientType.TO,
                    InternetAddress.parse("recipent_adresse"));
                message.setSubject("Testing Subject");
                message.setText("Dear Mail Crawler,"
                    + "\n\n No spam to my email, please!");

                Transport.send(message);

                System.out.println("Done");

            } catch (MessagingException e) {
                throw new RuntimeException(e);
            }
        }
    }

и это мой класс аутентификатора:

import javax.mail.Authenticator;
import javax.mail.PasswordAuthentication;

public class MyAuthenticator extends Authenticator {

     String user;
     String pw;
     public MyAuthenticator (String username, String password)
     {
        super();
        this.user = username;
        this.pw = password;
     }
    public PasswordAuthentication getPasswordAuthentication()
    {
       return new PasswordAuthentication(user, pw);
    }

}

я использую механизм NTLM, но получаю эту ошибку:

DEBUG: JavaMail version 1.4.7
DEBUG: successfully loaded resource: /META-INF/javamail.default.providers
DEBUG: Tables of loaded providers
DEBUG: Providers Listed By Class Name: {com.sun.mail.smtp.SMTPSSLTransport=javax.mail.Provider[TRANSPORT,smtps,com.sun.mail.smtp.SMTPSSLTransport,Oracle], com.sun.mail.smtp.SMTPTransport=javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle], com.sun.mail.imap.IMAPSSLStore=javax.mail.Provider[STORE,imaps,com.sun.mail.imap.IMAPSSLStore,Oracle], com.sun.mail.pop3.POP3SSLStore=javax.mail.Provider[STORE,pop3s,com.sun.mail.pop3.POP3SSLStore,Oracle], com.sun.mail.imap.IMAPStore=javax.mail.Provider[STORE,imap,com.sun.mail.imap.IMAPStore,Oracle], com.sun.mail.pop3.POP3Store=javax.mail.Provider[STORE,pop3,com.sun.mail.pop3.POP3Store,Oracle]}
DEBUG: Providers Listed By Protocol: {imaps=javax.mail.Provider[STORE,imaps,com.sun.mail.imap.IMAPSSLStore,Oracle], imap=javax.mail.Provider[STORE,imap,com.sun.mail.imap.IMAPStore,Oracle], smtps=javax.mail.Provider[TRANSPORT,smtps,com.sun.mail.smtp.SMTPSSLTransport,Oracle], pop3=javax.mail.Provider[STORE,pop3,com.sun.mail.pop3.POP3Store,Oracle], pop3s=javax.mail.Provider[STORE,pop3s,com.sun.mail.pop3.POP3SSLStore,Oracle], smtp=javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle]}
DEBUG: successfully loaded resource: /META-INF/javamail.default.address.map
DEBUG: getProvider() returning javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle]
DEBUG SMTP: useEhlo true, useAuth true
DEBUG SMTP: useEhlo true, useAuth true
DEBUG SMTP: trying to connect to host "echange_server.MyDomain.com", port 25, isSSL false
220 echange_server.MyDomain.com Microsoft ESMTP MAIL Service ready at Mon, 30 Sep 2013 09:01:08 +0100
DEBUG SMTP: connected to host "echange_server.MyDomain.com", port: 25

EHLO host.MyDomain.com
250-echange_server.MyDomain.com Hello [xx.xx.xx.xx]
250-SIZE
250-PIPELINING
250-DSN
250-ENHANCEDSTATUSCODES
250-STARTTLS
250-X-ANONYMOUSTLS
250-AUTH NTLM
250-X-EXPS GSSAPI NTLM
250-8BITMIME
250-BINARYMIME
250-CHUNKING
250-XEXCH50
250-XRDST
250 XSHADOW
DEBUG SMTP: Found extension "SIZE", arg ""
DEBUG SMTP: Found extension "PIPELINING", arg ""
DEBUG SMTP: Found extension "DSN", arg ""
DEBUG SMTP: Found extension "ENHANCEDSTATUSCODES", arg ""
DEBUG SMTP: Found extension "STARTTLS", arg ""
DEBUG SMTP: Found extension "X-ANONYMOUSTLS", arg ""
DEBUG SMTP: Found extension "AUTH", arg "NTLM"
DEBUG SMTP: Found extension "X-EXPS", arg "GSSAPI NTLM"
DEBUG SMTP: Found extension "8BITMIME", arg ""
DEBUG SMTP: Found extension "BINARYMIME", arg ""
DEBUG SMTP: Found extension "CHUNKING", arg ""
DEBUG SMTP: Found extension "XEXCH50", arg ""
DEBUG SMTP: Found extension "XRDST", arg ""
DEBUG SMTP: Found extension "XSHADOW", arg ""
DEBUG SMTP: Attempt to authenticate using mechanisms: NTLM
DEBUG SMTP: AUTH NTLM failed

Exception in thread "main" java.lang.RuntimeException: javax.mail.AuthenticationFailedException: 250-exchange_host.MyDomain.com Hello [xx.xx.xx.xx]

    at testPakcage.Main.main(Main.java:51)
Caused by: javax.mail.AuthenticationFailedException: 250-exchange_host.MyDomain.com Hello [xx.xx.xx.xx]

пожалуйста, помогите мне, я потратил несколько дней на поиск решения, но ничего не нашел


person Azzedine madi    schedule 29.09.2013    source источник
comment
вы проверили этот вопрос? stackoverflow.com/questions/4337812/   -  person epoch    schedule 29.09.2013
comment
@epoch: да, я сделал, это не то же самое, в этом примере я использую протокол smtp, а в другом он использует протокол imap и механизм LOGIN: DEBUG SMTP: механизмы проверки: LOGIN PLAIN DIGEST-MD5 NTLM AUTH LOGIN   -  person Azzedine madi    schedule 29.09.2013
comment
В зависимости от конфигурации биржи может потребоваться указать имя пользователя в форме ДОМЕН\имя пользователя.   -  person Jk1    schedule 29.09.2013
comment
@jk1: я пробую всю эту комбинацию: user_name, [email protected], myDomain.com\user_name, но появляется та же ошибка сообщения   -  person Azzedine madi    schedule 29.09.2013
comment
@Azzedinemadi, это не myDomain.com, это должно быть доменное имя Windows   -  person epoch    schedule 29.09.2013
comment
@epoch: я указал доменное имя своей компании, конечно, это то, что я имею в виду под своим доменом   -  person Azzedine madi    schedule 29.09.2013
comment
Судя по сообщению об ошибке в исключении, похоже, что что-то не так с обменом протоколами. Можете ли вы опубликовать весь вывод отладки? Кроме того, работает ли у вас антивирусная программа, которая может фильтровать ваши подключения к электронной почте?   -  person Bill Shannon    schedule 30.09.2013
comment
@BillShannon: хорошо, я отредактирую свой пост, чтобы поместить весь вывод отладки, а для антивируса я отключу его, но у меня та же ошибка msg   -  person Azzedine madi    schedule 30.09.2013
comment
Похоже, вы можете получить неожиданное исключение при попытке инициализировать аутентификацию NTLM. Пожалуйста, свяжитесь со мной по адресу [email protected], чтобы мы могли продолжить отладку.   -  person Bill Shannon    schedule 30.09.2013
comment
@BillShannon: спасибо, Билл, я так и сделаю   -  person Azzedine madi    schedule 30.09.2013
comment
После некоторой автономной отладки это оказалось проблемой установки или настройки JDK, связанной с поддержкой криптографии. JavaMail скрывал реальный источник ошибки, что я исправлю, но необходимо также устранить основную проблему, и она не связана с JavaMail.   -  person Bill Shannon    schedule 02.10.2013
comment
@BillShannon: спасибо, Билл, я сделал новую установку jdk, и она работает, ты ангел!! ^^   -  person Azzedine madi    schedule 02.10.2013


Ответы (2)


У меня это работает, подключаясь к нашему серверу Exchange 2010 через NTLM.

NTLM использует для аутентификации ваш логин и пароль Windows, а не адрес электронной почты и пароль.

Я сделал следующие изменения:

1) Имя пользователя должно быть логином Windows, а НЕ адресом электронной почты. NTLM использует ваши учетные данные Windows для аутентификации.

2) mail.smtp.auth.ntlm.domain должен быть вашим доменом Windows, то есть частью перед косой чертой, если вы обычно входите на свой компьютер с Windows с «MYDOMAIN\id12345» в качестве имени пользователя.

Обновленный код ниже:

public class Main {
  public static void main(String[] args) {

    // *** CHANGED ***
    final String username = "id12345"; // ID you log into Windows with
    final String password = "MyWindowsPassword";

    Properties props = new Properties();
    props.put("mail.smtp.auth", "true");
    props.put("mail.debug", "true");

    props.put("mail.smtp.host", "exchangeserver.mydomain.com");
    props.put("mail.smtp.port", "25");
    props.put("mail.smtp.auth.mechanisms","NTLM");

    // *** CHANGED ***
    props.put("mail.smtp.auth.ntlm.domain","WINDOMAIN"); // Domain you log into Windows with


    Session session = Session.getInstance(props,new MyAuthenticator(username,password));

    try {

        Message message = new MimeMessage(session);
        message.setFrom(new InternetAddress("[email protected]"));
        message.setRecipients(Message.RecipientType.TO,
                InternetAddress.parse("[email protected]"));
        message.setSubject("Test email");
        message.setText("TEST EMAIL");

        Transport.send(message);

        System.out.println("Done");

    } catch (MessagingException e) {
        e.printStackTrace();
    }
  }


  public static class MyAuthenticator extends Authenticator {

    String user;
    String pw;
    public MyAuthenticator (String username, String password)
    {
        super();
        this.user = username;
        this.pw = password;
    }
    public PasswordAuthentication getPasswordAuthentication()
    {
        return new PasswordAuthentication(user, pw);
    }
  }
}

Что бы это ни стоило, ваш оригинальный пост помог мне решить проблемы с подключением, которые у меня были, которые продолжались в течение нескольких дней.

И последнее, вам, вероятно, нужно отключить / изменить настройки антивируса, чтобы разрешить доступ к порту 25.

person Cookalino    schedule 05.11.2013

У меня была та же проблема, и после того, как я узнал, что javamail не поддерживает аутентификацию NTLMv2, я разработал обходной путь, для которого требуется библиотека JCIFS.

Я скачал исходный код API javamail (https://java.net/projects/javamail/pages/Home) и отредактировал класс com.sun.mail.auth.Ntlm, чтобы добавить отсутствующую поддержку NTLMv2 с использованием поддержки библиотеки JCIFS (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

... }

Вторая модификация заключалась в замене метода 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

Чтобы максимально включить отладку, я использовал следующий код в основном методе моего тестового класса:

    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

Перед применением исправления тест зависал после отправки сообщения типа 1, потому что мой сервер Exchange требовал аутентификации NTLMv2. После патча аутентификация прошла успешно.

person Andrea Luciano    schedule 27.09.2014