Código necesario para enviar un correo electrónico (e-mail) desde una aplicación en C#, usando Microsoft Visual Studio Community 2019 y una cuenta de GMail. Explicamos paso a paso con el código fuente cómo enviar el mail mediante SSL y cómo guardar la contraseña encriptada y otros datos de conexión en un fichero de configuración.
- Requisitos para enviar e-Mail con cuenta de GMail desde aplicación C# .Net.
- Envío de correos electrónicos e-mails con SSL usando cuenta de GMail desde aplicación C# .Net.
Requisitos para enviar e-Mail con cuenta de GMail desde aplicación C# .Net
A continuación indicamos los requisitos para poder desarrollar una aplicación en C# .Net que envíe un correo electrónico usando una cuenta de Google GMail y que guarde los datos de conexión al servidor en un fichero de configuración, con las contraseñas encriptadas.
Habilitar acceso de aplicaciones poco seguras en tu cuenta de GMail
En primer lugar tendremos que habilitar la opción de seguridad en nuestra cuenta de GMail. Para ello accederemos a GMail con el usuario y contraseña de nuestra cuenta y pulsaremos en «Cuenta»:
En «Seguridad» pulsaremos en «Activar acceso» dentro del grupo «Acceso de aplicaciones poco seguras:
Y deslizaremos el control a Sí en «Permitir el acceso de aplicaciones poco seguras»:
Google GMail nos advertirá con el siguiente texto, indicando que es una opción poco recomendable:
Algunos dispositivos y aplicaciones utilizan una tecnología de inicio de sesión poco segura, lo que aumenta la vulnerabilidad de tu cuenta. Te recomendamos que desactives el acceso de estas aplicaciones, aunque también puedes activarlo si quieres usarlas a pesar de los riesgos que conllevan. Desactivaremos este ajuste de forma automática si no lo utilizas.
Si no tenemos esta opción habilitada al enviar un correo electrónico con la cuenta de GMail desde nuestra aplicación C Sharp .Net nos mostrará el error:
System.Net.Mail.SmtpException El servidor SMTP requiere una conexión segura o el cliente no se autenticó. La respuesta del servidor fue: 5.7.0 Authentication Required.
Y hay que tener en cuenta algo muy importante: si dejamos de usar la aplicación de envío de mails durante unas semanas Google desactivará esta opción automáticamente, por lo que cuando volvamos a usarla volverá a mostrarnos el error anterior. En este caso tendremos que habilitarla de nuevo como hemos explicado anteriormente.
IDE de desarrollo Microsoft Visual Studio Community 2019
Necesitaremos disponer del IDE de desarrollo Microsoft Visual Studio Community 2019, también es válido para versiones anteriores. Este IDE es gratuito y puede descargarse e instarlarse de la web oficial de Microsoft, como indicamos en el siguiente artículo:
Envío de correos electrónicos e-mails con SSL usando cuenta de GMail desde aplicación C# .Net
Crearemos la aplicación como hemos explicado en este artículo y agregaremos las clases que a continuación detallamos.
Añadiremos una clase con el nombre CifrarDescifrarTexto, que será la encargada de cifrar y descifrar la contraseña de acceso a la cuenta de GMail para la autenticación en el envío del correo electrónico. De esta forma la contraseña no quedará a la vista en texto plano. Para ello pulsaremos en «Proyecto» – «Agregar clase»:
Introduciremos el nombre del fichero de clase, en nuestro caso «CifrarDescifrarTexto.cs» y pulsaremos «Agregar»:
Añadiremos el siguiente código a la clase, teniendo en cuenta que nuestra aplicación se llama «BotComunidadBiker», por lo que el «namespace» se llamara «BotComunidadBiker»:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
using System; using System.Security.Cryptography; using System.Text; using System.IO; namespace BotComunidadBiker { class CifrarDescifrarTexto { public string cifrarTextoAES(string textoCifrar, string palabraPaso, string valorRGBSalt, string algoritmoEncriptacionHASH, int iteraciones, string vectorInicial, int tamanoClave) { try { byte[] InitialVectorBytes = Encoding.ASCII.GetBytes(vectorInicial); byte[] saltValueBytes = Encoding.ASCII.GetBytes(valorRGBSalt); byte[] plainTextBytes = Encoding.UTF8.GetBytes(textoCifrar); PasswordDeriveBytes password = new PasswordDeriveBytes(palabraPaso, saltValueBytes, algoritmoEncriptacionHASH, iteraciones); byte[] keyBytes = password.GetBytes(tamanoClave / 8); RijndaelManaged symmetricKey = new RijndaelManaged(); symmetricKey.Mode = CipherMode.CBC; ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, InitialVectorBytes); MemoryStream memoryStream = new MemoryStream(); CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write); cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length); cryptoStream.FlushFinalBlock(); byte[] cipherTextBytes = memoryStream.ToArray(); memoryStream.Close(); cryptoStream.Close(); string textoCifradoFinal = Convert.ToBase64String(cipherTextBytes); return textoCifradoFinal; } catch { return null; } } public string descifrarTextoAES(string textoCifrado, string palabraPaso, string valorRGBSalt, string algoritmoEncriptacionHASH, int iteraciones, string vectorInicial, int tamanoClave) { try { byte[] InitialVectorBytes = Encoding.ASCII.GetBytes(vectorInicial); byte[] saltValueBytes = Encoding.ASCII.GetBytes(valorRGBSalt); byte[] cipherTextBytes = Convert.FromBase64String(textoCifrado); PasswordDeriveBytes password = new PasswordDeriveBytes(palabraPaso, saltValueBytes, algoritmoEncriptacionHASH, iteraciones); byte[] keyBytes = password.GetBytes(tamanoClave / 8); RijndaelManaged symmetricKey = new RijndaelManaged(); symmetricKey.Mode = CipherMode.CBC; ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, InitialVectorBytes); MemoryStream memoryStream = new MemoryStream(cipherTextBytes); CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read); byte[] plainTextBytes = new byte[cipherTextBytes.Length]; int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length); memoryStream.Close(); cryptoStream.Close(); string textoDescifradoFinal = Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount); return textoDescifradoFinal; } catch { return null; } } } } |
Crearemos otra clase, llamada «EscribirLog» que escribirá en un fichero de texto las acciones que se van realizando y los posibles errores que se puedan producir. Incluso en una aplicación de consola mostrará también el mensaje en la propia consola. El código de esta clase será:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
using System; using System.IO; namespace BotComunidadBiker { class EscribirLog { public string mensajeLog { get; set; } public Boolean mostrarConsola { get; set; } //Constructor si se pasa el mensaje por parámetro en la creación de la clase public EscribirLog(string mensajeEnviar, Boolean mostrarConsola) { mensajeLog = mensajeEnviar; if (mostrarConsola) monstrarMensajeConsola(); escribirLineaFichero(); } //Constructor si se pasa el mensaje por setter tras la creación de la clase public EscribirLog() { if (mostrarConsola) monstrarMensajeConsola(); escribirLineaFichero(); } public void monstrarMensajeConsola() { //Quitar posibles saltos de línea del mensaje mensajeLog = mensajeLog.Replace(Environment.NewLine, " | "); mensajeLog = mensajeLog.Replace("\r\n", " | ").Replace("\n", " | ").Replace("\r", " | "); Console.WriteLine(DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss") + " " + mensajeLog); } //Escribe el mensaje de la propiedad mensajeLog en un fichero en la carpeta del ejecutable public void escribirLineaFichero() { try { FileStream fs = new FileStream(@AppDomain.CurrentDomain.BaseDirectory + "estado.log", FileMode.OpenOrCreate, FileAccess.Write); StreamWriter m_streamWriter = new StreamWriter(fs); m_streamWriter.BaseStream.Seek(0, SeekOrigin.End); //Quitar posibles saltos de línea del mensaje mensajeLog = mensajeLog.Replace(Environment.NewLine, " | "); mensajeLog = mensajeLog.Replace("\r\n", " | ").Replace("\n", " | ").Replace("\r", " | "); m_streamWriter.WriteLine(DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss") + " " + mensajeLog); m_streamWriter.Flush(); m_streamWriter.Close(); } catch { //Silenciosa } } } } |
La siguiente clase, llamada «LeerGuardarDatosConfiguracion» leerá y escribirá los datos de configuración de la aplicación: usuario, contraseña, servidor de mail, puerto, SSL, etc. en un fichero de configuración XML:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
using System; using System.Configuration; namespace BotComunidadBiker { class LeerGuardarDatosConfiguracion { public LeerGuardarDatosConfiguracion() { } //Constructor si se pasan los parámetros clave y valor //escribe en el fichero de configuración el valor de esa clave public LeerGuardarDatosConfiguracion(string clave, string valor) { guardarValorConfiguracion(clave, valor); } //Lee un valor de una clave en el fichero de configuración XML de la aplicación public string leerValorConfiguracion(string clave) { try { string resultado = ConfigurationManager.AppSettings[clave].ToString(); return resultado; } catch (Exception error) { new EscribirLog("Error al leer valor de configuración: " + error.GetType().ToString() + " " + error.Message, false); return ""; } } //Guarda un valor en una clave en el fichero de configuración XML de la aplicación public void guardarValorConfiguracion(string clave, string valor) { try { Configuration ficheroConfXML = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); //Configuration ficheroConfXML = // ConfigurationManager.OpenExeConfiguration(AppDomain.CurrentDomain.BaseDirectory); //Eliminamos la clave actual (si existe), de lo contrario los valores //se irán acumulando separados por coma ficheroConfXML.AppSettings.Settings.Remove(clave); //Asignamos el valor en la clave indicada ficheroConfXML.AppSettings.Settings.Add(clave, valor); //Guardamos los cambios definitivamente en el fichero de configuración ficheroConfXML.Save(ConfigurationSaveMode.Modified); } catch (Exception error) { new EscribirLog("Error al guardar valor de configuración: " + error.GetType().ToString() + " " + error.Message, false); } } } } |
Y la última clase, llamada «EnviarEMail» será la que conecte con el servidor de GMail y envíe el correo electrónico:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
using System; using System.Net.Mail; namespace BotComunidadBiker { class EnviarEMail { public string mailDestinatario { get; set; } public string asunto { get; set; } public string cuerpo { get; set; } public string mailOrigen { get; set; } public string nombreOrigen { get; set; } public string servidorMail { get; set; } public int puerto { get; set; } public Boolean ssl { get; set; } public Boolean autenticacion { get; set; } public Boolean html { get; set; } public string usuario { get; set; } public string contrasena { get; set; } public bool mailEnviado { get; } //Constructor si se pasa el mensaje por parámetro en la creación de la clase public EnviarEMail(string mailDestinatario, string asunto, string cuerpo, string mailOrigen, string nombreOrigen, string servidorMail, int puerto, Boolean ssl, Boolean autenticacion, Boolean html, string usuario, string contrasena) { this.mailDestinatario = mailDestinatario; this.asunto = asunto; this.cuerpo = cuerpo; this.mailOrigen = mailOrigen; this.nombreOrigen = nombreOrigen; this.servidorMail = servidorMail; this.puerto = puerto; this.ssl = ssl; this.autenticacion = autenticacion; this.html = html; this.usuario = usuario; this.contrasena = contrasena; this.mailEnviado = enviarEmail(); } //Constructor si se pasan los datos del mail setter tras la creación de la clase public EnviarEMail() { this.mailEnviado = enviarEmail(); } //Constructor si solo se pasan como parámetro el asunto, cuerpo, destinatario //Se leen el resto de datos de fichero de configuración public EnviarEMail(string mailDestinatario, string asunto, string cuerpo, bool html) { this.mailDestinatario = mailDestinatario; this.asunto = asunto; this.cuerpo = cuerpo; this.html = html; //Leemos los datos que faltan (servidor, autenticación, email origen ...) del fichero de configuración LeerGuardarDatosConfiguracion fConf = new LeerGuardarDatosConfiguracion(); this.mailOrigen = fConf.leerValorConfiguracion("Email - Email origen"); this.nombreOrigen = fConf.leerValorConfiguracion("Email - Nombre origen"); this.puerto = int.Parse(fConf.leerValorConfiguracion("Email - Puerto")); this.servidorMail = fConf.leerValorConfiguracion("Email - Servidor"); this.ssl = Boolean.Parse(fConf.leerValorConfiguracion("Email - SSL")); this.autenticacion = Boolean.Parse(fConf.leerValorConfiguracion("Email - Autenticación")); this.usuario = fConf.leerValorConfiguracion("Email - Usuario"); CifrarDescifrarTexto cTexto = new CifrarDescifrarTexto(); this.contrasena = cTexto.descifrarTextoAES(fConf.leerValorConfiguracion("Email - Contraseña"), "Encriptad0", "SalT", "MD5", 22, "1234567891234567", 128); this.mailEnviado = enviarEmail(); } //Envía un correo electrónico con los datos pasados por setter o bien en el constructor de la clase public bool enviarEmail() { try { SmtpClient SmtpServer = new SmtpClient(); SmtpServer.UseDefaultCredentials = false; SmtpServer.Credentials = new System.Net.NetworkCredential(usuario, contrasena); SmtpServer.Port = puerto; SmtpServer.Host = servidorMail; SmtpServer.EnableSsl = ssl; MailMessage mail = new MailMessage(); mail.From = new MailAddress(mailOrigen); mail.To.Add(mailDestinatario); mail.Subject = asunto; mail.Body = cuerpo; mail.IsBodyHtml = html; mail.DeliveryNotificationOptions = DeliveryNotificationOptions.OnFailure; SmtpServer.Send(mail); new EscribirLog("Enviado email a " + mailDestinatario, true); return true; } catch (Exception error) { new EscribirLog("Error al enviar email a " + mailDestinatario + " " + error.GetType().ToString() + " " + error.Message, true); return false; } } } } |
Por último, en la propia aplicación, crearemos un procedimiento de ejemplo para usar la clase anterior y enviar un correo electrónico.
Un ejemplo de uso de la clase para guardar la configuración en el fichero XML y encriptar la contraseña:
1 2 3 4 5 6 7 8 9 10 |
new LeerGuardarDatosConfiguracion("Email - Email origen", "usuario@gmail.com"); new LeerGuardarDatosConfiguracion("Email - Nombre origen", "Nombre emisor mail"); new LeerGuardarDatosConfiguracion("Email - Servidor", "smtp.gmail.com"); new LeerGuardarDatosConfiguracion("Email - SSL", "true"); new LeerGuardarDatosConfiguracion("Email - Puerto", "587"); new LeerGuardarDatosConfiguracion("Email - Autenticación", "true"); new LeerGuardarDatosConfiguracion("Email - Usuario", "usuario@gmail.com"); CifrarDescifrarTexto cTexto = new CifrarDescifrarTexto(); new LeerGuardarDatosConfiguracion("Email - Contraseña", cTexto.cifrarTextoAES("contraseña_mail", "Encriptad0", "SalT", "MD5", 22, "1234567891234567", 128)); |
Y un ejemplo de uso para el envío del correo electrónico:
1 2 3 4 |
EnviarEMail enMail = new EnviarEMail(mailUsuario, "Asunto del correo electrónico", "Texto del correo electrónico con saltos de línea: " + Environment.NewLine + Environment.NewLine + "Saludos.", false); |