/* $Id: GetCertsFromPfx.cs $
 * Last updated:
 *   $Date: 2020-09-27 10:41:00 $
 *   $Version: 1.0.0 $
 */

/******************************* LICENSE ***********************************
 * Copyright (C) 2019-20 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>
****************************************************************************
*/

using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Diagnostics;
// Requires `CryptoSys PKI` to be installed on your system: available from <http://cryptosys.net/pki/>
// Either add a reference to `diCrSysPKINet.dll` (installed by default in `C:\Program Files (x86)\CryptoSysPKI\DotNet`)
// or add the source code module `CryptoSysPKI.cs` to your project.
using Pki = CryptoSysPKI;

namespace DIManagement.GetCertsFromPfx
{
    class Program
    {
        static void Main(string[] args)
        {
            GetCertsFromPfx.GetCerts("user.pfx", "password");
        }
    }
    class GetCertsFromPfx
    {
        /// <summary>
        /// Extract all X.509 certificates in a PFX/P12 file and save as separate CER files.
        /// </summary>
        /// <param name="pfxFile">Filename of PFX/P12 file.</param>
        /// <param name="password">Password for PFX file.</param>
        /// <remarks>Certificate files will be saved as the subject's common name (CN) or as certN.cer.</remarks>
        public static void GetCerts(string pfxFile, string password) 
        {
            string newp7file;
            string certFile;
            string subjectName;
            int r, ncerts, i;

            Console.WriteLine("PFX FILE: {0}", pfxFile);
            // Compose output filename
            newp7file = Path.ChangeExtension(pfxFile, ".p7b");
            Console.WriteLine("newp7file=" + newp7file);

            // Extract P7 chain file from PFX
            // Note: this .p7b file is not needed afterwards
            r = Pki.X509.GetP7ChainFromPFX(newp7file, pfxFile, password);
            Console.WriteLine("GetP7ChainFromPFX returns {0} (expecting >0)", r);
            Debug.Assert(r > 0, "Failed to extract P7 file from PFX");

            // How many certs to extract
            ncerts = Pki.X509.GetCertCountInP7Chain(newp7file);
            Console.WriteLine("P7 file has {0} certs", ncerts);

            char[] invalidchars = new char[] { ' ', '<', '>', '|', ':', '*', '?', '/', '\\' };
            Regex reg1 = new Regex(@"CN=([^;]+)(;|$)");

            for (i = 1; i <= ncerts; i++) {
                // Extract X.509certificate
                certFile = string.Format("cert{0}.cer", i);
                Console.WriteLine(" Extracting certificate {0}...", certFile);
                r = Pki.X509.GetCertFromP7Chain(certFile, newp7file, i);
                Console.WriteLine(" GetCertFromP7Chain returns {0} (expecting >0)", r);
                Debug.Assert(r > 0, "Failed to extract certificate");
                subjectName = Pki.X509.QueryCert(certFile, "subjectName");
                Console.WriteLine("  SubjectName='{0}'", subjectName);
                // Get the Common Name (CN)
                Match match = reg1.Match(subjectName);
                if (match.Success) {
                    // Create new cert name from CN
                    string newcertname = match.Groups[1].Value;
                    Console.WriteLine("  {0}", newcertname);
                    // Remove any invalid filename chars
                    newcertname = invalidchars.Aggregate(newcertname, (c1, c2) => c1.Replace(c2, '_'));
                    newcertname = newcertname.Replace(".", "");
                    newcertname += ".cer";
                    // Change name of certFile
                    if (File.Exists(newcertname)) {
                        File.Delete(newcertname);
                    }
                    System.IO.File.Move(certFile, newcertname);
                    Console.WriteLine("  File renamed as '{0}'", newcertname);
                }
        
            }
        }
    }
}