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

namespace PortugalTax_cs
{
    class PortugalTax_cs
    {
        // $Id: PortugalTax2.cs $
        // $Date: 2010-11-26 06:15Z $
        // $Revision: 2.0 $
        // $Author: dai $

        // *************************** COPYRIGHT NOTICE ******************************
        // This code was originally written by David Ireland and is copyright
        // (C) 2010 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 **************************

        // This module uses functions from the CryptoSys (tm) PKI Toolkit available from
        // <www.cryptosys.net/pki/>.
        // Include the module `basCrPKI` in your project.

        // NOTES:
        // (1) The key files in these tests are expected to exist in the current working directory.
        // (2) The word "signature" or "signature value" == the <Hash> field of the specification.

        // REFERENCES:
        // [ESPECIF-2010] "Especificação das Regras Técnicas para Certificação de Software
        // Portaria n.º 363/2010, de 23 de Junho", Direcção Geral dos Impostos (DGCI), Especificacao_regras_tecnicas_Certificacao_Softwar.pdf
        // <http://info.portaldasfinancas.gov.pt/NR/rdonlyres/BF3D4A62-3243-404F-8F94-DCB2B19547C3/44379/Especificacao_regras_tecnicas_Certificacao_Softwar.pdf>
        // (accessed 7 August 2010).
        //
        // [ADIT-2010] "- 1º Aditamento - Especificação das Regras Técnicas para Certificação de Software
        // Portaria n.º 363/2010, de 23 de Junho", Direcção Geral dos Impostos (DGCI), 1_Aditamento_Especificaca_regras_tecnicas.pdf
        // <http://info.portaldasfinancas.gov.pt/NR/rdonlyres/84B18C77-577B-4581-A846-2DB0201B0FB4/0/1_Aditamento_Especificaca_regras_tecnicas.pdf>
        // (accessed 21 November 2010).

        // ************************************************************************************
        // NOTES ON THE C# CODE
        // This C# code was converted from VB.NET using SharpDevelop by icsharpcode.net,
        // which in turn had been converted from a VB6 original. 
        // We've taken out the obvious VB bits which used Microsoft.VisualBasic,
        // but it's still not representative of good C# coding practice. Sorry.
        // We're sure you'll get the idea.
        //    -- DI Management.
        // ************************************************************************************

        public static void Main()
        {
            Console.WriteLine("PKI Version={0}", General.Version());
            Pt_CreateSignature_Especificacao();
            Pt_CreateSignature_SAFT_IDEMO();
            Pt_VerifySignature();
            Pt_ExtractDigest();
            Pt_CreateSignatureWithKeyAsString();
            Pt_Create_Keys();
            Pt_Test_DoKeyPairFilesMatch();
            Pt_SavePrivateKeyAsEncrypted();
            Pt_Make_X509_CertSelfSigned();
            Pt_QueryCert();
            Pt_GetPublicKeyFromFileAndCert();

        }

        // *******************
        // GENERIC FUNCTIONS *
        // *******************

        // Use overloads for optional parameter...
        public static string rsaCreateSignatureInBase64(string strMessage, string strKeyFile, bool ShowDebug)
        {
            return m_rsaCreateSignatureInBase64(strMessage, strKeyFile, ShowDebug);
        }
        public static string rsaCreateSignatureInBase64(string strMessage, string strKeyFile)
        {
            return m_rsaCreateSignatureInBase64(strMessage, strKeyFile, false);
        }
        
        private static string m_rsaCreateSignatureInBase64(string strMessage, string strKeyFile, bool ShowDebug) 
        {
            // $GENERIC-FUNCTION$
            // INPUT:  Message string to be signed; filename of private RSA key file (unencrypted OpenSSL format)
            // OUTPUT: Signature in base64 format
            string strPrivateKey = null;
            byte[] abMessage = null;
            int nMsgLen = 0;
            byte[] abBlock = null;
            int nBlkLen = 0;
            string strSigBase64 = null;

            // 1. Convert message into unambigous array of bytes and compute length
            abMessage = System.Text.Encoding.Default.GetBytes(strMessage);
            nMsgLen = abMessage.Length;
            if (ShowDebug) Console.WriteLine("Message length = " + nMsgLen + " bytes.");

            // 1a. While we're here, compute the digest of the input. (We don't need it but it's a check for later)
            string strDigest = null;
            strDigest = Hash.HexFromBytes(abMessage, HashAlgorithm.Sha1);
            if (ShowDebug) Console.WriteLine("DIGEST=" + strDigest);

            // 2. Read the private key file into our internal string format
            // (Note that strPrivateKey is a one-off, ephemeral, internal string we made when reading the key file.
            // You can't save it to use again.)
            strPrivateKey = Rsa.ReadPrivateKeyInfo(strKeyFile).ToString();
            if (strPrivateKey.Length <= 0)
            {
                return "**ERROR: cannot read private key file";
            }
            if (ShowDebug) Console.WriteLine("Private key size is " + Rsa.KeyBits(strPrivateKey) + " bits.");

            // 3. Encode (i.e. digest and pad) the message into format required for PKCS#1v1.5 signature
            // Required block length is key size in bytes
            nBlkLen = Rsa.KeyBytes(strPrivateKey);
            if (ShowDebug) Console.WriteLine("Key/block size is " + nBlkLen + " bytes.");
            abBlock = Rsa.EncodeMsgForSignature(nBlkLen, abMessage, HashAlgorithm.Sha1);
            // Show the encoded block in hex format (should be 0001FFFF...ending with the 20-byte digest)
            if (ShowDebug) Console.WriteLine(Cnv.ToHex(abBlock));

            // 4. Create the signature block using the private key
            abBlock = Rsa.RawPrivate(abBlock, strPrivateKey);
            // Show the signature block in hex format
            if (ShowDebug) Console.WriteLine(Cnv.ToHex(abBlock));

            // 5. Convert to base64 format
            strSigBase64 = Cnv.ToBase64(abBlock);
            if (ShowDebug) Console.WriteLine(strSigBase64);

            // Return base64 signature
            return strSigBase64;

        }

        public static string rsaVerifySignature(string strSigBase64, string strPublicKeyFileOrCert, string strTextToSign, bool ShowDebug)
        {
            return m_rsaVerifySignature(strSigBase64, strPublicKeyFileOrCert, strTextToSign, ShowDebug);
        }
        public static string rsaVerifySignature(string strSigBase64, string strPublicKeyFileOrCert, string strTextToSign)
        {
            return m_rsaVerifySignature(strSigBase64, strPublicKeyFileOrCert, strTextToSign, false);
        }
        private static string m_rsaVerifySignature(string strSigBase64, string strPublicKeyFileOrCert, string strTextToSign, bool ShowDebug)
        {
            string functionReturnValue = null;
            // $GENERIC-FUNCTION$
            // INPUT:  Signature value in base64 format (the <Hash> field);
            //         filename of RSA public key file or X.509 certificate containing the same public key;
            //         text that was signed.
            // OUTPUT: "OK" if signature is valid or error message beginning "**ERROR" if not.

            string strPublicKey = null;
            byte[] abBlock = null;
            byte[] abDigest = null;
            string strDigest = null;
            string strDigest1 = null;

            // 1. Read the public key file into our internal string format
            strPublicKey = Rsa.ReadPublicKey(strPublicKeyFileOrCert).ToString();

            if (strPublicKey.Length <= 0)
            {
                // Was not a public key file, so try reading an X.509 certificate instead
                strPublicKey = Rsa.GetPublicKeyFromCert(strPublicKeyFileOrCert).ToString();
                if (strPublicKey.Length <= 0)
                {
                    return "**ERROR: cannot read public key file";

                }
            }
            if (ShowDebug) Console.WriteLine("Public key size is " + Rsa.KeyBits(strPublicKey) + " bits.");

            // 2. Convert base64 signature to byte array
            abBlock = Cnv.FromBase64(strSigBase64);
            if (ShowDebug) Console.WriteLine("Signature block length = " + abBlock.Length + " bytes");
            if (ShowDebug) Console.WriteLine(Cnv.ToHex(abBlock));

            // 3. Decrypt the signature block using the RSA public key
            // (Note that strPublicKey is a one-off, ephemeral, internal string we made when reading the key file.
            // You can't save it to use again.)
            abBlock = Rsa.RawPublic(abBlock, strPublicKey);
            if (abBlock.Length <= 0)
            {
                return "**ERROR: invalid signature";
            }
            // Show the decrypted signature block in hex format
            if (ShowDebug) Console.WriteLine(Cnv.ToHex(abBlock));

            // 4. Extract the message digest from the block (presumed SHA-1)
            abDigest = Rsa.DecodeDigestForSignature(abBlock);
            if (abDigest.Length <= 0)
            {
                return "**ERROR: invalid signature";
            }
            if (ShowDebug) Console.WriteLine("Message digest is " + abDigest.Length + " bytes long");
            strDigest = Cnv.ToHex(abDigest);
            if (ShowDebug) Console.WriteLine("EXTRACTED DIGEST=" + strDigest);

            // 5. Compute the SHA-1 message digest of the text that was signed
            strDigest1 = Hash.HexFromString(strTextToSign, HashAlgorithm.Sha1);
            if (ShowDebug) Console.WriteLine("COMPUTED DIGEST =" + strDigest1);

            // 6. Compare these two digest values and return OK only if they match
            if (strDigest.ToUpper() == strDigest1.ToUpper())
            {
                functionReturnValue = "OK";
            }
            else
            {
                functionReturnValue = "**ERROR: invalid signature";
            }
            return functionReturnValue;

        }

        public static string hashHexFromString_SHA1(string strMessage)
        {
            // $GENERIC-FUNCTION$
            // INPUT:  Message to be hashed in a string of ANSI characters
            // OUTPUT: SHA-1 digest in hex-encoded format
            // REMARK: Hardly worth it as a function in .NET!

            return Hash.HexFromString(strMessage, HashAlgorithm.Sha1);

        }

        // Use overloads for optional parameters...
        public static string rsaGetDigestFromBase64Signature(string strSigBase64, string strKeyFile, bool ShowDebug)
        {
            return m_rsaGetDigestFromBase64Signature(strSigBase64, strKeyFile, ShowDebug);
        }
        public static string rsaGetDigestFromBase64Signature(string strSigBase64, string strKeyFile)
        {
            return m_rsaGetDigestFromBase64Signature(strSigBase64, strKeyFile, false);
        }
        private static string m_rsaGetDigestFromBase64Signature(string strSigBase64, string strKeyFile, bool ShowDebug)
        {
            // $GENERIC-FUNCTION$
            // INPUT:  Signature value in base64 format; filename of public key file.
            // OUTPUT: SHA-1 digest of signed message in hex-encoded format
            string strPublicKey = null;
            byte[] abBlock = null;
            byte[] abDigest = null;
            string strDigest = null;

            // 1. Convert to byte array
            abBlock = Cnv.FromBase64(strSigBase64);
            if (ShowDebug) Console.WriteLine("Signature block length = " + abBlock.Length + " bytes");
            if (ShowDebug) Console.WriteLine(Cnv.ToHex(abBlock));

            // 2. Read the public key file into our internal string format
            strPublicKey = Rsa.ReadPublicKey(strKeyFile).ToString();
            if (strPublicKey.Length <= 0)
            {
                return "**ERROR: cannot read public key file";
            }
            if (ShowDebug) Console.WriteLine("Public key size is " + Rsa.KeyBits(strPublicKey) + " bits.");

            // 3. Decrypt the signature block using the public key
            abBlock = Rsa.RawPublic(abBlock, strPublicKey);
            // Show the decrypted signature block in hex format
            if (ShowDebug) Console.WriteLine(Cnv.ToHex(abBlock));

            // 4. Extract the SHA-1 message digest from the block
            abDigest = Rsa.DecodeDigestForSignature(abBlock);
            if (abDigest.Length < 0)
            {
                return "**ERROR: Decryption Error";
            }
            if (ShowDebug) Console.WriteLine("Message digest is " + abDigest.Length + " bytes long");
            strDigest = Cnv.ToHex(abDigest);
            if (ShowDebug) Console.WriteLine("DIGEST=" + strDigest);

            // Return extracted digest in hex form
            return strDigest;

        }


        // *******
        // TESTS *
        // *******

        public static void Pt_CreateSignature_Especificacao()
        {
            // Compute the correct signature values for the examples given in [ESPECIF-2010]
            string strMessage = null;
            string strKeyFile = null;
            string strSigBase64 = null;

            // Private key file: sample provided by DGCI
            strKeyFile = "Chave_Privada.txt";

            // Registo 1
            // Message string to be signed as per [REF] specifications
            // (not including quotes; no intermediate spaces or CR-LF chars)
            strMessage = "2010-05-18;2010-05-18T11:22:19;FAC 001/14;3.12;";

            strSigBase64 = rsaCreateSignatureInBase64(strMessage, strKeyFile, false);
            Console.WriteLine("1: " + strSigBase64);
            // Original value = "Am1K5+CP4LDNVDZYvcL...UnuJrca+7emgb/kpU="
            // Correct value = "OpE9IFpK5cJO8SwC5BUy3XTCkjVK5JsjHo3TvWjM9D09aw9wabH+sGNOs7hx4iEoOP9UY6DGsR6PgIkAZSTYInhbgs2x9sxWkr417aCKoSGY4awDIVB9aUlQ91SseH3Hk5S24PfjXFDn44acWhQL4INp9Re+dC51YNC7MrpAmP4="

            // Registo 2
            strMessage = "2010-05-18;2010-05-18T15:43:25;FAC 001/15;25.62;" + "Am1K5+CP4LDNVDZYvcLYGpnu8/1b+WWkzgoe8sbZhvk6QFzFvNN77Zsq+cHNm52jCVS" + "EDgWLGHgPS1wcT8ZG7w6KgVq+2/VgOU+xKNt0lcC3gouyarZvcZpZclIReDgLh6m3nv8D" + "YYHKAOQc+eCi/BQ4LqUnuJrca+7emgb/kpU=";

            strSigBase64 = rsaCreateSignatureInBase64(strMessage, strKeyFile, false);
            Console.WriteLine("2: " + strSigBase64);
            // Original value = "Jh7/rmIILVwbrPLTdk...RG8JS1Uos78="
            // Correct value = "hsR2TYJtl0mad+zVAhGxNLxs6matD+T8Y8IpEo12I3szSohdwwWVOfPclnu6D23pZ0w8g/Eh0TOMzYNsdkkUJpM68/nKH2d8ehI8HT85NUyLgrGhC8msXHK+ASCCOU0RN4mr04249IG+MuOAlnW8EcMJNZA+lTf94MbpJNqRYUw="

        }

        public static void Pt_CreateSignature_SAFT_IDEMO()
        {
            // Reproduce the signatures (<Hash>) values in SAFT_IDEMO599999999.XML
            string strMessage = null;
            string strKeyFile = null;
            string strSigBase64 = null;

            // Sample XML data file and private key file provided by DGCI at:
            // <http://info.portaldasfinancas.gov.pt/pt/apoio_contribuinte/certificacaosoftware.htm>
            // <http://info.portaldasfinancas.gov.pt/NR/rdonlyres/371795DE-D83B-4B0E-B673-010C0F523EFB/0/SAFT_IDEMO599999999.XML>

            // Private key file:
            // <http://info.portaldasfinancas.gov.pt/NR/rdonlyres/70FDBA7F-1C48-496C-B9C3-4F45B4FAA55F/0/Chave_Privada.txt>
            strKeyFile = "Chave_Privada.txt";

            // Message string from 1st record in SAFT_IDEMO599999999.XML (starting at line 9492)
            // This is the first record of the series, so the "Hash" field is empty
            strMessage = "2008-03-10;2008-03-10T15:58:00;FT 1/1;28.07;";
            strSigBase64 = rsaCreateSignatureInBase64(strMessage, strKeyFile);
            Console.WriteLine("FT 1/1: " + strSigBase64);
            // Expected signature = "F8952fjEClltx2tF9m6/...jsablpR6A4="

            // Record 2: carry forward the Hash field from record 1
            strMessage = "2008-09-16;2008-09-16T15:58:00;FT 1/2;235.15;" + "F8952fjEClltx2tF9m6/QTFynFjSuiboMslNZ1ag9oR5iIivgYYa0cNa0wJeWXlsf8QQVHUol303hp7XmIy5/kFOiV0Cv8QH6SF0Q5zNsDtpeFh2ZJ256y0DkJMSQqCq3oSka+9zIXXRkXgEsSv6VScCYv8VTlIcGjsablpR6A4=";
            strSigBase64 = rsaCreateSignatureInBase64(strMessage, strKeyFile);
            Console.WriteLine("FT 1/2: " + strSigBase64);
            // Expected signature = "wh0uUgI/fLTt9Kpb/hFw.../bU651c3va0="

            // Record 3: carry forward the Hash field from record 2
            strMessage = "2008-09-16;2008-09-16T15:58:00;FT 1/3;679.61;" + "wh0uUgI/fLTt9Kpb/hFwN6VIkjWZWI8R2TxtHUMyRL0a7hyQLIvoxuqGzKfzUfvAV3E1gxpKZtai5qli6Nx7unqzC4vIoc6rtb3ObuxifXiBAUD95BMh31T73O6cgcwhGR0YhiV/E6jfCbihJL2B/2s+/qsaL7OY/bU651c3va0=";
            strSigBase64 = rsaCreateSignatureInBase64(strMessage, strKeyFile);
            Console.WriteLine("FT 1/3: " + strSigBase64);
            // Expected signature = "iVYbEDuefMedP5DHBfl+...Z4+0oX3qdxY="

            // ...etc, etc, ...
            Console.WriteLine("...");

            // Record 6: carry forward the Hash field from record 5
            strMessage = "2008-10-21;2008-10-21T15:32:00;FT 1/6;3600.00;" + "nv2NKxZ5c/1aC/D6RgCL0Z1EmvkELlxQ0qUQwu/5C+5fvDwb5+nigoN8G5NZjebQTJefCK3nT7DxYjfuTLaVwkDHsHDqW+WzNJ7r2VlGeeBV/TKpgYwy45Vb9dlpx3pwDftlfV44yLJN/uO6RIQnTU4o9+r0DtoPibhm8zEAaA4=";
            strSigBase64 = rsaCreateSignatureInBase64(strMessage, strKeyFile);
            Console.WriteLine("FT 1/6: " + strSigBase64);
            // Expected signature = "V5HNew6rKFxmSeNTSmp5...AqTsAdmi9WU="


            // Record NC 1/1: We start a new series, so leave hash field empty
            strMessage = "2008-09-16;2008-09-16T15:58:00;NC 1/1;235.15;" + "";
            strSigBase64 = rsaCreateSignatureInBase64(strMessage, strKeyFile);
            Console.WriteLine("NC 1/1: " + strSigBase64);
            // Expected signature = "jTCuqNUzz+QDJiHeOGwk...DpQ3kO770ko="

            // Record NC 1/2: carry forward the Hash field from record 1
            strMessage = "2008-09-16;2008-09-16T15:58:00;NC 1/2;2261.34;" + "jTCuqNUzz+QDJiHeOGwkJzBoJwqNOLRMs0ISI7TXddv5RrH8KmKtaMgzaZxWY9QO4U5aoasqHRieqof+7oXq0fALKcROyVxU/PQRsh7eKani46ENkrkQNXREjAdz1nvoCSAKphd21nfMJupWlYTAJV2H0A7I+MGcDpQ3kO770ko=";
            strSigBase64 = rsaCreateSignatureInBase64(strMessage, strKeyFile);
            Console.WriteLine("NC 1/2: " + strSigBase64);
            // Expected signature = "YIt8KKn+0m9HpK2BpsnY...vfxhM7re2SU="

        }



        public static void Pt_VerifySignature()
        {
            string strMessage = null;
            string strKeyFile = null;
            string strSigBase64 = null;
            string strStatus = null;

            // Public key file:
            // <http://info.portaldasfinancas.gov.pt/NR/rdonlyres/547D8EFD-4B88-4072-8CD8-17DF08FE847A/0/Chave_Publica.txt>
            strKeyFile = "Chave_Publica.txt";

            // Message string and "Hash" from 1st record in SAFT_IDEMO599999999.XML (starting at line 9492)
            strMessage = "2008-03-10;2008-03-10T15:58:00;FT 1/1;28.07;";
            strSigBase64 = "F8952fjEClltx2tF9m6/QTFynFjSuiboMslNZ1ag9oR5iIivgYYa0cNa0wJeWXlsf8QQVHUol303hp7XmIy5/kFOiV0Cv8QH6SF0Q5zNsDtpeFh2ZJ256y0DkJMSQqCq3oSka+9zIXXRkXgEsSv6VScCYv8VTlIcGjsablpR6A4=";
            strStatus = rsaVerifySignature(strSigBase64, strKeyFile, strMessage, true);
            Console.WriteLine("Result=" + strStatus);

            // Corrected version of Registo 2 in Especificacao [RE-1]
            strMessage = "2010-05-18;2010-05-18T15:43:25;FAC 001/15;25.62;" + "Am1K5+CP4LDNVDZYvcLYGpnu8/1b+WWkzgoe8sbZhvk6QFzFvNN77Zsq+cHNm52jCVS" + "EDgWLGHgPS1wcT8ZG7w6KgVq+2/VgOU+xKNt0lcC3gouyarZvcZpZclIReDgLh6m3nv8D" + "YYHKAOQc+eCi/BQ4LqUnuJrca+7emgb/kpU=";
            strSigBase64 = "hsR2TYJtl0mad+zVAhGxNLxs6matD+T8Y8IpEo12I3szSohdwwWVOfPclnu6D23pZ0w8g/Eh0TOMzYNsdkkUJpM68/nKH2d8ehI8HT85NUyLgrGhC8msXHK+ASCCOU0RN4mr04249IG+MuOAlnW8EcMJNZA+lTf94MbpJNqRYUw=";
            strStatus = rsaVerifySignature(strSigBase64, strKeyFile, strMessage, true);
            Console.WriteLine("Result=" + strStatus);

        }

        // Extract the message digest from a given signature.
        // (Use this in your debugging)

        public static void Pt_ExtractDigest()
        {
            string strKeyFile = null;
            string strSigBase64 = null;
            string strDigest = null;

            // Public key file we created ourselves
            strKeyFile = "Chave_Publica.txt";

            // Signature in base64 form.
            strSigBase64 = "F8952fjEClltx2tF9m6/QTFynFjSuiboMslNZ1ag9oR5iIivgYYa0cNa0wJeWXlsf8QQVHUol303hp7XmIy5/kFOiV0Cv8QH6SF0Q5zNsDtpeFh2ZJ256y0DkJMSQqCq3oSka+9zIXXRkXgEsSv6VScCYv8VTlIcGjsablpR6A4=";
            strDigest = rsaGetDigestFromBase64Signature(strSigBase64, strKeyFile);
            Console.WriteLine("DIGEST FOUND=" + strDigest);
            Console.WriteLine("EXPECTED    =" + "BB5C0F8FF294016FA4F0A3265410249D275B0986");
        }

        // Example to read in key files directly as a string...

        // Use this as an alternative to passing filenames.
        // The CryptoSys RSA_Read* functions will accept a string containing the file contents.
        // You must still use the RSA_Read* functions to obtain the ephemeral "internal" key strings to use with the RSA_Raw* functions.

        public static void Pt_CreateSignatureWithKeyAsString()
        {
            string strMessage = null;
            string strKeyFile = null;
            string strSigBase64 = null;
            string strDigest = null;
            string strStatus = null;

            // As an alternative to passing a filename, you may instead pass the key data directly as a "PEM" string
            strKeyFile = "-----BEGIN RSA PRIVATE KEY-----" + "MIICXgIBAAKBgQDWDX9wVqj6ZqNZU1ojwBpyKKkuzHTCmfK39xx/T9vWkqpcV7h3sx++ZOv2KhhNkIe/1I4OCWDPCXRE4g0uIQr0NS29vMlP3aHHayy76+lbBCNVcHFxM0ggjre1acnD0qUpZ6Vza7F+PpCyuypD2V/pkL1nX9Z6z5uYyqc0XaSFdwIDAQABAoGBAJCA7j6Vkl/w+GeuOJUX9AK" + "LZqN8TXquWUhOX4OnEt9Jhg7u/U55s31iPlWh12RNpQcg5IGfXSaH2GFEReeVUQGMrb89kkfbeY5HSRHh3/sBSyJTMn2cjsqfUnUJhywJPxT8NFIcS2pRBJe/QN/pL+M2jk+Fl40wyVXRhnog+4fhAkEA//Tijl5SA7a/uCyfOQkJ6yop13dfN4EHEWYMzI6SlnYWuJfdIOz4wkzBWgD0r/btFA" + "ths1zElmRWINjWsB84ZwJBANYWywqsZA4FShXkDEWfG1GbrEIXiOnPJay2p7en3DQ+lx4GfE10iO52f54QRu13SZp06050YkrWcRfBGCXaYHECQQCU8vMsmmLr2ltzWDRIQqRM/7pdsw/sAuAUFej42Tcg7BOI1IdQc9bHa1dRgyDhjbalZYIzmJamVjlw3/7/ewudAkB/ipatpiP5YldPkUtqU" + "q5QwOAvg5vSRtEYAr0KIZuDGGKoxY5aCnnlLn06qlHG+JDFzq+8ToOcOAKp9yQusNlRAkEA+0DarosTmn2I7+fj2/3ojVKdW/eIisz547U3bGbW/hBCZRi+y+cQnPlZ7Cr4LcGInhdxR+fSWptMNwrDCUiYHA==" + "-----END RSA PRIVATE KEY-----";

            // Exact message string to be signed:
            strMessage = "2008-03-10;2008-03-10T15:58:00;FT 1/1;28.07;";

            strSigBase64 = rsaCreateSignatureInBase64(strMessage, strKeyFile);

            Console.WriteLine("<Hash>=" + strSigBase64);
            // Expected signature = "F8952fjEClltx2tF9m6/...jsablpR6A4="

            // Similarly, we can pass the public key data as a "PEM" string
            strKeyFile = "-----BEGIN PUBLIC KEY-----" + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDWDX9wVqj6ZqNZU1ojwBpyKKkuzHTCmfK39xx/T9vWkqpcV7h3sx++ZOv2KhhNkIe/1I4OCWDPCXRE4g0uIQr0NS29vMlP3aHHayy76+lbBCNVcHFxM0ggjre1acnD0qUpZ6Vza7F+PpCyuypD2V/pkL1nX9Z6z5uYyqc0XaSFdwIDAQAB" + "-----END PUBLIC KEY-----";

            strDigest = rsaGetDigestFromBase64Signature(strSigBase64, strKeyFile);
            Console.WriteLine("DIGEST EXTRACTED=" + strDigest);

            strStatus = rsaVerifySignature(strSigBase64, strKeyFile, strMessage);
            Console.WriteLine("Result=" + strStatus);

        }

        //****************************************************************************************************************
        // THE FOLLOWING SHOWS HOW TO CREATE A PAIR OF RSA PUBLIC AND PRIVATE KEYS COMPATIBLE WITH THE OPENSSL PEM FORMAT.
        // AND HOW TO CREATE AN X.509 CERTIFICATE FROM THE KEYS AND HOW TO VERIFY THAT A PAIR OF KEYS MATCH.
        //****************************************************************************************************************

        public static void Pt_Create_Keys()
        {
            // Create an RSA key pair
            int nRet = 0;
            string strPublicKeyFile = null;
            string strEncPrivateKeyFile = null;
            string strPemPrivateKeyFile = null;
            StringBuilder sbIntPrivateKey = null;
            string strPassword = null;

            strPassword = "password";
            // Password for encrypted private key file (please pick something stronger!)

            // OpenSSL commands to create an RSA key pair:
            //   cmd> openssl genrsa -out PrivateKey.PEM 1024
            //   cmd> openssl rsa -in PrivateKey.PEM -out PublicKey.PEM -outform PEM –pubout
            //
            strPublicKeyFile = "Pt_PublicKey.PEM";
            // This is created in OpenSSL PEM format
            strEncPrivateKeyFile = "Pt_PrivateKey.EPK";
            // This is in encrypted form
            strPemPrivateKeyFile = "Pt_PrivateKey.PEM";
            // This is in OpenSSL PEM format

            // RSA_MakeKeys creates the key pair with an encrypted private key.
            Console.WriteLine("Creating keys. This may take a few seconds...");
            nRet = Rsa.MakeKeys(strPublicKeyFile, strEncPrivateKeyFile, 1024, Rsa.PublicExponent.Exp_EQ_65537, 2048, strPassword, 0, HashAlgorithm.Sha1, Rsa.Format.SSL, false
            );
            Console.WriteLine("RSA_MakeKeys returns " + nRet + " (expected 0)");

            // Adjust the white space in the public key file created here to suit the requirements of the DGCI.
            // The file is a valid PEM format either way, but DGCI insists it should be exactly 272 bytes long.
            // See <http://www.cryptosys.net/pki/portugal_DGCI_billing_software.html#key278vs272>.
            FixFileDosToUnix(strPublicKeyFile);

            // To save the encrypted private key in unencrypted OpenSSL format, we read the key into an internal key string
            // and then save to a file in the correct format.
            // Note that the "internal" key string is ephemeral.
            // CAUTION: saving a production private key in unencrypted form is a huge security risk!
            sbIntPrivateKey = Rsa.ReadEncPrivateKey(strEncPrivateKeyFile, strPassword);
            if (sbIntPrivateKey.Length == 0)
            {
                Console.WriteLine("Error reading encrypted private key file");
                return;
            }
            // Now save in correct form
            nRet = Rsa.SavePrivateKeyInfo(strPemPrivateKeyFile, sbIntPrivateKey.ToString(), Rsa.Format.SSL);
            Console.WriteLine("RSA_SavePrivateKeyInfo returns " + nRet + " (expected 0)");

            // Clear the private key
            Wipe.String(sbIntPrivateKey);

            // Do some checks that the OpenSSL keys match
            if (!Pt_DoKeyPairFilesMatch(strPemPrivateKeyFile, strPublicKeyFile))
            {
                Console.WriteLine("Error: keys do not match");
                return;
            }

        }

        public static bool FixFileDosToUnix(string fileName)
        {
            // Converts the line endings in a file from CR-LF pairs to single LF characters
            string strBuffer = null;
            FileInfo finfo = new FileInfo(fileName);

            // Check if file exists
            if (finfo.Exists)
            {
                // Read in the file to a string
                FileStream fsi = finfo.OpenRead();
                StreamReader sr = new StreamReader(fsi);
                strBuffer = sr.ReadToEnd();
                //'Console.WriteLine(strBuffer)
                sr.Close();
                fsi.Close();
            }
            else
            {
                return false;
            }

            // Edit the string
            Console.WriteLine("Input is {0} bytes long", strBuffer.Length);
            strBuffer = strBuffer.Replace("\r\n", "\n");
            Console.WriteLine("Output is {0} bytes long", strBuffer.Length);

            // Re-write the file
            FileStream fs = null;
            StreamWriter sw = null;
            fs = new FileStream(fileName, FileMode.Create, FileAccess.Write);
            sw = new StreamWriter(fs);
            sw.Write(strBuffer);
            sw.Close();
            fs.Close();
            return true;

            // Success

        }

        public static void Pt_Test_DoKeyPairFilesMatch()
        {
            // Check that the two OpenSSL-format key files match...
            string strPublicKeyFile = null;
            string strPemPrivateKeyFile = null;

            strPublicKeyFile = "Pt_PublicKey.PEM";
            // This is in OpenSSL PEM format
            strPemPrivateKeyFile = "Pt_PrivateKey.PEM";
            // This is in OpenSSL PEM format

            if (!Pt_DoKeyPairFilesMatch(strPemPrivateKeyFile, strPublicKeyFile))
            {
                Console.WriteLine("Error: keys do not match");
            }
            else
            {
                Console.WriteLine("OK, keys match.");
            }

        }

        public static bool Pt_DoKeyPairFilesMatch(string strPemPrivateKeyFile, string strPublicKeyFile)
        {
            // Returns TRUE if the public and private keys in the given files match or FALSE if they do not.
            int nRet = 0;
            StringBuilder sbIntPrivateKey = null;
            string strIntPublicKey = null;
            int nHashCodePub = 0;
            int nHashCodePri = 0;

            // Read in the keys from the files to internal key strings
            sbIntPrivateKey = Rsa.ReadPrivateKeyInfo(strPemPrivateKeyFile);
            if (sbIntPrivateKey.Length == 0)
            {
                Console.WriteLine("Error reading PEM private key file");
                return false;
            }
            strIntPublicKey = Rsa.ReadPublicKey(strPublicKeyFile).ToString();
            if (strIntPublicKey.Length == 0)
            {
                Console.WriteLine("Error reading PEM private key file");
                return false;
            }
            // Display the key lengths
            Console.WriteLine("Private key is " + Rsa.KeyBits(sbIntPrivateKey.ToString()) + " bits");
            Console.WriteLine("Public key is  " + Rsa.KeyBits(strIntPublicKey) + " bits");
            // Display the "hashcode" (this is an internal hash code which should be equal for matching keys)
            nHashCodePri = Rsa.KeyHashCode(sbIntPrivateKey.ToString());
            nHashCodePub = Rsa.KeyHashCode(strIntPublicKey);
            Console.WriteLine("Hashcodes are {0,8:X} and {0,8:X}", nHashCodePri, nHashCodePub);
            // Verify that a pair of "internal" RSA private and public key strings are matched
            nRet = Rsa.KeyMatch(sbIntPrivateKey.ToString(), strIntPublicKey);
            Console.WriteLine("RSA_KeyMatch returns " + nRet + " (0 => keys match)");

            // Clear the private key
            Wipe.String(sbIntPrivateKey);

            return (nRet == 0);

        }

        public static void Pt_SavePrivateKeyAsEncrypted()
        {
            // Save an unencrypted OpenSSL private key in PKCS-8 encrypted form
            int nRet = 0;
            string strEncPrivateKeyFile = null;
            string strOpenSSLPrivateKeyFile = null;
            StringBuilder sbIntPrivateKey = null;
            string strPassword = null;

            strOpenSSLPrivateKeyFile = "Pt_PrivateKey.pem";
            // This was created in unencrypted OpenSSL PEM format
            strEncPrivateKeyFile = "Pt_PrivateKeyEncrypted.pem";
            // This is in encrypted form
            strPassword = "password";
            // CAUTION: Pick something better than this!

            // Read private key info into ephemeral internal string
            sbIntPrivateKey = Rsa.ReadPrivateKeyInfo(strOpenSSLPrivateKeyFile);
            // Save in encrypted file form (set nCount to 2000 or so)
            nRet = Rsa.SaveEncPrivateKey(strEncPrivateKeyFile, sbIntPrivateKey.ToString(), 2000, strPassword, Rsa.PbeOptions.Default, Rsa.Format.PEM);
            Console.WriteLine("RSA_SaveEncPrivateKey returns " + nRet + " (expected 0)");

            // Clear the private key
            Wipe.String(sbIntPrivateKey);
        }

        public static void Pt_Make_X509_CertSelfSigned()
        {
            // Use the RSA key file to make a self-signed X.509 certificate containing the public key

            // Requirements from [ESPECIF-2010] 5.2.2:
            // Formato = x.509
            // Charset = UTF-8
            // Encoding = Base-64
            // Endianess = Little Endian         [COMMENT: this is not relevant for X.509!]
            // OAEP Padding = PKCS1 v1.5 padding [COMMENT: This is NOT "OAEP" padding]
            // Tamanho da chave privada = 1024 bytes
            // Formato do Hash da mensagem = SHA-1

            int nRet = 0;
            string strEncPrivateKeyFile = null;
            string strPassword = null;
            X509.KeyUsageOptions keyUsage = default(X509.KeyUsageOptions);
            X509.Options options = default(X509.Options);
            string strCertFile = null;
            string strDN = null;
            int nYearsValid = 0;
            int nCertNum = 0;

            // With CryptoSys PKI we need to use the encrypted private key file we created with RSA_MakeKeys, not the OpenSSL one.
            strEncPrivateKeyFile = "Pt_PrivateKey.EPK";
            // This is in encrypted form
            strPassword = "password";
            // The password for the encrypted private key
            strCertFile = "Pt_SelfSigned.cer";
            // The certificate file we are going to create
            nYearsValid = 10;
            // Make this as long as you want.
            nCertNum = 0x101;
            // Pick a number. Change this if you issue another certificate with a different key.
            // The distinguished name of both the subject and the issuer of the certificate...
            strDN = "C=PT;O=Exemplo Organização;CN=Certificado auto-assinado";
            // Options...
            keyUsage = X509.KeyUsageOptions.DigitalSignature | X509.KeyUsageOptions.KeyCertSign | X509.KeyUsageOptions.CrlSign;
            // We want UTF-8 text and the output in PEM format...
            options = X509.Options.UTF8String | X509.Options.FormatPem;

            // Create the certificate file
            Console.WriteLine("Creating self-signed X.509 certificate serial number 0x{0,8:X}" + nCertNum + " for subject '" + strDN + "'");
            nRet = X509.MakeCertSelf(strCertFile, strEncPrivateKeyFile, nCertNum, nYearsValid, strDN, "", keyUsage, strPassword, options);
            Console.WriteLine("X509.MakeCertSelf returns " + nRet + " (expected 0)");

        }

        public static void Pt_QueryCert()
        {
            // Query an X.509 certificate for selected information
            string strOutput = null;
            string strQuery = null;
            string strCertFile = null;

            strCertFile = "Pt_SelfSigned.cer";

            Console.WriteLine("For certificate file " + strCertFile);
            strQuery = "serialNumber";
            strOutput = X509.QueryCert(strCertFile, strQuery);
            if (strOutput.Length <= 0) return;

            // catch error
            Console.WriteLine(strQuery + "=" + strOutput);

            strQuery = "subjectName";
            // NB use of option to obtain UTF-8-encoded name in Latin-1 format
            strOutput = X509.QueryCert(strCertFile, strQuery, X509.Options.Latin1);
            if (strOutput.Length <= 0) return;

            // catch error
            Console.WriteLine(strQuery + "=" + strOutput);

            strQuery = "notAfter";
            strOutput = X509.QueryCert(strCertFile, strQuery);
            if (strOutput.Length <= 0) return;

            // catch error
            Console.WriteLine(strQuery + "=" + strOutput);

        }

        public static void Pt_GetPublicKeyFromFileAndCert()
        {
            // Read the public key from both the original public key file and from the X.509 certificate we created.
            // Display some info about the key.
            string strPublicKeyFile = null;
            string strCertFile = null;
            string strIntPublicKey = null;
            strPublicKeyFile = "Pt_PublicKey.PEM";
            strCertFile = "Pt_SelfSigned.cer";

            // NOTE:
            // The internal key string is ephemeral and encrypted: it will be different each time you read it,
            // although it will contain the same underlying key data..
            // It is only intended for "internal" use by CryptoSys PKI functions like RSA_RawPublic in the same process.
            // But the HashCode will always be the same for the same key value.

            // Read in public key from file created by RSA_MakeKeys
            strIntPublicKey = Rsa.ReadPublicKey(strPublicKeyFile).ToString();
            Console.WriteLine("Public key is  " + Rsa.KeyBits(strIntPublicKey) + " bits");
            Console.WriteLine("HashCode=0x{0,8:X}", Rsa.KeyHashCode(strIntPublicKey));
            Console.WriteLine(Pt_GetPublicKeyAsXml(strIntPublicKey));
            // Read in (the same) public key from certificate file
            strIntPublicKey = Rsa.GetPublicKeyFromCert(strCertFile).ToString();
            Console.WriteLine("Public key is  " + Rsa.KeyBits(strIntPublicKey) + " bits");
            Console.WriteLine("HashCode=0x{0,8:X}", Rsa.KeyHashCode(strIntPublicKey));
            Console.WriteLine(Pt_GetPublicKeyAsXml(strIntPublicKey));

        }

        public static string Pt_GetPublicKeyAsXml(string strIntPublicKey)
        {
            // Get the public key in <RSAKeyValue> XML form from an "internal" key string
            return Rsa.ToXMLString(strIntPublicKey, 0);
        }
    }
}