У меня была такая же проблема при отправке электронной почты через SMTP-коннектор Exhange.Узнав, что 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.После исправления проверка подлинности была выполнена успешно.
Другое решение - отправка электронной почты через ## Exchange webservice ## aka EWS с использованием ## EWS Java API ##, выпущенного и поддерживаемого 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();
}
}
Но опять-таки есть патч для применения во внутреннем классе NTLM EwsJCIFSNTLMScheme.java для включения NTLMv2, как указано в ответена этот пост:
Как использовать аутентификацию LDAP для подключения к веб-службам Exchange в Java?
То есть:
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());
}
}
Я пытался, и у меня это сработало.