2010-12-02 6 views
11

La tentative de connexion au serveur Exchange à l'aide de NTLM dans JavaMail. Je peux me connecter à SMTP, mais pas à IMAP. Je peux également m'authentifier via l'application OS X Mail.app en utilisant le même nom d'hôte/nom d'utilisateur/mot de passe, type de compte = "IMAP", Port 143, ssl = false, authentification = NTLM, Domain Name = "".Échec de l'authentification Javamail NTLM

Le code de connexion:

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 sortie:

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 

J'ai essayé envelopper le nom d'utilisateur avec antislashs, par http://www.oracle.com/technetwork/java/faq-135477.html#Exchange-login je reçois l'erreur suivante:

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

backslashs autour le nom d'utilisateur dans la partie de connexion SMTP provoque l'échec. Je ne peux pas dire si l'erreur "One time use" est un pas dans la bonne direction ou pas.

+0

Selon la FAQ de javamail, vous devriez utiliser \ bob \ bob (en supposant que "bob" soit à la fois le nom d'utilisateur NT et l'adresse mail). Est-ce que c'est ce que vous avez essayé quand vous avez eu l'erreur "usage unique"? – Chochos

+0

Si vous pouvez renifler la connexion Mail.app au serveur Exchange, vous pouvez voir comment elle s'authentifie (bien que vous ne sachiez pas comment générer les jetons, mais vous pourriez voir si elle fait quelque chose de différent de JavaMail). – Chochos

+0

@Chochos, je n'ai jamais été obligé d'entrer un nom d'utilisateur NT pour Mail.app. J'ai essayé aussi d'utiliser le nom d'utilisateur, mais j'ai la même erreur. –

Répondre

7

J'ai remarqué que - via SMTP - l'authentification NTLM ne fonctionnait pas avec une ancienne version de javax.mail (jusqu'à 1.4.1) mais elle fonctionne maintenant avec la version 1.4.5. Et le nom d'utilisateur à spécifier était de la forme "domaine \ nom d'utilisateur". Pourrait être le même effet (différence dans les versions de javax.mail) s'appliquerait également à IMAP.

-1

Essayez de définir le domaine « » aux propriétés du magasin imap:

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

Puisque dans SMTP vous connecter à l'aide LOGIN, l'utilisation du domaine n'est pas nécessaire. Mais dans NTLM le domaine est obligatoire.

+0

Salut Jon, pas de chance là-bas, même résultat. –

2

D'après ce que je me souviens à propos de NTLM, et vos messages de débogage NTLM je peux rassembler les éléments suivants:

  • NTLM a été conçu pour une authentification unique, prenant ainsi les informations d'identification de la machine Windows son fonctionnement sur - en particulier le JDK implémentation de NTLM.
    • NTLM a deux versions, bien trois pour être exact. NTLM v1, NTLMv2 et une autre version que je ne peux pas rappeler pour le moment. NTLM v1 a un trou de sécurité qui vous permet d'utiliser vraiment un nom d'utilisateur et un mot de passe et de vous connecter en utilisant le protocole NTLM. Dans NTLM v2, il a été corrigé, ce qui oblige l'implémentation à prendre le mot de passe (le mot de passe haché) de la machine Windows connectée.
    • Il semble que le protocole NTLM s'arrête dans votre cas après le premier message envoyé par le serveur Exchange. Remarquez qu'il a des drapeaux qui déclare quel type de NTLM est utilisé et d'autres tels que: chiffrement, etc.

Je vous suggère d'essayer en suivant la route dans laquelle les pouvoirs (u/p) sont prises automatiquement par le JDK de la machine Windows client.

+0

Et si je cours depuis autre chose que Windows? Puis-je toujours m'authentifier auprès d'un serveur NTLM? –

+0

Très probablement pas, au cas où nous parlons de NTLMv2. BTW: Je me souviens juste que la solution que nous employions à l'époque utilisait Kerberos. La plupart des systèmes d'échange prennent en charge l'authentification Kerberos et le protocole prend en charge les délégations d'informations d'identification, et je pense que cela peut également fonctionner si vous naviguez depuis une station autre que Windows. –

1

Voici ma solution de travail complète:

En utilisant NetBeans, créez un nouveau projet d'application java. Placez ce code là:

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

Modifiez le nom d'utilisateur, les mots de passe, les ports et les propriétés en fonction de vos paramètres spécifiques.

Vous devez obtenir le javamail-1.4.7 et charger le mail.jar de (http://www.oracle.com/technetwork/java/index-138643.html) dans le projet. Une chose que nous avons faite qui a éclairci ce que nos paramètres devraient être est de télécharger le client de messagerie Thunderbird qui peut découvrir automatiquement des informations sur le serveur d'échange pour s'assurer que tous nos paramètres étaient corrects. Si vous ne pouvez pas convaincre thunderbird de se connecter, alors ce code ne devrait pas fonctionner normalement.

+0

La question concerne IMAP, pas d'authentification SMTP – jechaviz

1

J'ai eu le même problème en envoyant un courrier électronique via le connecteur SMTP Exhange. Après avoir découvert que javamail ne prend pas en charge l'authentification NTLMv2, j'ai trouvé une solution de contournement qui nécessite la bibliothèque JCIFS.

Je downloded le code source javamail api (https://java.net/projects/javamail/pages/Home) et édité les com.sun.mail.auth.Ntlm de classe pour ajouter le support manquant pour NTLMv2 en utilisant le support de la bibliothèque JCIFS (http://jcifs.samba.org).

La première modification dans le fichier Ntlm.java était dans la méthode init0 et consistait à ajouter le drapeau manquant 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 deuxième modification a consisté à remplacer la méthode generateType3Msg avec ceci:

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

Le moyen le plus simple que j'ai trouvé pour patcher la bibliothèque est de compiler la classe et mettre à jour le fichier jar de la bibliothèque:

"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 

Pour activer le débogage, autant que possible, j'ai utilisé le code suivant dans la méthode principale de ma classe de test:

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

avec cette logging.properties:

# Logging 
    handlers = java.util.logging.ConsoleHandler 

    .level = INFO 

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

Avant d'appliquer la patcher le test a été bloqué après l'envoi du message de type 1, car mon serveur Exchange nécessitait une authentification NTLMv2. Après le patch, l'apprentissage a été fait avec succès.

Une autre solution consiste à envoyer des courriels via le ## ## échange webservice alias EWS en utilisant le ## EWS ## API Java et publié par Microsoft mantained (https://github.com/OfficeDev/ews-java-api), comme dans cet exemple:

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

} 

}

Mais encore une fois il y a un patch à appliquer dans le NTLM de classe interne de EwsJCIFSNTLMScheme.java pour permettre NTLMv2 comme indiqué dans la réponse à ce poste:

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

C'est:

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

}

J'ai essayé et cela a fonctionné pour moi.