使用.NET 4.5(System.IdentityModel)/ WIF解密SAML 2断言

时间:2015-02-11 11:06:06

标签: c# encryption wif saml-2.0

我正在尝试解密从基于Java的身份提供程序发出的加密SAML 2.0断言。

给出以下安全令牌处理程序的设置:

X509Certificate2 cert = ... // Contains private key
var serviceTokens = new List<SecurityToken>();
serviceTokens.Add(new X509SecurityToken(cert));

var issuers = new ConfigurationBasedIssuerNameRegistry(); 
issuers.AddTrustedIssuer("...thumbprint...", "nottherealname");

var configuration = new SecurityTokenHandlerConfiguration
            {
                AudienceRestriction = { AudienceMode = AudienceUriMode.Never },
                CertificateValidationMode = X509CertificateValidationMode.None,
                RevocationMode = X509RevocationMode.NoCheck,
                IssuerNameRegistry = issuers,
                MaxClockSkew = TimeSpan.FromMinutes(5),
                ServiceTokenResolver = SecurityTokenResolver.CreateDefaultSecurityTokenResolver(serviceTokens.AsReadOnly(), false)
            };

var tokenHandlers = SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection(configuration);

我得到一个加密的SAML断言,例如:

<saml:EncryptedAssertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
  <xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Type="http://www.w3.org/2001/04/xmlenc#Element">
    <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" />
    <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
      <xenc:EncryptedKey>
        <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
        <xenc:CipherData>


    <xenc:CipherValue>Fwhv/zEVi3eQvQN372L1S+pVDM5JKs1Kc2I25djuiOPdwKReCXRhnd5QL4Y8wJDWZ5vAlOxHkNAZ
OwOg4NsSI8KssrygNk4fwvNdVAGMB5ytI1QTGRqG6WwP4Em+uLN3VXbqiLWA9D6uO0BwATF9HdTb
j/IMhGCxZ1ZKrKQF5OL2PHKf4DqyNa5d9CNZenhYyYghgYrhgZtQVl/VARAp9VKsM/lbkPsEU8Ty
ow4LnTlYqBnykrOEJowN5B+HXGvfhbIBHyGzdCC+WbcEbI898zy/VhZ63VyFL2GSTdDWv10IEMy5
CHom4Qruer1xpyQMrxJ6EK30HMhVppToivgoFQ==</xenc:CipherValue>
        </xenc:CipherData>
      </xenc:EncryptedKey>
    </ds:KeyInfo>
    <xenc:CipherData>
      <xenc:CipherValue>...</xenc:CipherValue>
    </xenc:CipherData>
  </xenc:EncryptedData>
</saml:EncryptedAssertion>

当我尝试阅读令牌时:

var tokenReader = new XmlNodeReader(xmlDoc); // XML document with root element <saml:EncryptedAssertion ....
if (!tokenHandlers.CanReadToken(tokenReader)) throw new Exception("Unreadable token");

var token = tokenHandlers.ReadToken(tokenReader);

然后我在最后一个代码行上得到以下异常:

 ID4022: The key needed to decrypt the encrypted security token could not be resolved. Ensure that the SecurityTokenResolver is populated with the required key.

根据身份提供者,用于加密有效负载的对称密钥使用我的公钥加密。但是,似乎无法使用X509证书中的私钥来解密密钥。这是我对错误消息的解释。可能是错误信息错了吗?还有什么可能是错的?我的配置不完整吗?

2 个答案:

答案 0 :(得分:5)

我也遇到了这个问题,最后我用这个问题的答案来解决这个问题:How to disable Subject Key Identifier in SecurityTokenResolver

更改现有行

ServiceTokenResolver = SecurityTokenResolver.CreateDefaultSecurityTokenResolver(serviceTokens.AsReadOnly(), false)

ServiceTokenResolver = new Saml2SSOSecurityTokenResolver(serviceTokens)

并添加如下类:

private class Saml2SSOSecurityTokenResolver : SecurityTokenResolver
{
    List<SecurityToken> _tokens;

    public Saml2SSOSecurityTokenResolver(List<SecurityToken> tokens)
    {
        _tokens = tokens;
    }
    protected override bool TryResolveSecurityKeyCore(System.IdentityModel.Tokens.SecurityKeyIdentifierClause keyIdentifierClause, out System.IdentityModel.Tokens.SecurityKey key)
    {
        var token = _tokens[0] as X509SecurityToken;

        var myCert = token.Certificate;

        key = null;

        var ekec = keyIdentifierClause as EncryptedKeyIdentifierClause;

        if (ekec != null)
        {
            if (ekec.EncryptionMethod == "http://www.w3.org/2001/04/xmlenc#rsa-1_5")
            {
                var encKey = ekec.GetEncryptedKey();
                var rsa = myCert.PrivateKey as RSACryptoServiceProvider;
                var decKey = rsa.Decrypt(encKey, false);
                key = new InMemorySymmetricSecurityKey(decKey);
                return true;
            }

            var data = ekec.GetEncryptedKey();
            var id = ekec.EncryptingKeyIdentifier;
        }

        return true;
    }

    protected override bool TryResolveTokenCore(System.IdentityModel.Tokens.SecurityKeyIdentifierClause keyIdentifierClause, out System.IdentityModel.Tokens.SecurityToken token)
    {
        throw new NotImplementedException();
    }

    protected override bool TryResolveTokenCore(System.IdentityModel.Tokens.SecurityKeyIdentifier keyIdentifier, out System.IdentityModel.Tokens.SecurityToken token)
    {
        throw new NotImplementedException();
    }
}

答案 1 :(得分:3)

我试图以不同的方式解密EncryptedAssertion,最后使用https://www.nuget.org/packages/SAML2.Core(github:https://github.com/elerch/SAML2)来解决这个问题。

我按照以下步骤操作:

  1. 创建一个包含公共证书和私钥的pfx文件(PKCS#12),如下所示:
  2.   

    openssl pkcs12 -export -in -inkey -out cert_key.p12

    1. 使用saml打开文档:EncryptedAssertion作为根元素

    2. 阅读证书pkcs12容器

    3. 设置文档和密钥

    4. 解密文件
    5. 完整代码:

              var doc = LoadXmlDocument(@"path\to\xml\withencryptedassertion");
              var cert = new X509Certificate2(@"path\to\cert_key.p12", "<container_password>");
      
              var encryptedAssertion = new SAML2.Saml20EncryptedAssertion((RSA)cert.PrivateKey, doc);
              encryptedAssertion.Decrypt();
              var decryptedContent = encryptedAssertion.Assertion.InnerXml;
      

      LoadXmlDocument是一个基本的文件阅读器:

         public static XmlDocument LoadXmlDocument(string assertionFile) {
              using (var fs = File.OpenRead(assertionFile))
              {
                  var document = new XmlDocument { PreserveWhitespace = true };
                  document.Load(fs);
                  fs.Close();
                  return document;
              }
          }
      

      我使用https://developers.onelogin.com/saml/online-tools/生成证书和示例数据来测试此代码