TLS problems on Exchange Server rarely explain themselves in Exchange's own words. You get a Schannel event number in the System log, a TLS alert code, a hex error in a wizard or script, or a certificate status in the Exchange admin center. This quick reference decodes them: the meaning from Microsoft's documentation, the usual cause on an Exchange server and the first thing to check. Errors that already have a detailed guide on this site are linked at the end.
Codes in this quick reference
Schannel 36887: A fatal alert was received from the remote endpoint (TLS alert code in the text)System log (Schannel)Schannel 36888: A fatal alert was generated and sent to the remote endpointSystem log (Schannel)Schannel 36874: none of the cipher suites supported by the client application are supported by the serverSystem log (Schannel)Schannel 36873: No supported cipher suites were found when initiating a connectionSystem log (Schannel)Schannel 36870: A fatal error occurred when attempting to access the credential private keySystem log (Schannel)Schannel 36871: A fatal error occurred while creating a TLS credentialSystem log (Schannel)Schannel 36869: the credential's certificate does not have a private key information property attachedSystem log (Schannel)Schannel 36872 / 36886: No suitable default server credential exists on this systemSystem log (Schannel)
+ 41 more codes in the tables below.
How to read these errors#
- System log, source Schannel: Windows logs TLS handshake failures here, not in the Application log. Microsoft documents raising the
EventLoggingvalue underHKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNELfrom 1 to 3 for more detail; set it back when you're done. - Direction matters: 36887 means the other side sent a fatal alert (it rejected you); 36888 means this server generated one (it rejected the other side). Both carry a TLS alert code, decoded below. Current Windows versions say TLS where older documentation says SSL.
- Find the other party yourself: these events don't say which client or process was involved, so match the time against the SMTP protocol logs, IIS logs or your load balancer. Scanners and health probes trigger them too.
- Hex codes: SEC_E, CRYPT_E, CERT_E and NTE codes appear inside Schannel events, in
certutil -verifyoutput and in error text from the Hybrid Configuration Wizard, scripts and .NET applications.
# Which Schannel events, and how many, in the last 24 hours
Get-WinEvent -FilterHashtable @{ LogName = "System"; ProviderName = "Schannel"; StartTime = (Get-Date).AddDays(-1) } |
Group-Object Id | Sort-Object Count -Descending | Format-Table Count, Name -AutoSize
# The certificates Exchange uses, with status and private key
Get-ExchangeCertificate | Format-Table Thumbprint, Services, Status, HasPrivateKey, NotAfter -AutoSizeSchannel events in the System log#
| Event | Meaning | Usual cause | First check |
|---|---|---|---|
36887 | A fatal alert was received from the remote endpoint; the text gives the TLS alert code | The partner, client or a device in between refused the handshake | The alert code (next table); same time in SMTP or IIS logs |
36888 | A fatal alert was generated and sent to the remote endpoint | This server refused the handshake; alert 40 often comes with 36874 | The alert code; what the client offers |
36874 | None of the cipher suites supported by the client application are supported by the server; the request failed | Old clients, devices or scanners offering only protocols or ciphers you disabled | Health Checker TLS results (TLS hardening guide) |
36873 | No supported cipher suites were found when this server started a connection | Over-tight protocol or cipher settings, or an app asking for disabled protocols | Get-TlsCipherSuite; Health Checker |
36870 | A fatal error occurred when attempting to access the credential private key; the text gives a code such as 0x8009030D | Microsoft lists MachineKeys permissions, failed revocation or chain checks and expiry | certutil -verifystore My <thumbprint> |
36871 | A fatal error occurred while creating a TLS credential | A process asked for a protocol, cipher or certificate the server can't provide; admins often report it after TLS hardening | Which service fails at that time; protocol settings |
36869 | The credential's certificate does not have a private key information property attached | Certificate imported without its key, restored from a bad backup, or a failed enrollment | Get-ExchangeCertificate | fl Thumbprint,HasPrivateKey |
36872 / 36886 | No suitable default server credential exists on this system | Apps using the system default credential found none; apps that pick their own certificate, like IIS, aren't affected | Expected where no enterprise CA issues server certificates |
36876 | The certificate received from the remote server has not validated correctly; the text gives the error code | Partner certificate expired, untrusted or revoked | Decode the code (Windows codes table) |
36877 | The certificate received from the remote client application has not validated correctly | A connecting partner's certificate chains to a CA this server doesn't trust | Trusted root and intermediate stores |
36881 | The certificate received from the remote server has expired | The smart host's or partner's certificate lapsed | Ask the partner to renew it |
36882 | The certificate received from the remote server was issued by an untrusted certification authority | Private CA, a root missing on this server, or an incomplete chain | Their certificate with certutil -verify -urlfetch |
36883 | The certificate received from the remote server has been revoked | The CA revoked the partner's certificate | The partner must replace it |
36884 | The certificate received from the remote server does not contain the expected name | The connector targets a name or IP address that isn't on the partner's certificate | Get-SendConnector | fl SmartHosts,TlsAuthLevel,TlsDomain |
TLS alert codes in events 36887 and 36888#
The number in the event text is a TLS alert. Microsoft's table maps each alert (listed there as TLS1_ALERT_* and SSL3_ALERT_* constants) to the Schannel error Windows returns.
| Alert | Meaning | Usual cause | First check |
|---|---|---|---|
10 unexpected_message | An inappropriate message arrived (SEC_E_ILLEGAL_MESSAGE) | A device in the path interfering with the session | Firewall SMTP or TLS inspection |
20 bad_record_mac | A record failed its integrity check (SEC_E_MESSAGE_ALTERED) | Traffic altered or corrupted in transit | Devices that inspect or rewrite traffic |
40 handshake_failure | No acceptable set of security parameters (SEC_E_ILLEGAL_MESSAGE) | No protocol version or cipher suite in common | Enabled protocols and ciphers on both sides |
42 bad_certificate | The certificate was corrupt or its signature invalid (SEC_E_CERT_UNKNOWN) | Damaged certificate or badly built chain | The certificate and chain you present |
43 unsupported_certificate | The peer doesn't support this type of certificate (SEC_E_CERT_UNKNOWN) | A key type or signature algorithm the peer can't use | Key algorithm and size |
44 certificate_revoked | The certificate was revoked by its issuer (CRYPT_E_REVOKED) | A replaced certificate still bound somewhere | IIS bindings, SMTP services, connectors |
45 certificate_expired | The certificate expired or isn't valid yet (SEC_E_CERT_EXPIRED) | Expired certificate on a site or connector, or a wrong clock | NotAfter; w32tm /query /status |
46 certificate_unknown | Another certificate problem made it unacceptable (SEC_E_CERT_UNKNOWN) | Often trust or name problems as seen by the side that sent it | certutil -verify -urlfetch on your certificate |
48 unknown_ca | The issuing CA couldn't be found or trusted (SEC_E_UNTRUSTED_ROOT) | Missing intermediate on your server, a private CA or a self-signed certificate | Intermediates installed; complete chain |
49 access_denied | A valid certificate, but access control refused it (SEC_E_LOGON_DENIED) | The peer's policy doesn't accept this sender | The partner's TLS policy |
50 decode_error | A message couldn't be decoded (SEC_E_ILLEGAL_MESSAGE) | Malformed traffic, an old TLS stack or a middlebox | Devices in the path; client updates |
51 decrypt_error | A handshake cryptographic operation failed (SEC_E_DECRYPT_FAILURE) | Signature or key exchange problems | The certificate's key; devices in the path |
70 protocol_version | The offered protocol version isn't supported (SEC_E_UNSUPPORTED_FUNCTION) | One side only allows TLS versions the other has disabled | Health Checker; the client's TLS support |
71 insufficient_security | The peer needs stronger ciphers than offered (SEC_E_ALGORITHM_MISMATCH) | Weak cipher order or strong suites disabled | Cipher suites on both sides |
80 internal_error | An internal error unrelated to the peer (SEC_E_INTERNAL_ERROR) | A fault on the side that sent it | That side's own logs |
90 user_canceled | The handshake was canceled (SEC_E_UNFINISHED_CONTEXT_DELETED) | An application or probe closed the connection | Usually harmless |
Windows security and certificate error codes#
| Code | Message | Usual cause | First check |
|---|---|---|---|
0x80090331 SEC_E_ALGORITHM_MISMATCH | The client and server cannot communicate, because they do not possess a common algorithm. | TLS 1.2 not available to .NET or the OS on one side, or ciphers disabled | SystemDefaultTlsVersions and SchUseStrongCrypto (TLS guide) |
0x80090325 SEC_E_UNTRUSTED_ROOT | The certificate chain was issued by an authority that is not trusted. | Self-signed or private-CA certificate where a trusted one is expected, or a missing root | Issuer and chain of the certificate in use |
0x80090328 SEC_E_CERT_EXPIRED | The received certificate has expired. | Expired certificate on one side | NotAfter of the certificate presented |
0x80090326 SEC_E_ILLEGAL_MESSAGE | The message received was unexpected or badly formatted. | A failed handshake: alerts 10, 40, 50 and others map to it | The alert code in 36887 or 36888 |
0x80090327 SEC_E_CERT_UNKNOWN | An unknown error occurred while processing the certificate. | Alerts 42, 43 or 46: the peer rejected the certificate | certutil -verify -urlfetch |
0x8009030D SEC_E_UNKNOWN_CREDENTIALS | The credentials supplied to the package were not recognized | Schannel can't use the certificate, see event 36870 | certutil -verifystore My <thumbprint> |
0x8009030E SEC_E_NO_CREDENTIALS | No credentials are available in the security package | No usable certificate for the name or service | Certificate services and names (Event 12014 guide) |
0x80090302 SEC_E_UNSUPPORTED_FUNCTION | The function requested is not supported | Alert 70: protocol version mismatch | Enabled TLS versions on both sides |
0x80090016 NTE_BAD_KEYSET | Keyset does not exist | Private key deleted, unreadable or never imported | Reimport the PFX with its private key |
0x80092010 CRYPT_E_REVOKED | The certificate is revoked. | Revoked by the CA, often after a rekey | Bind the replacement everywhere |
0x80092012 CRYPT_E_NO_REVOCATION_CHECK | The revocation function was unable to check revocation for the certificate. | No reachable or valid revocation information | certutil -verify -urlfetch |
0x80092013 CRYPT_E_REVOCATION_OFFLINE | The revocation function was unable to check revocation because the revocation server was offline. | The server can't reach the CRL or OCSP address: proxy or firewall | netsh winhttp show proxy |
0x800B0109 CERT_E_UNTRUSTEDROOT | A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider. | Root CA missing from the Trusted Root store | Root store; root certificate updates |
0x800B010A CERT_E_CHAINING | A certificate chain could not be built to a trusted root authority. | Intermediate CA certificate missing | Install the CA's intermediates on every server |
0x800B0101 CERT_E_EXPIRED | A required certificate is not within its validity period (checked against the system clock) | Expired or not-yet-valid certificate, or a wrong clock | NotBefore and NotAfter; time sync |
0x800B010F CERT_E_CN_NO_MATCH | The certificate's CN name does not match the passed value. | Connecting by a name that isn't on the certificate | Certificate names vs the URL (name mismatch guide) |
0x8009310B CRYPT_E_ASN1_BADTAG | ASN1 bad tag value met. | The file isn't a certificate in the expected format or encoding | Download the issued certificate again |
Exchange certificate status errors#
| Message | Meaning | Usual cause | First check |
|---|---|---|---|
The certificate status could not be determined because the revocation check failed. | The EAC can't confirm the certificate isn't revoked | Exchange uses WinHTTP for HTTP traffic, and WinHTTP ignores the browser's proxy settings | netsh winhttp show proxy |
Status: RevocationCheckFailure | The same condition in Get-ExchangeCertificate | As above | Configure the WinHTTP proxy with a bypass list |
netsh winhttp show proxy
netsh winhttp set proxy proxy-server="http=proxy.contoso.com:8080" bypass-list="*.contoso.com"Watch out: Microsoft notes that if you set the WinHTTP proxy without the Exchange servers' names in the bypass list, the Exchange Management Shell and the EAC can't use Remote PowerShell.
Related errors with their own guides#
454 4.7.5and Event ID 12014 (no certificate for the connector FQDN): Event ID 12014 guide.TLS negotiation failed with error ...in the SMTP protocol log: SMTP protocol logging.- Outlook security alerts about certificate names: name mismatch guide; Autodiscover
GetLastErrorcertificate codes: Outlook error codes. - OWA and EAC sign-in loops from an expired OAuth certificate: expired Auth certificate.
How to verify a fix#
- Schannel: rerun the event count; the errors stop. If you also log informational events, event 36880 records each successful handshake with the protocol and cipher.
- Certificate:
Get-ExchangeCertificateshowsValid, a private key and the right services, andcertutil -verify -urlfetchreports no chain or revocation errors. - Mail flow and clients: the SMTP protocol log shows STARTTLS followed by a completed TLS session, and Health Checker shows no TLS warnings.
$cert = Get-Item Cert:\LocalMachine\My\<thumbprint>
Export-Certificate -Cert $cert -FilePath "$env:TEMP\exchange.cer" | Out-Null
certutil -verify -urlfetch "$env:TEMP\exchange.cer"
Get-ExchangeCertificate -Thumbprint <thumbprint> | Format-List Status, HasPrivateKey, Services, NotAfterGood: certutil ends with -verify command completed successfully, and the certificate status is Valid. Then set the Schannel EventLogging value back to 1.
References#
- Schannel Error Codes for TLS and SSL Alerts (Microsoft Learn)
- Schannel Events (Microsoft Learn, previous versions)
- COM Error Codes (Security and Setup) (Microsoft Learn): the SEC_E, CRYPT_E, CERT_E and NTE messages
- Certificate status could not be determined error (Microsoft Learn)

