我正在测试一些基于SAML2的登录流,使用的是与某些SAML2身份提供者集成的Keycloak。登录到服务时,我可以使用如下所示的断言生成SAML响应。我希望有一种解析/解密整个结构的通用方法,这样我就可以以解密XML的形式获得响应(这样我就可以在测试期间看到原始XML )。最好是构建一个.NET核心应用程序,或者是一个Java应用程序。我的身份代理已经将一个公钥交换给身份提供者。
我为没有成为这方面的专家而事先表示歉意。我看到了处理SAML响应的多个.NET代码片段,但在我看来,似乎有多种SAML响应格式,而且我不知道如何处理这种特定类型的SAML响应。
<?xml version="1.0" encoding="UTF-8"?>
<saml2p:Response xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" Destination="https://somedestination.com" ID="_8665f62b-e462-402f-88e9-2bfbbff836f4" InResponseTo="ID_d3975e20-95dc-4e08-9c75-8d611d8d92f8" IssueInstant="2021-02-16T13:10:03.619Z" Version="2.0">
<saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">https://someissuer.com</saml2:Issuer>
<saml2p:Status>
<saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</saml2p:Status>
<saml2:EncryptedAssertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
<xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="_96f98f75573fe57e6cb95d4f84a4460e" Type="http://www.w3.org/2001/04/xmlenc#Element">
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"/>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<xenc:EncryptedKey Id="_f494c7faf84f0cc2cf704adc6ce7b7e4"
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2009/xmlenc11#rsa-oaep"
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"/>
<xenc:CipherData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<xenc:CipherValue>someCipherValueElementPoppingUpHere</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedKey>
</ds:KeyInfo>
<xenc:CipherData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<xenc:CipherValue>someOtherCipherValueElementPoppingUpHere</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedData>
</saml2:EncryptedAssertion>
</saml2p:Response>发布于 2021-02-17 13:26:32
最后,我把这件事办好了(在这一过程中我可能会有所改进)。诀窍是首先使用RSA私钥解密第一个CipherValue,然后使用解密值作为AES密钥来解密后一个片段。我不是AES方面的专家,但在阅读了有关标准之后,我读到可以(据称)通过从AES密钥中提取前16位来确定所谓的IV密钥。由于某些原因,我遇到了一些奇怪的填充问题,我不得不通过简单的字符串替换来纠正这些问题。此外,如果使用其他加密标准,则应该修改实现。这个实现是基于.NET 5的,据我所知,.NET 5特有的特性是X509Certificate2.CreateFromPemFile方法。
namespace SAMLDecrypter
{
class Program
{
static void Main(string[] args)
{
var certPath = "cartPath";
var samlPath = "samlresponse.xml";
var saml = XElement.Parse(File.ReadAllText(samlPath));
var cert = X509Certificate2.CreateFromPemFile(certPath);
var cipherKeys = GetCipherValues(saml);
var privateKey = cert.GetRSAPrivateKey();
var aeskey = privateKey.Decrypt(Convert.FromBase64String(cipherKeys[0]), RSAEncryptionPadding.OaepSHA1);
var assertions = AesDecrypt(Convert.FromBase64String(cipherKeys[1]), aeskey);
assertions = XElement.Parse(assertions.Substring(assertions.IndexOf("<"))).ToString();
Console.WriteLine(assertions);
}
static string[] GetCipherValues(XElement saml)
{
var alg = XName.Get("Algorithm");
var encryptedData = XPathSelectElements(saml, "./saml2:EncryptedAssertion/xenc:EncryptedData").First();
var encryptionMethod = XPathSelectElements(encryptedData, "./xenc:EncryptionMethod").First().Attribute(alg).Value;
var keyInfo = XPathSelectElements(encryptedData, "./ds:KeyInfo").First();
var encryptionMethodKey = XPathSelectElements(keyInfo, "./xenc:EncryptedKey/xenc:EncryptionMethod").First().Attribute(alg).Value;
var cipherValueKey = XPathSelectElements(keyInfo, "./xenc:EncryptedKey/xenc:CipherData/xenc:CipherValue").First().Value;
var cipherValue = XPathSelectElements(encryptedData, "./xenc:CipherData/xenc:CipherValue").First().Value;
return new[] { cipherValueKey, cipherValue };
}
private static Dictionary<string, string> GetDefaultDict()
{
return new Dictionary<string, string>
{
{ "saml2", "urn:oasis:names:tc:SAML:2.0:assertion" },
{ "saml2p", "urn:oasis:names:tc:SAML:2.0:protocol" },
{ "xenc", "http://www.w3.org/2001/04/xmlenc#" },
{ "ds", "http://www.w3.org/2000/09/xmldsig#" }
};
}
private static IEnumerable<XElement> XPathSelectElements(XNode element, string query)
{
return XPathSelectElements(element, query, GetDefaultDict());
}
private static IEnumerable<XElement> XPathSelectElements(XNode element, string query, Dictionary<string,string> namespaces)
{
var namespaceManager = new XmlNamespaceManager(new NameTable());
namespaces.ToList().ForEach(entry => namespaceManager.AddNamespace(entry.Key, entry.Value));
return element.XPathSelectElements(query, namespaceManager);
}
static string AesDecrypt(byte[] cipherText, byte[] aesKey)
{
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (aesKey == null || aesKey.Length < 16)
throw new ArgumentNullException("Key");
var aesIV = aesKey.Take(16).ToArray();
using (var ms = new MemoryStream())
{
using (var aes = new AesManaged()) {
aes.Key = aesKey;
aes.IV = aesIV;
aes.Padding = PaddingMode.ISO10126;
CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write);
cs.Write(cipherText, 0, cipherText.Length);
cs.Close();
var result = Encoding.UTF8.GetString(ms.ToArray());
return result;
}
}
}
}
}https://stackoverflow.com/questions/66225416
复制相似问题