Adding Certificate .CER to SAML2 Configuration ITfoxtec.Identity.Saml2 - asp.net-core

I'm trying to implement SSO using SAML2 and ITfoxtec.Identity.Saml2 package in my ASP.NET MVC ( .NET 6.0 ).
I have the metadata file of the IDP ( Azure AD ) and the .cer certificate file ( both given by the IDP ).
When i'm testing the assertion , i'm getting SAML response success and i can read the claims but when i come to unbind i get " Signature is invalid " , i think because i did not add the public key ( .cer file ) to the Saml config but i'm not sure.
I found how to configure .pfx certificate file ( which required password (private key) ),but not the case for the .cer.
Any ideas please ?
Thanks.

It is enough to read the Azure AD IdP metadata. The IdP metadata contain the public .cer file.
You need to ensure that the code read the certificate correctly in the IdP metadata. Please use the following sample code as a reference.
foreach (var signingCertificate in entityDescriptor.IdPSsoDescriptor.SigningCertificates)
{
if (signingCertificate.IsValidLocalTime())
{
saml2Configuration.SignatureValidationCertificates.Add(signingCertificate);
}
}
if (saml2Configuration.SignatureValidationCertificates.Count <= 0)
{
throw new Exception("The IdP signing certificates has expired.");
}
if (entityDescriptor.IdPSsoDescriptor.WantAuthnRequestsSigned.HasValue)
{
saml2Configuration.SignAuthnRequest = entityDescriptor.IdPSsoDescriptor.WantAuthnRequestsSigned.Value;
}

Related

Get UserInfo from ADFS in UWP with ADAL

I am trying to authenticate the user with ADFS and I am using ADAL. Authetication seems to work since I can get the AccessToken. The problem is that looking at the code authResult contains a UserInfo where all properties (for instance GivenName or FamilyName) are null.
AuthenticationContext authContext = null;
AuthenticationResult authResult;
try
{
authContext = new AuthenticationContext(authority, false);
authResult = await authContext.AcquireTokenAsync(resource, clientId, new Uri(returnUri),
new PlatformParameters(PromptBehavior.Auto, false));
}
Those values are null because of ADFS configuration? I noted that decoding the AccessToken returned I can read User information. But I don't think that decoding the JWT Token is the right way to achieve those information. Do you have a better suggestion?
I have also seen people getting information by using claims, but I don't know exactly how to use it on UWP, since all the sample I found used
ClaimsPrincipal claimsPrincipal = System.Threading.Thread.CurrentPrincipal as ClaimsPrincipal;
But System.Threading.Thread is not available on UWP.
Normally, the access_token is used in Oauth and OpenID connect scenarios and intended to be consumed by the resource. To identify the user we should use the id_token( verify the token and extract the claims abut user by decoding the token). Please refer below about the usage of tokens:
id_token: A JWT token used to represent the identity of the user. The
'aud' or audience claim of the id_token matches the client ID of the
native or server application.
access_token: A JWT token used in Oauth and OpenID connect scenarios
and intended to be consumed by the resource. The 'aud' or audience
claim of this token must match the identifier of the resource or Web
API.
refresh_token: This token is submitted in place of collecting user
credentials to provide a single sign on experience. This token is
both issued and consumed by AD FS, and is not readable by clients
or resources.
And you can refer the link below about the native client to web API scenario for ADFS:
AD FS Scenarios for Developers - Native client to Web API
Depending on the ADFS version of your server. If your company is using Windows Server 2012 R2, then it is ADFS 3.0. I did successfully integrate with SSO login created by the admin of company I am working in. You should refer to this article before venturing in : https://learn.microsoft.com/en-us/previous-versions/adfs-windows-server-2012r2/dn660968(v=msdn.10). Note : you don't even need to make a web api of ToDoList.
using only GetAuthorizationHeader() and authenticationContext.AcquireTokenAsync(), you could obtain the token by asking the user to authorize their credentials and decrypt the receive token.
This is sample of code I did:
authority = https://contoso.com/adfs/ls (Endpoint from the ADFS metadata)
resourceURI = https://localhost:44300/ (Relying party, ask your ADFS admin to register)
clientID = it is recommended to use Package.appmanifest's package name from Packaging tab. As long as it is a unique ID.
clientReturnURI = use the following code to obtain the clientReturnURI (also available in the article in the link) :
string clientReturnURI = string.Format("ms-appx-web://Microsoft.AAD.BrokerPlugIn/{0}",WebAuthenticationBroker.GetCurrentApplicationCallbackUri().Host.ToUpper());
AuthenticationContext ac = new AuthenticationContext(Authority_Uri, false);
AuthenticationResult ar = await ac.AcquireTokenAsync(resourceURI, GlobalVar.clientID, new Uri(clientReturnURI), new PlatformParameters(PromptBehavior.Always, true));
var jwt = new JwtSecurityToken(ar.AccessToken);
string unique_name = jwt.Claims.First(c => c.Type == JwtRegisteredClaimNames.UniqueName).Value;
You can replace JwtRegisteredClaimNames.UniqueName with anything else. It depends what info/claims that is available in the access token. You should inspect the available info in the jwt by placing breakpoint at var jwt. Or you can decrypt the access token in the AuthenticationResult.AccessToken in this website :
https://jwt.ms/
Lastly, you need to install certificate from your ADFS admin and install the certificate across your web and UWP server to allow the application able to trust execute the actions.

Invalid Signature Key IdentityServer4

I'm facing this exception in my authenticated applications with identityserver4:
SecurityTokenInvalidSignatureException: IDX10503: Signature validation failed. Keys tried: 'Microsoft.IdentityModel.Tokens.RsaSecurityKey , KeyId:
'. Exceptions caught: ''. token: '{"alg":"RS256","typ":"JWT"}.{"nbf":1501510053,"exp":1501510353,"iss":"http://localhost:5000","aud":"clientId","nonce":"636371068531446271.Nzc2YzRjZGMtN2M0Ny00MGZiLWI3NTQtN2Q2NWIxZGM5MWUyODY2MGE5NTgtYWMzNS00YWJmLWEyMDktNjcwMmJhYTlkYWJk","iat":1501510053,"c_hash":"teGUAAIvU0EY9L1WzjfI2Q","sid":"c870b6184744290472b157ce5992f8e0","sub":"a3915666-8586-476b-9166-b5b183af609e","auth_time":1501508734,"idp":"local","amr":["pwd"]}'.
At this time, I don't have a certificate to use in my Sign in credentials in identity server. So I try this code, sometimes works, but I have sure this is wrong. Someone have a step by step how to use SignInCredentials without a .pfx certificate?
var signinkey = new RsaSecurityKey(RSA.Create());
var signingCredentials = new SigningCredentials(signinkey,
SecurityAlgorithms.RsaSha256);
// Adds IdentityServer
services.AddIdentityServer()
.AddSigningCredential(signingCredentials)
...
Use
services.AddIdentityServer().AddDeveloperSigningCredential()
This will create you a random RSA key pair at first launch and cache it on disk.

Spring Boot based webservices secured with Spring Security using client certificate authentication and custom RDN

I need to create webservices that are secured by client certificates which my company provides for most employees. Because some employees don't have a certificate or sometimes our browser fails to access them the authentication should be "forgiving", that means at least a http status code 401 should be presented to the caller. As I am providing only webservices the employees will never enter a protected URL directly, the webservice will always be called by a webpage that could react to a 401 and display a friendly message.
I found out that I can have Spring Boot / the embedded Tomcat do the clientauth-part. For this I used an example from the web that does this:
connector.setAttribute("SSLEnabled", true);
connector.setAttribute("sslProtocol", "TLS");
connector.setAttribute("protocol", "org.apache.coyote.http11.Http11Protocol");
connector.setAttribute("clientAuth", "want");
connector.setAttribute("keystoreFile", absoluteKeystoreFile);
connector.setAttribute("keystoreType", keystoreType);
connector.setAttribute("keystorePass", keystorePassword);
connector.setAttribute("keystoreAlias", keystoreAlias);
connector.setAttribute("keyPass", keystorePassword);
From what I learnt at least Spring Boot 1.2.1 makes all this easier so I will switch to using server.ssl.client-auth=want/need and the other parameters in application.properties soon.
My current setup uses code like this to retrieve the certificate and all this runs fine when called from my companies browser:
#RestController
public class WebGedoens {
#RequestMapping("/")
String home(HttpServletRequest request) {
X509Certificate[] certs = (X509Certificate[]) request
.getAttribute(JAVAX_SERVLET_REQUEST_X509_CERTIFICATE);
if (certs != null) {
for (int i = 0; i < certs.length; i++) {
System.out.println("Client Certificate [" + i + "] = "
+ certs[i].toString());
}
}
...
}
The above dump showed me this subject DN:
Subject: CN=John Doe, EMAILADDRESS=john.doe_17#company.com, O=Company, C=DE
Because there are many "John Does" I need to get the EMAILADDRESS RDN from the subject DN because the email received an appended number that allows to identify each unique person.
How can I enable Spring Security for my method and tell Spring Security to take the certificate (there may be more than one) and extract the correct RDN ?

Cannot find a token authenticator for the 'Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityToken' token

I am trying to use WS2007HttpRelayBinding with end to end security mode set to TransportWithMessageCredential. I am using IssuedToken as the credential type. I get the token from a ADFS 2.0 one calling the service I get the following in the on premises wcf trace log
Cannot find a token authenticator for the 'Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityToken' token type. Tokens of that type cannot be accepted according to current security settings.
update:
This is how I am configuring the service host
ServiceConfiguration serviceConfiguration = new ServiceConfiguration();
serviceConfiguration.ServiceCertificate = GetServiceCertificateWithPrivateKey();
serviceConfiguration.CertificateValidationMode = X509CertificateValidationMode.None;
serviceConfiguration.IssuerNameRegistry = new X509IssuerNameRegistry("localhost");
serviceConfiguration.SaveBootstrapTokens = true;
serviceConfiguration.SecurityTokenHandlers.AddOrReplace(new Saml2SecurityTokenHandler());
serviceConfiguration.SecurityTokenHandlers.Configuration.AudienceRestriction.AllowedAudienceUris.Add(new Uri("https://mynamespace.servicebus.windows.net/Service1/"));
FederatedServiceCredentials.ConfigureServiceHost(host, serviceConfiguration);
host.Open();
Can you verify if Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler is added in
<securityTokenHandlers>
<add type="Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler" />
</securityTokenHandlers>
Edit: And also be sure to verify certificates configuration.
Edit: Maybe this will also help MSDN WCF forums
The binding security elements is set to look for SAML 1.1 tokens. I added the following code to the server after constructing the ‘CustomBinding’ element
IssuedSecurityTokenParameters issuedTokenParameters =
myBinding.Elements.Find<TransportSecurityBindingElement>().EndpointSupportingTokenParameters.Endorsing[0] as IssuedSecurityTokenParameters;
issuedTokenParameters.TokenType = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0";
Alexey's answer is perfect for web.config/app.config modifications. Besides that you can also configure the token handler in code (sample from the How to: Authenticate with a Username and Password to a WCF Service Protected by ACS article (learn.microsoft.com) - How to: Authenticate with a User Name and Password):
//
// This must be called after all WCF settings are set on the service host so the
// Windows Identity Foundation token handlers can pick up the relevant settings.
//
ServiceConfiguration serviceConfiguration = new ServiceConfiguration();
serviceConfiguration.CertificateValidationMode = X509CertificateValidationMode.None;
// Accept ACS signing certificate as Issuer.
serviceConfiguration.IssuerNameRegistry = new X509IssuerNameRegistry( GetAcsSigningCertificate().SubjectName.Name );
// Add the SAML 2.0 token handler.
serviceConfiguration.SecurityTokenHandlers.AddOrReplace( new Saml2SecurityTokenHandler() );

Unable to sign security tokens with certificate in WIF scenario

I'm trying to implement a custom STS for a WIF scenario I'm investigating, but it's failing. It's failing when trying to obtain the private key from the certificate used to sign the tokens. I create the STS with the following configuration:
var signingCert = new X509Certificate2(#"C:\<path>\MySigningCertificate.pfx");
var config
= new SecurityTokenServiceConfiguration()
{
DisableWsdl = true,
TokenIssuerName = "Tribold",
SecurityTokenService = typeof(TriboldSecurityTokenService),
SigningCredentials = new X509SigningCredentials(signingCert),
CertificateValidationMode = X509CertificateValidationMode.Custom,
CertificateValidator = new CertificateValidator()
};
However, with WCF diagnostic logging configured, I get the following message in the Service Trace Viewer:
The private key is not present in the X.509 certificate.
This appears to be logged as the code comes out of my custom STS (i.e., after calling GetOutputClaimsIdentity(...) on my custom STS class, and therefore I can only assume that it's now trying to sign the issued security token and failing because it can't obtain a private key to do so.
The private key appears to be present on the loaded certificate:
Debug.Assert(signingCert.HasPrivateKey == true);
but it fails later on. I'm having no luck resolving this, please help!
It looks like thread "cant use .pfx file for X.509 certificates" in the Geneva (= AD FS 2.0) forums covers the same problem which you report. So the resolution reported there might work, which is "specifying the X509KeyStorageFlags.PersistKeySet flag when initiating the X509Certificate2 object".
I'd be surprised if you didn't have to specify a password when opening a PFX file. X509Certificate2 has overloads that take a password in the form of a string or a SecureString.