Search code examples
xmlpowershellpemsignedxml

Verifying XML Signature in Powershell with PEM Certificate


I am trying to create a powershell script that will consume data with in a XML document. However, prior to doing any work I need to verify the XML hasn't been tampered with by verifying the signature.

I have a copy of the public key for the cert used to sign the XML in PEM format, but I can not figure out how to get powershell to use that cert.

The closes I have come to getting this to work is the following code...

$Path = "data.xml"
$Xmldata = new-object Xml.XmlDocument
$Xmldata.PreserveWhitespace = $true
$Xmldata.Load($Path)

add-type -AssemblyName system.security
$SignedXml = New-Object System.Security.Cryptography.Xml.SignedXml -ArgumentList $Xmldata

$XmlNodeList = $Xmldata.EntitiesDescriptor.Signature

$XmlNodeList

$SignedXml.LoadXml($XmlNodeList)

$CertPath = "cert.pem"
$Check = $SignedXml.CheckSignature($CertPath, $true)

However, when this runs I get the following exception...

Exception calling "CheckSignature" with "2" argument(s): "SignatureDescription could not be created for the signature algorithm supplied." At line:34 char:1 + $Check = $SignedXml.CheckSignature($CertPath, $true) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], MethodInvocationException + FullyQualifiedErrorId : CryptographicException

Any help would be appreciated. Thanks!


Solution

  • After some intense additional searching I found out that SignedXML does not support the http://www.w3.org/2001/04/xmldsig-more#rsa-sha256 algorithm and that had to be added by hand. I had to add the follow code before creating the signedXML object...

    Add-Type @'
            public class RSAPKCS1SHA256SignatureDescription : System.Security.Cryptography.SignatureDescription
                {
                    public RSAPKCS1SHA256SignatureDescription()
                    {
                        base.KeyAlgorithm = "System.Security.Cryptography.RSACryptoServiceProvider";
                        base.DigestAlgorithm = "System.Security.Cryptography.SHA256Managed";
                        base.FormatterAlgorithm = "System.Security.Cryptography.RSAPKCS1SignatureFormatter";
                        base.DeformatterAlgorithm = "System.Security.Cryptography.RSAPKCS1SignatureDeformatter";
                    }
    
                    public override System.Security.Cryptography.AsymmetricSignatureDeformatter CreateDeformatter(System.Security.Cryptography.AsymmetricAlgorithm key)
                    {
                        System.Security.Cryptography.AsymmetricSignatureDeformatter asymmetricSignatureDeformatter = (System.Security.Cryptography.AsymmetricSignatureDeformatter)
                            System.Security.Cryptography.CryptoConfig.CreateFromName(base.DeformatterAlgorithm);
                        asymmetricSignatureDeformatter.SetKey(key);
                        asymmetricSignatureDeformatter.SetHashAlgorithm("SHA256");
                        return asymmetricSignatureDeformatter;
                    }
                }
    '@
        $RSAPKCS1SHA256SignatureDescription = New-Object RSAPKCS1SHA256SignatureDescription
        [System.Security.Cryptography.CryptoConfig]::AddAlgorithm($RSAPKCS1SHA256SignatureDescription.GetType(), "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256")
    

    This solution was adapted from a C# example of the same issue found at http://geekswithblogs.net/mkoerner/archive/2013/07/12/saml2-federationmetadata-validation.aspx.