The issue I ran into was the internal server was using SSL. Fortunately this was an easy fix which I'll detail here. This was a two part fix:
- Export the SSL certificate from the internal server for use in the code.
- Modify my code to use that certificate.
Export the Certificate
So, the first thing to do is export the SSL certificate from the server that you will be calling into. In my case, this is the internal server. Log into that machine and run the MMC console.
Next click File -> Add/Remove Snap-in.
In the next window click "Certificates" from the list and then click "Add". This will bring up another window, from here select "Computer Account" and click "Next".
In the next window choose "Local Computer" and click "Finish", then click "OK".
Now you will have a list of folders under the "Certificates" header. You will most likely find your certificate under "Personal". Hopefully when the certificate was installed a proper Friendly Name was given to it. In my case it was the same as the SharePoint web application name with port number. It was easy to find!
Right click the certificate and choose All Tasks -> Export.
Now you have a copy of the certificate you can use when calling the internal server. In my case, I copied this over to the external server to use in my calls.
Once we have our certificate exported copy it over to the external server and place it anywhere you like. In my case I put it on the D: in its own folder. Now for the code:
using (ClientContext context = new ClientContext("SERVERURL"))
ServicePointManager .ServerCertificateValidationCallback = delegate (object sender1, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
bool validationResult = true ;
context.ExecutingWebRequest += new EventHandler <WebRequestEventArgs >(context_ExecutingWebRequest);
Web web = context.Web;
List list = context.Web.Lists.GetByTitle("LISTNAME");
context.Credentials = new NetworkCredential ("USERNAME", "PASSWORD", "DOMAIN");
This code is a stripped down example of using the ClientContext class. As you can see there is no code after the ExecuteQuery method, nor do I perform any loads. This example isn't meant to show case using the ClientContext, only how to use a certificate with the call. Here are the important parts:
- ServicePointManager.ServerCertificateValidationCallback - this method simply validates the certificate to ensure its being used on the same domain. In my case it was not, so I simply return a true, since I know it will fail but I want to call across domains.
- context.ExecutingWebRequest - this sets a delegate method (below) that will actually apply the cert.
- context.Credentials - since I'm calling a different server, I need to specify credentials for a valid account. In my case we set up an account in AD that would be used to authenticate to SharePoint. That account was then given strict permissions in SharePoint, basically it could only add new items to one specific list in the environment.
static void context_ExecutingWebRequest(object sender, WebRequestEventArgs e)
HttpWebRequest webReq = e.WebRequestExecutor.WebRequest;
//webReq.Proxy = new WebProxy("http://[ProxyAddress]"); //Specify a proxy address if you need to
X509Certificate cert = X509Certificate.CreateFromCertFile("D:\yourcert.cer");
The ExecutingWebRequest method simply creates an HttpWebRequest and adds the certificate to it. As you can see it just grabs the cert from the D: where it is stored.
And thats it! Using this method I was able to use the client object model to connect from an external server to an internal server and add list items.