Skip to content

Commit 69810b0

Browse files
committed
Upgrated from .NET 4.5 to .NET 4.6.2.
Support added for SHA384/SHA512 using .NET library and custom code removed. Load Assertion in new XmlDocument before signature validation. Bug fixes.
1 parent 80328ec commit 69810b0

37 files changed

+258
-348
lines changed

src/ITfoxtec.Identity.Saml2.Mvc/ITfoxtec.Identity.Saml2.Mvc.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<AppDesignerFolder>Properties</AppDesignerFolder>
1010
<RootNamespace>ITfoxtec.Identity.Saml2.Mvc</RootNamespace>
1111
<AssemblyName>ITfoxtec.Identity.Saml2.Mvc</AssemblyName>
12-
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
12+
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
1313
<FileAlignment>512</FileAlignment>
1414
<TargetFrameworkProfile />
1515
<SccProjectName>SAK</SccProjectName>

src/ITfoxtec.Identity.Saml2.Mvc/Properties/AssemblyInfo.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
// General Information about an assembly is controlled through the following
55
// set of attributes. Change these attribute values to modify the information
66
// associated with an assembly.
7-
[assembly: AssemblyTitle("ITfoxtec.Identity.Saml2.Mvc")]
7+
[assembly: AssemblyTitle("ITfoxtec Identity SAML 2.0 MVC")]
88
[assembly: AssemblyDescription("")]
99
[assembly: AssemblyConfiguration("")]
1010
[assembly: AssemblyCompany("ITfoxtec")]
1111
[assembly: AssemblyProduct("ITfoxtec.Identity.Saml2.Mvc")]
12-
[assembly: AssemblyCopyright("Copyright © 2016")]
12+
[assembly: AssemblyCopyright("Copyright © 2017")]
1313
[assembly: AssemblyTrademark("")]
1414
[assembly: AssemblyCulture("")]
1515

@@ -30,6 +30,5 @@
3030
//
3131
// You can specify all the values or you can default the Build and Revision Numbers
3232
// by using the '*' as shown below:
33-
// [assembly: AssemblyVersion("1.0.*")]
34-
[assembly: AssemblyVersion("1.1.1.0")]
35-
[assembly: AssemblyFileVersion("1.1.1.0")]
33+
[assembly: AssemblyVersion("2.0.0.0")]
34+
[assembly: AssemblyFileVersion("2.0.0.0")]

src/ITfoxtec.Identity.Saml2.MvcCore/Configuration/Saml2ServiceCollectionExtensions.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,8 @@ public static class Saml2ServiceCollectionExtensions
99
/// <summary>
1010
/// Add SAML 2.0 configuration.
1111
/// </summary>
12-
public static IServiceCollection AddSaml2(this IServiceCollection services, Saml2Configuration configuration)
13-
{
14-
services.AddSingleton(configuration);
15-
12+
public static IServiceCollection AddSaml2(this IServiceCollection services)
13+
{
1614
services.AddAuthentication(Saml2Constants.AuthenticationScheme)
1715
.AddCookie(Saml2Constants.AuthenticationScheme, o =>
1816
{

src/ITfoxtec.Identity.Saml2.MvcCore/ITfoxtec.Identity.Saml2.MvcCore.csproj

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
<PropertyGroup>
44
<AssemblyTitle>ITfoxtec.Identity.Saml2.MvcCore</AssemblyTitle>
55
<VersionPrefix>2.0.0.0</VersionPrefix>
6-
<TargetFramework>net461</TargetFramework>
6+
<TargetFramework>net462</TargetFramework>
77
<GenerateDocumentationFile>true</GenerateDocumentationFile>
88
<AssemblyName>ITfoxtec.Identity.Saml2.MvcCore</AssemblyName>
9-
<PackageId>ITfoxtec.Identity.Saml2.MvcCore</PackageId>
9+
<PackageId>ITfoxtec Identity SAML 2.0 MVC Core</PackageId>
1010
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
1111
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
1212
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
13+
<Copyright>Copyright © 2017</Copyright>
14+
<Authors />
1315
</PropertyGroup>
1416

1517
<ItemGroup>

src/ITfoxtec.Identity.Saml2/Bindings/Saml2Binding.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ protected virtual Saml2Binding<T> BindInternal(Saml2Request saml2RequestResponse
4747

4848
if (saml2RequestResponse.Config.SigningCertificate != null)
4949
{
50-
if (saml2RequestResponse.Config.SigningCertificate.PrivateKey == null)
50+
if (saml2RequestResponse.Config.SigningCertificate.GetRSAPrivateKey() == null)
5151
{
52-
throw new ArgumentException("No Private Key present in Signing Certificate or missing private key read credentials.");
52+
throw new ArgumentException("No RSA Private Key present in Signing Certificate or missing private key read credentials.");
5353
}
5454
}
5555

@@ -89,8 +89,8 @@ protected Saml2Request UnbindInternal(HttpRequest request, Saml2Request saml2Req
8989
if (saml2RequestResponse.SignatureAlgorithm == null)
9090
saml2RequestResponse.SignatureAlgorithm = saml2RequestResponse.Config.SignatureAlgorithm;
9191

92-
if (saml2RequestResponse.SignatureValidationCertificates != null && saml2RequestResponse.SignatureValidationCertificates.Count(c => c.PublicKey == null) > 0)
93-
throw new ArgumentException("No Public Key present in at least Signature Validation Certificate.");
92+
if (saml2RequestResponse.SignatureValidationCertificates != null && saml2RequestResponse.SignatureValidationCertificates.Count(c => c.GetRSAPublicKey() == null) > 0)
93+
throw new ArgumentException("No RSA Public Key present in at least Signature Validation Certificate.");
9494

9595
return saml2RequestResponse;
9696
}

src/ITfoxtec.Identity.Saml2/Bindings/Saml2PostBinding.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ protected override Saml2PostBinding BindInternal(Saml2Request saml2RequestRespon
3232
{
3333
BindInternal(saml2RequestResponse);
3434

35-
if ((!(saml2RequestResponse is Saml2AuthnRequest) || saml2RequestResponse.Config.SignAuthnRequest) && saml2RequestResponse.Config.SigningCertificate != null)
35+
if ((!(saml2RequestResponse is Saml2AuthnRequest) || saml2RequestResponse.Config.SignAuthnRequest) && saml2RequestResponse.Config.SigningCertificate != null)
3636
{
3737
Cryptography.SignatureAlgorithm.ValidateAlgorithm(saml2RequestResponse.Config.SignatureAlgorithm);
3838
XmlDocument = XmlDocument.SignDocument(saml2RequestResponse.Config.SigningCertificate, saml2RequestResponse.Config.SignatureAlgorithm, CertificateIncludeOption, saml2RequestResponse.Id.Value);
@@ -65,7 +65,7 @@ you must press the Continue button once to proceed.
6565
yield return string.Format(
6666
@"<input type=""hidden"" name=""{0}"" value=""{1}""/>", messageName, Convert.ToBase64String(Encoding.UTF8.GetBytes(XmlDocument.OuterXml)));
6767

68-
if (!string.IsNullOrWhiteSpace(RelayState))
68+
if(!string.IsNullOrWhiteSpace(RelayState))
6969
{
7070
yield return string.Format(
7171
@"<input type=""hidden"" name=""{0}"" value=""{1}""/>", Saml2Constants.Message.RelayState, WebUtility.HtmlEncode(RelayState));

src/ITfoxtec.Identity.Saml2/Bindings/Saml2RedirectBinding.cs

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
using System.Security.Cryptography.X509Certificates;
1111
using ITfoxtec.Identity.Saml2.Util;
1212
using ITfoxtec.Identity.Saml2.Http;
13-
using System.IdentityModel.Tokens;
1413

1514
namespace ITfoxtec.Identity.Saml2
1615
{
@@ -40,7 +39,7 @@ protected override Saml2RedirectBinding BindInternal(Saml2Request saml2RequestRe
4039

4140
return this;
4241
}
43-
42+
4443
private string SigneQueryString(string queryString, X509Certificate2 signingCertificate)
4544
{
4645
var saml2Signed = new Saml2SignedText(signingCertificate, SignatureAlgorithm);
@@ -58,7 +57,7 @@ private IEnumerable<string> RequestQueryString(Saml2Request saml2RequestResponse
5857
yield return string.Join("=", Saml2Constants.Message.RelayState, Uri.EscapeDataString(RelayState));
5958
}
6059

61-
if ((!(saml2RequestResponse is Saml2AuthnRequest) || saml2RequestResponse.Config.SignAuthnRequest) && saml2RequestResponse.Config.SigningCertificate != null)
60+
if((!(saml2RequestResponse is Saml2AuthnRequest) || saml2RequestResponse.Config.SignAuthnRequest) && saml2RequestResponse.Config.SigningCertificate != null)
6261
{
6362
yield return string.Join("=", Saml2Constants.Message.SigAlg, Uri.EscapeDataString(SignatureAlgorithm));
6463
}
@@ -88,13 +87,13 @@ protected override Saml2Request UnbindInternal(HttpRequest request, Saml2Request
8887
if (!request.Query.AllKeys.Contains(messageName))
8988
throw new Saml2BindingException("HTTP Query String does not contain " + messageName);
9089

91-
if ((!(saml2RequestResponse is Saml2AuthnRequest) || saml2RequestResponse.Config.SignAuthnRequest) &&
92-
saml2RequestResponse.Config.SignatureValidationCertificates != null && saml2RequestResponse.Config.SignatureValidationCertificates.Count() > 0)
90+
if ((!(saml2RequestResponse is Saml2AuthnRequest) || saml2RequestResponse.Config.SignAuthnRequest) &&
91+
saml2RequestResponse.SignatureValidationCertificates != null && saml2RequestResponse.SignatureValidationCertificates.Count() > 0)
9392
{
94-
if (!request.Query.AllKeys.Contains(Saml2Constants.Message.Signature))
93+
if(!request.Query.AllKeys.Contains(Saml2Constants.Message.Signature))
9594
throw new Saml2BindingException("HTTP Query String does not contain " + Saml2Constants.Message.Signature);
9695

97-
if (!request.Query.AllKeys.Contains(Saml2Constants.Message.SigAlg))
96+
if(!request.Query.AllKeys.Contains(Saml2Constants.Message.SigAlg))
9897
throw new Saml2BindingException("HTTP Query String does not contain " + Saml2Constants.Message.SigAlg);
9998
}
10099

@@ -103,23 +102,23 @@ protected override Saml2Request UnbindInternal(HttpRequest request, Saml2Request
103102
RelayState = request.Query[Saml2Constants.Message.RelayState];
104103
}
105104

106-
if ((!(saml2RequestResponse is Saml2AuthnRequest) || saml2RequestResponse.Config.SignAuthnRequest) &&
107-
saml2RequestResponse.Config.SignatureValidationCertificates != null && saml2RequestResponse.Config.SignatureValidationCertificates.Count() > 0)
105+
if ((!(saml2RequestResponse is Saml2AuthnRequest) || saml2RequestResponse.Config.SignAuthnRequest) &&
106+
saml2RequestResponse.SignatureValidationCertificates != null && saml2RequestResponse.SignatureValidationCertificates.Count() > 0)
108107
{
109108
var actualAignatureAlgorithm = request.Query[Saml2Constants.Message.SigAlg];
110-
if (saml2RequestResponse.Config.SignatureAlgorithm == null)
109+
if (saml2RequestResponse.SignatureAlgorithm == null)
111110
{
112-
saml2RequestResponse.Config.SignatureAlgorithm = actualAignatureAlgorithm;
111+
saml2RequestResponse.SignatureAlgorithm = actualAignatureAlgorithm;
113112
}
114-
else if (!saml2RequestResponse.Config.SignatureAlgorithm.Equals(actualAignatureAlgorithm, StringComparison.InvariantCulture))
113+
else if (!saml2RequestResponse.SignatureAlgorithm.Equals(actualAignatureAlgorithm, StringComparison.InvariantCulture))
115114
{
116-
throw new Exception($"Signature Algorithm do not match. Expected algorithm {saml2RequestResponse.Config.SignatureAlgorithm} actual algorithm {actualAignatureAlgorithm}");
115+
throw new Exception($"Signature Algorithm do not match. Expected algorithm {saml2RequestResponse.SignatureAlgorithm} actual algorithm {actualAignatureAlgorithm}");
117116
}
118-
Cryptography.SignatureAlgorithm.ValidateAlgorithm(saml2RequestResponse.Config.SignatureAlgorithm);
119-
SignatureAlgorithm = saml2RequestResponse.Config.SignatureAlgorithm;
117+
Cryptography.SignatureAlgorithm.ValidateAlgorithm(saml2RequestResponse.SignatureAlgorithm);
118+
SignatureAlgorithm = saml2RequestResponse.SignatureAlgorithm;
120119

121120
Signature = request.Query[Saml2Constants.Message.Signature];
122-
ValidateQueryStringSignature(saml2RequestResponse, request.QueryString, messageName, Convert.FromBase64String(Signature), saml2RequestResponse.Config.SignatureValidationCertificates);
121+
ValidateQueryStringSignature(saml2RequestResponse, request.QueryString, messageName, Convert.FromBase64String(Signature), saml2RequestResponse.SignatureValidationCertificates);
123122
}
124123

125124
return Read(request, saml2RequestResponse, messageName, false);

src/ITfoxtec.Identity.Saml2/Configuration/Saml2Configuration.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.IdentityModel.Tokens;
43
using System.Security.Cryptography.X509Certificates;
4+
using System.Security.Cryptography.Xml;
55
using System.ServiceModel.Security;
66

77
namespace ITfoxtec.Identity.Saml2
@@ -17,7 +17,7 @@ public class Saml2Configuration
1717

1818
public Uri SingleLogoutDestination { get; set; }
1919

20-
public string SignatureAlgorithm { get; set; } = SecurityAlgorithms.RsaSha256Signature;
20+
public string SignatureAlgorithm { get; set; } = SignedXml.XmlDsigRSASHA256Url;
2121

2222
public X509Certificate2 SigningCertificate { get; set; }
2323
public X509Certificate2 DecryptionCertificate { get; set; }

src/ITfoxtec.Identity.Saml2/Cryptography/Saml2SignedText.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,25 @@ namespace ITfoxtec.Identity.Saml2.Cryptography
55
{
66
public class Saml2SignedText
77
{
8-
public Saml2Signer saml2Signer { get; protected set; }
8+
public Saml2Signer Saml2Signer { get; protected set; }
99

1010
public Saml2SignedText(X509Certificate2 certificate, string signatureAlgorithm)
1111
{
1212
if (certificate == null) throw new ArgumentNullException(nameof(certificate));
1313

14-
saml2Signer = new Saml2Signer(certificate, signatureAlgorithm);
14+
Saml2Signer = new Saml2Signer(certificate, signatureAlgorithm);
1515
}
1616

1717
public byte[] SignData(byte[] input)
1818
{
19-
return saml2Signer.CreateFormatter().CreateSignature(saml2Signer.HashAlgorithm.ComputeHash(input));
19+
var formatter = Saml2Signer.CreateFormatter();
20+
return formatter.CreateSignature(Saml2Signer.HashAlgorithm.ComputeHash(input));
2021
}
2122

2223
internal bool CheckSignature(byte[] input, byte[] signature)
2324
{
24-
return saml2Signer.CreateDeformatter().VerifySignature(saml2Signer.HashAlgorithm.ComputeHash(input), signature);
25+
var deformatter = Saml2Signer.CreateDeformatter();
26+
return deformatter.VerifySignature(Saml2Signer.HashAlgorithm.ComputeHash(input), signature);
2527
}
2628
}
2729
}
Lines changed: 12 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
using System;
2-
using System.IdentityModel.Tokens;
3-
using System.Reflection;
4-
using System.Security.Cryptography;
52
using System.Security.Cryptography.X509Certificates;
63
using System.Security.Cryptography.Xml;
74
using System.Xml;
@@ -10,123 +7,38 @@ namespace ITfoxtec.Identity.Saml2.Cryptography
107
{
118
public class Saml2SignedXml : SignedXml
129
{
13-
public XmlElement Element { get; protected set; }
14-
public Saml2Signer saml2Signer { get; protected set; }
10+
public Saml2Signer Saml2Signer { get; protected set; }
1511

1612
public Saml2SignedXml(XmlElement element, X509Certificate2 certificate, string signatureAlgorithm) : base(element)
1713
{
18-
if (element == null) throw new ArgumentNullException(nameof(element));
1914
if (certificate == null) throw new ArgumentNullException(nameof(certificate));
15+
if (signatureAlgorithm == null) throw new ArgumentNullException(nameof(signatureAlgorithm));
2016

21-
Element = element;
22-
saml2Signer = new Saml2Signer(certificate, signatureAlgorithm);
17+
Saml2Signer = new Saml2Signer(certificate, signatureAlgorithm);
2318
}
2419

2520
public void ComputeSignature(X509IncludeOption includeOption, string id)
2621
{
27-
SignedInfo.SignatureMethod = saml2Signer.SignatureAlgorithm;
28-
SignedInfo.CanonicalizationMethod = XmlDsigExcC14NTransformUrl;
29-
3022
var reference = new Reference("#" + id);
3123
reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
3224
reference.AddTransform(new XmlDsigExcC14NTransform());
33-
reference.DigestMethod = SignatureAlgorithm.DigestMethod(saml2Signer.SignatureAlgorithm);
25+
reference.DigestMethod = SignatureAlgorithm.DigestMethod(Saml2Signer.SignatureAlgorithm);
3426
AddReference(reference);
3527

36-
// SignedXml do not support SHA256 this is a hack to support both SHA1 and SHA256
37-
ComputeSignatureInternal();
28+
SignedInfo.CanonicalizationMethod = XmlDsigExcC14NTransformUrl;
29+
SignedInfo.SignatureMethod = Saml2Signer.SignatureAlgorithm;
30+
SigningKey = Saml2Signer.Certificate.GetRSAPrivateKey();
31+
ComputeSignature();
3832

3933
KeyInfo = new KeyInfo();
40-
KeyInfo.AddClause(new KeyInfoX509Data(saml2Signer.Certificate, includeOption));
34+
KeyInfo.AddClause(new KeyInfoX509Data(Saml2Signer.Certificate, includeOption));
4135
}
4236

4337
public new bool CheckSignature()
4438
{
45-
ValidateSignature();
46-
47-
var signatureAlgorithm = saml2Signer.SignatureAlgorithm;
48-
var actualAignatureAlgorithm = m_signature.SignedInfo.SignatureMethod;
49-
if (signatureAlgorithm == null)
50-
{
51-
signatureAlgorithm = actualAignatureAlgorithm;
52-
SignatureAlgorithm.ValidateAlgorithm(signatureAlgorithm);
53-
}
54-
else if (!signatureAlgorithm.Equals(actualAignatureAlgorithm, StringComparison.InvariantCulture))
55-
{
56-
throw new CryptographicException($"Signature Algorithm do not match. Expected algorithm {signatureAlgorithm} actual algorithm {actualAignatureAlgorithm}");
57-
}
58-
59-
// SignedXml do not support SHA256 this is a hack to support both SHA1 and SHA256
60-
if (!CheckSignatureInternal(signatureAlgorithm))
61-
{
62-
return false;
63-
}
64-
65-
return true;
66-
}
67-
68-
private void ValidateSignature()
69-
{
70-
if (SignedInfo.References.Count != 1)
71-
{
72-
throw new CryptographicException("There is not exactly one Signature Reference.");
73-
}
74-
75-
var reference = SignedInfo.References[0] as Reference;
76-
var referenceElement = GetIdElement(Element.OwnerDocument, reference.Uri.Substring(1));
77-
78-
if (referenceElement != Element)
79-
{
80-
throw new CryptographicException("Signature Reference is Incorrect reference.");
81-
}
82-
}
83-
84-
private void ComputeSignatureInternal()
85-
{
86-
BuildDigestedReferencesInvoker();
87-
88-
var formatter = saml2Signer.CreateFormatter();
89-
byte[] hashvalue = GetC14NDigestInvoker(saml2Signer.HashAlgorithm);
90-
91-
m_signature.SignatureValue = formatter.CreateSignature(hashvalue);
92-
}
93-
94-
private bool CheckSignatureInternal(string signatureAlgorithm)
95-
{
96-
if (!CheckSignedInfoInternal(signatureAlgorithm))
97-
{
98-
return false;
99-
}
100-
101-
if (!CheckDigestedReferencesInvoker())
102-
{
103-
return false;
104-
}
105-
106-
return true;
107-
}
108-
109-
private bool CheckSignedInfoInternal(string signatureAlgorithm)
110-
{
111-
var deformatter = saml2Signer.CreateDeformatter(signatureAlgorithm);
112-
byte[] hashvalue = GetC14NDigestInvoker(saml2Signer.HashAlgorithm);
113-
114-
return deformatter.VerifySignature(hashvalue, m_signature.SignatureValue);
115-
}
116-
117-
private void BuildDigestedReferencesInvoker()
118-
{
119-
typeof(SignedXml).InvokeMember("BuildDigestedReferences", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic, null, this, null);
120-
}
121-
122-
private byte[] GetC14NDigestInvoker(HashAlgorithm hash)
123-
{
124-
return (byte[])typeof(SignedXml).InvokeMember("GetC14NDigest", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic, null, this, new object[] { hash });
125-
}
126-
127-
private bool CheckDigestedReferencesInvoker()
128-
{
129-
return (bool)typeof(SignedXml).InvokeMember("CheckDigestedReferences", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic, null, this, null);
39+
SignedInfo.CanonicalizationMethod = XmlDsigExcC14NTransformUrl;
40+
SignedInfo.SignatureMethod = Saml2Signer.SignatureAlgorithm;
41+
return CheckSignature(Saml2Signer.Certificate, true);
13042
}
13143
}
13244
}

0 commit comments

Comments
 (0)