Unable to send mail to local SMTP server with TLS - ssl

So we have a local exchange server that we use as a SMTP server for our internal servers. I'm trying to fix so it works with TLS but right now it's not.
I have a wildcard certificate assigned to both SMTP and the IIS roles. But When I try to use send-mailmessage through PowerShell for example I am getting.
Send-MailMessage -SmtpServer mail.domain.com -UseSsl -port 465 -From fromaddress
-To tomailaddress -Subject test -BodyAsHtml test -Encoding ([System.Text.Encoding]::UTF8)
The remote certificate is invalid according to the validation procedure.
The certificate used is valid for me when I go to the IIS site (or any other site that uses the same certificate).
So what am I doing wrong? Or how can I troubleshoot this.

Keep noted that the SSL certificate for SMTP couldn´t be managed with the IIS. You need to use powershell here (Enable-ExchangeCertificate) [more infos here].
Example:
Enable-ExchangeCertificate -Thumbprint
434AC224C8459924B26521298CE8834C514856AB -Services SMTP
In general you can troubleshoot such a TLS connection (over port 465) with Openssl (see here for a howto).
The URL also has also some solutions in place, so I will not copy them all here. In general the issues are:
The SSL certificate isn´t fully trusted on the remote machine (or in the keystore, which didn´t apply to your powershell test, but might apply to your 3rd party software)
There is still a self signed SSL certificate in use with SMTP
Conclusion:
I think you still use a self signed ssl certificate on the SMTP port and should change that with the command above.

I had the same issue. First I used the below code to save a copy of the certificate used by the SMTP server (since I didn't have access to the server itself / even if you do have access, this shows which server's being presented so may flag if it's different to what you'd expected). I ran this via LinqPad5 as a c# Program.
void Main()
{
System.Net.ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(OutputCertificateCallback);
using (System.Net.Mail.SmtpClient S = new System.Net.Mail.SmtpClient("smtprelay.subdomain.example.com"))
{
S.EnableSsl = true;
using (System.Net.Mail.MailMessage M = new System.Net.Mail.MailMessage("john.bevan#example.com", "john.bevan#example.com", "Test", "Test"))
{
try
{
S.Send(M);
Console.WriteLine("Sent");
}
catch (Exception e)
{
Console.WriteLine("Error {0}", e);
}
}
}
}
private bool OutputCertificateCallback(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
Console.WriteLine(certificate);
System.IO.File.WriteAllBytes(#"C:\Temp\cert.cer", certificate.Export(System.Security.Cryptography.X509Certificates.X509ContentType.Cert));
var c2 = new System.Security.Cryptography.X509Certificates.X509Certificate2(certificate);
return c2.Verify();
}
I was then able to inspect the certificate by viewing the saved file: C:\Temp\cert.cer
Check the expiry date to ensure the certificate is valid.
If it's not, that's the problem. Get a new certificate.
Check that the root certificate in my client's trusted CAs:
Start, Run, MMC
File > Add/Remove SnapIn > Certificates > Add > My User Account > Finish > OK
Certificates - Current User > Trusted Root Certificate Authorities > Certificates
Search for the root CA (i.e. the one with the same name as your certificate) in this list
If it's not you may need to install the root CA, the intermediary CA, or the certificate itself (which you can do using the file you just saved using the above script).
Check that the port is open on the firewall. You can test this via the below PowerShell:
try {
$tcp = New-Object -TypeName 'system.net.sockets.tcpclient' -ArgumentList 'smtprelay.subdomain.example.com', 465
if ($tcp.Connected) {':)'} else {':('}
} catch {
':('
$_.Exception.Message
}
In my case, all of the above weren't enough to spot the issue. Eventually I realised the service used a wildcard certificate (e.g. *.example.com), but the mail server was on a subdomain (e.g. smtprelay.subdomain.example.com) so wasn't covered by this certificate. Using a wildcard on the subdomain (e.g. *.subdomain.example.com) would have worked; though we just requested that the service be given an explicitly named certificate (for smtprelay.subdomain.example.com), including the service's individual nodes' FQDNs as Subject Alternative Names, in case we ever needed to target a specific node for debugging.

Related

How to create a self-signed cert for a local hosted wss:// server

I am attempting to create a windows service that opens a secure web socket and listens for connections on a certain port (wss://localhost:1234). The sample code includes referencing a certificate for the server to use:
var serverCertificate = new X509Certificate2(certificatePath, "mypassword");
This works fine.
The issue happens when the a client attempts to connected:
using (SslStream n = new SslStream(client.TcpClientInstance.GetStream()))
{
n.AuthenticateAsServer(serverCertificate, clientCertificateRequired: false, enabledSslProtocols: System.Security.Authentication.SslProtocols.Tls12, checkCertificateRevocation: false);
This line errors out with a generic "A call to SSPI failed, see inner exception", "InnerException = {"An unknown error occurred while processing the certificate"}":
This occurs with a self-signed cert registered in the Trusted Root Certificate Authorities.
Interestingly enough if I use the generic localhost cert generated by Visual Studio it works.
Any thoughts welcome.
TIA
Might solve your issue.
Create a sub-domain like sub-domain.domain.com ( using your actual domain )
With an A record to 127.0.0.1
Request a SSL certificate for sub-domain.domain.com
When you open a connection to sub-domain.domain.com it will be valid and should resolve to 127.0.0.1 which is where your "server" is listening.

How to get Remote server untrusted SSL certificate using Apache HTTP Client API

I have a remote server which may or may not be running using a valid SSL cert (using self-signed SSL cert).
We are making connection to remote server, which may fail if remote server is using self-signed SSL cert. So, we want to be able to download/view the remote server cert if our SSL handshake fails.
If I use Apache HTTP Client then I couldn't find a method which could allow me to view remote server certificate (you can do it with HttpsURLConnection but we are trying to avoid using it see this example).
I also looked into Spring RestTemplate, and it didn't provide any option either - I searched on Google and didn't find anything around Spring or Apache HTTP Client.
This should give you pretty much a complete control over the process of trust verification.
SSLContext sslContext = SSLContextBuilder.create()
.loadTrustMaterial((chain, authType) -> {
for (X509Certificate cert: chain) {
System.out.println(cert.getSubjectDN());
}
// Let the standard trust managers decide
// whether or not the cert chain is trusted
return false;
})
.build();
CloseableHttpClient client = HttpClientBuilder.create()
.setSSLContext(sslContext)
.build();

Cannot perform HTTPS local tunnel fith Fiddler2 - Problems with certificates

I am trying to create an HTTPS-tunnel on my machine. My intention is having all requests to https://localhost:8888/<something> (the port where Fiddler is listening to) be directed to https://myserver.net/<something>. I am using the following script as per Fiddler doc:
static function OnBeforeRequest(oSession: Session) {
// <Fiddler 2 preexisting code>
// HTTPS redirect -----------------------
if (oSession.HTTPMethodIs("CONNECT") &&
(oSession.PathAndQuery == "localhost:8888"))
{
oSession.PathAndQuery = "myserver.net:443";
}
if (oSession.HostnameIs("localhost"))
oSession.hostname = "myserver.net";
// --------------------------------------
// <Fiddler 2 preexisting code>
}
Also in Fiddler settings I checked the decryption check and installed certificates as you can see in the image below:
I restart Fiddler, it prompts me to install its fake certificates, I agree. I can see the certificate in my Windows Certificate System Repository when using certmgr. It is a self-signed certificate.
So What I do is opening a browser and type: https://localhost:8888/mypage.html, and what I get is an error. Internet Explorer reports this:
Error: Mismatched Address. The security certificate presented by this
website was issued for a different website's address. This problem
might indicate an attempt to fool you or intercept any data...
When I get certificate info (basically the certificate presented by the contacted host is being rejected, the same certificate can be displayed), I can see that the rejected certificate was issued by Fiddler and the subject is myserver.net.
So the certificate is ok because it is certifying myserver.net, I see that the problem is that probably my browser was expecting a certificate whose subject is localhost. Is it true?
How to handle this situation?
Assumption
I can understand that the problem is a certificate being issued for a website which I did not ask for. So the solution would be using a certificate certifying localhost:8888?
A certificate is valid if it is directly or indirectly (via intermediate certificates) signed by a trusted CA and if the hostname matches the certificate. If the last condition would not be enforced anybody with a valid certificate from a trusted CA could incorporate any other site.
To make use of fiddler and not run into this problem you should configure your browser to use fiddler as a web proxy and then use the real URL inside the browser instead of ip:port of fiddler.

SSL client authentication returning Bad Certificate error

I was trying to connect to my custom wrote ssl server written in CPP. It has got client authentication features. Its throwing error Bad certificate when watched through Wireshark. At the server side the error returned was
14560:error:140890B2:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:no certificate returned:s3_srvr.c:2619:
I used the following code to force requesting client certificate
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
SSL_CTX_set_verify_depth(ctx, 1);
I could see client returning certificate in Wireshark.
Which function should be used to set the public key used for verifying the client certificate at the server side?
From the error messages it looks like your client does not present a certificate to server and you explicitely requested that a client needs to present one (in server code):
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
What you probably need is to tell your client code to use certificate (along with a private key):
SSL_CTX_use_certificate_chain_file(ctx, pcszCertPath);
SSL_CTX_use_PrivateKey_file(ctx, pcszPrivKeyPath,SSL_FILETYPE_PEM);
I hope that helps.
Also make sure that your server uses the same certificate chain (that it trusts the same CA's). If this is a problem, let me know and I'll help you do that.
With wireshark, you will find out if the server ever requested certificate from the client. The command would be "CertificateRequest".
I was getting a similar error (only line number different):
140671281543104:error:140890B2:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:no certificate returned:s3_srvr.c:3292:
I had generated self-signed certificates using the procedure mentioned in https://help.ubuntu.com/community/OpenSSL.
After juggling with the error for one day, i found that the error was because the self-generated CA was not in the trust chain of the machine I was using.
To add the CA to the trust chain in RHEL-7, one can follow the below procedure:
To add a certificate in the simple PEM or DER file formats to the
list of CAs trusted on the system:
Copy it to the
/etc/pki/ca-trust/source/anchors/
subdirectory, and run the
update-ca-trust
command.
If your certificate is in the extended BEGIN TRUSTED file format,
then place it into the main source/ directory instead.
I think the above procedure can be followed for fedora too.
If "update-ca-trust" command is not available, it might be useful to explore the commands like "update-ca-certificates".
Hope this will be useful to someone.
Difference Between SSLCACertificateFile and SSLCertificateChainFile
SSLCertificateChainFile is generally the correct option to choose, as it has the least impact; it causes the listed file to be sent along with the certificate to any clients that connect.
Provide all the Root CA into the following file to resolve the issue
SSLCACertificateFile (hereafter "CACert") does everything SSLCertificateChainFile does (hereafter "Chain"), and additionally permits the use of the cert in question to sign client certificates. This sort of authentication is quite rare (at least for the moment), and if you aren't using it, there's IMHO no reason to augment its functionality by using CACert instead of Chain. On the flipside, one could argue that there's no harm in the additional functionality, and CACert covers all cases. Both arguments are valid.
Needless to say, if you ask the cert vendor, they'll always push for CACert over Chain, since it gives them another thing (client certs) that they can potentially sell you down the line. ;)
Modify the server code with the below line:
Server code:
SSL_CTX_load_verify_locations(ctx,"client_certificate_ca.pem", NULL);
Here client_certificate_ca.pem is the file which generated the client certificates.
verify your client certificate with the "client_certificate_ca.pem" file using below command.
verify -CAfile client_certificate_ca.pem client_certificate.pem
Client code:
if (SSL_CTX_use_certificate_file(ctx, "client_certificate.pem", SSL_FILETYPE_PEM) <= 0)
{
}
if (SSL_CTX_use_PrivateKey_file(ctx, "client_certificate.ky", SSL_FILETYPE_PEM) <= 0)
{
}
if (!SSL_CTX_check_private_key(ctx))
{
}

How to generate an SSL client certificate from a disconnected network?

I have a unique situation where I need to implement client certificate authentication over HTTPS between IE browser and IIS 6. The browser and IIS are separated by a firewall that only allows the browser to connect to IIS on the SSL port.
We have an internal certificate server on the same network as IIS. I've generated an SSL server cert for IIS and that is installed. I configured IIS to only allow SSL, require client certificates.
The limitation here is the browser machine is on a disconnected network, so I can't go to the CA's http://caserver/CertSrv URL and request a client cert like you normally would.
I figured if there were a way that I could generate a CSR against the Root CA's public key, I can copy it to the CA server to generate the client cert. But, there appears to be no provision in IE or the Certificates MMC to do this. The Certificates MMC seems to require a direct connection to the CA.
Has anyone solved this before?
FYI, All servers referenced run Windows Server 2003.
Update: Thanks to Jonas Oberschweiber and Mark Sutton for pointing out the CertReq.exe command line tool. Using this, I've generated a CSR, and consequently a client certificate that installs successfully. However, IE is apparently not sending this client cert when accessing the IIS server in question; it still generates a 403.7 "Forbidden: SSL client certificate is required." I suspect that the reason is that the Subject field of the client cert does not match the user id of the account running IE, thus perhaps not sending a mismatching client cert. The Subject matches that of the user I used to submit the CSR and generate the client cert on the other end of the firewall.
Does the Subject field matter? Is there something else I need to do to enable IE to send this cert?
Use the certreq command on your client as follows
certreq -new -f filein c:\certrequest.req
Here is and example of the filein
[Version]
Signature="$Windows NT$"
[NewRequest]
Subject="CN=dc1.extranet.frbrikam.com"
EncipherOnly = False
Exportable = False
KeyLength = 1024
KeySpec = 1
KeyUsage = 0xA0
MachineKeySet = True
ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
ProviderType = 12
RequestType = CMC
[RequestAttributes]
CertificateTemplate=TLSServer
Replace the CertificateTemplate with the name of your certificate template
Once you have your request file you need to take it to the certificate authority on a usb stick and use the web enrolment interface as usual to process the request file.
Take the output certificate back to the client open it and click install.
You sound like you have already tried a couple of things so my guess is that you are already aware of these, but I'm going to post them anyway, just in case: Certificate Command Line Tools. I am not sure, however, if they do what you want.
Go the http://caserver/CertSrv site that you mentioned using a 3rd computer that can see the CA server. Select the 3rd option, download a CA cert, cert chai, or CRL. On the next page select 'Download CA Certificate Chain', which will download the p7b file. Using a flash drive (or email, etc) transfer this to the other computer which will allow you to import it into the trusted root servers in IE.
http://technet.microsoft.com/en-us/library/cc787796.aspx
Suggestiong for the update, just in case - what is the trusted cert list of in the server?
Subject DN being the same as Windows username has never been a problem for me - although I don't use IIS much. However, somewhere in IIS there is sure to be a trusted certificate list. This error sounds to me like the server's trusted certs list does not include the CA or Root CA that issued the client certificate.
This is particularly true if you never get a certificate selection popup window in IE when you hit the IIS server - even though you have a certificate configured in your IE cert store. That means that the client hit the server, the server gave a list of trusted certs and the client didn't have a cert that fit the list. So the SSL session went to the Forbidden error state.
If the certificate selection window popped up, and you selected and sent the cert, there may be other configuration problems on the server side..