I am building some .net code that will run unattended on a scheduled basis on one of our servers. Part of its execution requires that it execute a Java executable which talks to some web resources and services over SSL.
Java will absolutely throw a fit if it's doing something over SSL and it doesn't have every single certificate in the remote certificate's chain. So in .NET I need to be able to specify an https:// resource and need it to download the entire remote chain locally.
Why do I want to do this automatically? Because I want to make deployment simple and to not have to do this 4 times, and then again every time one of Oracle's certificates expires.
I am doing this:
X509Certificate2 lowestCert = SecurityHelper.DownloadSslCertificate(keyDownloadLocation);
X509Chain chain = new X509Chain();
chain.Build(lowestCert);
int counter = 0;
foreach (X509ChainElement el in chain.ChainElements) {
//Extract certificate
X509Certificate2 chainCert = el.Certificate;
//Come up with an alias and save location
string alias = certBaseName + counter;
string localLocation = Path.GetDirectoryName(
System.Reflection.Assembly.GetEntryAssembly().Location
) + #"\" +alias + ".cer";
//Save it locally
SecurityHelper.SaveCertificateToFile(chainCert, localLocation);
//Remove (if possible) and add to java's keystore
deleteKeyFromKeystore(
javaFolder,
alias,
certKeyStore,
SecurityHelper.GetEncryptedAppSetting("JavaKeystorePassword")
);
addKeytoKeyStore(
localLocation,
javaFolder,
alias,
certKeyStore,
SecurityHelper.GetEncryptedAppSetting("JavaKeystorePassword")
);
//Then delete
if (File.Exists(localLocation)) File.Delete(localLocation);
counter++;
}
SecurityHelper.DownloadSslCertificate is a custom method that creates an X509Certificate2 from a a website url.
cert is the lowest-level certificate and I can get that back, but what I can't do is get everything in the chain. chain.ChainElements is still only 1 level deep if the rest of the chain isn't already installed on the machine. My problem is that this is a brand new URL with totally unknown (to the machine) certificates, and I want to go up the stack and recursively download every one.
Is there a way to hit a URL unknown to the machine and programmatically downloading every certificate in the chain?
Edit: here is how I'm downloading the SSL Certificate:
public static X509Certificate2 DownloadSslCertificate(string strDNSEntry)
{
X509Certificate2 cert = null;
using (TcpClient client = new TcpClient())
{
client.Connect(strDNSEntry, 443);
SslStream ssl = new SslStream(client.GetStream(), false,
new RemoteCertificateValidationCallback(
(sender, certificate, chain, sslPolicyErrors) => {
return true;
}
), null);
try
{
ssl.AuthenticateAsClient(strDNSEntry);
}
catch (AuthenticationException e)
{
ssl.Close();
client.Close();
return cert;
}
catch (Exception e)
{
ssl.Close();
client.Close();
return cert;
}
cert = new X509Certificate2(ssl.RemoteCertificate);
ssl.Close();
client.Close();
return cert;
}
}
Edit #2 The solution to obtain remote SSL certificates that worked for me was to capture the chain in the validation callback that occurs during an SslStream.AuthenticateAsClient(url);
private static X509ChainElementCollection callbackChainElements = null;
private static bool CertificateValidationCallback(
Object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
callbackChainElements = chain.ChainElements;
return true;
}
And then to kickoff the validation with this callback, I had something like:
public static X509Certificate2 DownloadSslCertificate(string url)
{
X509Certificate2 cert = null;
using (TcpClient client = new TcpClient())
{
client.Connect(url, 443);
SslStream ssl = new SslStream(client.GetStream(), false, CertificateValidationCallback, null);
try
{
ssl.AuthenticateAsClient(url); //will call CertificateValidationCallback
}
... etc
Then by the time AuthenticateAsClient has finished, the local callbackChainElements variable is set to a collection of X509ChainElement.
Actually I did something very similar to this using Powershell.
One piece is changing ServicePointManager to ignore SSL validation (since you may not have the root certs locally, it sounds). It's ServerCertificateValidationCallback that you want to set to true, which tells it you're using a custom validation (or in your case, perhaps no validation).
From there, you're most of the way there with your chainelements. You can use the Export method to export the cert into a byte array:
foreach (X509ChainElement el in chain.ChainElements) {
var certToCreate = el.Certificate.Export(X509ContentType.Cert);
...
}
From there it's just a matter of doing what you want with the byte array. You can pass it to a constructor for another certificate object, which you can then import into the Java keystore.
Hope that helps, please let me know if you have further questions.
EDIT: Actually, I just noticed this post on tips working with X.509 certificates in .NET . The author recommends against writing directly from a byte array because it writes the temp file to a directory that doesn't get cleaned up. So his suggestion is to write the byte array to disk, and then manually clean it up when you're done.
Related
I know there are a lot of questions on this argument but I'm stuck into this for several days now so here I am.
I've got a root certificate and a client certificate. I need to replicate in a C# web API project what the command openssl verify -CAfile ca.pem client.pem does.
This is what i know for now (hope it's actually true):
Verify() method actually validate that the certificate is signed by an authority. It's like a format control. It doesn't matter which autority signed the certificate.
X509 Chain is the way to go. Add your ca certificate inside an extra store because i'm not going to install the certificate into Windows. Then build passing the client certificate. Let's the magic happens! Unfortunately I've got some problems with the configuration.
Let me be more clear with an example
private bool VerifyCertificate(X509Certificate2 client)
{
X509Chain chain = new X509Chain();
var stringCert = WebConfigurationManager.AppSettings["CACertificate"];
var byteCert = Encoding.ASCII.GetBytes(stringCert);
var authority = new X509Certificate2(byteCert);
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreWrongUsage;
chain.ChainPolicy.ExtraStore.Add(authority);
// Do the preliminary validation.
if (!chain.Build(client))
return false;
return true;
}
With this example the program returns false. The build is not passed. I'm sure the problem is with the ChainPolicy properties so i tried a different configuration
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
chain.ChainPolicy.VerificationTime = DateTime.Now;
chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 0, 0);
But this one is not going to verify anything, in fact using my ca cert the method returns true and using another ca certificate (which i didn't use to signed my client certificate) the method also returns true.
I Searched for an OpenSSL wrapper for C# and i found it but unfortunately is based on old libraries and the repo isn't manteined anymore. Also, i would achieve my goal using just .net framework if possible.
So guys, fast recap. I want to check that only the certificate that is firmed by my ca certificate can pass the validation, all the others must be stopped.
Thanks in advance for any help
ExtraStore isn't limiting, it provides extra certificates to help complete the chain. It provides no trust data.
In order to determine if the certificate is issued by the CA that you want you need to do something like:
private static readonly X509Certificate2 s_trustedRoot = ObtainTheRoot();
private static readonly byte[] s_normalizedRoot = s_trustedRoot.RawData;
private bool VerifyCertificate(X509Certificate2 candidate)
{
X509Chain chain = new X509Chain();
// set all the things you need to set to make it build
if (!chain.Build(candidate))
return false;
// Check that the root certificate was the expected one.
X509ChainElementCollection elements = chain.ChainElements;
return elements[elements.Count - 1].Certificate.RawData.SequenceEqual(s_normalizedRoot);
}
I promoted the cert and normalized byte form of it to statics on the assumption that they don't change once the process starts. If the cert can change dynamically then you should adjust accordingly.
Find the problem.
In my case this was the right response.
private bool VerifyCertificate(X509Certificate2 client)
{
X509Chain chain = new X509Chain();
var stringCert = WebConfigurationManager.AppSettings["CACertificate"];
var byteCert = Encoding.ASCII.GetBytes(stringCert);
var authority = new X509Certificate2(byteCert);
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
chain.ChainPolicy.ExtraStore.Add(authority);
// Do the preliminary validation.
if (!chain.Build(client))
return false;
// This piece makes sure it actually matches your known root
var valid = chain.ChainElements
.Cast<X509ChainElement>()
.Any(x => x.Certificate.Thumbprint == authority.Thumbprint);
if (!valid)
return false;
return true;
}
Now, debugging the application i have some considerations:
What the .Build() method is supposed to do?
I mean, using the certificate that is signed by the ca or using the self-signed certificate the method Always returns true BUT if i use the trusted one it will add the certificate inside the chain.ChainElementsotherwise nothing is added.
I need to understand this thing but all the test I've done said the method works
Using Visual Studio 2005 - C# 2.0, System.Net.WebClient.UploadData(Uri address, byte[] data) Windows Server 2003
So here's a stripped down version of the code:
static string SO_method(String fullRequestString)
{
string theUriStringToUse = #"https://10.10.10.10:443"; // populated with real endpoint IP:port
string proxyAddressAndPort = #"http://10.10.10.10:80/"; // populated with a real proxy IP:port
Byte[] utf8EncodedResponse; // for the return data in utf8
string responseString; // for the return data in utf16
WebClient myWebClient = new WebClient(); // instantiate a web client
WebProxy proxyObject = new WebProxy(proxyAddressAndPort, true);// instantiate & popuylate a web proxy
myWebClient.Proxy = proxyObject; // add the proxy to the client
myWebClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded"); // stick some stuff in the header
UTF8Encoding utf8Encoding = new UTF8Encoding(false);// create a utf8 encoding
Byte[] utf8EncodedRequest = HttpUtility.UrlEncodeToBytes(fullRequestString, utf8Encoding); // convert the request data to a utf8 byte array
try
{
utf8EncodedResponse = myWebClient.UploadData(theUriStringToUse, "POST", utf8EncodedRequest); // pass the utf8-encoded byte array
responseString = utf8Encoding.GetString(utf8EncodedResponse); // get a useable string out of the response
}
catch (Exception e)
{
// some other error handling
responseString = "<CommError><![CDATA[" + e.ToString() + "]]></CommError>";// show the basics of the problem
}
return responseString;// return whatever ya got
}
This is the error I get:
The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.
I don't have much control to see what's happening when the request goes out. I'm told that it's reaching the correct destination and there's a "certificate error". This is supposedly because there's a literal mismatch between the IP address in my request and the URL it resolves to. I have more than one IP I'm supposed to round-robin to so specifying the URL won't work. I'm not attaching a certificate - nor am I supposed to according to the endpoint owners. Per "them" the certificate error is 'normal and I am supposed to ignore it.
The cert in question is supposedly one of the many verisign certs that is "just there" on our server. The examples I've seen for ignoring cert errors all seem to imply that the requestor is attaching a specific x509 certificate (which I'm not).
I looked over .net WebService, bypass ssl validation! which kinda-sorta describes my problem - except it also kinda-sorta doesn't because I don't know which certificate (if any) I should reference.
Is there a way for me to ignore the error without actually knowing/caring what certificate is causing the problem?
and please - kid gloves, small words, and "for dummies" code as I'm not exactly a heavy hitter.
This traffic is over a private line - so my understanding is that ignoring the cert error is not as big a deal as if it were open internet traffic.
The SSL certificate is for a machine to establish a trust relationship. If you type in one IP address, and end up talking to another, that sounds the same as a DNS hijack security fault, the kind of thing SSL is intending to help you avoid - and perhaps something you don't want to put up with from "them".
If you may end up talking to more than machine (ideally they would make it appear as one for you), you will need a certificate for each of the possible machines to initiate trust.
To ignore trust (I've only ever had to do this temporarily in development scenarios) the following snippet may work for you, but I strongly recommend you consider the impact of ignoring trust before using it:
public static void InitiateSSLTrust()
{
try
{
//Change SSL checks so that all checks pass
ServicePointManager.ServerCertificateValidationCallback =
new RemoteCertificateValidationCallback(
delegate
{ return true; }
);
}
catch (Exception ex)
{
ActivityLog.InsertSyncActivity(ex);
}
}
I realize this is an old post, but I just wanted to show that there is a more short-hand way of doing this (with .NET 3.5+ and later).
Maybe it's just my OCD, but I wanted to minimize this code as much as possible. This seems to be the shortest way to do it, but I've also listed some longer equivalents below:
// 79 Characters (72 without spaces)
ServicePointManager.ServerCertificateValidationCallback = (a, b, c, d) => true;
Shortest way in .NET 2.0 (which is what the question was specifically asking about)
// 84 Characters
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
It's unfortunate that the lambda way requires you to define the parameters, otherwise it could be even shorter.
And in case you need a much longer way, here are some additional alternatives:
ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, errors) => true;
ServicePointManager.ServerCertificateValidationCallback = delegate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) { return true; };
// 255 characters - lots of code!
ServicePointManager.ServerCertificateValidationCallback =
new RemoteCertificateValidationCallback(
delegate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
{
return true;
});
This is somewhat the code we're using (not polished yet - I don't think I have the error-handling setup correctly but it should be close) based on thomas's suggestion (this is .NET 4.0 code, though):
var sslFailureCallback = new RemoteCertificateValidationCallback(delegate { return true; });
try
{
if (ignoreSslErrors)
{
ServicePointManager.ServerCertificateValidationCallback += sslFailureCallback;
}
response = webClient.UploadData(Options.Address, "POST", Encoding.ASCII.GetBytes(Options.PostData));
}
catch (Exception err)
{
PageSource = "POST Failed:\r\n\r\n" + err;
return PageSource;
}
finally
{
if (ignoreSslErrors)
{
ServicePointManager.ServerCertificateValidationCallback -= sslFailureCallback;
}
}
This code is much broader than you might expect. It is process-wide. The process might be the exe, IIS on this machine, or even DLLHost.exe. After calling it, have a finally block that restores things to normal by removing the delegate that always returns true.
I wanted to disable SSL verification for a specific domain without globally deactivating it because there might be other requests running which should not be affected, so I came up with this solution (please note that uri is a variable inside a class:
private byte[] UploadValues(string method, NameValueCollection data)
{
var client = new WebClient();
try
{
ServicePointManager.ServerCertificateValidationCallback +=
ServerCertificateValidation;
returnrclient.UploadValues(uri, method, parameters);
}
finally
{
ServicePointManager.ServerCertificateValidationCallback -=
ServerCertificateValidation;
}
}
private bool ServerCertificateValidation(object sender,
X509Certificate certificate, X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
var request = sender as HttpWebRequest;
if (request != null && request.Address.Host.Equals(
this.uri.Host, StringComparison.OrdinalIgnoreCase))
return true;
return false;
}
Here is the VB.net code to make WebClient ignore the SSL cert.
Net.ServicePointManager.ServerCertificateValidationCallback = New Net.Security.RemoteCertificateValidationCallback(Function() True)
I'm reading through Support Certificates In Your Applications With The .NET Framework 2.0 trying to determine how to set a CA for a SSL connection.
Around half-way down the article under Validating Certificates, MSDN presents some code:
static void ValidateCert(X509Certificate2 cert)
{
X509Chain chain = new X509Chain();
// check entire chain for revocation
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
// check online and offline revocation lists
chain.ChainPolicy.RevocationMode = X509RevocationMode.Online |
X509RevocationMode.Offline;
// timeout for online revocation list
chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 0, 30);
// no exceptions, check all properties
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
// modify time of verification
chain.ChainPolicy.VerificationTime = new DateTime(1999, 1, 1);
chain.Build(cert);
if (chain.ChainStatus.Length != 0)
Console.WriteLine(chain.ChainStatus[0].Status);
}
Then later:
// override default certificate policy
ServicePointManager.ServerCertificateValidationCallback =
new RemoteCertificateValidationCallback(VerifyServerCertificate);
I feel like I'm missing something really obvious. For example, I don't want a callback - I just want to say, "establish a SSL connection, and here's the one CA to trust". But I don't see that in the code above.
X509Chain does not appear to have an add method to add a CA or root of trust. Shouldn't the CA be set before the callback? But I don't see that in the code above.
In Java, it would be done with a TrustManager (or TrustManagerFactory) after loading the particular CA you want to use (for an example, see Use PEM Encoded CA Cert on filesystem directly for HTTPS request?).
Question: How does one set a CA to use for an SSL connection in .Net or C#?
The following code will avoid the Windows certificate stores and validate the chain with a CA on the filesystem.
The name of the function does not matter. Below, VerifyServerCertificate is the same callback as RemoteCertificateValidationCallback in SslStream class. It can also be used for the ServerCertificateValidationCallback in ServicePointManager.
static bool VerifyServerCertificate(object sender, X509Certificate certificate,
X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
try
{
String CA_FILE = "ca-cert.der";
X509Certificate2 ca = new X509Certificate2(CA_FILE);
X509Chain chain2 = new X509Chain();
chain2.ChainPolicy.ExtraStore.Add(ca);
// Check all properties
chain2.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
// This setup does not have revocation information
chain2.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
// Build the chain
chain2.Build(new X509Certificate2(certificate));
// Are there any failures from building the chain?
if (chain2.ChainStatus.Length == 0)
return false;
// If there is a status, verify the status is NoError
bool result = chain2.ChainStatus[0].Status == X509ChainStatusFlags.NoError;
Debug.Assert(result == true);
return result;
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
return false;
}
I have not figured out how to use this chain (chain2 below) by default such that there's no need for the callback. That is, install it on the ssl socket and the connection will "just work". And I have not figured out how install it such that its passed into the callback. That is, I have to build the chain for each invocation of the callback. I think these are architectural defects in .Net, but I might be missing something obvious.
I'm having trouble establishing a connection to a PostgreSQL database that is configured only to accept a valid SSL certificate. I can connect using pgAdmin III with the appropriate certificate and key, but I cannot get it working with Npgsql. When I try to open a connection to the database, I get System.IO.IOException: The authentication or decryption has failed. Here is my code:
NpgsqlConnectionStringBuilder csb = new NpgsqlConnectionStringBuilder();
csb.Database = "database";
csb.Host = "psql-server";
csb.UserName = "dreamlax"; // must match Common Name of client certificate
csb.SSL = true;
csb.SslMode = SslMode.Require;
NpgsqlConnection conn = new NpgsqlConnection(csb.ConnectionString);
conn.ProvideClientCertificatesCallback += new ProvideClientCertificatesCallback(Database_ProvideClientCertificatesCallback);
conn.CertificateSelectionCallback += new CertificateSelectionCallback(Database_CertificateSelectionCallback);
conn.CertificateValidationCallback += new CertificateValidationCallback(Database_CertificateValidationCallback);
conn.PrivateKeySelectionCallback += new PrivateKeySelectionCallback(Database_PrivateKeySelectionCallback);
conn.Open(); //System.IO.IOException: The authentication or decryption has failed
The callbacks are defined like this:
static void Database_ProvideClientCertificates(X509CertificateCollection clienteCertis)
{
X509Certificate2 cert = new X509Certificate2("mycert.pfx", "passphrase");
clienteCertis.Add(cert);
}
static X509Certificate Database_CertificateSelectionCallback(X509CertificateCollection clientCerts, X509Certificate serverCert, string host, X509CertificateCollection serverRequestedCerts)
{
return clienteCertis[0];
}
static AsymmetricAlgorithm Database_PrivateKeySelectionCallback(X509Certificate cert, string host)
{
X509Cerficate2 thisCert = cert as X509Certificate2;
if (cert != null)
return cert.PrivateKey;
else
return null;
}
static bool MyCertificateValidationCallback(X509Certificate certificate, int[] certificateErrors)
{
// forego server validation for now
return true;
}
I set breakpoints confirming that each callback was returning something valid, but still the IOException is thrown.
I fixed this problem by modifying the Npgsql source. Rather than using Mono's Mono.Security.Protocol.Tls.SslClientStream, I changed it to use System.Net.Security.SslStream instead. These were the steps I took:
Modify NpgsqlClosedState.cs:
Remove the using Mono.Security.Protocol.Tls; directive and the one below it.
Add a using System.Net.Security; directive.
In the NpgsqlClosedState.Open() method, where it says if (response == 'S'), changed it to:
if (response == 'S')
{
//create empty collection
X509CertificateCollection clientCertificates = new X509CertificateCollection();
//trigger the callback to fetch some certificates
context.DefaultProvideClientCertificatesCallback(clientCertificates);
// Create SslStream, wrapping around NpgsqlStream
SslStream sstream = new SslStream(stream, true, delegate(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors errors)
{
// Create callback to validate server cert here
return true;
});
sstream.AuthenticateAsClient(context.Host, clientCertificates, System.Security.Authentication.SslProtocols.Default, false);
stream = sstream;
}
Modify NpgsqlConnection.cs, remove the using Mono... directives. This will cause a number of errors regarding missing types, in particular, these will be regarding 3 sets of delegate/event combos that use Mono types. The errors will always appear in groups of three because these callbacks all tie in with Mono's SslClientStream class. Remove each group of three and replace it with a single ValidateServerCertificate delegate/event. This single event should be used in the constructor for the SslStream class that was used in step 1.3 above.
The changes to NpgsqlConnection.cs will trigger more errors in other files NpgsqlConnector.cs, NpgsqlConnectorPool.cs etc. but the fix is the same, replace the 3 Mono-based callbacks with the new ValidateServerCertificate.
Once all that is done, Npgsql can be used without Mono components and with (for me) working SSL certificate authentication.
My pull request on github can be found here.
Using Visual Studio 2005 - C# 2.0, System.Net.WebClient.UploadData(Uri address, byte[] data) Windows Server 2003
So here's a stripped down version of the code:
static string SO_method(String fullRequestString)
{
string theUriStringToUse = #"https://10.10.10.10:443"; // populated with real endpoint IP:port
string proxyAddressAndPort = #"http://10.10.10.10:80/"; // populated with a real proxy IP:port
Byte[] utf8EncodedResponse; // for the return data in utf8
string responseString; // for the return data in utf16
WebClient myWebClient = new WebClient(); // instantiate a web client
WebProxy proxyObject = new WebProxy(proxyAddressAndPort, true);// instantiate & popuylate a web proxy
myWebClient.Proxy = proxyObject; // add the proxy to the client
myWebClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded"); // stick some stuff in the header
UTF8Encoding utf8Encoding = new UTF8Encoding(false);// create a utf8 encoding
Byte[] utf8EncodedRequest = HttpUtility.UrlEncodeToBytes(fullRequestString, utf8Encoding); // convert the request data to a utf8 byte array
try
{
utf8EncodedResponse = myWebClient.UploadData(theUriStringToUse, "POST", utf8EncodedRequest); // pass the utf8-encoded byte array
responseString = utf8Encoding.GetString(utf8EncodedResponse); // get a useable string out of the response
}
catch (Exception e)
{
// some other error handling
responseString = "<CommError><![CDATA[" + e.ToString() + "]]></CommError>";// show the basics of the problem
}
return responseString;// return whatever ya got
}
This is the error I get:
The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.
I don't have much control to see what's happening when the request goes out. I'm told that it's reaching the correct destination and there's a "certificate error". This is supposedly because there's a literal mismatch between the IP address in my request and the URL it resolves to. I have more than one IP I'm supposed to round-robin to so specifying the URL won't work. I'm not attaching a certificate - nor am I supposed to according to the endpoint owners. Per "them" the certificate error is 'normal and I am supposed to ignore it.
The cert in question is supposedly one of the many verisign certs that is "just there" on our server. The examples I've seen for ignoring cert errors all seem to imply that the requestor is attaching a specific x509 certificate (which I'm not).
I looked over .net WebService, bypass ssl validation! which kinda-sorta describes my problem - except it also kinda-sorta doesn't because I don't know which certificate (if any) I should reference.
Is there a way for me to ignore the error without actually knowing/caring what certificate is causing the problem?
and please - kid gloves, small words, and "for dummies" code as I'm not exactly a heavy hitter.
This traffic is over a private line - so my understanding is that ignoring the cert error is not as big a deal as if it were open internet traffic.
The SSL certificate is for a machine to establish a trust relationship. If you type in one IP address, and end up talking to another, that sounds the same as a DNS hijack security fault, the kind of thing SSL is intending to help you avoid - and perhaps something you don't want to put up with from "them".
If you may end up talking to more than machine (ideally they would make it appear as one for you), you will need a certificate for each of the possible machines to initiate trust.
To ignore trust (I've only ever had to do this temporarily in development scenarios) the following snippet may work for you, but I strongly recommend you consider the impact of ignoring trust before using it:
public static void InitiateSSLTrust()
{
try
{
//Change SSL checks so that all checks pass
ServicePointManager.ServerCertificateValidationCallback =
new RemoteCertificateValidationCallback(
delegate
{ return true; }
);
}
catch (Exception ex)
{
ActivityLog.InsertSyncActivity(ex);
}
}
I realize this is an old post, but I just wanted to show that there is a more short-hand way of doing this (with .NET 3.5+ and later).
Maybe it's just my OCD, but I wanted to minimize this code as much as possible. This seems to be the shortest way to do it, but I've also listed some longer equivalents below:
// 79 Characters (72 without spaces)
ServicePointManager.ServerCertificateValidationCallback = (a, b, c, d) => true;
Shortest way in .NET 2.0 (which is what the question was specifically asking about)
// 84 Characters
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
It's unfortunate that the lambda way requires you to define the parameters, otherwise it could be even shorter.
And in case you need a much longer way, here are some additional alternatives:
ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, errors) => true;
ServicePointManager.ServerCertificateValidationCallback = delegate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) { return true; };
// 255 characters - lots of code!
ServicePointManager.ServerCertificateValidationCallback =
new RemoteCertificateValidationCallback(
delegate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
{
return true;
});
This is somewhat the code we're using (not polished yet - I don't think I have the error-handling setup correctly but it should be close) based on thomas's suggestion (this is .NET 4.0 code, though):
var sslFailureCallback = new RemoteCertificateValidationCallback(delegate { return true; });
try
{
if (ignoreSslErrors)
{
ServicePointManager.ServerCertificateValidationCallback += sslFailureCallback;
}
response = webClient.UploadData(Options.Address, "POST", Encoding.ASCII.GetBytes(Options.PostData));
}
catch (Exception err)
{
PageSource = "POST Failed:\r\n\r\n" + err;
return PageSource;
}
finally
{
if (ignoreSslErrors)
{
ServicePointManager.ServerCertificateValidationCallback -= sslFailureCallback;
}
}
This code is much broader than you might expect. It is process-wide. The process might be the exe, IIS on this machine, or even DLLHost.exe. After calling it, have a finally block that restores things to normal by removing the delegate that always returns true.
I wanted to disable SSL verification for a specific domain without globally deactivating it because there might be other requests running which should not be affected, so I came up with this solution (please note that uri is a variable inside a class:
private byte[] UploadValues(string method, NameValueCollection data)
{
var client = new WebClient();
try
{
ServicePointManager.ServerCertificateValidationCallback +=
ServerCertificateValidation;
returnrclient.UploadValues(uri, method, parameters);
}
finally
{
ServicePointManager.ServerCertificateValidationCallback -=
ServerCertificateValidation;
}
}
private bool ServerCertificateValidation(object sender,
X509Certificate certificate, X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
var request = sender as HttpWebRequest;
if (request != null && request.Address.Host.Equals(
this.uri.Host, StringComparison.OrdinalIgnoreCase))
return true;
return false;
}
Here is the VB.net code to make WebClient ignore the SSL cert.
Net.ServicePointManager.ServerCertificateValidationCallback = New Net.Security.RemoteCertificateValidationCallback(Function() True)