Using VS 2008 with .Net Framework 3.5 on windows 2003 server.
We have implemented SSO with SAML for security. We work at service provider end where we validate the Signed XML SAML Assertuib token generated from client's system.
As of now whatever signed documents we came across were using the Signature Algorithm "rsa-sha1", but now we have new customer who sends a file with the signature algorithm as "rsa-sha256" and here is the problem started.
public static string VerifySignature()
{
if (m_xmlDoc == null)
return "Could not load XMLDocument ";
try
{
XmlNamespaceManager nsm = new XmlNamespaceManager(new NameTable());
nsm.AddNamespace("dsig", SignedXml.XmlDsigNamespaceUrl);
XmlElement sigElt = (XmlElement)m_xmlDoc.SelectSingleNode(
"//dsig:Signature", nsm);
// Load the signature for verification
SignedXml sig = new SignedXml(m_xmlDoc);
sig.LoadXml(sigElt);
if (!sig.CheckSignature())
return "Invalid Signature";
}
catch (Exception ex)
{
return ex.Message;
}
return string.Empty;
}
Now, when I try the same code for this new customer (with signature algorithm rsa-sha256h) - this is not working and I am getting the error "SignatureDescription could not be created for the signature algorithm supplied."
Going through many blogs and articles in last 2-3 days, I came to know that SignedXml does not support sha256. Fine. But what next. Somewhere its mentioned that use the WIF, I have also checked & tried this.
I am also trying to use RSAPKCS1SignatureDeformatter's VerifySignature method. But not really sure what are the two parameters to be passed.
Dotnet 4.6.2+ has the newer sha
hashes built in. For dotnet 4 +, to get access to rsa-sha512, rsa-sha384, and rsa-sha256, you should include this code someplace.
/// <summary>Declare the signature type for rsa-sha512</summary>
public class RsaPkCs1Sha512SignatureDescription : SignatureDescription
{
public RsaPkCs1Sha512SignatureDescription()
{
KeyAlgorithm = typeof(RSACryptoServiceProvider).FullName;
DigestAlgorithm = typeof(SHA512CryptoServiceProvider).FullName;
FormatterAlgorithm = typeof(RSAPKCS1SignatureFormatter).FullName;
DeformatterAlgorithm = typeof(RSAPKCS1SignatureDeformatter).FullName;
}
public override AsymmetricSignatureDeformatter CreateDeformatter(AsymmetricAlgorithm key)
{
var sigProcessor = (AsymmetricSignatureDeformatter)CryptoConfig.CreateFromName(DeformatterAlgorithm);
sigProcessor.SetKey(key);
sigProcessor.SetHashAlgorithm("SHA512");
return sigProcessor;
}
public override AsymmetricSignatureFormatter CreateFormatter(AsymmetricAlgorithm key)
{
var sigProcessor =
(AsymmetricSignatureFormatter)CryptoConfig.CreateFromName(FormatterAlgorithm);
sigProcessor.SetKey(key);
sigProcessor.SetHashAlgorithm("SHA512");
return sigProcessor;
}
}
/// <summary>Declare the signature type for rsa-sha384</summary>
public class RsaPkCs1Sha384SignatureDescription : SignatureDescription {
public RsaPkCs1Sha384SignatureDescription()
{
KeyAlgorithm = typeof(RSACryptoServiceProvider).FullName;
DigestAlgorithm = typeof(SHA384CryptoServiceProvider).FullName;
FormatterAlgorithm = typeof(RSAPKCS1SignatureFormatter).FullName;
DeformatterAlgorithm = typeof(RSAPKCS1SignatureDeformatter).FullName;
}
public override AsymmetricSignatureDeformatter CreateDeformatter(AsymmetricAlgorithm key)
{
var sigProcessor = (AsymmetricSignatureDeformatter) CryptoConfig.CreateFromName(DeformatterAlgorithm);
sigProcessor.SetKey(key);
sigProcessor.SetHashAlgorithm("SHA384");
return sigProcessor;
}
public override AsymmetricSignatureFormatter CreateFormatter(AsymmetricAlgorithm key)
{
var sigProcessor =
(AsymmetricSignatureFormatter)CryptoConfig.CreateFromName(FormatterAlgorithm);
sigProcessor.SetKey(key);
sigProcessor.SetHashAlgorithm("SHA384");
return sigProcessor;
}
}
/// <summary>Declare the signature type for rsa-sha256</summary>
public class RsaPkCs1Sha256SignatureDescription : SignatureDescription
{
public RsaPkCs1Sha256SignatureDescription()
{
KeyAlgorithm = typeof(RSACryptoServiceProvider).FullName;
DigestAlgorithm = typeof(SHA256CryptoServiceProvider).FullName;
FormatterAlgorithm = typeof(RSAPKCS1SignatureFormatter).FullName;
DeformatterAlgorithm = typeof(RSAPKCS1SignatureDeformatter).FullName;
}
public override AsymmetricSignatureDeformatter CreateDeformatter(AsymmetricAlgorithm key)
{
var sigProcessor =
(AsymmetricSignatureDeformatter) CryptoConfig.CreateFromName(DeformatterAlgorithm);
sigProcessor.SetKey(key);
sigProcessor.SetHashAlgorithm("SHA256");
return sigProcessor;
}
public override AsymmetricSignatureFormatter CreateFormatter(AsymmetricAlgorithm key)
{
var sigProcessor =
(AsymmetricSignatureFormatter)CryptoConfig.CreateFromName(FormatterAlgorithm);
sigProcessor.SetKey(key);
sigProcessor.SetHashAlgorithm("SHA256");
return sigProcessor;
}
}
Then, you should activate these sig descriptions by calling code like this. You only need to call it once, so you can call it from a static constructor if you wish.
CryptoConfig.AddAlgorithm(typeof(RsaPkCs1Sha512SignatureDescription),
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha512");
CryptoConfig.AddAlgorithm(typeof(RsaPkCs1Sha384SignatureDescription),
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha384");
CryptoConfig.AddAlgorithm(typeof(RsaPkCs1Sha256SignatureDescription),
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
Tip o' the hat to Microsoft's Carlos Lopez and BitSchupster and Andrew on SO.
For .net 4 and earlier,
I found that the following works once you add the Security.Cryptography from http://clrsecurity.codeplex.com/
(Note X509CertificateFinder is my own, looks for the signature certificate in the certificate store by thumbprint)
/// <summary>
/// Validate an XmlDocuments signature
/// </summary>
/// <param name="xnlDoc"> The saml response with the signature elemenet to validate </param>
/// <returns> True if signature can be validated with certificate </returns>
public bool ValidateX509CertificateSignature(XmlDocument xnlDoc)
{
XmlNodeList XMLSignatures = xnlDoc.GetElementsByTagName("Signature", "http://www.w3.org/2000/09/xmldsig#");
// Checking If the Response or the Assertion has been signed once and only once.
if (XMLSignatures.Count != 1) return false;
var signedXmlDoc = new SignedXml(xnlDoc);
signedXmlDoc.LoadXml((XmlElement)XMLSignatures[0]);
var certFinder = new X509CertificateFinder();
var foundCert = certFinder.GetSignatureCertificate();
CryptoConfig.AddAlgorithm(typeof(RSAPKCS1SHA256SignatureDescription), "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
return signedXmlDoc.CheckSignature(foundCert,false);
}
This qualifies for "simple" but perhaps not "solution" :) For the few clients we've encountered this with, we've asked them to change their IdP to sign using SHA-1. They are able to change it, and when they do it works.
Not a technical solution, but it has worked 'in the field' so I'd thought I'd mention it.
Just update it to .NET framework 4.6.01590 or higher and it will support up to SHA-512 without any code change.