c# - Xml Signature for XmlElement fails to verify -
i apologize in advance rather lengthy block of code, it's smallest compilable example produce. omitted error checking original code. i'm using visual studio 2012 , .net 4.5, although nothing new 4.5, should work version.
i trying sign xml documents' elements protect them tampering. don't want protect whole document, elements. maybe different elements different keys.
however, when sign 3 example elements , try verify them, first 1 verifies, other 2 fail. make worse, first 1 succeeds if modify after being signed. have googled lot, read lot of tutorials , asked theoretical question here, don't have clue i'm doing wrong. can spot mistake?
note: i'd more happy offer same bounty that's on friday's question solving this.
the certificate created executing:
"c:\program files (x86)\microsoft sdks\windows\v7.1a\bin\makecert" -r -pe -n "cn=xmldsig_test" -b 01/01/2013 -e 01/01/2014 -sky signing -ss my
the test xml file is:
<?xml version="1.0" encoding="utf-8" ?> <packageroot> <package> <changes > <change/> </changes> </package> <package> <changes> <change/> <change/> </changes> </package> <package> <changes> <change/> <change/> <change/> </changes> </package> </packageroot>
the code sign , verify:
namespace soexample { using system; using system.security.cryptography.x509certificates; using system.security.cryptography.xml; using system.xml; public static class program { public static void sign(this xmlelement element, x509certificate2 certificate) { var identifier = guid.newguid().tostring(); element.setattribute("id", identifier); var signedxml = new signedxml(element) { signingkey = certificate.privatekey }; var reference = new reference("#" + identifier); reference.addtransform(new xmldsigenvelopedsignaturetransform()); signedxml.addreference(reference); signedxml.computesignature(); var xmldigitalsignature = signedxml.getxml(); element.appendchild(element.ownerdocument.importnode(xmldigitalsignature, true)); } public static bool verifysignature(this xmlelement element, x509certificate2 certificate) { var signedxml = new signedxml(element); xmlnodelist nodelist = element.getelementsbytagname("signature"); if (nodelist.count != 1) return false; signedxml.loadxml((xmlelement)nodelist[0]); return signedxml.checksignature(certificate, true); } public static void main() { var xmldoc = new xmldocument { preservewhitespace = true }; xmldoc.load("examplepackage.xml"); var certificate = getcertificatebysubject("cn=xmldsig_test"); foreach (xmlelement root in xmldoc.getelementsbytagname("packageroot")) { foreach (xmlelement package in root.getelementsbytagname("package")) { package.sign(certificate); } } xmldoc.save("test_signed.xml"); console.writeline("xml file signed."); console.writeline("press key verify"); console.readline(); var signeddoc = new xmldocument(); signeddoc.load("test_signed.xml"); foreach (xmlelement root in xmldoc.getelementsbytagname("packageroot")) { foreach (xmlelement package in root.getelementsbytagname("package")) { console.write("verifying package " + package.getattribute("id")); var success = package.verifysignature(certificate); console.writeline(success ? " successful!" : " failed!"); } } console.writeline("done."); console.readline(); } private static x509certificate2 getcertificatebysubject(string certificatesubject) { var store = new x509store("my", storelocation.currentuser); store.open(openflags.readonly | openflags.openexistingonly); foreach (x509certificate2 c in store.certificates) { if (c.subject == certificatesubject) { store.close(); return c; } } store.close(); return null; } } }
you have bug in test code. second foreach loops again on xmldoc
instead of signeddoc
. fixing change outcome fail nodes.
why fail don't yet know.
i couldn't find out why fail code found way make work. difference: signatures direct childs of root element:
public static void main() { // ... var signeddoc = new xmldocument { preservewhitespace = true }; signeddoc.load("test_signed.xml"); foreach (xmlelement root in signeddoc.getelementsbytagname("packageroot")) { foreach (xmlelement signature in root.getelementsbytagname("signature")) { var success = signature.verifysignature(certificate); console.writeline(success ? " successful!" : " failed!"); } } console.writeline("done."); console.readline(); } public static void sign(this xmlelement element, x509certificate2 certificate) { var identifier = guid.newguid().tostring("n"); element.setattribute("id", identifier); var signedxml = new signedxml(element) { signingkey = certificate.privatekey }; signedxml.addreference(new reference("#" + identifier)); signedxml.computesignature(); var xmldigitalsignature = signedxml.getxml(); element.ownerdocument.documentelement.appendchild( element.ownerdocument.importnode(xmldigitalsignature, true)); } public static bool verifysignature(this xmlelement element, x509certificate2 certificate) { var signedxml = new signedxml(element.ownerdocument); signedxml.loadxml(element); return signedxml.checksignature(certificate, true); }
one important detail notice: preservewhitespace
needs set true
signeddoc
, too.
Comments
Post a Comment