[英]Javamail NTLM Authentication Failure
Trying to connect to Exchange server using NTLM in JavaMail. 尝试在JavaMail中使用NTLM连接到Exchange服务器。 I can connect to SMTP, but not IMAP.
我可以连接到SMTP,但不能连接到IMAP。 I can also authenticate via the OS X Mail.app application using the identical host/username/password, account type = "IMAP", Port 143, ssl=false, authentication=NTLM, Domain Name="".
我还可以使用相同的主机/用户名/密码,帐户类型=“IMAP”,端口143,ssl = false,身份验证= NTLM,域名=“”通过OS X Mail.app应用程序进行身份验证。
The connecting code: 连接代码:
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");
}
}
The output: 输出:
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
I tried wrapping the username with backslashes, per http://www.oracle.com/technetwork/java/faq-135477.html#Exchange-login I get the following error: 我尝试用反斜杠包装用户名,根据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
Backslashes around the username in the SMTP connect portion cause it to fail. SMTP连接部分中用户名周围的反斜杠会导致其失败。 I can't tell if the "One time use" error is a step in the right direction or not.
我不知道“一次性使用”错误是否是朝着正确方向迈出的一步。
I have noticed that - via SMTP - the NTLM Authentication did not work with an older version of javax.mail (up to 1.4.1) but it now works with version 1.4.5. 我注意到 - 通过SMTP - NTLM身份验证不适用于旧版本的javax.mail(最高1.4.1),但它现在适用于1.4.5版本。 And the user name to be specified was of the form "domain\\username".
并且要指定的用户名的格式为“domain \\ username”。 Could be that same effect (difference in versions of javax.mail) would also apply to IMAP.
可能是同样的效果(javax.mail版本的差异)也适用于IMAP。
From what I recall about NTLM, and your NTLM debug messages I can gather the following: 从我记得的NTLM和你的NTLM调试消息,我可以收集以下内容:
I suggest you try following the road in which the credentials (u/p) are taken automatically by the JDK from the client Windows machine. 我建议您尝试遵循JDK从客户端Windows机器自动获取凭据(u / p)的道路。
Here is my complete working solution: 这是我完整的工作解决方案:
Using netbeans, create a new java application project. 使用netbeans,创建一个新的Java应用程序项目。 Put this code in there:
把这段代码放在那里:
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", "yourusername@youremailaddress.com");
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, "yourusername@youremailaddress.com");
// 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 = "yourusername@youremailaddress.com";
String password = "yourpassword";
authentication = new PasswordAuthentication(username, password);
}
protected PasswordAuthentication getPasswordAuthentication() {
return authentication;
}
}
}
Change the username, passwords, ports, and properties to your specific settings. 将用户名,密码,端口和属性更改为您的特定设置。
You will need to get the javamail-1.4.7
and load the mail.jar
from ( http://www.oracle.com/technetwork/java/index-138643.html ) into the project. 您需要获取
javamail-1.4.7
并将mail.jar
从( http://www.oracle.com/technetwork/java/index-138643.html )加载到项目中。
One thing we did which shed light on what our parameters should be is to download Thunderbird mail client which can auto-discover information about the exchange server to make sure all of our settings were right. 我们所做的一件事就是明确我们的参数应该是下载Thunderbird邮件客户端,它可以自动发现有关Exchange服务器的信息,以确保我们所有的设置都是正确的。 If you cant convince thunderbird to connect, then this code likewise shouldn't be expected to work.
如果你不能说服thunderbird连接,那么这个代码同样不应该起作用。
I had the same problem sending email via Exhange SMTP connector. 我通过Exhange SMTP连接器发送电子邮件时遇到同样的问题。 After finding out that javamail does not support NTLMv2 authentication, I worked out a workaround that requires JCIFS library though.
在发现javamail不支持NTLMv2身份验证之后,我找到了一个需要JCIFS库的解决方法。
I downloded the javamail api source code ( https://java.net/projects/javamail/pages/Home ) and edited the class com.sun.mail.auth.Ntlm to add the missing support for NTLMv2 using the JCIFS library support ( http://jcifs.samba.org ). 我下载了javamail api源代码( https://java.net/projects/javamail/pages/Home )并编辑了类com.sun.mail.auth.Ntlm,使用JCIFS库支持添加了对NTLMv2缺少的支持( http://jcifs.samba.org )。
The first modification in file Ntlm.java was in method init0, and consisted of adding the missing flag NTLMSSP_NEGOTIATE_NTLM2 : 文件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
// ...
The second modification was to replace the method generateType3Msg with this: 第二个修改是用这个替换方法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());
}
The simpest way I found to patch the library is to compile the class and update the library jar file: 我发现修补库的最简单的方法是编译类并更新库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
To enable debug as much as possible, I used the following code in the main method of my test 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");
with this logging.properties: 使用此logging.properties:
# Logging
handlers = java.util.logging.ConsoleHandler
.level = INFO
# Console Logging
java.util.logging.ConsoleHandler.level = INFO
Before applying the patch the test was stuck after sending Type 1 message, because my Exchange server required NTLMv2 authentication. 在应用修补程序之前,测试在发送Type 1消息后被卡住,因为我的Exchange服务器需要NTLMv2身份验证。 After the patch the athentication was done successfully.
在补丁之后,成功完成了身份验证。
Another solution is to send email via the ##Exchange webservice## aka EWS by using the ##EWS Java API## released and mantained by Microsoft ( https://github.com/OfficeDev/ews-java-api ), such as in this example: 另一种解决方案是使用## EWS Java API ##通过## Exchange Webservice ## aka EWS发送电子邮件,由Microsoft( 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("myuser@myserver.com"));
msg.setSubject("Test Mail");
msg.getToRecipients().add("myuser@gmail.com");
msg.setBody(MessageBody.getMessageBodyFromText("Email sent by Miscrosoft Java EWS API."));
msg.getAttachments().addFileAttachment("c:\\temp\\myattachement.pdf");
msg.send();
}
} }
But again there is a patch to apply in the inner class NTLM of EwsJCIFSNTLMScheme.java to enable NTLMv2 as indicated in the answer to this post: 但是再次有一个补丁应用于EwsJCIFSNTLMScheme.java的内部类NTLM以启用NTLMv2,如本文的回答中所示:
How to use LDAP authentication for the Exchange Web Services connection in Java? 如何在Java中使用LDAP身份验证进行Exchange Web服务连接?
That is: 那是:
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());
}
} }
I tried and it worked for me. 我试过,它对我有用。
Try setting the domain "" to the properties of the imap store : 尝试将域“”设置为imap商店的属性:
properties.setProperty("mail.imap.auth.ntlm.domain","");
Since in SMTP you are login using LOGIN, the use of the domain is not necessary. 由于在SMTP中您使用LOGIN登录,因此不需要使用域。 But in NTLM the domain is mandatory.
但在NTLM中,域名是强制性的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.