With FirmaSAT, you can do all operations necessary to sign an XML document in memory without using files at all.
With .NET code you must be careful to distinguish between byte[] array and string types, especially if you need to save your work eventually as a file.
If you have non-ASCII characters (like, say, "ñ") in your XML it can get messy handing UTF-8 encoded data in a normal string type. It is safer use a byte[] array.
Here is some code in C# that demonstrates some techniques.
1 using System; 2 using System.Text; 3 using System.IO; 4 using System.Diagnostics; 5 using FirmaSAT; 6 7 /*! SignXmlToBytes.cs v1.0.0 | (c) 2017 DI Management Services Pty Ltd <http://cryptosys.net/> | MIT */ 8 9 namespace SignXmlToBytes 10 { 11 class SignXmlToBytes 12 { 13 static void Main(string[] args) 14 { 15 Console.WriteLine("Version={0}", General.Version()); 16 byte[] xmlData; 17 byte[] signedData; 18 string baseFile = "cfdv33a-base.xml"; 19 string newFile = "signed-frombytes.xml"; 20 string xmlStr; 21 int r; 22 // We can pass the certificate and private key directly as strings 23 string certFileData = 24 "-----BEGIN CERTIFICATE-----" + 25 "MIIF+TCCA+GgAwIBAgIUMzAwMDEwMDAwMDAzMDAwMjM3MDgwDQYJKoZIhvcNAQELBQAwggFmMSAwHgY" + 26 "DVQQDDBdBLkMuIDIgZGUgcHJ1ZWJhcyg0MDk2KTEvMC0GA1UECgwmU2VydmljaW8gZGUgQWRtaW5pc3" + 27 "RyYWNpw7NuIFRyaWJ1dGFyaWExODA2BgNVBAsML0FkbWluaXN0cmFjacOzbiBkZSBTZWd1cmlkYWQgZ" + 28 "GUgbGEgSW5mb3JtYWNpw7NuMSkwJwYJKoZIhvcNAQkBFhphc2lzbmV0QHBydWViYXMuc2F0LmdvYi5t" + 29 "eDEmMCQGA1UECQwdQXYuIEhpZGFsZ28gNzcsIENvbC4gR3VlcnJlcm8xDjAMBgNVBBEMBTA2MzAwMQs" + 30 "wCQYDVQQGEwJNWDEZMBcGA1UECAwQRGlzdHJpdG8gRmVkZXJhbDESMBAGA1UEBwwJQ295b2Fjw6FuMR" + 31 "UwEwYDVQQtEwxTQVQ5NzA3MDFOTjMxITAfBgkqhkiG9w0BCQIMElJlc3BvbnNhYmxlOiBBQ0RNQTAeF" + 32 "w0xNzA1MTgwMzU0NTZaFw0yMTA1MTgwMzU0NTZaMIHlMSkwJwYDVQQDEyBBQ0NFTSBTRVJWSUNJT1Mg" + 33 "RU1QUkVTQVJJQUxFUyBTQzEpMCcGA1UEKRMgQUNDRU0gU0VSVklDSU9TIEVNUFJFU0FSSUFMRVMgU0M" + 34 "xKTAnBgNVBAoTIEFDQ0VNIFNFUlZJQ0lPUyBFTVBSRVNBUklBTEVTIFNDMSUwIwYDVQQtExxBQUEwMT" + 35 "AxMDFBQUEgLyBIRUdUNzYxMDAzNFMyMR4wHAYDVQQFExUgLyBIRUdUNzYxMDAzTURGUk5OMDkxGzAZB" + 36 "gNVBAsUEkNTRDAxX0FBQTAxMDEwMUFBQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJdU" + 37 "csHIEIgwivvAantGnYVIO3+7yTdD1tkKopbL+tKSjRFo1ErPdGJxP3gxT5O+ACIDQXN+HS9uMWDYnaU" + 38 "RalSIF9COFCdh/OH2Pn+UmkN4culr2DanKztVIO8idXM6c9aHn5hOo7hDxXMC3uOuGV3FS4ObkxTV+9" + 39 "NsvOAV2lMe27SHrSB0DhuLurUbZwXm+/r4dtz3b2uLgBc+Diy95PG+MIu7oNKM89aBNGcjTJw+9k+Wz" + 40 "JiPd3ZpQgIedYBD+8QWxlYCgxhnta3k9ylgXKYXCYk0k0qauvBJ1jSRVf5BjjIUbOstaQp59nkgHh45" + 41 "c9gnwJRV618NW0fMeDzuKR0CAwEAAaMdMBswDAYDVR0TAQH/BAIwADALBgNVHQ8EBAMCBsAwDQYJKoZ" + 42 "IhvcNAQELBQADggIBABKj0DCNL1lh44y+OcWFrT2icnKF7WySOVihx0oR+HPrWKBMXxo9KtrodnB1tg" + 43 "Ix8f+Xjqyphhbw+juDSeDrb99PhC4+E6JeXOkdQcJt50Kyodl9URpCVWNWjUb3F/ypa8oTcff/eMftQ" + 44 "ZT7MQ1Lqht+xm3QhVoxTIASce0jjsnBTGD2JQ4uT3oCem8bmoMXV/fk9aJ3v0+ZIL42MpY4POGUa/iT" + 45 "aawklKRAL1Xj9IdIR06RK68RS6xrGk6jwbDTEKxJpmZ3SPLtlsmPUTO1kraTPIo9FCmU/zZkWGpd8ZE" + 46 "AAFw+ZfI+bdXBfvdDwaM2iMGTQZTTEgU5KKTIvkAnHo9O45SqSJwqV9NLfPAxCo5eRR2OGibd9jhHe8" + 47 "1zUsp5GdE1mZiSqJU82H3cu6BiE+D3YbZeZnjrNSxBgKTIf8w+KNYPM4aWnuUMl0mLgtOxTUXi9MKnU" + 48 "ccq3GZLA7bx7Zn211yPRqEjSAqybUMVIOho6aqzkfc3WLZ6LnGU+hyHuZUfPwbnClb7oFFz1PlvGOpN" + 49 "DsUb0qP42QCGBiTUseGugAzqOP6EYpVPC73gFourmdBQgfayaEvi3xjNanFkPlW1XEYNrYJB4yNjphF" + 50 "rvWwTY86vL2o8gZN0Utmc5fnoBTfM9r2zVKmEi6FUeJ1iaDaVNv47te9iS1ai4V4vBY8r" + 51 "-----END CERTIFICATE-----"; 52 string keyFileData = 53 "-----BEGIN ENCRYPTED PRIVATE KEY-----" + 54 "MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQI5qDMtGWYa2wCAggA" + 55 "MBQGCCqGSIb3DQMHBAhFAqj+c0f8JASCBMhNUpNUp57vMu8L3LHBKRBTFl0VE3oq" + 56 "BIEKBHFYYz063iiS0Y3tPW3cplLTSqG25MdbIQcHCxwmPVYNdetHUjqjeR+TklWg" + 57 "tnMbLqvdMmmRxAFuHXznHFIa4U+YNedhFm7sdR2DsGFijm3vIpUbvpILtpTrhog/" + 58 "EHAvZXV6+F86cYc9+LUg3d0DRwJc+sWmk+2xOoXvOvvpnnQqfhQxkSknfITmc+HA" + 59 "WgHbKLK2q6e2RixjpWn0sA9LslYD0ZDn5uhrce+QEfK97asraFfiteqXf2Ll8B54" + 60 "Ku/er+O2JEu62vVDFumwMtZOuHKH4NbjOmMzKIwRTKp/1jp6OTGYSKIRiTDXnTET" + 61 "JwgItHahf7UAoM/qnkJa17Ood4hiCYopMyCXdhyMDJoFhWRanQODaiocb7XpMm1S" + 62 "EpTtHZeKgEVWSc/obYgSgs4iY497UR2MUVZQSCBdRXCgs5g1c31cCwAZ6r41KMoL" + 63 "OBVLtRXoT0mc0D6ovlwYuJhqYvuwjdNkWJS7qwXuy8b2ux4t027NGUXmgtb9XQDm" + 64 "8yJrdTtm0CktWPKe7i2tQtBC2tAjduGAlBrzY+whySRN8KUJQbYKhOBaLXgEPI93" + 65 "wi/SKHJO13WvfqqjKqrqJwB3tvhjz5E1uDKmDFoivdS76uq+k/xpmF5OWBmypWNV" + 66 "iw7kgvmH1OeTBKYkUHIL85skL6pdycGnTk3g0AmG9xtPYu6pdSqUv+N8QmTdmmdu" + 67 "85fDEN0fk2t2BRPANsbIqxopVfj5qIwm+8TbZDdNj8OssxrC5sRy5yDBjV4J+x25" + 68 "3yaILn7wgUR6Yj6GaHUUF4GISmFZ/PTbnVPDd424w6hGV8NKtUHXq5ms2kJXo6XG" + 69 "iGqjbdePM53QhdSrxTM5Dt76RcAInky6w5s/7gvT/w7tdbVA/SPhp4xgaT8Crmjb" + 70 "k3upcSqNI0HuROBxOs0gRRAWXScUZJ0Vd1V0F+C5cG2R1CtGTYeRmIAwLwcWf6Dj" + 71 "Y1Q+TOe/W3eTatOo+gIozjYDCk5ZNfeQzq4p1ApN6+gzS8kNxtvKOYJogjV74RK/" + 72 "Xl7u7oLv4SZT7Nl1YRpScW1ouIcNNTP0AC+j2OFZ3YueN8CcmvXbgSW8pYRooTxn" + 73 "Ffo9sdOL624uwRyb2DwwLO0Vo3aBIEIf8sm9sqocXmwh9sxFPEbTXPCuMSao8Qjy" + 74 "BOlsCem2589NVZs0h0ipGwdbatcjkgf+hzRoYBdlvHtKHJ8gL/A/Ap8z0+TK5NaV" + 75 "WUA+zXOZRZ66NYfs18DEbJKjwOcnnsLcfAMYoSn697148sL4JBv8IOmM6QXfxCl/" + 76 "0yU0d5/876L5jOL56lfH0eBk8s2nioAl3yRBl2wlihWi39sA0bsdHFKYEX+LqPBB" + 77 "CAdxZAvXCCJcdEdxOXSgEiFAmW9+IXFT/WJeGcZ4OmCd3Qf0fxGqFXA/9hIUumWd" + 78 "e6s0wN8LjXuFZQaMDaaVIGXKguP3OijsfBF0PYzI+L6CfUi2BLaYNJTlbQxbncmW" + 79 "2PKeDiypgt3ZY1PKV66o5OAJEAkV3vf9cRwXE5T8GwZHA+wx2rWC98hkH15xfI9q" + 80 "EsYulVdcXWzCF58HFQjUoDon0e/QMukS0eNgq9ipmoKAWKyy7+TQw7Xx3MmqkGlL" + 81 "HGM=" + 82 "-----END ENCRYPTED PRIVATE KEY-----"; 83 string password = "12345678a"; 84 85 // Show cert number in X.509 certificate 86 Console.WriteLine("NumberCert={0}", Sat.GetCertNumber(certFileData)); 87 88 // Read in XML data as a byte array 89 xmlData = ReadABinaryFile(baseFile); 90 91 // Sign it 92 signedData = Sat.SignXmlToBytes(xmlData, keyFileData, password, certFileData, 0); 93 Console.WriteLine("signedData has {0} bytes", signedData.Length); 94 Debug.Assert(signedData.Length > 0); 95 96 // Check the output is valid XML, 97 // but first put into a string because other functions require a string, not bytes. 98 // Use Sat.Asciify to avoid problems with UTF-8-encoded characters in a string. 99 xmlStr = Sat.Asciify(signedData); // signedData is type `byte[]`; xmlStr is type `string` 100 r = Sat.ValidateXml(xmlStr); 101 Console.WriteLine("Sat.ValidateXml(string) returns {0}", r); 102 Debug.Assert(0 == r); 103 r = Sat.VerifySignature(xmlStr); 104 Console.WriteLine("Sat.VerifySignature(string) returns {0}", r); 105 Debug.Assert(0 == r); 106 107 // Check cert number in XML 108 Console.WriteLine("NoCertificado={0}", Sat.GetXmlAttribute(xmlStr, "NoCertificado", "Comprobante")); 109 110 // Save bytes directly into a binary file 111 MakeABinaryFile(newFile, signedData); 112 113 // Now check the new file 114 r = Sat.ValidateXml(newFile); 115 Console.WriteLine("Sat.ValidateXml(file) returns {0}", r); 116 Debug.Assert(0 == r); 117 r = Sat.VerifySignature(newFile); 118 Console.WriteLine("Sat.VerifySignature(file) returns {0}", r); 119 Debug.Assert(0 == r); 120 121 } 122 123 /* UTILITIES FOR BINARY FILES */ 124 static byte[] ReadABinaryFile(string fileName) 125 { 126 byte[] b = new byte[0]; 127 FileInfo finfo = new FileInfo(fileName); 128 if (finfo.Exists) { 129 FileStream fsi = finfo.OpenRead(); 130 BinaryReader br = new BinaryReader(fsi); 131 int count = (int)fsi.Length; 132 b = br.ReadBytes(count); 133 br.Close(); 134 fsi.Close(); 135 } 136 Debug.Assert(finfo.Exists, "File '" + fileName + "' does not exist."); 137 return b; 138 } 139 static bool MakeABinaryFile(string fileName, byte[] data) 140 { 141 FileStream fs; 142 BinaryWriter bw; 143 fs = new FileStream(fileName, FileMode.Create, FileAccess.Write); 144 bw = new BinaryWriter(fs); 145 bw.Write(data); 146 bw.Close(); 147 fs.Close(); 148 return true; 149 } 150 151 } 152 }
string type, do this at line 89:
xmlData = System.Text.Encoding.UTF8.GetBytes(s);
string s = System.Text.Encoding.UTF8.GetString(signedData);
Output:
Version=80200 NumberCert=30001000000300023708 signedData has 6491 bytes Sat.ValidateXml(string) returns 0 Sat.VerifySignature(string) returns 0 NoCertificado=30001000000300023708 Sat.ValidateXml(file) returns 0 Sat.VerifySignature(file) returns 0
Download source code and xml (11.9 kB).
To contact us or comment on this page, please send us a message.
This page last updated 10 September 2025