2010-12-02 34 views
11

Intentando conectarse al servidor de Exchange utilizando NTLM en JavaMail. Puedo conectarme a SMTP, pero no a IMAP. También puedo autenticar a través de la aplicación OS X Mail.app usando el mismo host/nombre de usuario/contraseña, tipo de cuenta = "IMAP", puerto 143, ssl = falso, autenticación = NTLM, nombre de dominio = "".Error de autenticación de Javamail NTLM

El código de conexión:

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"); 

     } 
    } 

La salida:

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 

Intenté envolver el nombre de usuario con barras invertidas, por http://www.oracle.com/technetwork/java/faq-135477.html#Exchange-login me sale el siguiente error:

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

Las barras invertidas en torno el nombre de usuario en la porción de conexión SMTP hace que falle. No puedo decir si el error "Uso de un solo uso" es un paso en la dirección correcta o no.

+0

De acuerdo con las preguntas frecuentes de javamail, debe usar \ bob \ bob (suponiendo que "bob" es tanto el nombre de usuario NT como la dirección de correo electrónico). ¿Es eso lo que probaste cuando obtuviste el error de "uso único"? – Chochos

+0

Si puede oler la conexión de Mail.app al servidor de Exchange, puede ver cómo se autentica (aunque no sabrá cómo generar los tokens, pero puede ver si está haciendo algo diferente de JavaMail). – Chochos

+0

@ Chochos, nunca me pidieron que ingresara un nombre de usuario NT para Mail.app. Traté de usar el nombre de usuario también, pero obtengo el mismo error. –

Respuesta

7

Me he dado cuenta de que, a través de SMTP, la autenticación NTLM no funcionaba con una versión anterior de javax.mail (hasta 1.4.1), pero ahora funciona con la versión 1.4.5. Y el nombre de usuario que se especificará es de la forma "dominio \ nombre de usuario". Podría ser el mismo efecto (diferencia en las versiones de javax.mail) también se aplicaría a IMAP.

-1

intente configurar el dominio "" a las propiedades del almacén IMAP:

properties.setProperty("mail.imap.auth.ntlm.domain",""); 

Dado que en SMTP que se acceda mediante login, el uso del dominio no es necesario. Pero en NTLM el dominio es obligatorio.

+0

Hola Jon, no tuve suerte, el mismo resultado. –

2

De lo que recuerdo sobre NTLM, y sus mensajes NTLM de depuración que he entendido lo siguiente:

  • NTLM fue diseñado para inicio de sesión único, tomando así las credenciales de la máquina de las ventanas de su ejecución en - especialmente el JDK implementación de NTLM.
    • NTLM tiene dos versiones, bien tres para ser exactos. NTLM v1, NTLMv2 y otra versión que no puedo recordar en este momento. NTLM v1 tiene un agujero de seguridad que le permite usar realmente un nombre de usuario y contraseña y conectarse utilizando el protocolo NTLM. En NTLM v2 se arregló, lo que obliga a la implementación a tomar la contraseña (el paso de hash) de la máquina con sesión de Windows.
    • Parece que el protocolo NTLM en su caso se detiene después del primer mensaje enviado por el servidor de Exchange. Nótese que tiene banderas, que declara qué tipo de NTLM se utiliza y otros tales como: cifrado, etc.

le sugiero que trate siguiendo el camino en el que las credenciales (u/p) se toman automáticamente por el JDK de la máquina cliente de Windows.

+0

¿Y si estoy ejecutando algo que no sea Windows? ¿Todavía puedo autenticarme en un servidor NTLM? –

+0

Lo más probable es que no, en caso de que estemos hablando de NTLMv2. Por cierto: solo recuerdo que la solución que empleábamos en ese momento era usar Kerberos. La mayoría de la compatibilidad con el intercambio de Kerberos Authentication y el protocolo son compatibles con las delegaciones de credenciales, y creo que también puede funcionar si navega desde una estación que no sea de Windows. –

1

Aquí está mi solución de trabajo completo:

uso de NetBeans, cree un nuevo proyecto de aplicación java. Pon este código en allí:

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; 
     } 
    } 
} 

Cambiar el nombre de usuario, contraseñas, puertos y propiedades en la configuración específicos.

Deberá obtener el javamail-1.4.7 y cargar el mail.jar desde (http://www.oracle.com/technetwork/java/index-138643.html) en el proyecto.

Una cosa que hicimos que arrojó luz sobre cuáles deberían ser nuestros parámetros es descargar el cliente de correo de Thunderbird que puede descubrir automáticamente la información sobre el servidor de intercambio para asegurarse de que todas nuestras configuraciones fueran correctas. Si no puedes convencer a Thunderbird de que se conecte, entonces tampoco debería esperarse que este código funcione.

+0

La pregunta es sobre IMAP, sin autenticación SMTP – jechaviz

1

Tuve el mismo problema al enviar un correo electrónico a través del conector SMTP de Exhange. Después de descubrir que javamail no es compatible con la autenticación NTLMv2, he resuelto una solución que requiere la biblioteca JCIFS.

I downloded el código fuente API JavaMail (https://java.net/projects/javamail/pages/Home) y editó el com.sun.mail.auth.Ntlm clase añade el apoyo que falta para NTLMv2 utilizando el soporte de bibliotecas JCIFS (http://jcifs.samba.org).

La primera modificación en el archivo Ntlm.java estaba en init0 método, y consistió en la adición de la bandera falta 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 
// ... 

La segunda modificación fue la de sustituir el método generateType3Msg con esto:

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()); 
} 

la forma simpest que encontré para parchear la biblioteca es compilar la clase y actualizar el archivo jAR de biblioteca:

"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 

Para habilitar la depuración medida de lo posible, que utiliza el código siguiente en el método principal de mi clase de prueba:

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"); 

con este logging.properties:

# Logging 
    handlers = java.util.logging.ConsoleHandler 

    .level = INFO 

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

Antes de aplicar el parche, la prueba se bloqueó después de enviar un mensaje de tipo 1, porque mi servidor de Exchange requirió autenticación NTLMv2. Después del parche, la autenticación se realizó con éxito.

Otra solución es enviar correo electrónico a través del servicio web ## Cambio ## también conocido como sistema de alerta temprana mediante el uso de la API de EWS ## Java ## liberado y mantenida por Microsoft (https://github.com/OfficeDev/ews-java-api), como en este ejemplo:

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(); 

} 

}

Pero de nuevo hay un parche para aplicar en el NTLM clase interna de EwsJCIFSNTLMScheme.java para permitir NTLMv2 como se indica en la respuesta a este mensaje:

How to use LDAP authentication for the Exchange Web Services connection in Java?

Eso es:

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()); 
} 

}

intenté y funcionó para mí.

Cuestiones relacionadas