How to use X509Certificate2 as the credentials for a SharePointService request - sharepoint

I have a MOSS 2007 farm that requires a client certificate to access. I have written several methods that use the built in SharePoint services to retrieve data from the site. However, outside of my local test environment everything requires a client cert to access the services.
My method for retrieving the cert is:
private static X509Certificate2 GetCertCreds()
{
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
try
{
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certs = store.Certificates.Find(X509FindType.FindBySubjectKeyIdentifier, "SiteIdentityCertificateSerialNumber", true);
Assert.IsNotNull(certs);
return certs[0];
}
finally
{
store.Close();
}
}
Then in use I have to have something LIKE:
using (ListsServiceProxy.Lists service = new ListsServiceProxy.Lists())
{
service.Crendentials = GetCredentials();
XmlNode idResultsNode = service.GetListItems(documentLibraryName, null, queryNode, viewNode, "1", optionNode, null);
}
This is not compiling because of the type mismatch. Am I going about this the wrong way? If not is there a way to use the certificate as the services credentials? I think in end what I'm trying to do is convert an X509Certificate2 to ICredentials. Any help or advice would be greatly appreciated.

It turns out I had it all wrong. To correctly do this I replaced:
service.Credentials = GetCertCreds()
with
service.ClientCertificates.Add(GetCretCreds());
Adding the certificate into the service's client certificates collection is all that needed to happen.

Related

Adding .cer file with public key to .NET Core 2 with Sustainsys

I have a .cer file which includes the public key that I need in order to test authentication against AD FS.
I can easily add it in the MVC sample in Web.config (setting signingCertificate fileName) existing in the solution and it seems to work but I can't find any way to add in the AspNetCore2 project.
The closest I've found is the SigningServiceCertificate but that only has a getter and I've read in another thread that it's supposedly to add the public key to the SigningKeys collection somehow, I just can't figure it out how.
Thank you!
I'm not sure exactly what you are asking however I use Sustainsys with a certificate and I use the following line:
var idp = new Sustainsys.Saml2.IdentityProvider(new entityId("https://sso.acme.com"), opt.SPOptions) { ... insert properties ... }
idp.SigningKeys.AddConfiguredKey(new X509Certificate2("customer-certificate.cer"));
opt.SPOptions.ServiceCertificates.Add(EncryptionHelper.GetX509Certificate2("INSERT-THUMBPRINT-OF-YOUR-CERTIFICATE"));
Where EncryptionHelper.GetX509Certificate2 is a custom helper I've written to read my certificate.
Here is the code for EncryptionHelper - I produced it on the back of one of the many examples online.
public static X509Certificate2 GetX509Certificate2(string thumbprint)
{
X509Certificate2 retVal = null;
var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
var certCollection = store.Certificates;
var signingCert = certCollection.Find(X509FindType.FindByThumbprint, thumbprint, false);
if (signingCert.Count > 0)
{
retVal = signingCert[0];
}
return retVal;
}
Note that if you are running this in Azure then you also need to add the Thumbprint to Application Settings using these instructions - https://learn.microsoft.com/en-us/azure/app-service/app-service-web-ssl-cert-load
You can find the thumbprint as per these instructions - https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-retrieve-the-thumbprint-of-a-certificate

Azure app services does it have a certificate store?

I have a project that communicates with a client server, the client server sends a self-signed certificate and I should trust him in order the continue the connection.
private bool VerifyServerCertificate(LdapConnection ldapConnection, X509Certificate certificate)
{
foreach (var cert in CertificateFileNames)
{
var certPath = Directory.GetFiles($#"{AppDomain.CurrentDomain.BaseDirectory}", $"{cert}",System.IO.SearchOption.AllDirectories);
X509Store store = new X509Store(StoreName.TrustedPeople, StoreLocation.CurrentUser);
try
{
if (File.Exists(certPath.FirstOrDefault()))
{
var certToAdd = new X509Certificate2(X509Certificate.CreateFromCertFile(certPath.FirstOrDefault()));
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certs = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, certToAdd.SubjectName.Name, true);
if (certs.Count.Equals(0))
{
store.Open(OpenFlags.ReadWrite);
store.Add(new X509Certificate2(X509Certificate.CreateFromCertFile(cert)));
store.Close();
}
}
}
catch
{
throw new Exception("Cannot install the certificate.");
}
}
X509Certificate2 certificate2 = new X509Certificate2(certificate);
return certificate2.Verify();
}
This works fine but the project needs to be deployed as a Azure app service. So my question is, the app service environment does it have a "Trusted People" storage ?
The method receives de X509certificate from the connection and verify against the trusted people storage so I need to install the certificates before the validation.
Thanks.

IdentityServer4 invalid_token "The issuer is invalid" on Azure, working on localhost

Help please, I'm building a .NET Core API with ionic front end. I want to use ASPNET Core Identity so I was more or less following this example
https://identityserver4.readthedocs.io/en/release/quickstarts/6_aspnet_identity.html
here is what I have in Startup.cs
// Adds IdentityServer
services.AddIdentityServer()
.AddTemporarySigningCredential()
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients(Configuration))
.AddAspNetIdentity<ApplicationUser>();
and
app.UseIdentity();
app.UseIdentityServer();
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = API_address,
RequireHttpsMetadata = false,
ApiName = "myAPIs"
});
and in my Config.cs file for in memory configurations I have
public class Config
{
// scopes define the resources in your system
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
};
}
// scopes define the API resources in your system
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource(
"myAPIs", // Api resource name
"My API Set #1", // Display name
new[] { JwtClaimTypes.Name, JwtClaimTypes.Role }) // Claims to be included in access token
};
}
// client want to access resources (aka scopes)
public static IEnumerable<Client> GetClients(IConfigurationRoot configuration)
{
return new List<Client>
{
new Client
{
ClientId = "myClient",
ClientName = "My Custom Client",
AllowedCorsOrigins = new List<string>
{
"whateverINeedHere"
},
AccessTokenLifetime = 60 * 60 * 24,
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
RequireClientSecret = false,
AccessTokenType = AccessTokenType.Jwt,
AllowedScopes =
{
"myAPIs"
}
}
};
}
}
Now the problem is that when I test this locally, everything works just fine.
I hit the /connect/token endpoint, I get a token response, hit the controller that needs token authorization and my claims are there. But when I deploy it to Azure, when I want to use the token (issued from that environment) I get 401 Unauthorized with response header invalid_token "The issuer is invalid". I've Googled, but people get invalid tokens with signature problems, not issuer. I've never used identity server before and to me this looks like it's some configuration problem. I have compared tokens I get from identity server on jwt.io, they look exactly the same, only difference being the issuer localhost -> myAPIAddress.
Can someone point me to the right direction?
This smells like it could be the temporary signing credentials. I also ran into problems when deploying to Azure when my cert wasn't loading.
I suggest you create a self signed cert and add it to azure using the following instructions. (Note this can be done in the new portal).
https://azure.microsoft.com/en-us/blog/using-certificates-in-azure-websites-applications/
REMEMBER: Make sure you add the WEBSITE_LOAD_CERTIFICATES application setting!
Also for your benefit, here's the code I use to load the cert in my startup.cs. I keep a copy of the cert in the repository so I can load it from disk as a fallback (when I'm on my dev machine).
X509Certificate2 cert = null;
using (X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser))
{
certStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certCollection = certStore.Certificates.Find(
X509FindType.FindByThumbprint,
// Replace below with your cert's thumbprint
"A9781679661914B7539BE020EE9C4F6880579F42",
false);
// Get the first cert with the thumbprint
if (certCollection.Count > 0)
{
cert = certCollection[0];
// Use certificate
Log.Logger.Information($"Successfully loaded cert from registry: {cert.FriendlyName}");
}
}
// Fallback to local file for development
if (cert == null)
{
cert = new X509Certificate2(Path.Combine(_env.ContentRootPath, "myauth.pfx"), "mypassword");
Log.Logger.Information($"Falling back to cert from file. Successfully loaded : {cert.FriendlyName}");
}
services.AddIdentityServer()
.AddSigningCredential(cert)
Could be you've got an SSL/TLS issue between client and IdentityServer, are you able to view logged exceptions from IdentityServer itself? You may see something like:
"... Could not establish trust relationship for the SSL/TLS..."
If you're running IdentityServer on HTTPS you need to make sure you've got its domain/sub-domain in your certificate.
Either way, IdentityServer logs lots of useful info so turn on logging and check out what it says, that should point you in the right direction.

Programmatically getting the list of azure virtual machine sizes

I am new to Azure management libraries for .net. How can we enumerate VM instance sizes available with respect to subscription or in general using Azure Management libraries for .Net or Rest APIs?
Please suggest.
You can get a list of VM sizes for a region by calling
https://management.azure.com/subscriptions/{subscription-id}/providers/Microsoft.Compute/locations/{location}/vmSizes?api-version={api-version}
As documented here - List all available virtual machine sizes in a region
There is also a .Net Class for the same, but I've not found any examples of it being used - documented here - VirtualMachineSizeOperationsExtensions.List
You can get list of VM sizes by region fillter
AuthenticationContext authenticationContext = new AuthenticationContext("https://login.windows.net/tenantdomainname.onmicrosoft.com"]);
UserCredential uc = new UserCredential(authusername,authpassword);
token = authenticationContext.AcquireToken("https://management.core.windows.net/", nativetenantid, uc);
var credentials = new TokenCredentials(token);
var computeClient = new ComputeManagementClient(credentials) { SubscriptionId = subscriptionid};
var virtualMachineSize = computeClient.VirtualMachineSizes.List(region_name).ToList();
you must need create one native client api on Azure Active Directory for token base authentication otherwise you can also use certification base authentication for client authorization.
i am using Microsoft.Azure.Management.Compute.dll, v10.0.0.0 for compute resources.
you can download here: https://www.nuget.org/packages/Microsoft.Azure.Management.Compute/13.0.4-prerelease
You can get list of VM Size by using Certificate Base Authentication
Get Certificate method
private static X509Certificate2 GetStoreCertificate(string subscriptionId, string thumbprint)
{
List<StoreLocation> locations = new List<StoreLocation>
{
StoreLocation.CurrentUser,
StoreLocation.LocalMachine
};
foreach (var location in locations)
{
X509Store store = new X509Store(StoreName.My, location);
try
{
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
X509Certificate2Collection certificates = store.Certificates.Find(
X509FindType.FindByThumbprint, thumbprint, false);
if (certificates.Count == 1)
{
return certificates[0];
}
}
finally
{
store.Close();
}
}
throw new ArgumentException(string.Format("A Certificate with Thumbprint '{0}' could not be located.",thumbprint));
}
here i describe same way to get VM size
private static X509Certificate2 Certificate = GetStoreCertificate(Your-subscriptionid,Your-thumbprint);
Microsoft.Azure.CertificateCloudCredentials credentials = new Microsoft.Azure.CertificateCloudCredentials(Your-subscriptionid, Certificate);
var computeClient = new ComputeManagementClient(credentials) { SubscriptionId = Your-subscriptionid};
var virtualMachineSize = computeClient.VirtualMachineSizes.List(Your-region_name).ToList();

Connect to a SharePoint site when IIS requires client certificates

I currently have an application developed in C# that helps me in managing permissions on our Share-point 2013 site. Recently, I learned we may be loosing our local instance and moving to another instance that's behind a cac enforced IIS. I have converted one of my test sites to require certificates and have tried several way to send the cert to the IIS server but I still get
"The remote server returned and error: (403) Forbidden.
Below is a few things I have tried.
var handler = new WebRequestHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Automatic;
handler.ClientCertificates.Add(pki.GetClientCertificate());
handler.UseProxy = false;
using (var client = new HttpClient(handler))
{
context connection code here
}
the pki.GetClientCertificate is a method, I made that returns a selected certificate in this case my cac cert. Its funny that SharePoint designer connects without issue or prompt. Any help on this matter would be much appreciated.
Just to add some more things I have tried
context.Credentials = new SharePointOnlineCredentials(uli.username, uli.password);
the uli username is the certificate converted to username I have a class that dose the conversion. the password is the pin converted to a secure string. I get the same message even when adding the credentials to the context.
I found a workable but slow solution here:
http://sharepoint.findincity.net/view/635399286724222582121618/ssl-certificate-error-when-using-client-object-model
The only issue with this is every time I call the context I have to send the certificate chain. One thing I changed from this users code is the following.
static void context_ExecutingWebRequest(object sender, WebRequestEventArgs e)
{
IntPtr ptr = IntPtr.Zero;
X509Certificate2 certificate = null;
X509Certificate t = null;
var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
// Nothing to do if no cert found.
HttpWebRequest webReq = e.WebRequestExecutor.WebRequest;
//webReq.Proxy = new WebProxy("http://[ProxyAddress]");
//Specify a proxy address if you need to
// X509Certificate cert = pki.GetClientCertificate();
foreach (X509Certificate c in store.Certificates)
{
webReq.ClientCertificates.Add(c);
}
}
I just dumped all my certificates into the request because I didn't want to have a prompt every time I clicked something. So if anyone has a more efficient way to do this let me know.
The code below shows the use of the clientcontext and how it validates your cert
using (context = new ClientContext(siteurl))
{
ServicePointManager.ServerCertificateValidationCallback = delegate(object sender1, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
bool validationResult = true;
return validationResult;
};
context.ExecutingWebRequest += new EventHandler<WebRequestEventArgs>(context_ExecutingWebRequest);
//add all your context commands below this line
}

Resources