SPTrustedRootAuthority from Visual Studio (C#) - sharepoint-2010

How can I import the certificates to SharePoint 2010 (the certs that will be used for the trusted identity provider, and it is a chain cert) using c# and SharePoint 2010 apis?
PowerShell has New-SPTrustedRootAuthority, but I cant use powershell.
I use reflection on the dll, and New-SPTrustedRootAuthority is using SPTrustedRootAuthorityManager class which is internal.
Any ideas?

This will add the specified X509 certificate to SharePoint 2010's 'Trusted Relationships' store.
Usage
var cert = new X509Certificate2(#"C:\my_cert.cer");
AddTrustedRootAuthority("My Cert", cert);
Util
public static void AddTrustedRootAuthority(string name, X509Certificate2 certificate)
{
Type SPTrustedRootAuthorityManagerType = Type.GetType("Microsoft.SharePoint.Administration.SPTrustedRootAuthorityManager, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c", true);
Type SPTrustedRootAuthorityType = Type.GetType("Microsoft.SharePoint.Administration.SPTrustedRootAuthority, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c", true);
object manager = SPTrustedRootAuthorityManagerType.GetProperty("LocalOrThrow", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null, null);
object authority = Activator.CreateInstance(SPTrustedRootAuthorityType, BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { name, manager, certificate }, null);
SPTrustedRootAuthorityType.GetMethod("Update", Type.EmptyTypes).Invoke(authority, null);
}

Related

Custom X509CertificateValidator with TLS 1.2

I'm having a problem with a custom certificate validator when using TLS 1.2
I have set up a custom validator by inheriting from X509CertificateValidator and implementing the Validate() function.
However, for some reason Validate() function never gets called, and my client gets the error:
The caller was not authenticated by the service
The inner exception:
The request for security token could not be satisfied because authentication failed.
This works fine with TLS 1.0 (with that enabled, I can set a breakpoint in Validate() and it gets hit, but disabled, it doesn't.)
As advised by other questions, I have tried adding this both in the client and the server:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
...and this in the client's config file:
<AppContextSwitchOverrides value="Switch.System.ServiceModel.DisableUsingServicePointManagerSecurityProtocols=false;Switch.System.Net.DontEnableSchUseStrongCrypto=false;;Switch.System.Net.DontEnableSystemDefaultTlsVersions=false" />
... and this in the server's web.config file:
<add key="AppContext.SetSwitch:Switch.System.Net.DontEnableSchUseStrongCrypto" value="false" />
<add key="AppContext.SetSwitch:Switch.System.Net.DontEnableSystemDefaultTlsVersions" value="false" />
Here is the code that creates the custom validator:
protected override void ApplyConfiguration() // Overrides ServiceHost.ApplyConfiguration()
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
base.ApplyConfiguration();
var binding = new MyAppHttpBinding(); // Custom object inheriting from CustomBinding - See below...
AddServiceEndpoint(typeof(IMyService), binding);
Credentials.ClientCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.Custom;
var configuration = WebConfigurationManager.OpenWebConfiguration("~");
var clientCertificate = configuration.GetCertificate("MyApp.ClientCertificate");
var serviceCertificate = configuration.GetCertificate("MyApp.ServerCertificate");
Credentials.ClientCertificate.Authentication.CustomCertificateValidator = new ThumbprintCertificateValidator(new[] { clientCertificate });
Credentials.ServiceCertificate.Certificate = serviceCertificate;
}
... and the Validate() function...
(Doesn't appear to hit this with just TLS 1.2)
public void Validate(string thumbprint)
{
var valid = Thumbprints
.Contains(thumbprint);
if (!valid)
{
throw new SecurityTokenValidationException("Certificate thumbprint does not match any in certificate store.");
}
}
/// <summary>
/// Validates the certificate's thumbprint with those specified.
/// </summary>
public override void Validate(X509Certificate2 certificate)
{
var thumbprint = certificate.Thumbprint;
Validate(thumbprint);
}
Here is the initialisation code for MyAppHttpBinding called from its constructor
var sslNegotiationBindingElement = SecurityBindingElement.CreateSslNegotiationBindingElement(true);
sslNegotiationBindingElement.MessageSecurityVersion = MessageSecurityVersion.WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10;
var secureConversationBindingElement = SecurityBindingElement.CreateSecureConversationBindingElement(sslNegotiationBindingElement);
Elements.Add(new TransactionFlowBindingElement());
Elements.Add(secureConversationBindingElement);
Elements.Add(new TextMessageEncodingBindingElement());
Elements.Add(new HttpTransportBindingElement());
To use TLS, see the documentation Transport Layer Security (TLS) best practice with the .NET Framework.
1.For TLS 1.2, target .NET Framework 4.7 or higher on your application and .NET Framework 4.7.1 or higher on your WCF application.
In the example below, replace 4.7.2 with whatever version of the framework you are currently using >= 4.7
<system.web>
<compilation targetFramework="4.7.2"></compilation>
<httpRuntime targetFramework="4.7.2" />
</system.web>
2.It is recommended not to specify the TLS version. Configure your code to let the OS decide the TLS version.
3.One more symbol (;) is written in the screenshot code, and the configuration of the client and server should be as consistent as possible.
https://learn.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/runtime/appcontextswitchoverrides-element

RSA KeyPair public key import not working

I encounter weird problem with RSA KeyPairs in IIS. I have an application which generates signed JWT tokens using private key. I exported public key and I want to use it in another application which consumes these JWT tokens (for JWT Bearer authentication).
Technically there is an javascript client which obtains JWT token from first applications WebAPI backend and use it when authenticates to second WebAPI backend.
This code is used to load public key and setup authentication (uses OWIN startup):
// USE from container
string KEY_CONTAINER_NAME = "MyContainer";
int KEY_SIZE = 2048;
CspParameters cspParams = new CspParameters();
cspParams.Flags |= CspProviderFlags.UseMachineKeyStore;
cspParams.KeyContainerName = KEY_CONTAINER_NAME;
var rsa = new RSACryptoServiceProvider(KEY_SIZE, cspParams);
// Api controllers with an [Authorize] attribute will be validated with JWT
appBuilder.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { "MyAudience" },
TokenValidationParameters = new TokenValidationParameters
{
IssuerSigningToken = new System.IdentityModel.Tokens.RsaSecurityToken(rsa),
ValidAudience = "MyAudience",
ValidIssuer = "MyIssuer"
}
});
This command I used to import public key to store:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319>.\aspnet_regiis.exe -pi MyContainer C:\PublicKey.xml
This is not working. Public key is loaded and used but never verifies incoming tokens. However, when I load the key right from XML file it works! So I presume the exported XML file is OK. I have no idea why this is happening? Then I tried to export imported public key and compare content of these files. It differs!
This command I used to export imported public key from store (for file content comparement):
C:\Windows\Microsoft.NET\Framework64\v4.0.30319>.\aspnet_regiis.exe -px MyContainer C:\PublicKey_exp.xml
When I load public key from file, not from the store, it works fine:
StreamReader sr = new StreamReader("C:\\PublicKey.xml");
String xml = sr.ReadToEnd();
sr.Close();
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(xml);

wcf wss1.1 BindarySecurityToken UsernameToken configuration for client

I want to both sign the message with the certificate and include the username/password. I can get WCF to do either of these but not both at the same time.
I'm trying to setup a client to connect to a SOAP service (WS-Security 2004) that is using both x509 certificates and a username/password using WCF.
I have certificates for both the server and client and SSL URI (https://) for the service endpoint. The client messages needs to be signed:
<wsse:BinarySecurityToken
EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"
ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"
wsu:Id="CertId-1317568">
MIICejCCAeOgAwIBAgIRAKxcIy5wGNq2XWsruqtiXY4wDQYJKoZIhvcNAQEF
...
</wsse:BinarySecurityToken>
and the header also needs to include the UsernameToken:
<wsse:UsernameToken>
<wsse:Username>VendorName0001</wsse:Username>
<wsse:Password
Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">
password
</wsse:Password>
</wsse:UsernameToken>
I tried Rick Strahl's solution but I don't need NONCE and I still need to x509 signature.
My code (which I know isn't quite right), because the BasicHttpSecurityMode.TransportWithMessageCredential seems to override the username setting for the message.
var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
var uri = "https://company.com/service.svc";
var client = new ServiceNamespace.ServiceWrapperClient(binding, new EndpointAddress(uri));
client.ClientCredentials.UserName.UserName = "username";
client.ClientCredentials.UserName.Password = "password";
client.ClientCredentials.ClientCertificate.SetCertificate(
StoreLocation.LocalMachine,
StoreName.My,
X509FindType.FindByThumbprint,
"1946c826c9bfb0b25b437da763a7c637eb19f963");
client.ClientCredentials.ServiceCertificate.SetDefaultCertificate(
StoreLocation.LocalMachine,
StoreName.TrustedPublisher,
X509FindType.FindBySubjectName,
"TEST subject name");
This worked for me:
Update the base class on the web reference
//public partial class FooServiceWrapper : System.Web.Services.Protocols.SoapHttpClientProtocol {
public partial class FooServiceWrapper : Microsoft.Web.Services3.WebServicesClientProtocol {
Use Soap Context Security to add the tokens I needed
client.Url = ServiceUrl;
client.SoapVersion = System.Web.Services.Protocols.SoapProtocolVersion.Soap11;
client.RequestSoapContext.Security.Tokens.Add(
new UsernameToken("username", "password", PasswordOption.SendPlainText));
client.RequestSoapContext.Security.MustUnderstand = false;
var certToken = new X509SecurityToken(new X509Certificate2(clientCert));
client.RequestSoapContext.Security.Tokens.Add(certToken);
client.RequestSoapContext.Security.Elements.Add(new MessageSignature(certToken));

Ignore SSL Validation for Portable Class Library Targeting Windows 8.1 using a Soap client

I CANNOT use the following namespaces:
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
Using a service reference generated soapclient.
How can I ignore ssl validation?
Not sure if this works in your scenario, but you can override the ServerCertificateValidationCallback of the global ServicePointManager of the application :
ServicePointManager.ServerCertificateValidationCallback = delegate(
Object obj, X509Certificate certificate, X509Chain chain,
SslPolicyErrors errors)
{
return true;
};
ServicePointManager is in the System.Net namespace, so it should be ok.

WCF certificate security with Mono

I'm trying to migrate an existing application to Mono (v2.10.2).
Therefore I created a test WCF service with BasicHttpBinding and message security. The client works perfectly with .NET, but when running with Mono it fails.
The client factory is instantiated as follows:
//var certificate = CertificateUtil.GetCertificate(StoreLocation.LocalMachine,
// StoreName.My, X509FindType.FindBySubjectDistinguishedName, CertName, true);
var certificate = new X509Certificate2("certificate.pfx", "password");
var binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.Message;
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.Certificate;
var epa = new EndpointAddress(
new Uri("http://localhost:53076/Service1.svc"),
new X509CertificateEndpointIdentity(certificate));
var factory = new ChannelFactory<IService1>(binding, epa);
factory.Credentials.ServiceCertificate.DefaultCertificate = certificate;
factory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
factory.Credentials.ServiceCertificate.Authentication.RevocationMode = X509RevocationMode.NoCheck;
factory.Credentials.ClientCertificate.Certificate = certificate;
var client = factory.CreateChannel();
In Mono the application fails within CreateChannel throwing the exception:
System.InvalidOperationException: The binding does not support any of the channel types that the contract 'IService1' allows.
I debugged into the Mono source code and found out that the problem is that AsymmetricSecurityBindingElement.InitiatorTokenParameter == null.
I'm new to Mono, maybe you could point me to a documentation/tutorial which covers this topic.
UPDATE:
With the aid of konrad.kruczynski the certificate object has a private key now. The exception is still the same. So this is not a certificate store issue.
Yes, certificates created on Windows usually does not contain private key. They can be found in some kind of cache. You should be able to create certificate with private key using this instruction. X509Certificate2 should consume the file without problems. You can also try procedure described here. In case of any problems just write.
It is also worth adding, that certificates created such way on Linux works perfectly on Windows too.
Update:
I'm not sure whether I understood your comment correctly. You can load PFX certificate using code like that:
var myCert = new X509Certificate2("filename.pfx", "password");
Given certficate contained key, it worked for me.