¿Cómo crear un archivo pfx en c#? cancelación de factura electrónica SAT cfdi 3.3

Para cancelar una factura electrónica es necesario crear un tercer certificado llamado pfx, este certificado se debe crear por medio de Openssl en conjunto de nuestro archivo Cer y nuestro archivo Key los cuales debemos ya tener si es que estamos timbrando.

Para crear el archivo pfx utilizaremos la clase Process de .Net, esta clase sirve para mandar comandos como si estuviéramos en consola, de esta manera utilizaremos los comandos de Openssl.

Les recuerdo que antes deben instalar Openssl y como está en el siguiente ejemplo debe estar instalado en la unidad c:/openssl-win32.

En caso que se tenga Openssl instalado en otra ruta distinta, solo modifiquen esa parte del código. A continuación la clase mágica:

 public class PFX
    {
        string cer = "";
        string key = "";
        string clavePrivada = "";
        string ArchivoPFX = "";
        string ArchivoKPEM = "";
        string ArchivoCPEM = "";

        public string error = "";
        public string mensajeExito = "";

        ///
<summary>
        ///
        /// </summary>

        /// <param name="cer_">archivo cer a utilizar</param>
        /// <param name="key_">archivo key a utilizar</param>
        /// <param name="clavePrivada_">clave para el archivo pfx</param>
        /// <param name="archivoPfx_">ruta del archivo pfx</param>
        /// <param name="path">ruta para archivos temporales PEM</param>
        public PFX(string cer_, string key_, string clavePrivada_, string archivoPfx_, string pathTemp)
        {
            cer = cer_;
            key = key_;
            clavePrivada = clavePrivada_;

            ArchivoKPEM = pathTemp + "k.pem";
            ArchivoCPEM = pathTemp + "c.pem";
            ArchivoPFX = archivoPfx_;
        }

        public bool creaPFX()
        {
            bool exito = false;

            //validaciones
            if (!File.Exists(cer))
            {
                error = "No existe el archivo cer en el sistema";
                return false;
            }
            if (!File.Exists(key))
            {
                error = "No existe el archivo key en el sistema";
                return false;
            }
            if (clavePrivada.Trim().Equals(""))
            {
                error = "No existe una clave privada aun en el sistema";
                return false;
            }

            //creamos objetos Process
            System.Diagnostics.Process proc = new System.Diagnostics.Process();
            System.Diagnostics.Process proc2 = new System.Diagnostics.Process();
            System.Diagnostics.Process proc3 = new System.Diagnostics.Process();
            proc.EnableRaisingEvents = false;
            proc2.EnableRaisingEvents = false;
            proc3.EnableRaisingEvents = false;

            //openssl x509 -inform DER -in certificado.cer -out certificado.pem
            proc.StartInfo.FileName = "openssl";
            proc.StartInfo.Arguments = "x509 -inform DER -in \"" + cer + "\" -out \"" + ArchivoCPEM + "\"";
            proc.StartInfo.WorkingDirectory = @"C:\openssl-win32\bin\";
            proc.Start();
            proc.WaitForExit();

            //openssl pkcs8 -inform DER -in llave.key -passin pass:a0123456789 -out llave.pem
            proc2.StartInfo.FileName = "openssl";
            proc2.StartInfo.Arguments = "pkcs8 -inform DER -in \"" + key + "\" -passin pass:" + clavePrivada + " -out \"" + ArchivoKPEM + "\"";
            proc2.StartInfo.WorkingDirectory = @"C:\openssl-win32\bin\";
            proc2.Start();
            proc2.WaitForExit();

            //openssl pkcs12 -export -out archivopfx.pfx -inkey llave.pem -in certificado.pem -passout pass:clavedesalida
            proc3.StartInfo.FileName = "openssl";
            proc3.StartInfo.Arguments = "pkcs12 -export -out \"" + ArchivoPFX + "\" -inkey \"" + ArchivoKPEM + "\" -in \"" + ArchivoCPEM + "\" -passout pass:" + clavePrivada;
            proc3.StartInfo.WorkingDirectory = @"C:\openssl-win32\bin\";
            proc3.Start();
            proc3.WaitForExit();

            proc.Dispose();
            proc2.Dispose();
            proc3.Dispose();

            //enviamos mensaje exitoso
            if (System.IO.File.Exists(ArchivoPFX))
                mensajeExito = "Se ha creado el archivo PFX ";
            else
            {
                error = "Error al crear el archivo PFX, puede ser que el cer o el key no sean archivos con formato correcto";
                return false;
            }

            //eliminamos los archivos pem
            if (System.IO.File.Exists(ArchivoCPEM)) System.IO.File.Delete(ArchivoCPEM);
            if (System.IO.File.Exists(ArchivoKPEM)) System.IO.File.Delete(ArchivoKPEM);

            exito = true;

            return exito;
        }
    }

Para utilizarla de la siguiente manera:


//yo siempre utilizo la misma clave privada de mi timbrado para encriptar el pfx
//la ruta para archivos temporales solo se utiliza para archivos que serán eliminados al final
oPFX = new PFX("algunaruta/archivocer.cer", "algunaruta/archivokey.key", "algunpassword", "algunacarpeta/miarchivo.pfx", "rutaparacreararchivostemporalesPem/");

if (oPFX.creaPFX())
                    Console.WriteLine("Archivo creado con éxito");
else
                    Console.WriteLine(oPFX.error);

Ignorar el certificado al momento de una petición a un API en C# .Net

Algunas veces necesitamos ignorar los certificados cuando nos conectamos a un API la cual pide SSL por ejemplo y necesitamos hacer pruebas, para ello podemos hacer el siguiente truco, agregamos el siguiente método (AceptarTodosLosCertificados) en nuestra clase:

class Conexion{

        public bool AceptarTodosLosCertificados(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certification, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
        {
            return true;
        }

}

Y ya solo invocamos la siguiente línea antes de hacer la petición a la API:

class Conexion{

        public bool AceptarTodosLosCertificados(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certification, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
        {
            return true;
        }

	   public bool Connect(){

            try
            {

                //linea donde invocamos el metodo
                ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(AceptarTodosLosCertificados);

                //aquí el codigo para la petición

                var httpWebRequest = (HttpWebRequest)WebRequest.Create("https://www.paginaconseguridad.com/lalala");
                httpWebRequest.ContentType = "application/json; charset=utf-8";
                httpWebRequest.Method = "POST";
                httpWebRequest.Accept = "application/json";

                //y aqui mas codigo .....

                return true;
            }catch(Exception ex){
                error = ex.Message;
                return false;
            }

        }

}

Así de sencillo nos brincaremos el certificado.