Skip to content

Commit

Permalink
Fixes GlobalEnableSha256XmlSignatures on .NET prior to 4.6.2
Browse files Browse the repository at this point in the history
- This was broken by the signature strength validation added recently.
- Fixes #672
  • Loading branch information
AndersAbel committed Mar 29, 2017
1 parent 5b0763f commit c4b05a1
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 26 deletions.
30 changes: 30 additions & 0 deletions Kentor.AuthServices.Tests/Configuration/OptionsTests.cs
Expand Up @@ -9,6 +9,7 @@
using Kentor.AuthServices.Tests.Helpers;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography.Xml;

namespace Kentor.AuthServices.Tests.Configuration
{
Expand Down Expand Up @@ -64,5 +65,34 @@ public void Options_GlobalEnableSha256Signatures_DoesntBreakJwtSecurityTokenHand
handler.Invoking(h => h.WriteToken(token))
.ShouldNotThrow();
}

[TestMethod]
public void Options_GlobalEnableSha256Signatures_DoesntAlterKnownAlgorithmsIfSha256AlreadyPresent()
{
var knownAlgorithmsCopy = XmlHelpers.KnownSigningAlgorithms.ToList();

Options.AddRsaSha256IfMissing(knownAlgorithmsCopy);

knownAlgorithmsCopy.ShouldBeEquivalentTo(XmlHelpers.KnownSigningAlgorithms);
}

[TestMethod]
public void Options_GlobalEnableSha256Signatures_AddsSha256IfOnlySha1InList()
{
var knownAlgorithms = new List<string>()
{
SignedXml.XmlDsigRSASHA1Url
};

Options.AddRsaSha256IfMissing(knownAlgorithms);

var expected = new List<string>()
{
SignedXml.XmlDsigRSASHA1Url,
SignedXml.XmlDsigRSASHA256Url
};

knownAlgorithms.ShouldBeEquivalentTo(expected);
}
}
}
16 changes: 14 additions & 2 deletions Kentor.AuthServices/Configuration/Options.cs
@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Security.Cryptography;

namespace Kentor.AuthServices.Configuration
Expand Down Expand Up @@ -69,15 +71,25 @@ public IdentityProviderDictionary IdentityProviders
}
}

static internal readonly string RsaSha256Namespace = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
internal const string RsaSha256Uri = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";

/// <summary>
/// Make Sha256 signature algorithm available in this process (not just Kentor.AuthServices)
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Sha" )]
public static void GlobalEnableSha256XmlSignatures()
{
CryptoConfig.AddAlgorithm(typeof(ManagedSHA256SignatureDescription), RsaSha256Namespace);
CryptoConfig.AddAlgorithm(typeof(ManagedSHA256SignatureDescription), RsaSha256Uri);

AddRsaSha256IfMissing((IList<string>)XmlHelpers.KnownSigningAlgorithms);
}

internal static void AddRsaSha256IfMissing(IList<string> knownAlgorithms)
{
if (knownAlgorithms.Count == 1)
{
knownAlgorithms.Add(RsaSha256Uri);
}
}
}
}
46 changes: 22 additions & 24 deletions Kentor.AuthServices/XmlHelpers.cs
Expand Up @@ -257,41 +257,39 @@ public static void Sign(this XmlElement xmlElement, X509Certificate2 cert, bool
{
FixSignatureIndex(signedXml, signatureElement);

try
{
foreach(var keyIdentifier in signingKeys)
{
var key = ((AsymmetricSecurityKey)keyIdentifier.CreateKey())
.GetAsymmetricAlgorithm(SignedXml.XmlDsigRSASHA1Url, false);
CheckSha256Support(signedXml);

if(signedXml.CheckSignature(key))
{
ValidateCertificate(validateCertificate, keyIdentifier);
return;
}
}
var containedKey = signedXml.Signature.KeyInfo.OfType<KeyInfoX509Data>()
.SingleOrDefault()?.Certificates.OfType<X509Certificate2>()
.SingleOrDefault();
foreach (var keyIdentifier in signingKeys)
{
var key = ((AsymmetricSecurityKey)keyIdentifier.CreateKey())
.GetAsymmetricAlgorithm(SignedXml.XmlDsigRSASHA1Url, false);

if (containedKey != null && signedXml.CheckSignature(containedKey, true))
if (signedXml.CheckSignature(key))
{
throw new InvalidSignatureException("The signature verified correctly with the key contained in the signature, but that key is not trusted.");
ValidateCertificate(validateCertificate, keyIdentifier);
return;
}

throw new InvalidSignatureException("Signature didn't verify. Have the contents been tampered with?");
}
catch (CryptographicException)
var containedKey = signedXml.Signature.KeyInfo.OfType<KeyInfoX509Data>()
.SingleOrDefault()?.Certificates.OfType<X509Certificate2>()
.SingleOrDefault();

if (containedKey != null && signedXml.CheckSignature(containedKey, true))
{
CheckSha256Support(signedXml);
throw;
throw new InvalidSignatureException("The signature verified correctly with the key contained in the signature, but that key is not trusted.");
}

throw new InvalidSignatureException("Signature didn't verify. Have the contents been tampered with?");
}

private static readonly Lazy<object> rsaSha256Algorithm =
new Lazy<object>(() => CryptoConfig.CreateFromName(Options.RsaSha256Uri));

[ExcludeFromCodeCoverage]
private static void CheckSha256Support( SignedXml signedXml )
private static void CheckSha256Support(SignedXml signedXml)
{
if (signedXml.SignatureMethod == Options.RsaSha256Namespace && CryptoConfig.CreateFromName( signedXml.SignatureMethod ) == null)
if (signedXml.SignatureMethod == Options.RsaSha256Uri
&& rsaSha256Algorithm.Value == null)
{
throw new InvalidSignatureException("SHA256 signatures require the algorithm to be registered at the process level. Upgrade to .Net 4.6.2 or call Kentor.AuthServices.Configuration.Options.GlobalEnableSha256XmlSignatures() on startup to register.");
}
Expand Down

0 comments on commit c4b05a1

Please sign in to comment.