Skip to content

Commit c84bbd2

Browse files
sgramponeBeta Bot
authored andcommitted
Cherry pick branch 'genexuslabs:gamsaml20_eo' into beta
1 parent f464219 commit c84bbd2

7 files changed

Lines changed: 282 additions & 1 deletion

File tree

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,6 @@ ClientBin/
226226
*.dbmdl
227227
*.dbproj.schemaview
228228
*.jfm
229-
*.pfx
230229
*.publishsettings
231230
orleans.codegen.cs
232231

dotnet/src/extensions/gam/src/DotNetFramework/GamSaml20/Properties/AssemblyInfo.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@
77
// set of attributes. Change these attribute values to modify the information
88
// associated with an assembly.
99
[assembly: AllowPartiallyTrustedCallers]
10+
[assembly: InternalsVisibleTo("GamTest, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e52e348955bc6a30ec9c7319f94e2ec18fd73d4445273f81bd2132e7309b52f3582b72d9dcd0231806e6eea947b40d948767cd8bdc33b738e2787bef3d0c8200979748684fe3aa4a39adc2e81e05750291dde1b82362d82dc0e611c6048d51483f67bd656f21db0a70679b30ec744b3e66d518d25b66f66be72674f664c58c97")]

dotnet/src/extensions/gam/test/DotNetFramework/GamTest/GamTest.csproj

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,14 @@
5454
<None Update="Resources\dummycerts\RSA_sha256_2048\sha256d_key.key">
5555
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
5656
</None>
57+
<None Update="Resources\dummycerts\saml20\javacert.crt">
58+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
59+
</None>
60+
<None Update="Resources\dummycerts\saml20\mykeystore.pfx">
61+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
62+
</None>
63+
<None Update="Resources\dummycerts\saml20\sha512_cert.crt">
64+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
65+
</None>
5766
</ItemGroup>
5867
</Project>
Binary file not shown.
Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Reflection;
6+
using System.Security.Cryptography;
7+
using System.Security.Policy;
8+
using System.Text;
9+
using System.Text.Encodings.Web;
10+
using System.Threading.Tasks;
11+
using System.Web;
12+
using System.Xml;
13+
using GamSaml20;
14+
using GamSaml20.Utils;
15+
using NUnit.Framework;
16+
17+
namespace GamTest.Saml20
18+
{
19+
[TestFixture]
20+
public class TestRedirectBinding
21+
{
22+
private static string resources;
23+
private static string BASE_PATH;
24+
25+
private static string password;
26+
private static RedirectBinding redirectBindingLogoutResponse;
27+
28+
[SetUp]
29+
public virtual void SetUp()
30+
{
31+
BASE_PATH = GetStartupDirectory();
32+
resources = Path.Combine(BASE_PATH, "Resources", "dummycerts");
33+
password = "dummy1";
34+
35+
redirectBindingLogoutResponse = new RedirectBinding();
36+
string logoutResponse = "SAMLResponse=fVHLauQwEPwVo7tsSX7JwnYICVkCyR52ZnPYyyBLPYnBozZueZPPX8%2BEOQSWHJvqquqqbm8%2BTlPyFxYaMXRMpoIlEBz6Mbx27Pf%2BgWuWULTB2wkDdCwgu%2BlbsqdpNk%2F4imv8BTRjIEg2pUDmAnVsXYJBSyOZYE9AJjqzu31%2BMioVZl4wosOJJfdAcQw2XszfYpzJZNmEzk5vSDH7cft8OLMOdxgCuIjLT4gPmaUJU0vzB0se7zt2GKq8sMo7nov6yItKAdd60FwcRV0rXRWFhm01XC%2Fd40aq68ZrbS23FQheQJVz7SvNS%2B%2B9ODYbLO1GIlrhMZwbiB1TQpVclFw2e1kaKY3KU1XWf1jycm1wy8f69kJbPhv5vgtLBMs5P%2Buv%2BSlS%2Bj4Gj%2B%2BUBohZCa4ehmPDpa7VFs81fFBCcitBatk01ZCXWZt9el5%2Fs4s2rvR1ukMPyYudVvj%2BJrpsm93qHBCxrG%2Bzr6LZ%2F%2F7f%2FwM%3D&RelayState=http%3A%2F%2Frelaystate.com&SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha256&Signature=ZhxqgSDAmtwxtUAXCafCNAXKLwL9iPgsqInuZfQ97dyPsGyszpgJftjgHBtoQpz159NjFpX0dGicVier2TQa82JBqgxUvdPT6mg%2FppdG7Z%2BnOXNttflqCd7mA3b%2FUOmWE4XgODz2mym%2BNPBmETAYmKofXo5ghpQc8IgGpI166%2F5VOwwhLcrg76HeYSxubxS4BoFUtLmpRnkaww9VQPZPIyh4kBmsCqe%2FV4QvM626ehdXDjPIciBgylt2ENMfQGZo83ubMB7KxgDNdErBgmTpILxftLn3ZH0FJAbM%2B3bzj6DFJ1yLuyUnUbdxOjoKaRskil853jKqmbvQtxRQ4QvZIg%3D%3D";
37+
redirectBindingLogoutResponse.Init(logoutResponse);
38+
}
39+
40+
[Test]
41+
public void TestSignatureValidation_true()
42+
{
43+
SamlParms parms = new SamlParms();
44+
parms.TrustedCertPath = Path.Combine(resources, "saml20", "javacert.crt");
45+
Console.WriteLine(parms.TrustedCertPath);
46+
Assert.IsTrue(redirectBindingLogoutResponse.VerifySignatures(parms), "TestSignatureValidation_true Logout");
47+
48+
}
49+
50+
[Test]
51+
public void TestSignatureValidation_false()
52+
{
53+
SamlParms parms = new SamlParms();
54+
parms.TrustedCertPath = Path.Combine(resources, "saml20", "sha512_cert.crt");
55+
Assert.IsFalse(redirectBindingLogoutResponse.VerifySignatures(parms), "TestSignatureValidation_false Logout");
56+
}
57+
58+
[Test]
59+
public void TestGetLogoutAssertions()
60+
{
61+
string expected = "{\"Destination\": \"https://localhost/GAM_SAML_ConnectorNetF/aslo.aspx\",\"InResponseTo\": \"_779d88aa-a6e0-4e63-8d68-5ddd0f97791a\",\"Value\": \"urn:oasis:names:tc:SAML:2.0:status:Success\",\"Issuer\": \"https://sts.windows.net/5ec7bbf9-1872-46c9-b201-a1e181996b35/\" }";
62+
Assert.AreEqual(expected, redirectBindingLogoutResponse.GetLogoutAssertions());
63+
}
64+
65+
[Test]
66+
public void TestIsLogout()
67+
{
68+
string loginResponse = "SAMLResponse=5Vbfb%2BJGEH7uSf0frH03%2FoGNjRW40qSpkJJcFOipvZdoWQ%2FgO3vX3V0Hcn99Zw0mYHqE3lWqTn2yPDOe%2Beb7dmZ98XZd5NYTSJUJPiBexyUWcCbSjC8G5LfptR0TS2nKU5oLDgPCBXk7vFC0yP0yeQBVCq7AwiRcJRvrgFSSJ4KqTCWcFqASzZLJ6PYm8TtuUkqhBRM52X6zVgOy1LpMHGe1WnVW3Y6QC8d3Xc%2F5%2FfZmwpZQUGJdgdIZp7oGuQ3PBaP5Uijt%2FDq6fTQFHi8F58C0kHegrx1KmepQVa6JNb4akCy1w27kRj0v7rluHHleEEZe0I17YRB3%2BxjEm36mYkAeGcy7bBa5duy5gR0wd2bHaRrazAv6EaPhPEgR2VipCsbcUKQHxHf90HZD23enXj8JeokXdmK394FY7xuKkQQy%2FPHNDxsOk%2Fp7uU%2Fgaf6oUiAND8S6FrKg%2BnS4sWDj8zo0Aa4z%2FUyGe4SLT5p2mCgcWH%2Fyn0q6iP7sxn88FM%2FTMI0unH2QL6jLZKKprpSxtEyXIgXrPc0rOA1M1dHJpGIMlCJOndw5yr5ladR0%2FVVEbdWPfb%2FXDyK%2F6wZ9PAhhL%2FK9KHY9P%2Br77jco2ZLyv5ClgTCpZh9xAGpTY7vDWuOrV2F5Ha8Nq6BZPkpTaQQa0hw%2B4hqQovMZZjTPhfppYQIMygbQptRB9S0inMx5ZrIaDW9BL0V6miFWJDOgEuSG4lP5rqimXze8d0K%2F4%2B%2FkaK5BtiUPvY3kEUr%2BACwrMzDn4p8vH2fLh%2FPFBjYKOscSbnvG2DQzgcog%2FhlQHzg6oXtwz2zrQKdRlWKDDJBFLTO2g3UUMJzcY8sN2p31oMcvJWv8Lw3ttzmq9JKb0YcCqbbq1zPmcYLHE1ONeQrrc5VvtY6FEJKGtW73%2FOK5zHGjPMB8eHLpsISZODTf42MlZHqPFx7qCelUUo4nVOoXlv4me4vHFrIDx46qAxI1Mj6rNBx6j9yWmVUcQVySv5gxJrXhnN1FMYWxNmtihnGsNaW7MvVNsLvqs9fvejurFWeAPwgqS%2FRziSjXuPExIV%2Bct4YOq7cobXyv0nKdSaXN6%2FdCzWKdwtO%2Fy8GNWGT8%2F3k0jqwHA7XzNn8ZaNz9vjQ30fAv&RelayState=http%3A%2F%2Frelaystate.com&SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha256&Signature=aeNkIQLkkrNIxVgd1slzZXJpkEzvU0LIwqMR9wWLRT%2FjMHo7ldaCeGlFk3H%2Bbr4l3qEttjsTBWgTGgPDgzax7DDCUSvJPdAh0YB14T9oZ243cxap2OOi483TkBPt%2BwM6Q4AaePWbH1NdUvFUmP9ovl4Ub3iC4O%2FmZFRR3l4TU4z5ZR5OO8%2FFm%2BppvYXf%2FJDbsTLkKgF72a1lD1YhNWdqYKx3%2BQ22x94osmXis3omG7cdNDlo8ULesWL2RVXzftjmHa9zqWidTrHjyA6fSouTV3pQHmzrI8t9g3tuk5jKzTbOPmF2KBhEPzvN26jH2Bdy5b4PCvkJ1L9VeJKlGwBejQ%3D%3D";
69+
RedirectBinding redirectBindingLoginResponse = new RedirectBinding();
70+
redirectBindingLoginResponse.Init(loginResponse);
71+
Assert.IsTrue(redirectBindingLogoutResponse.IsLogout(), "TestIsLogout Logout");
72+
Assert.IsFalse(redirectBindingLoginResponse.IsLogout(), "TestIsLogout Login");
73+
}
74+
75+
[Test]
76+
public void TestGetRelayState()
77+
{
78+
string expected = "http://relaystate.com";
79+
Assert.AreEqual(expected, redirectBindingLogoutResponse.GetRelayState());
80+
}
81+
82+
83+
[Test]
84+
public void TestLoginRequest()
85+
{
86+
string function = "Login";
87+
RedirectBinding redirectBinding = new RedirectBinding();
88+
SamlParms parms = CreateParameters();
89+
string samlRequest = RedirectBinding.Login(parms, "http://relaystate.com");
90+
string queryString = GetQueryString(samlRequest);
91+
Dictionary<string, string> redirectMessage = ParseRedirect(queryString);
92+
93+
//test login request parameters
94+
TestRequestParameters(redirectMessage, function);
95+
96+
//test login signature
97+
TestRequestSignature(redirectMessage, function);
98+
99+
//test login request xml parameters
100+
string xml = System.Text.Encoding.UTF8.GetString(GamSaml20.Utils.Encoding.DecodeAndInflateXmlParameter(redirectMessage["SAMLRequest"]));
101+
102+
string expectedXml = $"<AuthnRequest ID=\"_idtralala\" Version=\"2.0\" IssueInstant=\"{GetIssuerInstant(xml, "AuthnRequest").Trim()}\" Destination=\"http://endpoint/saml\" AssertionConsumerServiceURL=\"http://myapp.com/acs\" ForceAuthn=\"false\" xmlns=\"urn:oasis:names:tc:SAML:2.0:protocol\">\r\n" +
103+
" <Issuer xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\">EntityID</Issuer>\r\n" +
104+
" <NameIDPolicy Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress\" AllowCreate=\"true\" SPNameQualifier=\"SPEntityID\" />\r\n" +
105+
" <RequestedAuthnContext Comparison=\"exact\">\r\n" +
106+
" <AuthnContextClassRef xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\">urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</AuthnContextClassRef>\r\n" +
107+
" </RequestedAuthnContext>\r\n" +
108+
"</AuthnRequest>";
109+
110+
//"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><saml2p:AuthnRequest AssertionConsumerServiceURL=\"http://myapp.com/acs\" Destination=\"http://endpoint/saml\" ForceAuthn=\"false\" ID=\"_idtralala\" IssueInstant=\"" + GetIssuerInstant(xml, "saml2p:AuthnRequest") + "\" Version=\"2.0\" xmlns:saml2p=\"urn:oasis:names:tc:SAML:2.0:protocol\"><saml2:Issuer xmlns:saml2=\"urn:oasis:names:tc:SAML:2.0:assertion\"/><saml2p:NameIDPolicy AllowCreate=\"true\" Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress\" SPNameQualifier=\"SPEntityID\"/><saml2p:RequestedAuthnContext Comparison=\"exact\"><saml2:AuthnContextClassRef xmlns:saml2=\"urn:oasis:names:tc:SAML:2.0:assertion\">urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml2:AuthnContextClassRef></saml2p:RequestedAuthnContext></saml2p:AuthnRequest>";
111+
Assert.AreEqual(expectedXml, xml, "Test Login request xml parameters");
112+
113+
}
114+
115+
[Test]
116+
public void TestLogoutRequest()
117+
{
118+
string function = "Logout";
119+
RedirectBinding redirectBinding = new RedirectBinding();
120+
SamlParms parms = CreateParameters();
121+
string samlRequest = RedirectBinding.Logout(parms, "http://relaystate.com");
122+
string queryString = GetQueryString(samlRequest);
123+
Dictionary<string, string> redirectMessage = ParseRedirect(queryString);
124+
125+
//test logout request parameters
126+
TestRequestParameters(redirectMessage, function);
127+
128+
//test logout signature
129+
TestRequestSignature(redirectMessage, function);
130+
131+
//test logout request xml parameters
132+
string xml = System.Text.Encoding.UTF8.GetString(GamSaml20.Utils.Encoding.DecodeAndInflateXmlParameter(redirectMessage["SAMLRequest"]));
133+
string expectedXml = $"<LogoutRequest ID=\"_idtralala\" Version=\"2.0\" IssueInstant=\"{GetIssuerInstant(xml, "LogoutRequest")}\" Destination=\"http://idp.com/slo\" Reason=\"urn:oasis:names:tc:SAML:2.0:logout:user\" xmlns=\"urn:oasis:names:tc:SAML:2.0:protocol\">\r\n" +
134+
" <Issuer xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\">SPEntityID</Issuer>\r\n" +
135+
" <NameID xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\">nameID</NameID>\r\n" +
136+
" <SessionIndex>123456789</SessionIndex>\r\n" +
137+
"</LogoutRequest>";
138+
Assert.AreEqual(expectedXml, xml, "Test Logout request xml parameters");
139+
140+
}
141+
142+
private static string GetIssuerInstant(string xml, string name)
143+
{
144+
XmlDocument doc = SamlAssertionUtils.CanonicalizeXml(xml);
145+
XmlNodeList nodeList = doc.GetElementsByTagName(name);
146+
string issuer = nodeList[0].Attributes.GetNamedItem("IssueInstant").Value;
147+
return issuer;
148+
149+
}
150+
151+
private bool VerifySignature_internal(string certPath, Dictionary<string, string> redirectMessage)
152+
{
153+
154+
byte[] signature = GamSaml20.Utils.Encoding.DecodeParameter(redirectMessage["Signature"]);
155+
string value;
156+
string signedMessage;
157+
if (redirectMessage.TryGetValue("RelayState", out value))
158+
{
159+
signedMessage = $"SAMLRequest={redirectMessage["SAMLRequest"]}";
160+
signedMessage += $"&RelayState={redirectMessage["RelayState"]}";
161+
signedMessage += $"&SigAlg={redirectMessage["SigAlg"]}";
162+
}
163+
else
164+
{
165+
signedMessage = $"SAMLRequest={redirectMessage["SAMLRequest"]}";
166+
signedMessage += $"&SigAlg={redirectMessage["SigAlg"]}";
167+
}
168+
169+
byte[] query = System.Text.Encoding.UTF8.GetBytes(signedMessage);
170+
try
171+
{
172+
RSACryptoServiceProvider csp = Keys.GetPublicRSACryptoServiceProvider(certPath);
173+
174+
if (csp == null)
175+
{
176+
Assert.Fail("VerifySignature_internal logout RSACryptoServiceProvider is null");
177+
}
178+
string sigalg = HttpUtility.UrlDecode(redirectMessage["SigAlg"]);
179+
GamSaml20.Utils.Hash hash = HashUtils.GetHashFromSigAlg(sigalg);
180+
181+
return csp.VerifyData(query, CryptoConfig.MapNameToOID(HashUtils.ValueOf(hash)), signature);
182+
}
183+
catch (Exception e)
184+
{
185+
Assert.Fail("VerifySignature_internal", e);
186+
return false;
187+
}
188+
}
189+
190+
private void TestRequestSignature(Dictionary<string, string> redirectMessage, string function)
191+
{
192+
bool verifies = VerifySignature_internal(Path.Combine(resources, "saml20", "javacert.crt"), redirectMessage);
193+
Assert.IsTrue(verifies, $"Test {function} request signature");
194+
}
195+
196+
private void TestRequestParameters(Dictionary<string, string> redirectMessage, string function)
197+
{
198+
string relayState = DecodeParm(redirectMessage["RelayState"]);
199+
Assert.AreEqual("http://relaystate.com", relayState, $"Test {function} parameters RelayState");
200+
string sigAlg = DecodeParm(redirectMessage["SigAlg"]);
201+
Assert.AreEqual("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", sigAlg, $"Test {function} request parameters SigAlg");
202+
}
203+
204+
private static string DecodeParm(string parm)
205+
{
206+
try
207+
{
208+
return HttpUtility.UrlDecode(parm);
209+
}
210+
catch (Exception e)
211+
{
212+
Console.WriteLine(e.StackTrace);
213+
return "";
214+
}
215+
}
216+
217+
private static string GetQueryString(string samlRequest)
218+
{
219+
try
220+
{
221+
string[] uri = samlRequest.Split('?');
222+
return uri[1];
223+
}
224+
catch (Exception e)
225+
{
226+
Console.WriteLine(e.StackTrace);
227+
return "";
228+
}
229+
}
230+
231+
private static SamlParms CreateParameters()
232+
{
233+
SamlParms parms = new SamlParms();
234+
parms.CertPath = Path.Combine(resources, "saml20", "mykeystore.pfx");
235+
parms.CertPass = password;
236+
parms.Acs = "http://myapp.com/acs";
237+
parms.ForceAuthn = false;
238+
parms.ServiceProviderEntityID = "SPEntityID";
239+
parms.IdentityProviderEntityID = "EntityID";
240+
parms.PolicyFormat = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress";
241+
parms.AuthContext = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport";
242+
parms.EndPointLocation = "http://endpoint/saml";
243+
parms.Id = "_idtralala";
244+
parms.SingleLogoutEndpoint = "http://idp.com/slo";
245+
parms.SessionIndex = "123456789";
246+
parms.NameID = "nameID";
247+
return parms;
248+
}
249+
250+
private static Dictionary<string, string> ParseRedirect(string request)
251+
{
252+
Dictionary<string, string> result = new Dictionary<string, string>();
253+
string[] redirect = request.Split('&');
254+
255+
foreach (string s in redirect)
256+
{
257+
string[] res = s.Split('=');
258+
result[res[0]] = res[1];
259+
}
260+
return result;
261+
}
262+
263+
private static string GetStartupDirectory()
264+
{
265+
#pragma warning disable SYSLIB0044
266+
string dir = Assembly.GetCallingAssembly().GetName().CodeBase;
267+
#pragma warning restore SYSLIB0044
268+
Uri uri = new Uri(dir);
269+
return Path.GetDirectoryName(uri.LocalPath);
270+
}
271+
}
272+
}

0 commit comments

Comments
 (0)