Closed cotepatrice closed 10 years ago
Do you need to encrypt the token from ADFS to IdSrv? Try without first.
Well, we do encrypt all our tokens from every RP in ADFS. It's a company standard. But when I remove encryption, the JWT token gets created and returned by IdSrv. I know this is a certificate problem more than an IdSrv problem, but I must admit I'm totally lost on this one. Like I said, everything seems to be set correctly as for access to the private key. This is the correct certificate (same thumprint, IssuerName, etc...).
Have you also configured the decryption key on the ADFS integration config page.
OUpsss... I didn't upload the cer. Now it's done but still same error. Do I need the private key in a pfx ?
And in the Key configuration section of IdSrv, do I need to select the certificate there too or is it just for assertion flow scenarios ?
Even with encryption certificate loaded in IdSrv, private key access to the user set to "Full control" + "Read", still no luck at all. The certificates are in "My" store and I copied it to "Trusted Root CA" too. It is always the same error in the log file.
The error happens in the "ToSecurityToken()" extension method in the Thinktecture.IdentityModel.Extensions.SecurityTokensExtensions class, called from the Thinktecture.IdentityServer.Protocols.AdfsIntegration.AdfsController.CreateTokenResponse() method. I didn't manage to debug it inside, but that's where it happens.
I've found this thread to be exactly the same problem that I have. So it means this doesn't work with encrypted SAML token I guess. Do you have plans to fix this with v3 ?
https://github.com/thinktecture/Thinktecture.IdentityServer.v2/issues/420
I don't think this is related -
just to make sure - are you using the "ADFS integration" feature or just ADFS as an IdP ?
Yes, it's "ADFS integration", not ADFS as an idP. The issue #420 is the exact same case from what I read. Works withour encryption, but get the same error when adding a certificate.
Here's a batch of print screens that shows my configuration and the debug values in Identity Server source code :
ADFS :
Identity Server machine :
Visual Studio debug infos :
Hm - I am pretty sure we implemented decryption - otherwise there would be no point in having the config UI and setting. I'd need to debug through the code - which I don't have the time right now.
I'll see what I can find out ASAP
OK I see the issue - this cert is not for decrypting incoming tokens - it is rather for encryping outgoing ones.
Seems we don't have decryption support right now - I can see two possible solutions:
The token handler has a Configuration property where you can set the resolver in a similar manner IIRC
Actually -
check this here:
This is in the AdfsBridgeDecryption branch - I haven't tested it, but give it a try.
You need to configure the decryption cert on the admin/keys page for that.
Good ! I'll give it a try today (it's 8:00 AM here) and let you know if it works ASAP. Thanks for your help !
OK. The problem is that the AdfsBridge.ConvertSamlToJwt() takes a SecurityToken as parameter and we have a GenerixXmlSecurityToken. So the code inside the controller method CreateTokenResponse is like this right now :
private HttpResponseMessage CreateTokenResponse(GenericXmlSecurityToken token, string scope) { var response = new TokenResponse();
if (ConfigurationRepository.AdfsIntegration.PassThruAuthenticationToken)
{
response.AccessToken = token.TokenXml.OuterXml;
response.ExpiresIn = (int)(token.ValidTo.Subtract(DateTime.UtcNow).TotalSeconds);
}
else
{
var bridge = new AdfsBridge(ConfigurationRepository);
response = bridge.ConvertSamlToJwt(token.ToSecurityToken(), scope);
}
return Request.CreateResponse<TokenResponse>(HttpStatusCode.OK, response);
}
As you can see, it uses the extension method ToSecurityToken() to convert it to security token before calling ConvertSamlToJwt(). This extension method fails when we use an encrypted token since it uses a default security token handler if we don't give it one. But since there is an overload where we can pass it an handler, here is the modified code that I put inside the AdfsController :
private HttpResponseMessage CreateTokenResponse(GenericXmlSecurityToken token, string scope) { var response = new TokenResponse();
if (ConfigurationRepository.AdfsIntegration.PassThruAuthenticationToken)
{
response.AccessToken = token.TokenXml.OuterXml;
response.ExpiresIn = (int)(token.ValidTo.Subtract(DateTime.UtcNow).TotalSeconds);
}
else
{
var bridge = new AdfsBridge(ConfigurationRepository);
if (ConfigurationRepository.Keys.DecryptionCertificate != null)
{
var configuration = new SecurityTokenHandlerConfiguration
{
AudienceRestriction = {AudienceMode = AudienceUriMode.Never},
CertificateValidationMode = X509CertificateValidationMode.None,
RevocationMode = X509RevocationMode.NoCheck,
CertificateValidator = X509CertificateValidator.None,
ServiceTokenResolver = SecurityTokenResolver.CreateDefaultSecurityTokenResolver(
new ReadOnlyCollection<SecurityToken>(new SecurityToken[]
{new X509SecurityToken(ConfigurationRepository.Keys.DecryptionCertificate)}), false)
};
var handler = SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection(configuration);
response = bridge.ConvertSamlToJwt(token.ToSecurityToken(handler), scope);
}
else
{
response = bridge.ConvertSamlToJwt(token.ToSecurityToken(), scope);
}
}
return Request.CreateResponse<TokenResponse>(HttpStatusCode.OK, response);
}
Tested with these scenarios :
I don't know if this logic shoud go there, but that's the fastest way I found so that we don't break any compatibility. I'll wait for your comments on this.
Cool. If it is tested and works for you - send me a PR.
(maybe check is the decryption cert in IdSrv is set or not / if it is null.
For the null check, I thougt that's what this line was doing ?
if (ConfigurationRepository.Keys.DecryptionCertificate != null)
I have setup the ADFS integration inside IdSrv. I have the ADFS RP encryption certificate installed on the idSrv with the private key access given to the user of the app pool running idSrv. I tripled check that. In the UI, I added the protocol, and put the values for ADFS UserName Endpoint, ADFS Issuer URI and ADFS Signing Certificate Thumbprint. Also double checked that it is the correct values in the AD FS 2.0 management tool.
When I use OAuth2Client to make the call, I can see in the ADFS server security log that the token has been succesfully issued for the correct Relying Party.
private static TokenResponse RequestToken(string scope) { "Requesting token.".ConsoleYellow(); var client = new OAuth2Client( "https://my-iis-server/idsrv/issue/adfs", "clientId_defined_in_idsrv", "jnfviuhriw8687wedcbedjnwiecv987y6vibrfviuhfv876=");
But the response I get is in error with an InternalServerError. Trace is activated on my idSrv instance, so I checked it. The message I get from the systemIdentityModel.svclog file is this one :
ID4036 : la clé nécessaire pour déchiffrer le jeton de sécurité chiffré n'a pas pu être résolue à partir de l'identificateur de clé de sécurité suivant « <e:EncryptedKey xmlns:e="http://www.w3.org/2001/04/xmlenc#"><e:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" xmlns="http://www.w3.org/2000/09/xmldsig#" /></e:EncryptionMethod><KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"><o:SecurityTokenReference xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><X509Data><X509IssuerSerial><X509IssuerName>CN=GeoTrust SSL CA - G2, O=GeoTrust Inc., C=US</X509IssuerName><X509SerialNumber>23205537042980196506765197579588450542</X509SerialNumber></X509IssuerSerial></X509Data></o:SecurityTokenReference></KeyInfo><e:CipherData><e:CipherValue>Pqyz5xgkTphI0/PzqMethuV0bKJUBg5bjksTSEaGDQ+GZOVEv3Fr1X5B9r4cRh+4UszhUL3HRHEeWYMtxiTNRxQ0nU0Xp31WU4Gu5loPXIlLMLZsvo1NwCh3DXvaQfWl9nXmvkNQGZFNq7qAPMC4YPWfvvxbWDUgqfCN6qVZqrAE53Gifs9wWYwkyT9NZNR0PXziIVLgWqfEp6xXSUkQlhL448FjuzBDXcjqcrLpwaSJbINW2W1c8nAXdDyFYuJg7fXKpm3t/3Burvg0gitveREPRSqC15ADbCT/yY0u9vIyzd... ». Vérifiez que le SecurityTokenResolver est renseigné avec la clé requise.
Which would be like this in english :
ID4036: The key needed to decrypt the encrypted security token could not be resolved from the following security key identifier 'X'. Ensure that the SecurityTokenResolver is populated with the required key.
So it's quite easy to understand, the X509 certificate needed to decrypt the incoming SAML token has the IssuerName value of "CN=GeoTrust SSL CA - G2, O=GeoTrust Inc" is missing on the IdSrv hosting machine. BUT... the certificate is present in "My" certificate store and in the "Trusted root authority" store. When I check the "Manage privete key" window, I see that the user of the app pool running idSrv (NetworkService) has Read/Write access. Can anyone help me about where to look to get it working ? I'm totally stuck for 2 days now...
The only thing that seems strange is that the serial number in the trace file's message does not seem to exists. Either on the ADFS machine nor on the idSrv server...