I want to get my Exchange emails on android and for that I am using javamail api for android... it works great on gmail and yahoo using imap.
The problem is that my exchange server has self signed certificate so android don't like this too much and I get 03-14 12:46:13.698: WARN/System.err(281): javax.mail.MessagingException: Not trusted server certificate;
I have seen this example: Sending Email in Android using JavaMail API without using the default/built-in app where somebody makes a send example over ssl.. I think I can use that JSSEProvider to accept my self signed certificate but I don't know how can I use it.
Please help me!
I have been having the same problem, and managed to get around it by configuring a trust manager as detailed at http://java.sun.com/products/javamail/javamail-1.4.2/SSLNOTES142.txt.
What I did was create my own TrustManager:
package com.myapp;
import javax.net.ssl.X509TrustManager;
import java.security.cert.X509Certificate;
/**
* DummyTrustManager - NOT SECURE
*/
public class DummyTrustManager implements X509TrustManager {
public void checkClientTrusted(X509Certificate[] cert, String authType) {
// everything is trusted
}
public void checkServerTrusted(X509Certificate[] cert, String authType) {
// everything is trusted
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
and use this in my own SSLSocketFactory:
package com.myapp;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import javax.net.SocketFactory;
import javax.net.ssl.*;
/**
* DummySSLSocketFactory
*/
public class DummySSLSocketFactory extends SSLSocketFactory {
private SSLSocketFactory factory;
public DummySSLSocketFactory() {
try {
SSLContext sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(null,
new TrustManager[] { new DummyTrustManager()},
null);
factory = (SSLSocketFactory)sslcontext.getSocketFactory();
} catch(Exception ex) {
// ignore
}
}
public static SocketFactory getDefault() {
return new DummySSLSocketFactory();
}
public Socket createSocket() throws IOException {
return factory.createSocket();
}
public Socket createSocket(Socket socket, String s, int i, boolean flag)
throws IOException {
return factory.createSocket(socket, s, i, flag);
}
public Socket createSocket(InetAddress inaddr, int i,
InetAddress inaddr1, int j) throws IOException {
return factory.createSocket(inaddr, i, inaddr1, j);
}
public Socket createSocket(InetAddress inaddr, int i)
throws IOException {
return factory.createSocket(inaddr, i);
}
public Socket createSocket(String s, int i, InetAddress inaddr, int j)
throws IOException {
return factory.createSocket(s, i, inaddr, j);
}
public Socket createSocket(String s, int i) throws IOException {
return factory.createSocket(s, i);
}
public String[] getDefaultCipherSuites() {
return factory.getDefaultCipherSuites();
}
public String[] getSupportedCipherSuites() {
return factory.getSupportedCipherSuites();
}
}
To get this to work in javamail-android, you need to specify the new SSLSocketFactory before you get a Session instance:
Properties props = new Properties();
props.setProperty( "mail.imaps.socketFactory.class", "com.myapp.DummySSLSocketFactory" );
session = Session.getDefaultInstance( props );
The TrustManager which we defined now be used instead of the default one, and all certificates will be accepted.
Obviously there are some security issues with blindly accepting all certificates, and I would suggest doing some checking in your TrustManager, otherwise you could open yourself up to all kinds of security issues (such as man-in-the-middle attacks). Also, I would only use this where you really have to: for example you say that GMail and Ymail is working, so I would not use this mechanism when connecting to those.
I would put in an exception handler to catch the "Certificate not trusted" exception, and prompt the user to accept an untrusted certificate (with the necessary warning to only do this for servers that are absolutely trusted) before actually overriding the TrustManager.
I had the same problem, and solved it in what I believe is a cleaner and more succinct way. I used the same reference linked to by Mark Allison, but instead of overriding the SSLSocketFactory and implementing your own TrustManager, I added the following:
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
// or
// sf.setTrustedHosts(new String[] { "my-server" });
props.put("mail.smtp.ssl.enable", "true");
// also use following for additional safety
//props.put("mail.smtp.ssl.checkserveridentity", "true");
props.put("mail.smtp.ssl.socketFactory", sf);
For more info, look in the "Socket Factories" section of the link: http://java.sun.com/products/javamail/javamail-1.4.2/SSLNOTES142.txt
Of note, when I did this, I had to use an "imap" store, as opposed to "imaps":
Store store = session.getStore("imap");
Here's my complete code, which works well for me when accessing an Exchange 2010 server:
import javax.mail.*;
import java.util.*;
import com.sun.mail.util.MailSSLSocketFactory;
.
.
.
// Set the socket factory to trust all hosts
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
// create the properties for the Session
Properties props = new Properties();
props.put("mail.imap.ssl.enable", "true");
props.put("mail.imap.ssl.socketFactory", sf);
// Get session
Session session = Session.getInstance(props, null);
//session.setDebug(true);
// Get the store
Store store = session.getStore("imap");
System.out.println("Establishing connection with IMAP server.");
store.connect("host", 993, "username", "password");
System.out.println("Connection established with IMAP server.");
// List all folders (including subfolders)
Folder[] allFolders = store.getDefaultFolder().list("*");
store.close();