Disabling encryption in Windows Identity Foundation - wcf

Can I disable encryption of the request security token response and only manage signatures?
I'm creating a custom STS extending Microsoft.IdentityModel.SecurityTokenService.SecurityTokenService based on the demos of the WIF SDK and I cannot manage to setup not using encryption.

I just ran the "Add STS Reference" wizard in Visual Studio, selecting the option to create a new STS. The template that the tool generated does add support for token encryption, but if no cert is supplied, thne it is disabled: (I left all the default comments)
protected override Scope GetScope( IClaimsPrincipal principal, RequestSecurityToken request )
{
ValidateAppliesTo( request.AppliesTo );
//
// Note: The signing certificate used by default has a Distinguished name of "CN=STSTestCert",
// and is located in the Personal certificate store of the Local Computer. Before going into production,
// ensure that you change this certificate to a valid CA-issued certificate as appropriate.
//
Scope scope = new Scope( request.AppliesTo.Uri.OriginalString, SecurityTokenServiceConfiguration.SigningCredentials );
string encryptingCertificateName = WebConfigurationManager.AppSettings[ "EncryptingCertificateName" ];
if ( !string.IsNullOrEmpty( encryptingCertificateName ) )
{
// Important note on setting the encrypting credentials.
// In a production deployment, you would need to select a certificate that is specific to the RP that is requesting the token.
// You can examine the 'request' to obtain information to determine the certificate to use.
scope.EncryptingCredentials = new X509EncryptingCredentials( CertificateUtil.GetCertificate( StoreName.My, StoreLocation.LocalMachine, encryptingCertificateName ) );
}
else
{
// If there is no encryption certificate specified, the STS will not perform encryption.
// This will succeed for tokens that are created without keys (BearerTokens) or asymmetric keys.
scope.TokenEncryptionRequired = false;
}
// Set the ReplyTo address for the WS-Federation passive protocol (wreply). This is the address to which responses will be directed.
// In this template, we have chosen to set this to the AppliesToAddress.
scope.ReplyToAddress = scope.AppliesToAddress;
return scope;
}

I create a CustomSecurityHandler and override its GetEncryptingCredentials method returning null value like the following lines and it works:
public class MyCustomSecurityTokenHandler : Saml11SecurityTokenHandler
{
public MyCustomSecurityTokenHandler(): base() {}
protected override EncryptingCredentials GetEncryptingCredentials(SecurityTokenDescriptor tokenDescriptor)
{
return null;
}
}
then in the SecurityTokenService class i override the GetSecurityTokenHandler returning the custom class created before:
protected override SecurityTokenHandler GetSecurityTokenHandler(string requestedTokenType)
{
MyCustomSecurityTokenHandler tokenHandler = new MyCustomSecurityTokenHandler();
return tokenHandler;
}

Related

Host name check in Custom Trust Manager

We have a java client that allows both secure and non-secure connections to LDAP hosts.
It comes as part of a software suite which
has its own server component.
We are good with non-secure connections but need to switch to secure only.
The trusted public certificates are maintained (root+intermediate+host are copy pasted into one PEM file) in a
central location with the server component external to the clients.
The custom trust manager downloads the externally held trusted certificates on demand
and builds the trusted certificate chain. This way, I guess, it avoids pre-saving the trusted certicate chain in each client.
Our LDAP hosts are load balanced and that setup has not gone well with the trust manager. When we investigated, we found two questionable lines
in the code.
An environment variable to by-pass the host name verification.
if ("T".equals(System.getenv("IGNORE_HOSTNAME_CHECK"))) return true;
It seems like doing something similar to below which I have seen elsewhere.
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
Host name check relies on CN value of subject alone.
if (this.tgtHostname.equalsIgnoreCase(leafCn)) return true;
I have skimmed through some RFCs related to TLS and have come across SNI, SAN:DNSName and MITM warnings
but my rudimentary knowledge is not enough to make a case one way or the other.
Any advice on improvements (or against the use of it altogether) around commented out lines labelled H1 and H2 will be greatly valued.
I intend to pass them on to the right entity later.
The cut-down version of checkServerTrusted() of the custom trust manager is pasted below.
public void checkServerTrusted(X509Certificate[] certsRcvdFromTgt, String authType) throws CertificateException
{
// Some stuff
// Verify that the last certificate in the chain corresponds to the tgt server we want to access.
checkLastCertificate(certsRcvdFromTgt[certsRcvdFromTgt.length - 1]);
// Some more stuff
}
private boolean checkLastCertificate(X509Certificate leafCert) throws CertificateException
{
// need some advice here ... (H1)
if ("T".equals(System.getenv("IGNORE_HOSTNAME_CHECK"))) return true;
try
{
String leafCn = null;
X500Principal subject = leafCert.getSubjectX500Principal();
String dn = subject.getName();
LdapName ldapDN = new LdapName(dn);
for (Rdn rdn : ldapDN.getRdns())
{
if (rdn.getType().equalsIgnoreCase("cn"))
{
leafCn = rdn.getValue().toString();
break;
}
}
// need some advice here ... (H2)
if (this.tgtHostname.equalsIgnoreCase(leafCn)) return true;
}
catch (InvalidNameException e){/*error handling*/}
throw new CertificateException("Failed to verify that the last certificate in the chain is for target " + this.tgtHostname);
}

Mvc Application, user authentication STS (ADFS)

In MVC Application, user authentication STS (ADFS) working for one ADFS, but I want to change the all parameter at run time for different ADFS which are configured in Web.config like this :
authority name
validIssuers
issuer (in system.identityModel.services section)
etc
I'm done with this issue.
we can change all following parameters dynamically as below:
FederatedAuthentication.FederationConfiguration.IdentityConfiguration.IssuerNameRegistry = new Trust(trust);
FederatedAuthentication.FederationConfiguration.IdentityConfiguration.AudienceRestriction.AllowedAudienceUris.Add(new Uri("https://localhost:44300"));
FederatedAuthentication.FederationConfiguration.WsFederationConfiguration.PassiveRedirectEnabled = true;
FederatedAuthentication.FederationConfiguration.WsFederationConfiguration.Issuer = "https://sts.domainame.com/adfs/ls/";
FederatedAuthentication.FederationConfiguration.WsFederationConfiguration.Realm = "https://localhost:44300";
Trust Class:
public class Trust : IssuerNameRegistry
{
string trust;
public Trust(string trust)
{
this.trust= trust;
}
public override string GetIssuerName(SecurityToken securityToken)
{
return trust;
}
}

WebAPI 2 Create Custom Authentication Token

I want to create Custom Bearer Token, with some additional information to be store in the token.
Just want to Use Create Token functionality.(something like FormsAuthentication) without using default implementation(ASP.NET Identity) of User Tables.
1) Custom Login method(MyLogin), that will create custom bearer token with additional information(IP Address embedded into token).
2) on subsequent request be able to inspect the additional information and reject(treat the request as unauthenticated) if the additional information does not match some rule.
In case i receive the bearer token and find the request is coming from different IP address then the one embedded inside it, clear/Invalidate the Bearer Token and treat the current request as UnAuthenticated.
I'm by no means an expert but this is the information i gathered.
This seems to be relatively simple to do with ASP.NET Identity.
You need to create your own implementation of a token provider which implements the IAuthenticationTokenProvider interface. You implement the create method so it creates the token just the way you want and then you supply your provider when configuring the authentication middleware.
The configuration in your starup class would look something like this:
public class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureOAuth(app);
//Rest of code is here;
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/yourtokenendpoint"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider(),
AccessTokenProvider = new YourCustomTokenProvider() // YourCustomTokenProvider implements IAuthenticationTokenProvider
};
OAuthBearerAuthenticationOptions bearerOptions = new OAuthBearerAuthenticationOptions()
{
AccessTokenProvider = new YourCustomTokenProvider() // YourCustomTokenProvider implements IAuthenticationTokenProvider
}
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(bearerOptions);
}
}
I have never done this myself but I hope this was of some use.
EDIT: To validate the the token you could create a custom action filter that you decorate your controller actions with. In this action filter you could validate the token and do whatever you like with the request. See this guide.

Read SSL Certificate Details on WP8

I want to read certificate details (e.g. expiration date or CN) for security reasons.
Usually there are some properties in network classes available, that allow to check the certificate. This is missing in WP8 implementations.
Also I tried to create an SslStream but also there is no way to get any certificate detail like the RemoteCertificate on .net 4.5.
var sslStream = new SslStream(new NetworkStream(e.ConnectSocket));
The SslStream is missing everything relating security. So it looks like also BountyCastle and other libraries cannot be able to get the certificate, because the underlying framework doesn't support it.
So my questions are:
Can I read the CN or other Certificate details on WP8 using other approaches.?
If not, how can you create then seriously secure apps (line banking) on WP8 using techniques like SSL Pinning or client side certificate validation and is there any reason why this is not supported in WP8?
Regards
Holger
I issued a user voice request to Microsoft .NET team asking them to provide a solution for reading server SSL certificate details from portable class libraries (targeting also WP8). You can vote it here: http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/4784983-support-server-ssl-certificate-chain-inspection-in
On Windows Phone 8.1 this can be done with HttpClient, as well as with StreamSocket (as Mike suggested).
Example for certificate validation with StreamSocket can be found here (Scenario5_Certificate in source code).
Certificate validation with HttpClient can be done by handling the ERROR_INTERNET_INVALID_CA exception, validating the server certificate using the HttpTransportInformation class, creating new instance of HttpBaseProtocolFilter class and specifying the errors to ignore.
Note that not all the errors are ignorable. You will receive an exception if you'll try to add Success, Revoked,
InvalidSignature, InvalidCertificateAuthorityPolicy, BasicConstraintsError, UnknownCriticalExtension or OtherErrors enum values.
I'm adding a sample code that bypasses certificate errors using HttpClient:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using Windows.Security.Cryptography.Certificates;
using Windows.Web.Http;
using Windows.Web.Http.Filters;
namespace Example.App
{
public class HttpsHandler
{
private const int ERROR_INTERNET_INVALID_CA = -2147012851; // 0x80072f0d
public static async void HttpsWithCertificateValidation()
{
Uri resourceUri;
if (!Uri.TryCreate("https://www.pcwebshop.co.uk/", UriKind.Absolute, out resourceUri))
return;
IReadOnlyList<ChainValidationResult> serverErrors = await DoGet(null, resourceUri);
if (serverErrors != null)
{
HttpBaseProtocolFilter filter = new HttpBaseProtocolFilter();
foreach (ChainValidationResult value in serverErrors)
{
try {
filter.IgnorableServerCertificateErrors.Add(value);
} catch (Exception ex) {
// Note: the following values can't be ignorable:
// Success Revoked InvalidSignature InvalidCertificateAuthorityPolicy
// BasicConstraintsError UnknownCriticalExtension OtherErrors
Debug.WriteLine(value + " can't be ignorable");
}
}
await DoGet(filter, resourceUri);
}
}
private static async Task<IReadOnlyList<ChainValidationResult>> DoGet(HttpBaseProtocolFilter filter, Uri resourceUri)
{
HttpClient httpClient;
if (filter != null)
httpClient = new HttpClient(filter);
else
httpClient = new HttpClient();
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, resourceUri);
bool hadCertificateException = false;
HttpResponseMessage response;
String responseBody;
try {
response = await httpClient.SendRequestAsync(requestMessage);
response.EnsureSuccessStatusCode();
responseBody = await response.Content.ReadAsStringAsync();
} catch (Exception ex) {
hadCertificateException = ex.HResult == ERROR_INTERNET_INVALID_CA;
}
return hadCertificateException ? requestMessage.TransportInformation.ServerCertificateErrors : null;
}
}
}
After trying open source libs like bouncyCastle, supersocket or webSocket4net I tested an evaluation of a commercial lib named ELDOS SecureBlackbox. This test was successfull. Here is a code snipped, that gets the X509Certificates with all details:
public void OpenSSL()
{
var c = new TElSimpleSSLClient();
c.OnCertificateValidate += new TSBCertificateValidateEvent(OnCertificateValidate);
c.Address = "myhostname.com";
c.Port = 443;
c.Open();
c.Close(false);
}
private void OnCertificateValidate(object sender, TElX509Certificate x509certificate, ref TSBBoolean validate)
{
validate = true;
}
The validation is getting all certificates... if validate is set to true, the next certificate will be shown. That means the callback is called forreach certificate there.
Regards
Holger
For WP8, you can use the StreamSocket class, which has an UpgradeToSslAsync() method that will do the TLS handshake for you as an async operation. Once that completes, you can use the .Information.ServerCertificate property to check that you got the server certificate you were expecting.

Display service's certificate in WCF client?

I've got a WCF client and service. The service is configured to use a certificate for encryption. This is all working fine. We're using self-signed certificates for testing.
Except that one of my QA guys has deleted the certificate from his client PC and he can still connect to the service.
This leads to my question:
In Internet Explorer (and other browsers), when you're connected via HTTPS, you can see the server's certificate by clicking on the padlock icon. I'd like to do something similar in my WCF client, so that the user can verify the server's identity. Is there a way in my WCF client to get hold of the server certificate and to display it?
One way to achieve this is by using a custom certificate validator (in which case the server cert will be passed in to the Validate method, and from there you can do what you like with it (i.e. save the certificate somewhere the client can use, and then validate it using one of the default validators))
public class MyX509CertificateValidator : X509CertificateValidator
{
private readonly X509CertificateValidationMode _validationMode;
private readonly WcfClient _client;
public MyX509CertificateValidator(WcfClient client, X509CertificateValidationMode validationMode)
{
_client = client;
_validationMode = validationMode;
}
public override void Validate(X509Certificate2 certificate)
{
if (certificate == null)
{
throw new ArgumentNullException("certificate");
}
_client.ServerCertificate = certificate;
switch (_validationMode)
{
case X509CertificateValidationMode.None:
None.Validate(certificate);
return;
case X509CertificateValidationMode.PeerOrChainTrust:
PeerOrChainTrust.Validate(certificate);
return;
case X509CertificateValidationMode.PeerTrust:
PeerTrust.Validate(certificate);
return;
default:
ChainTrust.Validate(certificate);
return;
}
}
}
The encryption will use the server side certificate, just like it does for a https site.
You could use the client certificates for authentication, but this is something else.