The "UntrustedRoot" error in Exchange 2007 typically indicates the remote SMTP server is presenting a certificate that chains to a root CA not trusted by your local certificate store. This often happens when:
- The certificate was replaced with one from an internal CA
- The certificate chain is incomplete
- The root CA was recently removed from your trusted store
For quick verification before diving into code solutions, you can use these manual methods:
# Using OpenSSL (Linux/macOS)
openssl s_client -connect mail.ourclient.com:25 -starttls smtp -showcerts
# Using PowerShell (Windows)
Test-NetConnection -ComputerName mail.ourclient.com -Port 25
Here's a complete C# solution to inspect remote SMTP certificates:
using System;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
public class SmtpCertificateInspector
{
public static X509Certificate2 GetRemoteCertificate(string host, int port = 25)
{
using (var client = new TcpClient(host, port))
using (var stream = client.GetStream())
{
// SMTP protocol initialization
byte[] buffer = new byte[1024];
stream.Read(buffer, 0, buffer.Length);
stream.Write(System.Text.Encoding.ASCII.GetBytes("EHLO example.com\r\n"), 0, 19);
stream.Read(buffer, 0, buffer.Length);
// Start TLS negotiation
stream.Write(System.Text.Encoding.ASCII.GetBytes("STARTTLS\r\n"), 0, 10);
stream.Read(buffer, 0, buffer.Length);
// Create SSL stream
var sslStream = new SslStream(stream, false,
new RemoteCertificateValidationCallback(ValidateServerCertificate), null);
try
{
sslStream.AuthenticateAsClient(host);
return new X509Certificate2(sslStream.RemoteCertificate);
}
finally
{
sslStream.Close();
}
}
}
private static bool ValidateServerCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
// For inspection purposes, we accept all certificates
return true;
}
}
For those preferring Python:
import smtplib
import ssl
from socket import create_connection
def get_smtp_certificate(hostname, port=25):
context = ssl.create_default_context()
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
sock = create_connection((hostname, port))
server = smtplib.SMTP(hostname, port)
server.starttls(context=context)
cert = server.sock.getpeercert(binary_form=True)
return ssl.DER_cert_to_PEM_cert(cert)
Once you have the certificate, check these critical properties:
cert = GetRemoteCertificate("mail.ourclient.com");
Console.WriteLine($"Issuer: {cert.Issuer}");
Console.WriteLine($"Subject: {cert.Subject}");
Console.WriteLine($"Thumbprint: {cert.Thumbprint}");
Console.WriteLine($"NotBefore: {cert.NotBefore}");
Console.WriteLine($"NotAfter: {cert.NotAfter}");
For Exchange 2007 specifically, you may need to:
- Add the root CA certificate to the Trusted Root Certification Authorities store
- Update the TLSSendDomainSecureList with the correct certificate requirements
- Configure certificate override settings in the transport configuration
When Exchange 2007 attempts to establish TLS-secured connections with external mail servers, certificate validation failures can halt message delivery. The specific error UntrustedRoot
indicates the remote server's certificate chain can't be verified against the local trust store.
Unlike HTTPS where browsers provide certificate inspection UI, SMTP requires command-line tools:
openssl s_client -connect mail.ourclient.com:25 -starttls smtp -showcerts
Sample output structure:
CONNECTED(00000003)
depth=1 C = US, O = "Some CA", CN = Intermediate CA
verify error:num=20:unable to get local issuer certificate
---
Certificate chain
0 s:/CN=mail.ourclient.com
i:/C=US/O=Some CA/CN=Intermediate CA
1 s:/C=US/O=Some CA/CN=Intermediate CA
i:/C=US/O=Root CA/CN=Root Certificate
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIG... [truncated]
-----END CERTIFICATE-----
For repeated checks, create a PowerShell script to validate the certificate chain:
# SMTP TLS Certificate Checker
$smtpServer = "mail.ourclient.com"
$port = 25
$tcpClient = New-Object System.Net.Sockets.TcpClient
$tcpClient.Connect($smtpServer, $port)
$sslStream = New-Object System.Net.Security.SslStream($tcpClient.GetStream())
$sslStream.AuthenticateAsClient($smtpServer)
$certificate = $sslStream.RemoteCertificate
$certChain = New-Object System.Security.Cryptography.X509Certificates.X509Chain
$certChain.Build([System.Security.Cryptography.X509Certificates.X509Certificate2]$certificate)
Write-Host "Certificate Subject: $($certificate.Subject)"
Write-Host "Issuer: $($certificate.Issuer)"
Write-Host "Chain Status: $($certChain.ChainStatus[0].Status)"
For Exchange 2007 on Windows Server 2008:
- Check trusted root certificates in MMC Certificates snap-in (Local Computer)
- Compare with remote server's certificate chain
- Export and import missing intermediate certificates if necessary
When the client claims no certificate changes occurred:
Component | Expected | Actual |
---|---|---|
Root CA | Public CA (e.g., DigiCert) | Internal CA (XYZ-CORP-ROOT) |
Chain Depth | 3 (End-Entity → Intermediate → Root) | 2 (Missing intermediate) |