Reputation: 143
I have implemented a custom STS in order to authenticate users from our web application into a SharePoint instance hosted elsewhere, and is displayed in a frame in the application.
This worked fine during development, and also during testing, however the following exception is being thrown intermittently during UAT:
[System.Security.Cryptography.CryptographicException] Invalid algorithm specified.
at System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr)
at System.Security.Cryptography.Utils.SignValue(SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, Byte[] hash, Int32 cbHash, ObjectHandleOnStack retSignature)
at System.Security.Cryptography.Utils.SignValue(SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, Byte[] hash)
at System.Security.Cryptography.RSACryptoServiceProvider.SignHash(Byte[] rgbHash, Int32 calgHash)
at System.Security.Cryptography.RSAPKCS1SignatureFormatter.CreateSignature(Byte[] rgbHash)
at System.IdentityModel.SignedXml.ComputeSignature(HashAlgorithm hash, AsymmetricSignatureFormatter formatter, String signatureMethod)
at System.IdentityModel.SignedXml.ComputeSignature(SecurityKey signingKey)
at System.IdentityModel.EnvelopedSignatureWriter.ComputeSignature()
at System.IdentityModel.EnvelopedSignatureWriter.OnEndRootElement()
at System.IdentityModel.Tokens.SamlSecurityTokenHandler.WriteAssertion(XmlWriter writer, SamlAssertion assertion)
at System.IdentityModel.Tokens.SecurityTokenHandlerCollection.WriteToken(XmlWriter writer, SecurityToken token)
at System.IdentityModel.Protocols.WSTrust.WSTrustSerializationHelper.WriteRSTRXml(XmlWriter writer, String elementName, Object elementValue, WSTrustSerializationContext context, WSTrustConstantsAdapter trustConstants)
at System.IdentityModel.Protocols.WSTrust.WSTrustSerializationHelper.WriteKnownResponseElement(RequestSecurityTokenResponse rstr, XmlWriter writer, WSTrustSerializationContext context, WSTrustResponseSerializer responseSerializer, WSTrustConstantsAdapter trustConstants)
at System.IdentityModel.Protocols.WSTrust.WSTrust13ResponseSerializer.WriteKnownResponseElement(RequestSecurityTokenResponse rstr, XmlWriter writer, WSTrustSerializationContext context)
at System.IdentityModel.Protocols.WSTrust.WSTrustSerializationHelper.WriteResponse(RequestSecurityTokenResponse response, XmlWriter writer, WSTrustSerializationContext context, WSTrustResponseSerializer responseSerializer, WSTrustConstantsAdapter trustConstants)
at System.IdentityModel.Protocols.WSTrust.WSTrust13ResponseSerializer.WriteXml(RequestSecurityTokenResponse response, XmlWriter writer, WSTrustSerializationContext context)
at System.IdentityModel.Services.WSFederationSerializer.GetResponseAsString(RequestSecurityTokenResponse response, WSTrustSerializationContext context)
at System.IdentityModel.Services.SignInResponseMessage..ctor(Uri baseUrl, RequestSecurityTokenResponse response, WSFederationSerializer federationSerializer, WSTrustSerializationContext context)
at System.IdentityModel.Services.FederatedPassiveSecurityTokenServiceOperations.ProcessSignInRequest(SignInRequestMessage requestMessage, ClaimsPrincipal principal, SecurityTokenService sts, WSFederationSerializer federationSerializer)
at Web.Secure.SharePoint.ProcessRequest()
The implementation of the STS is as follows:
using System.IdentityModel;
using System.IdentityModel.Configuration;
using System.IdentityModel.Protocols.WSTrust;
using System.IdentityModel.Tokens;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
namespace Core.Services
{
public class SharepointSecurityTokenService : SecurityTokenService
{
public SharepointSecurityTokenService(SecurityTokenServiceConfiguration securityTokenServiceConfiguration)
: base(securityTokenServiceConfiguration)
{
}
protected override Scope GetScope(ClaimsPrincipal principal, RequestSecurityToken request)
{
var scope = new Scope(request.AppliesTo.Uri.OriginalString, SecurityTokenServiceConfiguration.SigningCredentials);
scope.TokenEncryptionRequired = false;
scope.ReplyToAddress = request.ReplyTo;
return scope;
}
protected override ClaimsIdentity GetOutputClaimsIdentity(ClaimsPrincipal principal, RequestSecurityToken request, Scope scope)
{
var identity = new ClaimsIdentity(principal.Claims);
return identity;
}
}
}
and the code that calls this and generates the response form is as follows:
var identity = (ClaimsIdentity)HttpContext.Current.User.Identity;
identity.AddClaim(new Claim(ClaimTypes.Role, role));
var claimsPrinciple = new ClaimsPrincipal(identity);
var requestMessage = (SignInRequestMessage)WSFederationMessage.CreateFromNameValueCollection(sharePointLibraryUri, parameters);
var sharepointCertificate = Global.AppCache.GetSharepointCertificate();
if (sharepointCertificate == null)
{
throw new SharePointRequestException("No SharePoint signing certificate.", requestId);
}
var signingCredentials = new X509SigningCredentials(sharepointCertificate);
var config = new SecurityTokenServiceConfiguration(Settings.Default.SharePointTokenIssuerName, signingCredentials);
var sts = new SharepointSecurityTokenService(config);
var responseMessage = FederatedPassiveSecurityTokenServiceOperations.ProcessSignInRequest(requestMessage, claimsPrinciple, sts);
var responseForm = responseMessage.WriteFormPost();
Response.Write(responseForm);
To confirm we have used the same certificate on all environments, and it consistently works in development, but fails on the server. To confuse matters further it has worked for a few hours at a time on this server, but for no apparent reason will stop working again. I do not know what triggers it to either start or stop working.
Upvotes: 3
Views: 892
Reputation: 945
You could be missing a reference to the the algorithm. You may need to include this line when you're application starts up.
CryptoConfig.AddAlgorithm(typeof(RSAPKCS1SHA256SignatureDescription), "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
Upvotes: 0