using System;
using System.Diagnostics;
using System.Text;
using CryptoSysPKI;

namespace TestChile
{
    class TestChile
    {
        // $Id: TestChile.cs $
        // $Date: 2010-06-24 06:10 $
        // $Author: dai $

        // This module uses functions from the CryptoSys (tm) PKI Toolkit available from
        // <www.cryptosys.net/pki/>.

        // *************************** COPYRIGHT NOTICE ******************************
        // This code was originally written by David Ireland and is copyright
        // (C) 2008-10 DI Management Services Pty Ltd <www.di-mgt.com.au>.
        // Provided "as is". No warranties. Use at your own risk. You must make your
        // own assessment of its accuracy and suitability for your own purposes.
        // It is not to be altered or distributed, except as part of an application.
        // You are free to use it in any application, provided this copyright notice
        // is left unchanged.
        // ************************ END OF COPYRIGHT NOTICE **************************

        // NOTES: Some of these procedures are specific for the Chile SII examples.
        // Other procedures are more generic and could be used in more general PKI
        // programs, perhaps with minor amendments.
        // These are marked $GENERIC-FUNCTION$.

        // Ref: F60T33-ejemplo.xml

        public static void Read_SmallerKeys()
        {
            // From manual_certificacion.pdf, 2003-11-26, p25
            string strKeyPem = null;
            int nRet = 0;
            string strPrivateKey = null;
            string strPublicKey = null;
            string strXmlKey = null;
            string strSig64 = null;
            byte[] abData = null;
            int nDataLen = 0;
            int nKeyLen = 0;
            byte[] abDigest = null;

            // CAUTION: Your real Private Key should be kept SECRET and should never be hard-coded in code like, er, this...
            // Put PEM data into a string
            // (Note two critical vbCrLF's added before and after base64 data)
            strKeyPem = "-----BEGIN RSA PRIVATE KEY-----" + "\n" 
                + "MIIBOwIBAAJBANGuDuim8fEI9yuIlkj+MOyp3mWHifoP6a4oWLSBKJSrd3MpEsZd" 
                + "czvL0l7t/e0IU5rF+0gRLnU1Mfvtsw1wYWcCAQMCQQCLyV9FxKFLW09yWw7bVCCd" 
                + "xpRDr7FRX/EexZB4VhsNxm/vtJfDZyYle0Lfy42LlcsXxPm1w6Q6NnjuW+AeBy67" 
                + "AiEA7iMi5q5xjswqq+49RP55o//jqdZL/pC9rdnUKxsNRMMCIQDhaHdIctErN2hC" 
                + "IP9knS3+9zra4R+5jSXOvI+3xVhWjQIhAJ7CF0R0S7SIHHKe04NUURf/7RvkMqm1" 
                + "08k74sdnXi3XAiEAlkWk2vc2HM+a1sCqQxNz/098ketqe7NuidMKeoOQObMCIQCk" 
                + "FAMS9IcPcMjk7zI2r/4EEW63PSXyN7MFAX7TYe25mw==" 
                + "\n" + "-----END RSA PRIVATE KEY-----";

            // Read the PEM private key data into an internal key string we can use later on...
            // -- this example key is stored in a PKCS#8 PrivateKeyInfo without a password :-(
            strPrivateKey = Rsa.ReadPrivateKeyInfo(strKeyPem).ToString();
            // CAUTION: the above trick to read into a string is a big security risk
            // -- the point of using a StringBuilder is that it can be wiped later

            Console.WriteLine("Private key is " + Rsa.KeyBits(strPrivateKey) + " bits");

            // Now we compare this private key with the published public key in the XML file...

            // Form XML string of public key in proper XMLSIG format using <RSAPK> element from XML file
            // <RSAPK><M>0a4O6Kbx8Qj3K4iWSP4w7KneZYeJ+g/prihYtIEolKt3cykSxl1zO8vSXu397QhTmsX7SBEudTUx++2zDXBhZw==</M><E>Aw==</E></RSAPK>
            // NOTE: We have changed the element names to the required XMLSIG format:
            // <RSAPK> --> <RSAKeyValue>
            // <M>     --> <Modulus>
            // <E>     --> <Exponent>
            strXmlKey = "<RSAKeyValue>" 
                + "<Modulus>0a4O6Kbx8Qj3K4iWSP4w7KneZYeJ+g/prihYtIEolKt3cykSxl1zO8vSXu397QhTmsX7SBEudTUx++2zDXBhZw==</Modulus>" 
                + "<Exponent>Aw==</Exponent></RSAKeyValue>";
            // Convert XML form to our internal format
            strPublicKey = Rsa.FromXMLString(strXmlKey, false);
            Console.WriteLine("Public key is " + Rsa.KeyBits(strPublicKey) + " bits");

            // Check that key pairs match...
            nRet = Rsa.KeyMatch(strPrivateKey, strPublicKey);
            Console.WriteLine("RSA_KeyMatch returns " + nRet + " (expecting 0)");

            // Then use the PUBLIC key to extract the message digest from the signature value...

            // Extract digest from FRMT
            // <FRMT algoritmo="SHA1withRSA">GbmDcS9e/jVC2LsLIe1iRV12Bf6lxsILtbQiCkh6mbjckFCJ7fj/kakFTS06Jo8i
            // S4HXvJj3oYZuey53Krniew==</FRMT>
            strSig64 = "GbmDcS9e/jVC2LsLIe1iRV12Bf6lxsILtbQiCkh6mbjckFCJ7fj/kakFTS06Jo8iS4HXvJj3oYZuey53Krniew==";

            // Convert signatureValue to byte format
            abData = System.Convert.FromBase64String(strSig64);
            Console.WriteLine("SIGNATURE=" + Cnv.ToHex(abData));
            nDataLen = abData.Length;
            Console.WriteLine("Signature is " + nDataLen + " bytes long");
            // Check the length
            nKeyLen = Rsa.KeyBytes(strPublicKey);
            Console.WriteLine("Key is " + nKeyLen + " bytes long");
            if (nKeyLen != nDataLen)
            {
                Console.WriteLine("ERROR: Key is the wrong length!");
                return;
            }
            // Decrypt the signature
            abData = Rsa.RawPublic(abData, strPublicKey);
            if (nRet != 0)
            {
                Console.WriteLine("ERROR: " + General.ErrorLookup(nRet));
                return;
            }
            Console.WriteLine("EMSA BLOCK=" + Cnv.ToHex(abData));

            // Extract digest from block
            abDigest = Rsa.DecodeDigestForSignature(abData);

            // Display digest bytes as hex and base64
            Console.WriteLine("DIGEST=" + Cnv.ToHex(abDigest));
            Console.WriteLine("base64=" + System.Convert.ToBase64String(abDigest));

        }

        public static void MakeFRMTdigest()
        {
            // This should give the same digest value we obtained above.
            string strData = null;
            string strDigest = null;
            // Put <DD> data into a single string with no spaces between elements (and fix double-quotes for VB when hard-coded here!)
            strData = "<DD><RE>97975000-5</RE><TD>33</TD><F>60</F><FE>2003-10-13</FE><RR>77777777-7</RR><RSR>EMPRESA  LTDA</RSR><MNT>119000</MNT><IT1>Parlantes Multimedia 180W.</IT1><CAF version=\"1.0\"><DA><RE>97975000-5</RE><RS>RUT DE PRUEBA</RS><TD>33</TD><RNG><D>1</D><H>200</H></RNG><FA>2003-09-04</FA><RSAPK><M>0a4O6Kbx8Qj3K4iWSP4w7KneZYeJ+g/prihYtIEolKt3cykSxl1zO8vSXu397QhTmsX7SBEudTUx++2zDXBhZw==</M><E>Aw==</E></RSAPK><IDK>100</IDK></DA><FRMA algoritmo=\"SHA1withRSA\">g1AQX0sy8NJugX52k2hTJEZAE9Cuul6pqYBdFxj1N17umW7zG/hAavCALKByHzdYAfZ3LhGTXCai5zNxOo4lDQ==</FRMA></CAF><TSTED>2003-10-13T09:33:20</TSTED></DD>";
            // Create an SHA-1 digest value in hex format
            strDigest = Hash.HexFromString(strData, HashAlgorithm.Sha1);
            Console.WriteLine("SHA1(DD)=" + strDigest);
            // Display in base64
            Console.WriteLine("SHA1(DD)=" + System.Convert.ToBase64String(Cnv.FromHex(strDigest)));
        }

        public static void SignDDtoMakeFRMTsig()
        {
            // Use the RSA private key to sign the <DD> data.
            // INPUT:  RSA private key in PEM format
            //         Data-to-be signed
            // OUTPUT: SignatureValue in base64 form

            string strKeyPem = null;
            string strData = null;
            string strPrivateKey = null;
            int nKeyBytes = 0;
            byte[] abBlock = null;
            byte[] abData = null;
            int nDataLen = 0;
            string strSigVal64 = null;

            // Input data:
            strData = "<DD><RE>97975000-5</RE><TD>33</TD><F>60</F><FE>2003-10-13</FE><RR>77777777-7</RR><RSR>EMPRESA  LTDA</RSR><MNT>119000</MNT><IT1>Parlantes Multimedia 180W.</IT1><CAF version=\"1.0\"><DA><RE>97975000-5</RE><RS>RUT DE PRUEBA</RS><TD>33</TD><RNG><D>1</D><H>200</H></RNG><FA>2003-09-04</FA><RSAPK><M>0a4O6Kbx8Qj3K4iWSP4w7KneZYeJ+g/prihYtIEolKt3cykSxl1zO8vSXu397QhTmsX7SBEudTUx++2zDXBhZw==</M><E>Aw==</E></RSAPK><IDK>100</IDK></DA><FRMA algoritmo=\"SHA1withRSA\">g1AQX0sy8NJugX52k2hTJEZAE9Cuul6pqYBdFxj1N17umW7zG/hAavCALKByHzdYAfZ3LhGTXCai5zNxOo4lDQ==</FRMA></CAF><TSTED>2003-10-13T09:33:20</TSTED></DD>";
            strKeyPem = "-----BEGIN RSA PRIVATE KEY-----" + "\n" 
                + "MIIBOwIBAAJBANGuDuim8fEI9yuIlkj+MOyp3mWHifoP6a4oWLSBKJSrd3MpEsZd" 
                + "czvL0l7t/e0IU5rF+0gRLnU1Mfvtsw1wYWcCAQMCQQCLyV9FxKFLW09yWw7bVCCd" 
                + "xpRDr7FRX/EexZB4VhsNxm/vtJfDZyYle0Lfy42LlcsXxPm1w6Q6NnjuW+AeBy67" 
                + "AiEA7iMi5q5xjswqq+49RP55o//jqdZL/pC9rdnUKxsNRMMCIQDhaHdIctErN2hC" 
                + "IP9knS3+9zra4R+5jSXOvI+3xVhWjQIhAJ7CF0R0S7SIHHKe04NUURf/7RvkMqm1" 
                + "08k74sdnXi3XAiEAlkWk2vc2HM+a1sCqQxNz/098ketqe7NuidMKeoOQObMCIQCk" 
                + "FAMS9IcPcMjk7zI2r/4EEW63PSXyN7MFAX7TYe25mw==" 
                + "\n" + "-----END RSA PRIVATE KEY-----";

            // Read the PEM private key data into an internal key string
            strPrivateKey = Rsa.ReadPrivateKeyInfo(strKeyPem).ToString();
            // CAUTION: the above trick to read into a string is a big security risk
            // -- the point of using a StringBuilder is that it can be wiped later

            Console.WriteLine("Private key is " + Rsa.KeyBits(strPrivateKey) + " bits long");

            // Convert the input data string into a byte array
            abData = System.Text.Encoding.Default.GetBytes(strData);
            Console.WriteLine("Data=(0x)" + Cnv.ToHex(abData));
            nDataLen = abData.Length;
            Console.WriteLine("Input data is " + nDataLen + " bytes long");

            // Encode into a block the exact same length as the RSA key...

            // Get the length of the key in bytes -- we need an output block exactly this length
            nKeyBytes = Rsa.KeyBytes(strPrivateKey);
            Console.WriteLine("Private key is  " + nKeyBytes + " bytes long");

            // Encode the data -- this computes the message digest and then encodes into an EMSA block in one step
            abBlock = Rsa.EncodeMsgForSignature(nKeyBytes, abData, HashAlgorithm.Sha1);
            if (abBlock.Length == 0)
            {
                Console.WriteLine("ERROR: RSA_EncodeMsg failed. Error: " + General.LastError());
                return;
            }
            Console.WriteLine("EMSA BLOCK=" + Cnv.ToHex(abBlock));

            // Sign, i.e. encrypt, the encoded block with the RSA private key
            abBlock = Rsa.RawPrivate(abBlock, strPrivateKey);
            if (abBlock.Length == 0)
            {
                Console.WriteLine("ERROR: RSA_RawPrivate failed. Error: " + General.LastError());
                return;
            }
            Console.WriteLine("SIGNATURE= " + Cnv.ToHex(abBlock));

            // Convert byte array to base64
            strSigVal64 = System.Convert.ToBase64String(abBlock);
            Console.WriteLine("SignatureValue=" + strSigVal64);

        }

        public static string XmlHexFromInternalKey(string strKey)
        {
            string functionReturnValue = null;
            // $GENERIC-FUNCTION$
            // INPUT:  Public or private RSA key in internal format
            // OUTPUT: String containing RSAKeyValue in XML format
            string strXmlKey = null;
            Rsa.XmlOptions nOptions = default(Rsa.XmlOptions);

            nOptions = Rsa.XmlOptions.HexBinaryFormat | Rsa.XmlOptions.ExcludePrivateParams | Rsa.XmlOptions.ForceRSAKeyValue;
            strXmlKey = Rsa.ToXMLString(strKey, nOptions);
            if (strXmlKey.Length > 0)
            {
                functionReturnValue = strXmlKey;
            }
            else
            {
                functionReturnValue = "**Failed to create public key in XML";
            }
            return functionReturnValue;

        }

        public static bool IsCertAndKeyValueMatched(string strCert64, string strXmlKey)
        {
            // $GENERIC-FUNCTION$
            // Compare the public key values in <RSAKeyValue> and <X509Certificate> values
            // INPUT:  RSAKeyValue in base64 string INCLUDING <RSAKeyValue> tags;
            //         X509Certificate in base64 string EXCLUDING <X509Certificate> tags
            // OUTPUT: True if public key matches; otherwise False.
            // REMARKS: The X509Certificate contains the public key value plus other info.

            string strPublicKey = null;
            string strQuery = null;
            string strOutput = null;
            int nHashCode1 = 0;
            int nHashCode2 = 0;
            bool result = false;

            // Query the certificate for details...
            strQuery = "issuerName";
            strOutput = X509.QueryCert(strCert64, strQuery);
            if (strOutput.Length == 0)
            {
                Console.WriteLine("ERROR: Error reading certificate: " + General.LastError());
                return result;
                // ERROR
            }
            Console.WriteLine(strQuery + "=" + strOutput);

            strQuery = "serialNumber";
            strOutput = X509.QueryCert(strCert64, strQuery);
            if (strOutput.Length == 0)
            {
                Console.WriteLine("ERROR: Error reading certificate: " + General.LastError());
                return result;
                // ERROR
            }
            Console.WriteLine(strQuery + "=" + strOutput);

            strQuery = "subjectName";
            strOutput = X509.QueryCert(strCert64, strQuery);
            if (strOutput.Length == 0)
            {
                Console.WriteLine("ERROR: Error reading certificate: " + General.LastError());
                return result;
                // ERROR
            }
            Console.WriteLine(strQuery + "=" + strOutput);

            strQuery = "notBefore";
            strOutput = X509.QueryCert(strCert64, strQuery);
            if (strOutput.Length == 0)
            {
                Console.WriteLine("ERROR: Error reading certificate: " + General.LastError());
                return result;
                // ERROR
            }
            Console.WriteLine(strQuery + "=" + strOutput);

            strQuery = "signatureAlgorithm";
            strOutput = X509.QueryCert(strCert64, strQuery);
            if (strOutput.Length == 0)
            {
                Console.WriteLine("ERROR: Error reading certificate: " + General.LastError());
                return result;
                // ERROR
            }
            Console.WriteLine(strQuery + "=" + strOutput);

            // Extract public key from X.509 certificate
            strPublicKey = Rsa.GetPublicKeyFromCert(strCert64).ToString();
            if (strPublicKey.Length == 0)
            {
                Console.WriteLine("ERROR: Error reading certificate key: " + General.LastError());
                return result;
                // ERROR
            }
            Console.WriteLine("Public key extracted from <X509Certificate>:");
            Console.WriteLine("Public key is " + Rsa.KeyBits(strPublicKey) + " bits");
            nHashCode1 = Rsa.KeyHashCode(strPublicKey);
            Console.WriteLine("Public key hashcode = {0,8:X}", nHashCode1);

            // While we're here, display the public key in XML format
            Console.WriteLine(XmlHexFromInternalKey(strPublicKey));

            // Convert XML form to our internal format (this requires the <RSAKeyvalue> tags)
            strPublicKey = Rsa.FromXMLString(strXmlKey, true);
            if (strPublicKey.Length == 0)
            {
                Console.WriteLine("ERROR: Unable to read XML key string");
                return result;
            }
            Console.WriteLine("Public key given in <RSAKeyValue>:");
            Console.WriteLine("Public key is " + Rsa.KeyBits(strPublicKey) + " bits");
            nHashCode2 = Rsa.KeyHashCode(strPublicKey);
            Console.WriteLine("Public key hashcode = {0,8:X}", nHashCode2);
            Console.WriteLine(XmlHexFromInternalKey(strPublicKey));

            if (nHashCode1 == nHashCode2)
            {
                result = true;
                Console.WriteLine("OK, public keys match.");
            }
            else
            {
                result = false;
                Console.WriteLine("ERROR: public keys do not match.");
            }
            return result;

        }

        public static void CheckExampleCertAndRSAKey()
        {
            // Ref: F60T33-ejemplo.xml, 2003-10-13

            // THIS TEST FAILS BECAUSE THE RSAKeyValue IS WRONG

            // <RSAKeyValue>
            // <Modulus>
            // tNEknkb1kHiD1OOAWlLKkcH/UP5UGa6V6MYso++JB+vYMg2OXFROAF7G8BNFFPQx
            // iuS/7y1azZljN2xq+bW3bAou1bW2ij7fxSXWTJYFZMAyndbLyGHM1e3nVmwpgEpx
            // BHhZzPvwLb55st1wceuKjs2Ontb13J33sUb7bbJMWh0=
            // </Modulus>
            // <Exponent>
            // AQAB
            // </Exponent>
            // </RSAKeyValue>

            // <X509Certificate>MIIEgjCCA+ugAwIBAgIEAQAApzANBgkqhkiG9w0BAQUFADCBtTELMAkGA1UEBhMC
            // Q0wxHTAbBgNVBAgUFFJlZ2lvbiBNZXRyb3BvbGl0YW5hMREwDwYDVQQHFAhTYW50
            // aWFnbzEUMBIGA1UEChQLRS1DRVJUQ0hJTEUxIDAeBgNVBAsUF0F1dG9yaWRhZCBD
            // ZXJ0aWZpY2Fkb3JhMRcwFQYDVQQDFA5FLUNFUlRDSElMRSBDQTEjMCEGCSqGSIb3
            // DQEJARYUZW1haWxAZS1jZXJ0Y2hpbGUuY2wwHhcNMDMxMDAxMTg1ODE1WhcNMDQw
            // OTMwMDAwMDAwWjCBuDELMAkGA1UEBhMCQ0wxFjAUBgNVBAgUDU1ldHJvcG9saXRh
            // bmExETAPBgNVBAcUCFNhbnRpYWdvMScwJQYDVQQKFB5TZXJ2aWNpbyBkZSBJbXB1
            // ZXN0b3MgSW50ZXJub3MxDzANBgNVBAsUBlBpc28gNDEjMCEGA1UEAxQaV2lsaWJh
            // bGRvIEdvbnphbGV6IENhYnJlcmExHzAdBgkqhkiG9w0BCQEWEHdnb256YWxlekBz
            // aWkuY2wwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALxZlVh1xr9sKQIBDF/6
            // Va+lsHQSG5AAmCWvtNTIOXN3E9EQCy7pOPHrDg6EusvoHyesZSKJbc0TnIFXZp78
            // q7mbdHijzKqvMmyvwbdP7KK8LQfwf84W4v9O8MJeUHlbJGlo5nFACrPAeTtONbHa
            // ReyzeMDv2EganNEDJc9c+UNfAgMBAAGjggGYMIIBlDAjBgNVHREEHDAaoBgGCCsG
            // AQQBwQEBoAwWCjA3ODgwNDQyLTQwCQYDVR0TBAIwADA8BgNVHR8ENTAzMDGgL6At
            // hitodHRwOi8vY3JsLmUtY2VydGNoaWxlLmNsL2UtY2VydGNoaWxlY2EuY3JsMCMG
            // A1UdEgQcMBqgGAYIKwYBBAHBAQKgDBYKOTY5MjgxODAtNTAfBgNVHSMEGDAWgBTg
            // KP3S4GBPs0brGsz1CJEHcjodCDCB0AYDVR0gBIHIMIHFMIHCBggrBgEEAcNSBTCB
            // tTAvBggrBgEFBQcCARYjaHR0cDovL3d3dy5lLWNlcnRjaGlsZS5jbC8yMDAwL0NQ
            // Uy8wgYEGCCsGAQUFBwICMHUac0VsIHRpdHVsYXIgaGEgc2lkbyB2YWxpZG8gZW4g
            // Zm9ybWEgcHJlc2VuY2lhbCwgcXVlZGFuZG8gZWwgQ2VydGlmaWNhZG8gcGFyYSB1
            // c28gdHJpYnV0YXJpbywgcGFnb3MsIGNvbWVyY2lvIHkgb3Ryb3MwCwYDVR0PBAQD
            // AgTwMA0GCSqGSIb3DQEBBQUAA4GBABMfCyJF0mNXcov8iEWvjGFyyPTsXwvsYbbk
            // OJ41wjaGOFMCInb4WY0ngM8BsDV22bGMs8oLyX7rVy16bGA8Z7WDUtYhoOM7mqXw
            // /Hrpqjh3JgAf8zqdzBdH/q6mAbdvq/yb04JHKWPC7fMFuBoeyVWAnhmuMZfReWQi
            // MUEHGGIW</X509Certificate>

            string strCert64 = null;
            string strXmlKey = null;

            strCert64 = "MIIEgjCCA+ugAwIBAgIEAQAApzANBgkqhkiG9w0BAQUFADCBtTELMAkGA1UEBhMCQ0wxHTAbBgNVBAgUFFJlZ2lvbiBNZXRyb3BvbGl0YW5hMREwDwYDVQQHFAhTYW50aWFnbzEUMBIGA1UEChQLRS1DRVJUQ0hJTEUxIDAeBgNVBAsUF0F1dG9yaWRhZCBDZXJ0aWZpY2Fkb3JhMRcwFQYDVQQDFA5FLUNFUlRDSElMRSBD" 
                + "QTEjMCEGCSqGSIb3DQEJARYUZW1haWxAZS1jZXJ0Y2hpbGUuY2wwHhcNMDMxMDAxMTg1ODE1WhcNMDQwOTMwMDAwMDAwWjCBuDELMAkGA1UEBhMCQ0wxFjAUBgNVBAgUDU1ldHJvcG9saXRhbmExETAPBgNVBAcUCFNhbnRpYWdvMScwJQYDVQQKFB5TZXJ2aWNpbyBkZSBJbXB1ZXN0b3MgSW50ZXJub3MxDzANBgNVBAsU" 
                + "BlBpc28gNDEjMCEGA1UEAxQaV2lsaWJhbGRvIEdvbnphbGV6IENhYnJlcmExHzAdBgkqhkiG9w0BCQEWEHdnb256YWxlekBzaWkuY2wwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALxZlVh1xr9sKQIBDF/6Va+lsHQSG5AAmCWvtNTIOXN3E9EQCy7pOPHrDg6EusvoHyesZSKJbc0TnIFXZp78q7mbdHijzKqvMmyv" 
                + "wbdP7KK8LQfwf84W4v9O8MJeUHlbJGlo5nFACrPAeTtONbHaReyzeMDv2EganNEDJc9c+UNfAgMBAAGjggGYMIIBlDAjBgNVHREEHDAaoBgGCCsGAQQBwQEBoAwWCjA3ODgwNDQyLTQwCQYDVR0TBAIwADA8BgNVHR8ENTAzMDGgL6AthitodHRwOi8vY3JsLmUtY2VydGNoaWxlLmNsL2UtY2VydGNoaWxlY2EuY3JsMCMG" 
                + "A1UdEgQcMBqgGAYIKwYBBAHBAQKgDBYKOTY5MjgxODAtNTAfBgNVHSMEGDAWgBTgKP3S4GBPs0brGsz1CJEHcjodCDCB0AYDVR0gBIHIMIHFMIHCBggrBgEEAcNSBTCBtTAvBggrBgEFBQcCARYjaHR0cDovL3d3dy5lLWNlcnRjaGlsZS5jbC8yMDAwL0NQUy8wgYEGCCsGAQUFBwICMHUac0VsIHRpdHVsYXIgaGEgc2lk" 
                + "byB2YWxpZG8gZW4gZm9ybWEgcHJlc2VuY2lhbCwgcXVlZGFuZG8gZWwgQ2VydGlmaWNhZG8gcGFyYSB1c28gdHJpYnV0YXJpbywgcGFnb3MsIGNvbWVyY2lvIHkgb3Ryb3MwCwYDVR0PBAQDAgTwMA0GCSqGSIb3DQEBBQUAA4GBABMfCyJF0mNXcov8iEWvjGFyyPTsXwvsYbbkOJ41wjaGOFMCInb4WY0ngM8BsDV22bGM" 
                + "s8oLyX7rVy16bGA8Z7WDUtYhoOM7mqXw/Hrpqjh3JgAf8zqdzBdH/q6mAbdvq/yb04JHKWPC7fMFuBoeyVWAnhmuMZfReWQiMUEHGGIW";

            strXmlKey = "<RSAKeyValue><Modulus>" 
                + "tNEknkb1kHiD1OOAWlLKkcH/UP5UGa6V6MYso++JB+vYMg2OXFROAF7G8BNFFPQx" 
                + "iuS/7y1azZljN2xq+bW3bAou1bW2ij7fxSXWTJYFZMAyndbLyGHM1e3nVmwpgEpx" 
                + "BHhZzPvwLb55st1wceuKjs2Ontb13J33sUb7bbJMWh0=</Modulus>" 
                + "<Exponent>AQAB</Exponent></RSAKeyValue>";

            Console.WriteLine(IsCertAndKeyValueMatched(strCert64, strXmlKey));

        }

        public static void CheckManualCertAndRSAKey()
        {
            // Ref: manual_certificacion.pdf, 2003-11-26

            // THIS TEST FAILS BECAUSE THE RSAKeyValue IS WRONG

            // <RSAKeyValue>
            // <Modulus>
            // tNEknkb1kHiD1OOAWlLKkcH/UP5UGa6V6MYso++JB+vYMg2OXFROAF7G8BNFFPQx
            // iuS/7y1azZljN2xq+bW3bAou1bW2ij7fxSXWTJYFZMAyndbLyGHM1e3nVmwpgEpx
            // BHhZzPvwLb55st1wceuKjs2Ontb13J33sUb7bbJMWh0=
            // </Modulus>
            // <Exponent>
            // AQAB
            // </Exponent>
            // </RSAKeyValue>

            // <X509Certificate>MIIEPjCCA6mgAwIBAgIDAgGKMAsGCSqGSIb3DQEBBDCBsTEdMBsGA1UECBQUUmVn
            // aW9uIE1ldHJvcG9saXRhbmExETAPBgNVBAcUCFNhbnRpYWdvMSIwIAYDVQQDFBlF
            // LUNlcnRjaGlsZSBDQSBJbnRlcm1lZGlhMTYwNAYDVQQLFC1FbXByZXNhIE5hY2lv
            // bmFsIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExFDASBgNVBAoUC0UtQ0VS
            // VENISUxFMQswCQYDVQQGEwJDTDAeFw0wMjEwMDIxOTExNTlaFw0wMzEwMDIwMDAw
            // MDBaMIHXMR0wGwYDVQQIFBRSZWdpb24gTWV0cm9wb2xpdGFuYTEnMCUGA1UECxQe
            // U2VydmljaW8gZGUgSW1wdWVzdG9zIEludGVybm9zMScwJQYDVQQKFB5TZXJ2aWNp
            // byBkZSBJbXB1ZXN0b3MgSW50ZXJub3MxETAPBgNVBAcUCFNhbnRpYWdvMR8wHQYJ
            // KoZIhvcNAQkBFhB3Z29uemFsZXpAc2lpLmNsMSMwIQYDVQQDFBpXaWxpYmFsZG8g
            // R29uemFsZXogQ2FicmVyYTELMAkGA1UEBhMCQ0wwXDANBgkqhkiG9w0BAQEFAANL
            // ADBIAkEAvNQyaLPd3cQlBr0fQWooAKXSFan/WbaFtD5P7QDzcE1pBIvKY2Uv6uid
            // ur/mGVB9IS4Fq/1xRIXy13FFmxLwTQIDAQABo4IBgjCCAX4wIwYDVR0RBBwwGqAY
            // BggrBgEEAcNSAaAMFgowNzg4MDQ0Mi00MDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6
            // Ly9jcmwuZS1jZXJ0Y2hpbGUuY2wvRWNlcnRjaGlsZUNBSS5jcmwwIwYDVR0SBBww
            // GqAYBggrBgEEAcEBAqAMFgo5NjkyODE4MC01MIHmBgNVHSAEgd4wgdswgdgGCCsG
            // AQQBw1IAMIHLMDYGCCsGAQUFBwIBFipodHRwOi8vd3d3LmUtY2VydGNoaWxlLmNs
            // L3BvbGl0aWNhL2Nwcy5odG0wgZAGCCsGAQUFBwICMIGDGoGARWwgdGl0dWxhciBo
            // YSBzaWRvIHZhbGlkYWRvIGVuIGZvcm1hIHByZXNlbmNpYWwsIHF1ZWRhbmRvIGhh
            // YmlsaXRhZG8gZWwgQ2VydGlmaWNhZG8gcGFyYSB1c28gdHJpYnV0YXJpbywgcGFn
            // b3MsIGNvbWVyY2lvIHUgb3Ryb3MwCwYDVR0PBAQDAgTwMAsGCSqGSIb3DQEBBAOB
            // gQB2V4cTj7jo1RawmsRQUSnnvJjMCrZstcHY+Ss3IghVPO9eGoYzu5Q63vzt0Pi8
            // CS91SBc7xo+LDoljaUyjOzj7zvU7TpWoFndiTQF3aCOtTkV+vjCMWW3sVHes4UCM
            // DkF3VYK+rDTAadiaeDArTwsx4eNEpxFuA/TJwcXpLQRCDg==</X509Certificate>

            string strCert64 = null;
            string strXmlKey = null;

            strCert64 = "MIIEPjCCA6mgAwIBAgIDAgGKMAsGCSqGSIb3DQEBBDCBsTEdMBsGA1UECBQUUmVnaW9uIE1ldHJvcG9saXRhbmExETAPBgNVBAcUCFNhbnRpYWdvMSIwIAYDVQQDFBlFLUNlcnRjaGlsZSBDQSBJbnRlcm1lZGlhMTYwNAYDVQQLFC1FbXByZXNhIE5hY2lvbmFsIGRlIENlcnRpZmljYWNpb24gRWx" 
                + "lY3Ryb25pY2ExFDASBgNVBAoUC0UtQ0VSVENISUxFMQswCQYDVQQGEwJDTDAeFw0wMjEwMDIxOTExNTlaFw0wMzEwMDIwMDAwMDBaMIHXMR0wGwYDVQQIFBRSZWdpb24gTWV0cm9wb2xpdGFuYTEnMCUGA1UECxQeU2VydmljaW8gZGUgSW1wdWVzdG9zIEludGVybm9zMScwJQYDVQQKFB5TZXJ2aWNpbyBkZSBJbXB1ZXN" 
                + "0b3MgSW50ZXJub3MxETAPBgNVBAcUCFNhbnRpYWdvMR8wHQYJKoZIhvcNAQkBFhB3Z29uemFsZXpAc2lpLmNsMSMwIQYDVQQDFBpXaWxpYmFsZG8gR29uemFsZXogQ2FicmVyYTELMAkGA1UEBhMCQ0wwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAvNQyaLPd3cQlBr0fQWooAKXSFan/WbaFtD5P7QDzcE1pBIvKY2Uv6ui" 
                + "dur/mGVB9IS4Fq/1xRIXy13FFmxLwTQIDAQABo4IBgjCCAX4wIwYDVR0RBBwwGqAYBggrBgEEAcNSAaAMFgowNzg4MDQ0Mi00MDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuZS1jZXJ0Y2hpbGUuY2wvRWNlcnRjaGlsZUNBSS5jcmwwIwYDVR0SBBwwGqAYBggrBgEEAcEBAqAMFgo5NjkyODE4MC01MIHmBgNVHSA" 
                + "Egd4wgdswgdgGCCsGAQQBw1IAMIHLMDYGCCsGAQUFBwIBFipodHRwOi8vd3d3LmUtY2VydGNoaWxlLmNsL3BvbGl0aWNhL2Nwcy5odG0wgZAGCCsGAQUFBwICMIGDGoGARWwgdGl0dWxhciBoYSBzaWRvIHZhbGlkYWRvIGVuIGZvcm1hIHByZXNlbmNpYWwsIHF1ZWRhbmRvIGhhYmlsaXRhZG8gZWwgQ2VydGlmaWNhZG8" 
                + "gcGFyYSB1c28gdHJpYnV0YXJpbywgcGFnb3MsIGNvbWVyY2lvIHUgb3Ryb3MwCwYDVR0PBAQDAgTwMAsGCSqGSIb3DQEBBAOBgQB2V4cTj7jo1RawmsRQUSnnvJjMCrZstcHY+Ss3IghVPO9eGoYzu5Q63vzt0Pi8CS91SBc7xo+LDoljaUyjOzj7zvU7TpWoFndiTQF3aCOtTkV+vjCMWW3sVHes4UCMDkF3VYK+rDTAadi" 
                + "aeDArTwsx4eNEpxFuA/TJwcXpLQRCDg==";

            strXmlKey = "<RSAKeyValue><Modulus>" 
                + "tNEknkb1kHiD1OOAWlLKkcH/UP5UGa6V6MYso++JB+vYMg2OXFROAF7G8BNFFPQx" 
                + "iuS/7y1azZljN2xq+bW3bAou1bW2ij7fxSXWTJYFZMAyndbLyGHM1e3nVmwpgEpx" 
                + "BHhZzPvwLb55st1wceuKjs2Ontb13J33sUb7bbJMWh0=</Modulus>" 
                + "<Exponent>AQAB</Exponent></RSAKeyValue>";

            Console.WriteLine(IsCertAndKeyValueMatched(strCert64, strXmlKey));

        }

        public static string Hash_String_SHA1(string strMessage, bool ShowDebug)
        {
            // $GENERIC-FUNCTION$
            // INPUT:  Message as an ANSI string
            // OUTPUT: SHA-1 message digest in hex format
            byte[] abMessage = null;
            int nmLen = 0;
            string strDigest = null;

            // Convert ANSI text to bytes
            abMessage = System.Text.Encoding.Default.GetBytes(strMessage);
            nmLen = abMessage.Length;

            if (ShowDebug)
            {
                Console.WriteLine("M (ansi):" + "\n" + "'" + System.Text.Encoding.Default.GetString(abMessage) + "'");
                Console.WriteLine("M (hex):  " + Cnv.ToHex(abMessage));
                Console.WriteLine("Message is " + nmLen + " bytes long");
            }

            strDigest = Hash.HexFromBytes(abMessage, HashAlgorithm.Sha1);
            if (strDigest.Length == 0)
            {
                Console.WriteLine("FAILED to create message digest");
            }

            return strDigest;

        }

        public static string Hash_File_SHA1(string strFileName)
        {
            // $GENERIC-FUNCTION$
            // INPUT:  Full path to file
            // OUTPUT: SHA-1 message digest in hex format
            string strDigest = null;

            strDigest = Hash.HexFromFile(strFileName, HashAlgorithm.Sha1);
            if (strDigest.Length == 0)
            {
                Console.WriteLine("FAILED to create message digest");
            }

            return strDigest;

        }


        public static void Test_Make_Hash_of_File()
        {
            Console.WriteLine("DIGEST=" + Hash_File_SHA1("data.canon.ok.txt"));

        }

        public static string MakeDigestValueOfSignedInfo(string strDigestValue64, string strReference, bool fIncludeXMLSchema, bool ShowDebug)
        {
            // $GENERIC-FUNCTION$
            // INPUT:  DigestValue of data in base64 format
            //         Reference as in <Reference URI="#...">
            //         Flag to include XMLSchema-instance attribute
            // OUTPUT: SHA-1 digest of canonicalized <SignedInfo> element in HEX format
            // REMARKS: This is for the specific form with no whitespace before any element

            string strSignedInfoCanonic = null;
            string strDigest = null;
            string strFirstLine = null;
            string chLf = String.Format("\n");    // String with single LF char

            //SPECIFIC FORM (no whitespace before elements):
            // <SignedInfo>
            // <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
            // <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
            // <Reference URI="#...">
            // <Transforms>
            // <Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
            // </Transforms>
            // <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
            // <DigestValue>...</DigestValue>
            // </Reference>
            // </SignedInfo>

            // To canonicalize the SignedInfo, do the following:-
            // 1. Replace any CR-LF pairs with single LF char
            // 2. Add the xmlns attributes to the SignedInfo tag
            // 3. Convert the empty 'Method' elements to start-end tag pairs
            // 4. Use double-quotes (") around attribute values
            // 5. Do NOT change any other whitespace chars outside the tags
            // -- assumes all other c14n aspects are dealt with in the hard-coding

            // We can hard-code all of the above directly; well, almost...
            if (fIncludeXMLSchema)
            {
                strFirstLine = "<SignedInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">";
            }
            else
            {
                strFirstLine = "<SignedInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">";
            }

            strSignedInfoCanonic = strFirstLine 
                + chLf + "<CanonicalizationMethod Algorithm=\"http://www.w3.org/TR/2001/REC-xml-c14n-20010315\"></CanonicalizationMethod>" 
                + chLf + "<SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\"></SignatureMethod>" 
                + chLf + "<Reference URI=\"#" + strReference + "\">" 
                + chLf + "<Transforms>" 
                + chLf + "<Transform Algorithm=\"http://www.w3.org/TR/2001/REC-xml-c14n-20010315\"></Transform>" 
                + chLf + "</Transforms>" 
                + chLf + "<DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"></DigestMethod>" 
                + chLf + "<DigestValue>" + strDigestValue64 + "</DigestValue>" 
                + chLf + "</Reference>" 
                + chLf + "</SignedInfo>";

            if (ShowDebug)
            {
                Console.WriteLine(strSignedInfoCanonic);
            }
            strDigest = Hash_String_SHA1(strSignedInfoCanonic, false);
            return strDigest;

        }

        public static void Test_DigestSignedInfo()
        {
            string strComputed = null;
            string strCorrect = null;

            Console.WriteLine();
            Console.WriteLine("F60T33 Outer XML-Dsig:");
            strCorrect = "98B466FFE6A5E73B85962BC7300841C5F1693D3A";
            strComputed = MakeDigestValueOfSignedInfo("4OTWXyRl5fw3htjTyZXQtYEsC3E=", "SetDoc", true, false);
            Console.WriteLine("COMPUTED=" + strComputed);
            Console.WriteLine("CORRECT= " + strCorrect);

            Console.WriteLine("F60T33 Inner XML-Dsig:");
            strCorrect = "353CABFE369AAD9BEA21F5FA06CB367960FFB7D4";
            strComputed = MakeDigestValueOfSignedInfo("hlmQtu/AyjUjTDhM3852wvRCr8w=", "F60T33", false, false);
            Console.WriteLine("COMPUTED=" + strComputed);
            Console.WriteLine("CORRECT= " + strCorrect);

        }

        public static string XMLDSIG_VerifySigUsingRSAKeyValue(string strSig64, string strXmlKey)
        {
            // $GENERIC-FUNCTION$
            // INPUT:  strSig64  = <SignatureValue> in base64 excluding XML tags
            //         strXmlKey = <RSAKeyValue> in XMLSIG format including XML tags
            // OUTPUT: DigestValue in hex format
            int nRet = 0;
            byte[] abData = null;
            int nDataLen = 0;
            string strPublicKey = null;
            int nKeyLen = 0;
            byte[] abDigest = null;
            int nDigLen = 0;
            string result = "";

            Console.WriteLine("SigBase64=" + strSig64);
            // Convert XML key form to our internal format
            strPublicKey = Rsa.FromXMLString(strXmlKey, false);
            if (strPublicKey.Length == 0)
            {
                Console.WriteLine("ERROR: Unable to read XML key string");
                return result;
            }
            Console.WriteLine("Key length is " + Rsa.KeyBits(strPublicKey) + " bits");

            // Convert signatureValue to byte format
            abData = System.Convert.FromBase64String(strSig64);
            Console.WriteLine("SIGNATURE=" + Cnv.ToHex(abData));
            nDataLen = abData.Length;
            Console.WriteLine("Signature is " + nDataLen + " bytes long");
            // Verify the key - this should return 1
            nRet = Rsa.CheckKey(strPublicKey);
            Console.WriteLine("RSA_CheckKey returns " + nRet + " (expecting 1)");
            // Check the length
            nKeyLen = Rsa.KeyBytes(strPublicKey);
            Console.WriteLine("Key is " + nKeyLen + " bytes long");
            if (nKeyLen != nDataLen)
            {
                Console.WriteLine("ERROR: Key is the wrong length!");
                return result;
            }
            // Decrypt the signature
            abData = Rsa.RawPublic(abData, strPublicKey);
            if (abData.Length == 0)
            {
                Console.WriteLine("ERROR: " + General.LastError());
                return result;
            }
            Console.WriteLine("EMSA BLOCK=" + Cnv.ToHex(abData));

            // Extract digest from block
            abDigest = Rsa.DecodeDigestForSignature(abData);
            nDigLen = abDigest.Length;
            Console.WriteLine("RSA_DecodeMsg returns " + nDigLen + " (expecting 20)");
            if (nDigLen <= 0)
            {
                Console.WriteLine("ERROR: Cannot extract digest");
                return result;
            }

            // Display bytes as hex and base64
            Console.WriteLine("digest=" + Cnv.ToHex(abDigest));
            Console.WriteLine("base64=" + System.Convert.ToBase64String(abDigest));
            Console.WriteLine();
            // Return result
            return Cnv.ToHex(abDigest);

        }

        public static string XMLDSIG_VerifySigUsingCert(string strSig64, string strCert64)
        {
            // $GENERIC-FUNCTION$
            // INPUT:  strSig64  = <SignatureValue> content in base64 excluding tags
            //         strCert64 = <X509Certificate> content in base64 excluding tags
            // OUTPUT: digestValue in hex format
            byte[] abData = null;
            string strPublicKey = null;
            int nCheck = 0;
            int nDataLen = 0;
            int nKeyLen = 0;
            byte[] abDigest = null;
            string strQuery = null;
            string strOutput = null;
            string strXmlKey = null;
            string result = "";

            // Query the certificate for details
            strQuery = "subjectName";
            strOutput = X509.QueryCert(strCert64, strQuery);
            if (strOutput.Length == 0)
            {
                Console.WriteLine("ERROR: Error reading certificate: " + General.LastError());
                return result;
                // ERROR
            }
            Console.WriteLine(strQuery + "=" + strOutput);

            // Extract public key from X.509 certificate in internal string form
            strPublicKey = Rsa.GetPublicKeyFromCert(strCert64).ToString();
            Console.WriteLine("Chars in internal key = " + strPublicKey.Length);
            if (strPublicKey.Length == 0)
            {
                Console.WriteLine("ERROR: Error reading certificate key");
                return result;
                // ERROR
            }
            //Console.WriteLine("PUBLIC KEY (internal)=" + strPublicKey);

            // While we're here, display the public key in XML format
            strXmlKey = Rsa.ToXMLString(strPublicKey, 0);
            if (strXmlKey.Length > 0)
            {
                Console.WriteLine(strXmlKey);
            }
            else
            {
                Console.WriteLine("**Failed to create public key in XML");
            }

            // Convert signatureValue to byte format
            abData = System.Convert.FromBase64String(strSig64);
            Console.WriteLine("SIGNATURE=" + Cnv.ToHex(abData));
            nDataLen = abData.Length;
            Console.WriteLine("Signature is " + nDataLen + " bytes long");
            // Verify the key - this should return 1
            nCheck = Rsa.CheckKey(strPublicKey);
            Console.WriteLine("RSA_CheckKey returns " + nCheck + " (expecting 1)");
            // Check the length
            nKeyLen = Rsa.KeyBytes(strPublicKey);
            Console.WriteLine("Key is " + nKeyLen + " bytes long");
            if (nKeyLen != nDataLen)
            {
                Console.WriteLine("ERROR: Key is the wrong length!");
                return result;
            }
            // Decrypt the signature
            abData = Rsa.RawPublic(abData, strPublicKey);
            Console.WriteLine("EMSA BLOCK=" + Cnv.ToHex(abData));

            // Extract digest from block
            abDigest = Rsa.DecodeDigestForSignature(abData);
            Console.WriteLine("RSA_DecodeMsg returns " + abDigest.Length + " (expecting 20)");
            if (abDigest.Length == 0)
            {
                Console.WriteLine("ERROR: Cannot extract digest");
                return result;
            }

            // Display bytes as hex and base64
            Console.WriteLine("digest=" + Cnv.ToHex(abDigest));
            Console.WriteLine("base64=" + System.Convert.ToBase64String(abDigest));
            // Return digest in hex
            return Cnv.ToHex(abDigest);
        }

        public static void TestReadFRMA()
        {
            string strCert64 = null;
            string strSig64 = null;
            string strDigestHex = null;

            //<FRMA algoritmo="SHA1withRSA">g1AQX0sy8NJugX52k2hTJEZAE9Cuul6pqYBdFxj1N17umW7zG/hAavCALKByHzdYAfZ3LhGTXCai5zNxOo4lDQ==</FRMA>
            strSig64 = "g1AQX0sy8NJugX52k2hTJEZAE9Cuul6pqYBdFxj1N17umW7zG/hAavCALKByHzdYAfZ3LhGTXCai5zNxOo4lDQ==";

            // [2009-05-07] We have the certificate for the signer of this now...
            strCert64 = "-----BEGIN CERTIFICATE-----" + "\n" 
                + "MIICTzCCAfmgAwIBAwIBZDANBgkqhkiG9w0BAQQFADCBrzELMAkGA1UEBhMCQ0wx" 
                + "CzAJBgNVBAgTAkNTMQswCQYDVQQHEwJDczEnMCUGA1UEChMeU2VydmljaW8gZGUg" 
                + "SW1wdWVzdG9zIEludGVybm9zMRkwFwYDVQQLExBPZmljaW5hIEludGVybmV0MScw" 
                + "JQYDVQQDEx5TZXJ2aWNpbyBkZSBJbXB1ZXN0b3MgSW50ZXJub3MxGTAXBgkqhkiG" 
                + "9w0BCQEWCnNpaUBzaWkuY2wwHhcNMDMwNDI5MjAxNDA1WhcNMDQwNDIzMjAxNDA1" 
                + "WjCBrzELMAkGA1UEBhMCQ0wxCzAJBgNVBAgTAkNTMQswCQYDVQQHEwJDczEnMCUG" 
                + "A1UEChMeU2VydmljaW8gZGUgSW1wdWVzdG9zIEludGVybm9zMRkwFwYDVQQLExBP" 
                + "ZmljaW5hIEludGVybmV0MScwJQYDVQQDEx5TZXJ2aWNpbyBkZSBJbXB1ZXN0b3Mg" 
                + "SW50ZXJub3MxGTAXBgkqhkiG9w0BCQEWCnNpaUBzaWkuY2wwXDANBgkqhkiG9w0B" 
                + "AQEFAANLADBIAkEAyPvwDcshpVAApYVSLF3lKKc+DOFswDqx5ep95LKigRHjUvrv" 
                + "jct9UeNJq9SxBdzU9nz54TEVBYyfAVQpG4xxUwIDAQABMA0GCSqGSIb3DQEBBAUA" 
                + "A0EADvJX1C7hUFD2Eq9jNZpeJ/YBOZx1SBmAHSeXud6fTw98+AR4X3YDmzO9D4Kd" 
                + "hEFi3NK4anpjiPOKbA8fBWyyBA==" 
                + "-----END CERTIFICATE-----";

            strDigestHex = XMLDSIG_VerifySigUsingCert(strSig64, strCert64);

        }

        public static void MakeFRMAdigest()
        {
            // This should give the same digest value we obtained above.
            string strData = null;
            string strDigest = null;
            // Put <DA> data into a single string with no spaces between elements
            strData = "<DA><RE>97975000-5</RE><RS>RUT DE PRUEBA</RS><TD>33</TD><RNG><D>1</D><H>200</H></RNG><FA>2003-09-04</FA><RSAPK><M>0a4O6Kbx8Qj3K4iWSP4w7KneZYeJ+g/prihYtIEolKt3cykSxl1zO8vSXu397QhTmsX7SBEudTUx++2zDXBhZw==</M><E>Aw==</E></RSAPK><IDK>100</IDK></DA>";
            // Create an SHA-1 digest value in hex format
            //'strDigest = String(PKI_SHA1_CHARS, " ")
            strDigest = Hash.HexFromString(strData, HashAlgorithm.Sha1);
            Console.WriteLine("SHA1(DA)=" + strDigest);
            // Display in base64
            Console.WriteLine("SHA1(DA)=" + System.Convert.ToBase64String(Cnv.FromHex(strDigest)));
        }

        public static void TestReadFRMT()
        {
            string strXmlKey = null;
            string strSig64 = null;

            // <FRMT algoritmo="SHA1withRSA">GbmDcS9e/jVC2LsLIe1iRV12Bf6lxsILtbQiCkh6mbjckFCJ7fj/kakFTS06Jo8iS4HXvJj3oYZuey53Krniew==</FRMT>
            strSig64 = "GbmDcS9e/jVC2LsLIe1iRV12Bf6lxsILtbQiCkh6mbjckFCJ7fj/kakFTS06Jo8iS4HXvJj3oYZuey53Krniew==";

            // Form XML string of public key in proper XMLSIG format using <RSAPK> element in XML file
            // <RSAPK><M>0a4O6Kbx8Qj3K4iWSP4w7KneZYeJ+g/prihYtIEolKt3cykSxl1zO8vSXu397QhTmsX7SBEudTUx++2zDXBhZw==</M><E>Aw==</E></RSAPK>
            strXmlKey = "<RSAKeyValue><Modulus>0a4O6Kbx8Qj3K4iWSP4w7KneZYeJ+g/prihYtIEolKt3cykSxl1zO8vSXu397QhTmsX7SBEudTUx++2zDXBhZw==</Modulus>" 
                + "<Exponent>Aw==</Exponent></RSAKeyValue>";

            XMLDSIG_VerifySigUsingRSAKeyValue(strSig64, strXmlKey);

        }

        public static void TestF60T33OuterXmlSigByCert()
        {
            // THIS DOES NOT WORK! Because the X509 Certificate provided does not correspond to the key used to make the signature.
            string strSig64 = null;
            string strCert64 = null;

            strCert64 = "MIIEgjCCA+ugAwIBAgIEAQAApzANBgkqhkiG9w0BAQUFADCBtTELMAkGA1UEBhMCQ0wxHTAbBgNVBAgUFFJlZ2lvbiBNZXRyb3BvbGl0YW5hMREwDwYDVQQHFAhTYW50aWFnbzEUMBIGA1UEChQLRS1DRVJUQ0hJTEUxIDAeBgNVBAsUF0F1dG9yaWRhZCBDZXJ0aWZpY2Fkb3JhMRcwFQYDVQQDFA5FLUNFUlRDSElMRSBD" 
                + "QTEjMCEGCSqGSIb3DQEJARYUZW1haWxAZS1jZXJ0Y2hpbGUuY2wwHhcNMDMxMDAxMTg1ODE1WhcNMDQwOTMwMDAwMDAwWjCBuDELMAkGA1UEBhMCQ0wxFjAUBgNVBAgUDU1ldHJvcG9saXRhbmExETAPBgNVBAcUCFNhbnRpYWdvMScwJQYDVQQKFB5TZXJ2aWNpbyBkZSBJbXB1ZXN0b3MgSW50ZXJub3MxDzANBgNVBAsU" 
                + "BlBpc28gNDEjMCEGA1UEAxQaV2lsaWJhbGRvIEdvbnphbGV6IENhYnJlcmExHzAdBgkqhkiG9w0BCQEWEHdnb256YWxlekBzaWkuY2wwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALxZlVh1xr9sKQIBDF/6Va+lsHQSG5AAmCWvtNTIOXN3E9EQCy7pOPHrDg6EusvoHyesZSKJbc0TnIFXZp78q7mbdHijzKqvMmyv" 
                + "wbdP7KK8LQfwf84W4v9O8MJeUHlbJGlo5nFACrPAeTtONbHaReyzeMDv2EganNEDJc9c+UNfAgMBAAGjggGYMIIBlDAjBgNVHREEHDAaoBgGCCsGAQQBwQEBoAwWCjA3ODgwNDQyLTQwCQYDVR0TBAIwADA8BgNVHR8ENTAzMDGgL6AthitodHRwOi8vY3JsLmUtY2VydGNoaWxlLmNsL2UtY2VydGNoaWxlY2EuY3JsMCMG" 
                + "A1UdEgQcMBqgGAYIKwYBBAHBAQKgDBYKOTY5MjgxODAtNTAfBgNVHSMEGDAWgBTgKP3S4GBPs0brGsz1CJEHcjodCDCB0AYDVR0gBIHIMIHFMIHCBggrBgEEAcNSBTCBtTAvBggrBgEFBQcCARYjaHR0cDovL3d3dy5lLWNlcnRjaGlsZS5jbC8yMDAwL0NQUy8wgYEGCCsGAQUFBwICMHUac0VsIHRpdHVsYXIgaGEgc2lk" 
                + "byB2YWxpZG8gZW4gZm9ybWEgcHJlc2VuY2lhbCwgcXVlZGFuZG8gZWwgQ2VydGlmaWNhZG8gcGFyYSB1c28gdHJpYnV0YXJpbywgcGFnb3MsIGNvbWVyY2lvIHkgb3Ryb3MwCwYDVR0PBAQDAgTwMA0GCSqGSIb3DQEBBQUAA4GBABMfCyJF0mNXcov8iEWvjGFyyPTsXwvsYbbkOJ41wjaGOFMCInb4WY0ngM8BsDV22bGM" 
                + "s8oLyX7rVy16bGA8Z7WDUtYhoOM7mqXw/Hrpqjh3JgAf8zqdzBdH/q6mAbdvq/yb04JHKWPC7fMFuBoeyVWAnhmuMZfReWQiMUEHGGIW";
            strSig64 = "sBnr8Yq14vVAcrN/pKLD/BrqUFczKMW3y1t3JOrdsxhhq6IxvS13SgyMXbIN/T9ciRaFgNabs3pi732XhcpeiSmD1ktzbRctEbSIszYkFJY49k0eB+TVzq3eVaQr4INrymfuOnWj78BZcwKuXvDy4iAcx6/TBbAAkPFwMP9ql2s=";

            XMLDSIG_VerifySigUsingCert(strSig64, strCert64);

        }

        public static void TestF60T33OuterXmlSigByRSA()
        {
            // THIS WORKS...
            string strSig64 = null;
            string strXmlKey = null;

            strXmlKey = "<RSAKeyValue><Modulus>" 
                + "tNEknkb1kHiD1OOAWlLKkcH/UP5UGa6V6MYso++JB+vYMg2OXFROAF7G8BNFFPQx" 
                + "iuS/7y1azZljN2xq+bW3bAou1bW2ij7fxSXWTJYFZMAyndbLyGHM1e3nVmwpgEpx" 
                + "BHhZzPvwLb55st1wceuKjs2Ontb13J33sUb7bbJMWh0=</Modulus>" 
                + "<Exponent>AQAB</Exponent></RSAKeyValue>";
            strSig64 = "sBnr8Yq14vVAcrN/pKLD/BrqUFczKMW3y1t3JOrdsxhhq6IxvS13SgyMXbIN/T9ciRaFgNabs3pi732XhcpeiSmD1ktzbRctEbSIszYkFJY49k0eB+TVzq3eVaQr4INrymfuOnWj78BZcwKuXvDy4iAcx6/TBbAAkPFwMP9ql2s=";

            XMLDSIG_VerifySigUsingRSAKeyValue(strSig64, strXmlKey);

        }

        public static void TestF60T33InnerXmlSigByRSA()
        {
            // THIS WORKS...
            string strSig64 = null;
            string strXmlKey = null;

            strXmlKey = "<RSAKeyValue><Modulus>" 
                + "tNEknkb1kHiD1OOAWlLKkcH/UP5UGa6V6MYso++JB+vYMg2OXFROAF7G8BNFFPQx" 
                + "iuS/7y1azZljN2xq+bW3bAou1bW2ij7fxSXWTJYFZMAyndbLyGHM1e3nVmwpgEpx" 
                + "BHhZzPvwLb55st1wceuKjs2Ontb13J33sUb7bbJMWh0=</Modulus>" 
                + "<Exponent>AQAB</Exponent></RSAKeyValue>";
            strSig64 = "JG1Ig0pvSIH85kIKGRZUjkyX6CNaY08Y94j4UegTgDe8+wl61GzqjdR1rfOK9BGn93AMOo6aiAgolW0k/XklNVtM/ZzpNNS3d/fYVa1q509mAMSXbelxSM3bjoa7H6Wzd/mV1PpQ8zK5gw7mgMMP4IKxHyS92G81GEguSmzcQmA=";

            XMLDSIG_VerifySigUsingRSAKeyValue(strSig64, strXmlKey);

        }

        public static void TestF60T33FRMTSigByRSA()
        {
            string strSig64 = null;
            string strXmlKey = null;

            strXmlKey = "<RSAKeyValue><Modulus>" 
                + "0a4O6Kbx8Qj3K4iWSP4w7KneZYeJ+g/prihYtIEolKt3cykSxl1zO8vSXu397QhTmsX7SBEudTUx++2zDXBhZw==</Modulus>" 
                + "<Exponent>Aw==</Exponent></RSAKeyValue>";
            strSig64 = "GbmDcS9e/jVC2LsLIe1iRV12Bf6lxsILtbQiCkh6mbjckFCJ7fj/kakFTS06Jo8iS4HXvJj3oYZuey53Krniew==";

            XMLDSIG_VerifySigUsingRSAKeyValue(strSig64, strXmlKey);

        }


        public static void Main()
        {
            Console.WriteLine("DOING ALL TESTS...");

            Console.WriteLine("\n Read_SmallerKeys() \n");
            Read_SmallerKeys();

            Console.WriteLine("\n MakeFRMTdigest() \n");
            MakeFRMTdigest();

            Console.WriteLine("\n SignDDtoMakeFRMTsig() \n");
            SignDDtoMakeFRMTsig();

            Console.WriteLine("\n CheckExampleCertAndRSAKey() \n");
            CheckExampleCertAndRSAKey();

            Console.WriteLine("\n CheckManualCertAndRSAKey() \n");
            CheckManualCertAndRSAKey();

            Console.WriteLine("\n Test_Make_Hash_of_File() \n");
            Test_Make_Hash_of_File();

            Console.WriteLine("\n Test_DigestSignedInfo() \n");
            Test_DigestSignedInfo();

            Console.WriteLine("\n TestReadFRMA() \n");
            TestReadFRMA();

            Console.WriteLine("\n MakeFRMAdigest() \n");
            MakeFRMAdigest();

            Console.WriteLine("\n TestReadFRMT() \n");
            TestReadFRMT();

            Console.WriteLine("\n TestF60T33OuterXmlSigByCert() \n");
            TestF60T33OuterXmlSigByCert();

            Console.WriteLine("\n TestF60T33OuterXmlSigByRSA() \n");
            TestF60T33OuterXmlSigByRSA();

            Console.WriteLine("\n TestF60T33InnerXmlSigByRSA() \n");
            TestF60T33InnerXmlSigByRSA();

            Console.WriteLine("\n TestF60T33FRMTSigByRSA() \n");
            TestF60T33FRMTSigByRSA();

        }
    }
}