Search code examples
c#.netcryptographyecdsaxml-signature

How to correctly CryptoConfig.AddAlgorithm for ECDsa in C# .NET?


I have a XMLDocument that was signed with edcsa-sha384, out of the box .NET SignedXml does not support that algorithm, so I followed this article (https://www.scottbrady91.com/c-sharp/ecdsa-xml-dotnet) and called AddAlgorithm at the beginning of the program:

CryptoConfig.AddAlgorithm(typeof(Ecdsa384SignatureDescription),
                          "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384");

Before AddAlgorithm, SignedXml was able to CheckSignature just fine for rsa-sha256, it was able to go through the XMLDocument and perform the signature check without throwing any errors...

After AddAlgorithm, it throws an error on the very second line of the xml file... Does anyone know what else I am missing to get ECDsa-RSA-384 to work?

InvalidOperationException
 > There is an error in the XML document.
 > <MySpecialDoc xmlns='https://www.hello.com/world/1.0'> was not expected.

   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events)
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader)

The XML starts with something like this:

<?xml version="1.0"?>
<MySpecialDoc xmlns='https://www.hello.com/world/1.0' id="9baedc57-c4ef-41f5-8bdf-33fb14af3f71">
...

If you know an article on how to register custom algorithm, please share it. Thanks!


Solution

  • Ok, I've figured out the root cause of my error, it has nothing to do with how I AddAlgorithm, it was actually working perfectly. The error I was seeing was due to the namespace in my XmlDocument being different. I generated some C# object class which had the namespace coded against 1.1 and the XML I was reading was an older 1.0!!!!

    I spent a lot of time researching and getting ECDsa algorithm to work with SignedXml, there actually isn't that much information out there regarding this, so hopefully this answer will be able to help someone else.

    To add unsupported algorithm to SignedXml, you must first create a SignatureDescription class, then register for SignedXml to use via CryptoConfig.AddAlgorithm.

    This is an example of SignatureDescription for ECDsa P-384, tested in .NET 8 at the time of typing this. Code for Ecdsa384SignatureDescription.cs (you may wrap it around the System.Security.Cryptography namespace, but not sure if that would clash if .NET actually provide the implementation):

    using System.Security.Cryptography;
    
    public class ECDsaCngSignatureFormatter : AsymmetricSignatureFormatter
    {
        private ECDsaCng? key;
    
        public ECDsaCngSignatureFormatter(ECDsaCng key)
        {
            this.key = key;
        }
    
        public override void SetKey(AsymmetricAlgorithm key) => this.key = key as ECDsaCng;
    
        public override void SetHashAlgorithm(string strName) { }
    
        public override byte[] CreateSignature(byte[] rgbHash) => key!.SignHash(rgbHash);
    }
    
    public class ECDsaCngSignatureDeformatter : AsymmetricSignatureDeformatter
    {
        private ECDsaCng? key;
    
        public ECDsaCngSignatureDeformatter(ECDsaCng key)
        {
            this.key = key;
        }
    
        public override void SetKey(AsymmetricAlgorithm key) => this.key = key as ECDsaCng;
    
        public override void SetHashAlgorithm(string strName) { }
    
        public override bool VerifySignature(byte[] rgbHash, byte[] rgbSignature) => key!.VerifyHash(rgbHash, rgbSignature);
    }
    
    
    public class Ecdsa384SignatureDescription : SignatureDescription
    {
        public Ecdsa384SignatureDescription()
        {
            KeyAlgorithm = typeof(ECDsaCng).AssemblyQualifiedName;
        }
        public override HashAlgorithm CreateDigest() => SHA384.Create();
    
        public override AsymmetricSignatureFormatter CreateFormatter(AsymmetricAlgorithm key)
        {
            if (!(key is ECDsaCng ecdsa) || ecdsa.KeySize != 384)
                throw new InvalidOperationException("Requires EC key using P-384");
            return new ECDsaCngSignatureFormatter(ecdsa);
        }
    
        public override AsymmetricSignatureDeformatter CreateDeformatter(AsymmetricAlgorithm key)
        {
            if (!(key is ECDsaCng ecdsa) || ecdsa.KeySize != 384)
                throw new InvalidOperationException("Requires EC key using P-384");
            return new ECDsaCngSignatureDeformatter(ecdsa);
        }
    }
    

    Once you've created the class, you need to register it somewhere in your program, to do so:

    CryptoConfig.AddAlgorithm(typeof(Ecdsa384SignatureDescription),
                              "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384");
    

    You should be able to sign and verify signature with ECDsa P-384 now.