// $Id: MakeCertChain.cs $

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

// Make a chain of X.509 certificates [Carl] --> [Ian] --> [Enid] then create a PFX file.
// Carl is a trusted root CA. Ian and Enid's certs are created from scratch.

/******************************* LICENSE ***********************************
 * Copyright (C) 2019 David Ireland, DI Management Services Pty Limited.
 * All rights reserved. <www.di-mgt.com.au> <www.cryptosys.net>
 * The code in this module is licensed under the terms of the MIT license.  
 * For a copy, see <http://opensource.org/licenses/MIT>
****************************************************************************
*/

namespace MakeCertChain
{
    class MakeCertChain
    {
        static void Main(string[] args)
        {
            //MakeKeys();   // Only call this once. Uncomment to redo.
            MakeCertForIan();
            MakeCertForEnid();
            MakeP7();
            CheckChain();
            MakePFX();
        }

        // Only call this once!
        static void MakeKeys()
        {
            // Create two new sets of 1024-bit RSA keys
            string prikeyfile, pubkeyfile;
            int r;

            prikeyfile = "IanRSA.p8e";
            pubkeyfile = "IanRSA.pub";
            r = Rsa.MakeKeys(pubkeyfile, prikeyfile, 1024, Rsa.PublicExponent.Exp_EQ_65537, 2000, "password", Rsa.PbeOptions.Pbe_Pbkdf2_aes128_CBC, true);
            Console.WriteLine("Rsa.MakeKeys returns {0}", r);
            prikeyfile = "EnidRSA.p8e";
            pubkeyfile = "EnidRSA.pub";
            r = Rsa.MakeKeys(pubkeyfile, prikeyfile, 1024, Rsa.PublicExponent.Exp_EQ_65537, 2000, "password", Rsa.PbeOptions.Pbe_Pbkdf2_aes128_CBC, true);
            Console.WriteLine("Rsa.MakeKeys returns {0}", r);

        }

        // Make an intermediate cert
        static void MakeCertForIan()
        {
            string pubkeyfile;
            string issuercert, issuerkey;
            string certname;
            int r;
            X509.KeyUsageOptions kuo = X509.KeyUsageOptions.DigitalSignature | X509.KeyUsageOptions.KeyCertSign | X509.KeyUsageOptions.CrlSign;
            certname = "IanRSASignedByCarl.cer";
            pubkeyfile = "IanRSA.pub";
            issuercert = "CarlRSASelf.cer";
            issuerkey = "CarlPrivRSASign.p8e";
            r = X509.MakeCert(certname, issuercert, pubkeyfile, issuerkey, 0x3002, 20, "O=Administración de certificados;CN=Ian", "notBefore=2019-01-01", kuo, "password", 
                SigAlgorithm.Rsa_Sha256, X509.CertOptions.SetAsCA | X509.CertOptions.UTF8String);
            Console.WriteLine("X509.MakeCert returns {0} (expected 0)", r);
            if (r != 0) disp_error(r);
            ShowFileInfo(certname);
        }

        // Make an end-user cert
        static void MakeCertForEnid()
        {
            string pubkeyfile;
            string issuercert, issuerkey;
            string certname;
            int r;
            X509.KeyUsageOptions kuo = X509.KeyUsageOptions.DigitalSignature;
            certname = "EnidRSASignedByIan.cer";
            pubkeyfile = "EnidRSA.pub";
            issuercert = "IanRSASignedByCarl.cer";
            issuerkey = "IanRSA.p8e";
            r = X509.MakeCert(certname, issuercert, pubkeyfile, issuerkey, 0, 19, "CN=Enid", "notBefore=2019-01-02;rfc822Name=enid@example.org;serialNumber=#x02deadbeef123456", kuo, "password", 
                SigAlgorithm.Rsa_Sha256, X509.CertOptions.UTF8String);
            Console.WriteLine("X509.MakeCert returns {0} (expected 0)", r);
            if (r != 0) disp_error(r);
            ShowFileInfo(certname);
        }

        static void MakeP7()
        {
            string fname = "enid.p7b";
            // List of all the cert files we want to include
            string certlist = "EnidRSASignedByIan.cer;IanRSASignedByCarl.cer;CarlRSASelf.cer";
            int r;
            // Use the Cms.MakeSigData method with "CertsOnly" option. inputFile and privateKey are ignored.
            r = Cms.MakeSigData(fname, "", certlist, "", Cms.Options.CertsOnly);
            Console.WriteLine("Cms.MakeSigData returns {0} (expected 0)", r);
            if (r != 0) disp_error(r);
            ShowFileInfo(fname);
        }

        static void CheckChain()
        {
            string fname = "enid.p7b";
            string trustedCert = "CarlRSASelf.cer";
            int r;
            r = X509.ValidatePath(fname, trustedCert, false);
            Console.WriteLine("X509.ValidatePath(.p7b) returns {0} (expected 0)", r);
            if (r != 0) disp_error(r);
            string certlist = "EnidRSASignedByIan.cer;IanRSASignedByCarl.cer;CarlRSASelf.cer";
            r = X509.ValidatePath(certlist, trustedCert, false);
            Console.WriteLine("X509.ValidatePath(list) returns {0} (expected 0)", r);
            if (r != 0) disp_error(r);
        }

        static void MakePFX()
        {
            string pfxname = "enid.pfx";
            string certlist = "EnidRSASignedByIan.cer;IanRSASignedByCarl.cer;CarlRSASelf.cer";
            string prikeyfile = "EnidRSA.p8e";
            int r = Pfx.MakeFile(pfxname, certlist, prikeyfile, "password", "Enid", Pfx.Options.Default);
            Console.WriteLine("Pfx.MakeFile returns {0} (expected 0)", r);
            if (r != 0) disp_error(r);
            // NOTE: pfx will be different each time (encryption IVs) 
            ShowFileInfo(pfxname);
        }

        static void ShowFileInfo(string fname)
        {
            if (File.Exists(fname)) {
                long flen = new System.IO.FileInfo(fname).Length;
                Console.WriteLine("  File '{0}': {1} bytes, SHA-1={2}", fname, flen, Hash.HexFromFile(fname, HashAlgorithm.Sha1));
            } else {
                Console.WriteLine("  File '{0}' DOES NOT EXIST!", fname);
            }
        }

        //***********************
        // DISPLAY ERROR DETAILS
        //***********************
        /// <summary>
        /// Display details of last error given error code
        /// </summary>
        /// <param name="nErr">Error code returned</param>
        static void disp_error(int nErr)
        {
            string s = General.LastError();
            Console.WriteLine("ERROR Returned={0}/{1}: {2}; {3}", nErr, General.ErrorCode(), General.ErrorLookup(nErr), s);
        }

        /// <summary>
        /// Display details of last error (no error code)
        /// </summary>
        static void disp_error()
        {
            string s = General.LastError();
            int nErr = General.ErrorCode();
            Console.WriteLine("ERROR {0}: {1}: {2}", nErr, General.ErrorLookup(nErr), s);
        }


    }
}