Unable to Verify Signed XML with X509 Certificate

EDN Admin

Well-known member
Joined
Aug 7, 2010
Messages
12,794
Location
In the Machine
Im writing a simple example to sign an XML message and then trying to verify the signatures. The signing of the message works fine (Or I think it does) but when Im trying to verify the same - the signature is not verified. here is my code sample:

<pre lang="x-vbnet Run the following command to create a certificate
and place it in the store.
makecert -r -pe -sk marty -n "CN=testing;OU=testing;O=testing;C=US" -ss root -# 1234567 -sr localmachine -a sha1 -b 01/01/2010 testing.pfx
Imports System
Imports System.Security.Cryptography
Imports System.Security.Cryptography.Xml
Imports System.Security.Cryptography.X509Certificates
Imports System.Text
Imports System.Xml
Imports Microsoft.Web.Services3
Imports Microsoft.Web.Services3.Security.Tokens

Module Module1
Private Certificate As String = "CN=testing, OU=testing, O=testing, C=US"

Sub Main(ByVal args() As String)

Try
Dim path As String = "C:Temp"
Create an XML file to sign.
CreateSomeXml(path & "Example.xml")
Console.WriteLine("New XML file created.")

Sign the XML that was just created and save it in a
new file.
SignXMLFile(path & "Example.xml", path & "SignedExample.xml", Certificate)
Console.WriteLine("XML file signed.")

If VerifyXmlFile(path & "SignedExample.xml", Certificate) Then
Console.WriteLine("The XML signature is valid.")
Else
Console.WriteLine("The XML signature is not valid.")
End If

Catch e As CryptographicException
Console.WriteLine(e.Message)
End Try
Console.Read()

End Sub

Verify the signature of an XML file against an asymetric
algorithm and return the result.
Function VerifyXmlFile(ByVal FileName As String, ByVal CertificateSubject As String) As [Boolean]
Check the args.
If Nothing = FileName Then
Throw New ArgumentNullException("FileName")
End If
If Nothing = CertificateSubject Then
Throw New ArgumentNullException("CertificateSubject")
End If
Load the certificate from the store.
Dim cert As X509Certificate2 = GetCertificateBySubject(CertificateSubject)

Create a new XML document.
Dim xmlDocument As New XmlDocument()

Load the passed XML file into the document.
xmlDocument.Load(FileName)

Create a new SignedXml object and pass it
the XML document class.
Dim signedXml As New SignedXml(xmlDocument)

Find the "Signature" node and create a new
XmlNodeList object.
Dim nodeList As XmlNodeList = xmlDocument.GetElementsByTagName("Signature")

Load the signature node.
signedXml.LoadXml(CType(nodeList(0), XmlElement))

Check the signature and return the result.
Return signedXml.CheckSignature(cert, True)

End Function

Function GetCertificateBySubject(ByVal CertificateSubject As String) As X509Certificate2
Check the args.
If Nothing = CertificateSubject Then
Throw New ArgumentNullException("CertificateSubject")
End If

Load the certificate from the certificate store.
Dim cert As X509Certificate2 = Nothing

Dim store As New X509Store(StoreName.Root, StoreLocation.LocalMachine)

Try
Open the store.
store.Open(OpenFlags.ReadOnly Or OpenFlags.OpenExistingOnly)

Get the certs from the store.
Dim CertCol As X509Certificate2Collection = store.Certificates

Find the certificate with the specified subject.
Dim c As X509Certificate2
For Each c In CertCol
If c.Subject = CertificateSubject Then
cert = c
Exit For
End If
Next c

Throw an exception of the certificate was not found.
If cert Is Nothing Then
Throw New CryptographicException("The certificate could not be found.")
End If
Finally
Close the store even if an exception was thrown.
store.Close()
End Try

Return cert

End Function

Create example data to sign.
Sub CreateSomeXml(ByVal FileName As String)
Check the args.
If Nothing = FileName Then
Throw New ArgumentNullException("FileName")
End If

Dim xmlDoc = <A:Diagnostic xmlns:A="http://www.routeone.com/namespace.messaging.diag#
<A:DiagnosticMessage>
<A:RequestMessage>100027</A:RequestMessage>
</A:DiagnosticMessage>
<A:SourceIdentifier>RouteOne</A:SourceIdentifier>
</A:Diagnostic>

Save the XML document to the file name specified.
Dim xmltw As New XmlTextWriter(FileName, New UTF8Encoding(False))
Try
xmlDoc.WriteTo(xmltw)
Finally
xmltw.Close()
End Try

End Sub

Public Sub SignXMLFile(ByVal FileName As String, ByVal SignedFileName As String, ByVal SubjectName As String)
Dim Envelope As New SoapEnvelope()

Create a new XML document.
Dim doc As New XmlDocument()


THIS MUST BE ON! This matters because the the SignatureValue generation
accounts for whitespace when generating the value
doc.PreserveWhitespace = True

Load the passed XML file using its name.
doc.Load(New XmlTextReader(FileName))

Create Headers and Body for the XML
Dim Header As XmlElement = Envelope.CreateHeader()
Dim Body As XmlElement = Envelope.CreateBody()
Body.SetAttribute("Id", "Body")
Envelope.Body.AppendChild(Envelope.ImportNode(doc.DocumentElement, True))

Sign the message
SignMessage(Envelope)

Save the signed XML document to a file specified
using the passed string.
Dim xmltw As New XmlTextWriter(SignedFileName, New UTF8Encoding(False))
Try
Envelope.WriteTo(xmltw)
Finally
xmltw.Close()
End Try

End Sub

<summary>
Sign the envelope with the body already inside of it
</summary>
<param name="envelope SoapEnvelope to sign</param>
Private Sub SignMessage(ByRef envelope As SoapEnvelope)
Dim header As XmlElement
Dim soapsec As XmlElement

Envelop Header
header = envelope.Header

Dim cert As X509Certificate2 = GetCertificateBySubject(Certificate)
Dim signatureToken As New X509SecurityToken(cert)
Dim requestContext As SoapContext = envelope.Context

Create SOAP-SEC:Security element in the header
soapsec = TryCast(envelope.CreateNode(XmlNodeType.Element, "Security", "http://schemas.xmlsoap.org/soap/security/2000-12"), XmlElement)
soapsec.SetAttribute("mustUnderstand", "", "1")
soapsec.Prefix = "SOAP-SEC"
header.AppendChild(TryCast(soapsec, XmlNode))

Create a subclassed SignedXML instance
Load the SOAP message into the signedXML instance
Dim signedXml As New SignedXml(envelope)

Create a reference to be signed. This is the Body element
of the SOAP message. The Body element has to have an Id or
id attribute with a value of "Body".
Dim bodyReference As New Reference()
bodyReference.Uri = "#Body"

Add a transformation to the reference.
The following transformation results in http://www.w3.org/2001/10/xml-exc-c14n#.
Dim trans As New XmlDsigExcC14NTransform()
bodyReference.AddTransform(trans)

Add the reference to the SignedXml object.
signedXml.AddReference(bodyReference)

Add an RSAKeyValue KeyInfo (optional; helps recipient find key to validate).
Dim keyInfo As New KeyInfo()
KeyInfo
Dim KeyX509Data As New KeyInfoX509Data()
KeyX509Data.AddIssuerSerial(cert.Issuer, cert.GetSerialNumberString())

Load the certificate into a KeyInfoX509Data object
and add it to the KeyInfo object.
keyInfo.AddClause(KeyX509Data)

Set the signing key and KeyInfo
signedXml.SigningKey = cert.PrivateKey
signedXml.KeyInfo = keyInfo

Compute the Signature
signedXml.ComputeSignature()

soapsec.AppendChild(TryCast(signedXml.GetXml(), XmlNode))
End Sub

End Module
[/code]


View the full article
 
Back
Top