Claims系列 - ID4036错误(The key needed to decrypt the encrypted security token could not be resolved fro...

错误现象

 

错误原因

a) 在SecurityTokenService.GetScope()方法中,设置了Scope.EncryptingCredentials属性为某个X509证书(公钥);

b) Relying Party应用程序中没有设置对应的证书(包含私钥)。

 

原因分析

从出错页面的规模信息可知, ID4036是由ID1044错误引发的。

导致ID1044异常的代码为Microsoft.IdentityModel.Web.TokenReceiver.ReadToken(String tokenXml, XmlDictionaryReaderQuotas readerQuotas)。

通过.Net Relector查看TokenReceiver.ReadToken()的内部实现如下:

 1 public SecurityToken ReadToken(string tokenXml, XmlDictionaryReaderQuotas readerQuotas)
 2 {
 3  //此处无关代码被省略
 4  catch (EncryptedTokenDecryptionFailedException exception)
 5     {
 6         string str3;
 7         if (this._serviceConfiguration.ServiceCertificate == null)
 8         {
 9             str3 = SR.GetString("NoCert"new object[0]);
10         }
11         else
12         {
13             str3 = "[Thumbprint] " + this._serviceConfiguration.ServiceCertificate.Thumbprint;
14         }
15         throw DiagnosticUtil.ExceptionUtil.ThrowHelperError(new SecurityTokenException(SR.GetString("ID1044"new object[] { str3 }), exception));
16     }
17     return token2;
18 }

显而易见,ID1044异常是由于this._serviceConfiguration.ServiceCertificate == null引起。

 

再查看TokenReceiver类,可知_serviceConfiguration是其私有字段,拍被设置的地方是在构造函数之中,并且是从外部传过来的.

1 public TokenReceiver(ServiceConfiguration serviceConfiguration)
2 {
3     if (serviceConfiguration == null)
4     {
5         throw DiagnosticUtil.ExceptionUtil.ThrowHelperArgumentNull("serviceConfiguration");
6     }
7     this._serviceConfiguration = serviceConfiguration;
8 }

那么此构造函数在什么地方调用的呢?从调用堆栈可知,推测可能是WSFederationAuthenticationModule.SignInWithResponseMessage(HttpRequest request).

打开.Net Reflector一看,果不其然:

 1 private void SignInWithResponseMessage(HttpRequest request)
 2 {
 3 //省略前面的无关代码
 4 
 5     if (!e.Cancel)
 6     {
 7         TokenReceiver receiver = new TokenReceiver(base.ServiceConfiguration);
 8         IClaimsPrincipal claimsPrincipal = receiver.AuthenticateToken(e.SecurityToken, true, HttpContext.Current.Request.RawUrl);
 9 
10 //省略后面的无关代码
11 
12     }
13 }

 传入的参数值为base.ServiceConfiguration,即HttpModuleBase.ServiceConfiguration;其定义如下:

 1 public ServiceConfiguration ServiceConfiguration
 2 {
 3     get
 4     {
 5         return this._serviceConfiguration;
 6     }
 7     set
 8     {
 9         if (value == null)
10         {
11             throw DiagnosticUtil.ExceptionUtil.ThrowHelperArgumentNull("value");
12         }
13         this._serviceConfiguration = value;
14     }
15 }

由于ServiceConfiguration是可读写属性,因此可能从外部和内部设置值。

先看外部有没有可能。从调用堆栈可知,其调用都为WSFederationAuthenticationModule.OnAuthenticateRequest(Object sender, EventArgs args):

ExpandedBlockStart.gifView Code
 1 protected virtual void OnAuthenticateRequest(object sender, EventArgs args)
 2 {
 3     HttpRequest request = HttpContext.Current.Request;
 4     if (this.CanReadSignInResponse(request))
 5     {
 6         try
 7         {
 8             this.SignInWithResponseMessage(request);
 9         }
10         catch (Exception exception)
11         {
12             if (DiagnosticUtil.IsFatal(exception))
13             {
14                 throw;
15             }
16             if (DiagnosticUtil.TraceUtil.ShouldTrace(TraceEventType.Warning))
17             {
18                 DiagnosticUtil.TraceUtil.TraceString(TraceEventType.Warning, SR.GetString("ID8020"new object[] { exception }), new object[0]);
19             }
20             ErrorEventArgs args2 = new ErrorEventArgs(exception);
21             this.OnSignInError(args2);
22             if (!args2.Cancel)
23             {
24                 throw;
25             }
26         }
27     }
28 }

从其实现看,并没有设置ServiceConfiguration值的地方;回过头再看HttpModuleBase,可知设置的地方是Init()函数:

1  public  void Init(HttpApplication context)
2 {
3      this._serviceConfiguration = FederatedAuthentication.ServiceConfiguration;
4      this.InitializeModule(context);
5 }

并且直接引用了FederatedAuthentication.ServiceConfiguration属性。再查看这个属性的定义:

 

 1  public  static ServiceConfiguration ServiceConfiguration
 2 {
 3      get
 4     {
 5          lock (_serviceConfigurationLock)
 6         {
 7              if (_serviceConfiguration ==  null)
 8             {
 9                 _serviceConfiguration =  new ServiceConfiguration();
10                 ServiceConfigurationCreatedEventArgs e =  new ServiceConfigurationCreatedEventArgs(_serviceConfiguration);
11                 EventHandler<ServiceConfigurationCreatedEventArgs> serviceConfigurationCreated = ServiceConfigurationCreated;
12                  if (serviceConfigurationCreated !=  null)
13                 {
14                     serviceConfigurationCreated( null, e);
15                 }
16                 _serviceConfiguration = e.ServiceConfiguration;
17                  if (!_serviceConfiguration.IsInitialized)
18                 {
19                     _serviceConfiguration.Initialize();
20                 }
21             }
22              return _serviceConfiguration;
23         }
24     }
25 }

从代码中可得到两点信息:1)_serviceConfiguration是在第一次请求时new出来的; 2)在第一次请求时会触发ServiceConfigurationCreated事件,并可访问到

_serviceConfiguration。

再看ServiceConfiguration的构造函数:

ExpandedBlockStart.gif View Code
 1  public ServiceConfiguration()
 2 {
 3      this._certificateValidationMode = DefaultCertificateValidationMode;
 4      this._claimsAuthenticationManager =  new ClaimsAuthenticationManager();
 5      this._claimsAuthorizationManager =  new ClaimsAuthorizationManager();
 6      this._exceptionMapper =  new ExceptionMapper();
 7      this._revocationMode = DefaultRevocationMode;
 8      this._serviceName = DefaultServiceName;
 9      this._serviceMaxClockSkew = DefaultMaxClockSkew;
10      this._trustedStoreLocation = DefaultTrustedStoreLocation;
11     MicrosoftIdentityModelSection current = MicrosoftIdentityModelSection.Current;
12     ServiceElement element = (current !=  null) ? current.ServiceElements.GetElement(DefaultServiceName) :  null;
13      this.LoadConfiguration(element);
14 }

注意到最后调用了LoadConfiguration进行初始化,再看其内部实现:

 1  protected  void LoadConfiguration(ServiceElement element)
 2 {
 3      if (element !=  null)
 4     {
 5 
 6  // 省略前面无关代码
 7           if (( this._serviceCertificate ==  null) && element.ServiceCertificate.IsConfigured)
 8         {
 9              this._serviceCertificate = GetServiceCertificate(element);
10         }
11  // 省略后面无关代码
12      }
13      this._securityTokenHandlerCollectionManager =  this.LoadHandlers(element);
14 }

再看GetServiceCertificate()

 

 1  private  static X509Certificate2 GetServiceCertificate(ServiceElement element)
 2 {
 3     X509Certificate2 certificate2;
 4      try
 5     {
 6         X509Certificate2 certificate = element.ServiceCertificate.GetCertificate();
 7          if (certificate !=  null)
 8         {
 9             X509Util.EnsureAndGetPrivateRSAKey(certificate);
10         }
11         certificate2 = certificate;
12     }
13      catch (ArgumentException exception)
14     {
15          throw DiagnosticUtil.ExceptionUtil.ThrowHelperConfigurationError(element,  " serviceCertificate ", exception);
16     }
17      return certificate2;
18 }

至此, 终于知道X509证书默认是从ServiceElement即配置文件中的<microsoft.identitymodel><service><servicecertificate>节点。由此我们可得到如下两种解决方案:

解决方案

1 设置配置文件中的<microsoft.identitymodel><service><servicecertificate>节点

  a)找开Relying Party应用程序的配置文件;

  b)设置X509证书如下:

 

2 在FederatedAuthentication.ServiceConfigurationCreated事件处理函数中设置

   a) 在Relying Party工程中添加Global.asax文件(如果不存在的话);

   b) 添加Application_Start事件处理函数

 

转载于:https://www.cnblogs.com/beiguren/archive/2012/07/26/2609505.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值