通过证书认证获取SAML令牌时遇类型转换错误求助
我之前踩过一模一样的坑!这个错误的核心原因很明确:你的客户端证书用了DSA加密算法,但WCF的WSTrust通道在生成签名时,默认会调用RSA的签名格式化器,把DSA类型的密钥强行转成RSA,自然就抛出类型转换异常了——你看堆栈里的RSAPKCS1SignatureFormatter.SetKey(AsymmetricAlgorithm key)这行,就是这里出的问题。
下面给你几个可行的解决方案,按优先级排序:
方案一:换个RSA算法的证书(最省心)
如果你的STS服务支持RSA证书,直接换一个用RSA生成的客户端证书是最快的解决办法。毕竟WCF对RSA的支持是原生且完善的,很多默认配置都是针对RSA设计的,没必要跟DSA死磕。
方案二:修正Cryptoconfig配置,让WCF识别DSA签名
你说之前试过Cryptoconfig但没解决,大概率是配置写错了。试试在你的App.config/Web.config里添加这段配置,把DSA的签名算法和对应的处理类绑定起来:
<configuration> <mscorlib> <cryptographySettings> <cryptoNameMapping> <cryptoClasses> <cryptoClass DsaSignatureDescription="System.Security.Cryptography.DSASignatureDescription, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> </cryptoClasses> <nameEntry name="http://www.w3.org/2000/09/xmldsig#dsa-sha1" class="DsaSignatureDescription" /> </cryptoNameMapping> </cryptographySettings> </mscorlib> </configuration>
这段配置告诉WCF:当遇到http://www.w3.org/2000/09/xmldsig#dsa-sha1这个签名算法时,用DSA对应的处理类来处理,而不是硬套RSA的逻辑。
方案三:手动指定WSTrustChannel的签名算法
在创建WSTrustChannelFactory的时候,手动指定签名算法为DSA对应的URI,强制WCF用DSA的签名逻辑:
private static string GetSamlToken() { "Requesting identity token".ConsoleYellow(); var stsBinding = new WS2007HttpBinding(); stsBinding.Security.Mode = SecurityMode.TransportWithMessageCredential; stsBinding.Security.Message.EstablishSecurityContext = false; stsBinding.Security.Message.NegotiateServiceCredential = false; stsBinding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate; var factory = new WSTrustChannelFactory(stsBinding, "https://sometestservice.com/service"); factory.TrustVersion = TrustVersion.WSTrust13; // 新增这行:指定DSA签名算法 factory.Credentials.ServiceCertificate.Authentication.SignatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#dsa-sha1"; factory.Credentials.ClientCertificate.SetCertificate( StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, "你的证书指纹"); // 剩下的代码... }
方案四:自定义SecurityTokenHandler(进阶)
如果上面的方法都不管用,你可以自定义一个支持DSA的X509SecurityTokenHandler,替换掉WCF默认的handler:
public class DsaX509SecurityTokenHandler : X509SecurityTokenHandler { protected override SecurityKey CreateSecurityKey(X509Certificate2 certificate) { // 判断证书是否是DSA算法 if (certificate.PublicKey.Oid.FriendlyName.Equals("DSA", StringComparison.OrdinalIgnoreCase)) { return new X509AsymmetricSecurityKey(certificate) { AsymmetricAlgorithm = certificate.PublicKey.Key as DSACryptoServiceProvider }; } // 其他情况用默认逻辑 return base.CreateSecurityKey(certificate); } }
然后在配置里注册这个自定义handler:
<system.identityModel> <identityConfiguration> <securityTokenHandlers> <!-- 移除默认的X509 handler --> <remove type="System.IdentityModel.Tokens.X509SecurityTokenHandler, System.IdentityModel" /> <!-- 添加自定义的DSA handler --> <add type="YourProjectNamespace.DsaX509SecurityTokenHandler, YourProjectAssemblyName" /> </securityTokenHandlers> </identityConfiguration> </system.identityModel>
最后提醒你:先确认你的STS服务是否支持DSA签名算法,如果服务端只认RSA,那再怎么折腾客户端也没用,还是得换RSA证书。
内容的提问来源于stack exchange,提问作者Esen

