Hi Gourav,
Exchange Server on-prem does not support authenticating IMAP or SMTP clients with a user’s client certificate.
Certificate-based auth in Exchange is implemented on IIS virtual directories such as OWA, ECP and ActiveSync, and you can extend the same IIS client-certificate mapping to EWS. IMAP and POP run as Windows services, not in IIS, and SMTP client submission is handled by the Front End receive connector. None of those endpoints accept a user client certificate for per-mailbox authentication. Microsoft's CBA guidance is limited to IIS vdirs, and Microsoft’s on-prem modern auth matrix shows IMAP and POP are not supported there either. For SMTP, certificates are used for server-to-server mutual TLS on receive connectors, not for authenticating end-users sending mail.
What works well on Exchange on-prem for a Java app that must be pure CBA is to use EWS over HTTPS, enable client-certificate mapping on the EWS virtual directory in IIS, and then call EWS SOAP from Java while presenting the user’s client cert. Microsoft's docs show how to turn on CBA at the IIS layer, and although the article walks through OWA and ActiveSync, the same IIS setting applies to EWS.
Here is a minimal Java 8 PoC that loads a user's client certificate from a PKCS#12 file, builds an HTTPS connection to EWS, and runs a simple FindItem request against the Inbox. On the server side you must have EWS reachable at https://mail.example.com/EWS/Exchange.asmx, require client certificates on the EWS vdir, and map the client cert to the correct AD user (UPN mapping works well). On the client side replace file paths, passwords and URLs as appropriate.
import javax.net.ssl.*;
import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.CertificateException;
public class EwsCbaPoC {
// Call this once at startup to enable mutual TLS using the user's client cert.
static void configureClientCert(String pfxPath, String pfxPassword,
String truststorePath, String truststorePassword)
throws KeyStoreException, IOException, NoSuchAlgorithmException,
CertificateException, UnrecoverableKeyException, KeyManagementException {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
try (FileInputStream fis = new FileInputStream(pfxPath)) {
keyStore.load(fis, pfxPassword.toCharArray());
}
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, pfxPassword.toCharArray());
TrustManagerFactory tmf;
if (truststorePath != null) {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); // JKS by default
try (FileInputStream fis = new FileInputStream(truststorePath)) {
trustStore.load(fis, truststorePassword.toCharArray());
}
tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
} else {
tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init((KeyStore) null); // use default JVM trust
}
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> hostname.equalsIgnoreCase("mail.example.com"));
}
static String callEws(String url, String soapAction, String bodyXml) throws IOException {
byte[] payload = bodyXml.getBytes(StandardCharsets.UTF_8);
HttpsURLConnection conn = (HttpsURLConnection) new URL(url).openConnection();
conn.setConnectTimeout(15000);
conn.setReadTimeout(30000);
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "text/xml; charset=UTF-8");
conn.setRequestProperty("SOAPAction", soapAction);
try (OutputStream os = conn.getOutputStream()) {
os.write(payload);
}
int code = conn.getResponseCode();
InputStream is = (code >= 200 && code < 300) ? conn.getInputStream() : conn.getErrorStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[8192];
int r;
while ((r = is.read(buf)) != -1) baos.write(buf, 0, r);
String resp = new String(baos.toByteArray(), StandardCharsets.UTF_8);
if (code < 200 || code >= 300) throw new IOException("HTTP " + code + ": " + resp);
return resp;
}
public static void main(String[] args) throws Exception {
configureClientCert("C:/certs/user.pfx", "pfxPassword",
null, null); // or supply a corporate truststore
String ewsUrl = "https://mail.example.com/EWS/Exchange.asmx";
String findItemBody =
"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" " +
"xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\">" +
"<soap:Header>" +
"<t:RequestServerVersion Version=\"Exchange2013\"/>" +
"</soap:Header>" +
"<soap:Body>" +
"<FindItem xmlns=\"http://schemas.microsoft.com/exchange/services/2006/messages\" Traversal=\"Shallow\">" +
"<ItemShape><t:BaseShape>IdOnly</t:BaseShape>" +
"<t:AdditionalProperties>" +
"<t:FieldURI FieldURI=\"item:Subject\"/>" +
"<t:FieldURI FieldURI=\"item:DateTimeReceived\"/>" +
"<t:FieldURI FieldURI=\"item:From\"/>" +
"</t:AdditionalProperties></ItemShape>" +
"<ParentFolderIds><t:DistinguishedFolderId Id=\"inbox\"/></ParentFolderIds>" +
"<MaxEntriesReturned>10</MaxEntriesReturned>" +
"</FindItem>" +
"</soap:Body></soap:Envelope>";
String response = callEws(ewsUrl,
"http://schemas.microsoft.com/exchange/services/2006/messages/FindItem",
findItemBody);
System.out.println(response);
}
}
To send mail without SMTP, post a CreateItem SOAP with MessageDisposition set to SendAndSaveCopy and provide a Message with ToRecipients and a Body. That works entirely over HTTPS with the user's client certificate mapped to their AD identity, so no passwords and no OAuth are involved. Microsoft's EWS authentication article covers the protocol side, while the CBA article covers the IIS side.
If you must keep IMAP and SMTP in this Java app, Exchange on-prem can only authenticate IMAP with user credentials using login or integrated mechanisms, and SMTP client submission on port 587 requires user authentication with TLS. Certificates there are for transport security and server-to-server trust, not for user sign-in.
I hope this helps clarifying!