miércoles, 25 de julio de 2012

MailMessage - Envia mail con GMail (1/3)

 

Introducción


Enviar un mail es una tarea bastante recurrente en las características de toda aplicación, pero existen varias formas de configurarla dependiendo de la necesidad

Analizaremos en este artículo como enviar un mail haciendo uso de un servicio de SMTP publico como es GMail.

Para llevar a cabo la tarea se puede configurar directamente las propiedades, pero es aun mejor si se configuran, lo cual permitiría realizar cambios sin tener que recompilar el código.

 

Envió del Mail configurando las propiedades


Enviar un mail definiendo toda la configuración por código no es aconsejable, pero si es útil cuando uno aun esta realizando prueba del funcionamiento del envió del correo para definir la información que después se volcaría al config

 

[TestMethod]
public void SendMailSinConfig()
{

    List<string> destinatarios = new List<string>()
    {
        "xx@gmail.com",
        "xx@hotmail.com"
    };


    //
    // se crea el mensaje
    //
    string body = "";

    using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MailSendTest.MailBody.txt"))
    using (StreamReader reader = new StreamReader(stream))
    {
        body = reader.ReadToEnd();
    }

    MailMessage mail = new MailMessage()
    {
        From = new MailAddress("xx@gmail.com"),
        Body = body,
        Subject = "Mail Test",
        IsBodyHtml = false
    };


    //
    // se asignan los destinatarios
    //
    foreach (string item in destinatarios)
    {
        mail.To.Add(new MailAddress(item));
    }


    //
    // se define el smtp
    //
    SmtpClient smtp = new SmtpClient()
    {
        Host = "smtp.gmail.com",
        Port = 587,
        UseDefaultCredentials = false,
        Credentials = new NetworkCredential("xx@gmail.com", "password"),
        EnableSsl = true
    };
    

    smtp.Send(mail);

}

En este ejemplo además se añadió un adicional que ayuda bastante en la forma de declarar el texto del cuerpo del mensaje, consiste básicamente en embeber un archivo en el assembly del proyecto.

imagen1

En el código se toma el assembly que se esta ejecutando y de este el stream basado en el namespace que define la ruta al archivo dentro del proyecto.

 

Envió del mail usando Archivo de Configuración


Si bien el MailMessage no varia en la forma de programarse, si lo hizo notablemente la definición de datos que suelen modificarse con mas frecuencia durante la implementación, lo cual implicaría recompilar el código continuamente.

[TestMethod]
public void SendMailUsandoConfig()
{
    List<string> destinatarios = new List<string>()
    {
        "xx@gmail.com",
        "xx@hotmail.com"
    };


    //
    // se crea el mensaje
    //
    string body = "";
   
    using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MailSendTest.MailBody.txt"))
    using (StreamReader reader = new StreamReader(stream))
    {
        body = reader.ReadToEnd();
    }

    MailMessage mail = new MailMessage()
    {
        Body = body,
        Subject = "Mail Test",
        IsBodyHtml = false
    };


    //
    // se asignan los destinatarios
    //
    foreach (string item in destinatarios)
    {
        mail.To.Add(new MailAddress(item));
    }


    //
    // se define el smtp
    //
    SmtpClient smtp = new SmtpClient();
    smtp.Send(mail);

}

El cliente de smtp toma la información del .config

<system.net>
  <mailSettings>
    <smtp from="xx@gmail.com">
      <network host="smtp.gmail.com" port="587" password="password" userName="xx@gmail.com" enableSsl="true" defaultCredentials="false" />
    </smtp>
  </mailSettings>
</system.net>

Pero seguramente habrán notado un problema, la información esta disponible a simple vista, lo cual no es bueno si se define el usuario y password de la cuenta de correo. Es por eso que puede aplicarse encriptación a esta sección del config

 

Envió del mail usando config seguro


Para probarlo es que se creo el segundo proyecto de test, en este se han agregado método para poder proteger la sección del archivo de configuración.

Es necesario mencionar que si bien podría haberse usado de forma directa un sistema de protección como es el RSAProtectedConfigurationProvider, se opta por crear una key propia, esto es debido a que será necesario exportar la clave que se genere para poder llevarla a las PCs (o al servidor) donde resida la aplicación cuando se realice el deploy de la misma.

Sera necesario ejecutar algunos comandos, es por eso que se accede a la consola que proporciona el VS, ya que se hará uso del aspnet_regiis, se hace uso de la misma técnica que se aplica en un entorno web para proteger el config.

imagen2

Los pasos para poder realizar esto serian:

1 - Se crea la key

aspnet_regiis -pc "MailSendKeys" –exp

Es muy importante el –exp ya que este permitirá la exportación de las keys a un archivo

2 - Se exporta la key

aspnet_regiis -px "MailSendKeys" "C:\MailSendKeysFile.xml" -pri

El parámetro –pri exporta la ket publica y privada necesarias para poder trabajar en la encriptación

 

3- Se importa en las PCs donde se hará uso la aplicación

aspnet_regiis -pi "MailSendKeys" "C:\MailSendKeysFile.xml"

Sin esta key en la pc donde estará la aplicación no se podrá leer la sección del config

 

Nota: Las key importadas podrán encontrar en la carpeta:

C:\Documents and Settings\All Users\Application Data\Microsoft\Crypto\RSA\MachineKeys

 

4 – Encriptar la sección del .config

Para ello se definieron dos métodos que solo serán usados una única vez cuando se defina la información que debe configurarse

private void ProtegerMailSettings()
{

    Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
    SmtpSection configSection = config.GetSection("system.net/mailSettings/smtp") as SmtpSection;

    if (!configSection.SectionInformation.IsProtected)
    {
        //configSection.SectionInformation.ProtectSection("RSAProtectedConfigurationProvider");
        configSection.SectionInformation.ProtectSection("MailProtectedProvider");
        
        config.Save();
    }

}

private void DesprotegerMailSettings()
{

    Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
    SmtpSection configSection = config.GetSection("system.net/mailSettings/smtp") as SmtpSection;

    if (configSection.SectionInformation.IsProtected)
    {
        configSection.SectionInformation.UnprotectSection();
        config.Save();
    }

}

El test de encriptación solo se invoca en una única oportunidad, para que algo como esto:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">

  <configProtectedData>
    <providers>
      <add name="MailProtectedProvider"
           type="System.Configuration.RsaProtectedConfigurationProvider, System.Configuration, Version=2.0.0.0,
                 Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
           keyContainerName="MailSendKeys"
           useMachineContainer="true" />
    </providers>
  </configProtectedData>
  
  <system.net>
    <mailSettings>
      <smtp from="xx@gmail.com">
        <network host="smtp.gmail.com" port="587" password="password" userName="xx@gmail.com" enableSsl="true" defaultCredentials="false" />
      </smtp>
    </mailSettings>
  </system.net>

</configuration>

 

Pase a ser algo como ser:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">

  <configProtectedData>
    <providers>
      <add name="MailProtectedProvider"
           type="System.Configuration.RsaProtectedConfigurationProvider, System.Configuration, Version=2.0.0.0,&#xD;&#xA;                 Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

           keyContainerName="MailSendKeys"
           useMachineContainer="true" />
    </providers>
  </configProtectedData>
  
  <system.net>
    <mailSettings>
      <smtp configProtectionProvider="MailProtectedProvider">
        <EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
          xmlns="http://www.w3.org/2001/04/xmlenc#">
          <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
          <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
            <EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
              <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
              <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
                <KeyName>Rsa Key</KeyName>
              </KeyInfo>
              <CipherData>
                <CipherValue>OBiEstszUoonJNlFT9FwMePjtX+AdCufd2I8OAzHDYoi7A6GTTuaSqvM6KR9ANLC8fPmcRt/8PMoOK9nnVkRMlc4haKjoL+V1CmEBaqYOD23lBA5bGGIYdtDCZH9N597juNXkx8ISiESNPi9phO999T7cKPbAQ1rkQXmOGOQ9KU=</CipherValue>
              </CipherData>
            </EncryptedKey>
          </KeyInfo>
          <CipherData>
            <CipherValue>zEAsDA84EFb7ta4I0yzOS200HWB9JfPPtioQjn/GEZSGY3rqvmOwKhYGErxIR0E2k+ewPNJKypedeMyB3lAebrHs8kvwKY6R3glrDuvkcJlfN1Ihl3CcFjeBwE5Rkqmx4Jso5d6d8t4P5+GwqxStjJn/evfXBINv3EawkwATc11ch/aSKM1OUMXSz2XUnfAV7mwFehCk+V09YCXL+KjlgqAm3FwSAHCxCV0g1lLnebf7T1d2fELIjw==</CipherValue>
          </CipherData>
        </EncryptedData>
      </smtp>
    </mailSettings>
  </system.net>

</configuration>

 

La sección <configProtectedData> permite definir el proveedor custom que uno creara para hacer uso de la key registrada desde la linea de comando.

imagen4

El keyContainerName corresponde al nombre de la key creada por línea de comando. Mientras que el name del proveedor se asigna a la sección que se esta encriptado.

También es muy importante validar que este definido

imagen3

 

Link de referencia


How To: Encrypt Configuration Sections in ASP.NET 2.0 Using RSA

Importing and Exporting Protected Configuration RSA Key Containers

 

Código


[C#]
 

22 comentarios:

  1. Estoy haciendo un aplicativo para mandar a gmail, pero mi consulta es respecto a seguridad. Como Aria para mandar a varios correos con documento adjunto y encriptarlo todo=?.
    Aparte como aria para el control de lectura para gmail sin utilizar outlook o thunderbird y poder utilizar un aplicativo para gmail. Y gracias por su respuesta respeto a acortar URL ya encontre un aplicativo el problema es que dicho aplicativo me sale error de fecha.

    ResponderEliminar
  2. hola Kely

    te refieres a enciptar el contenido del mail ? para eso se suele usar PGP

    hay tool que permiten obtener claves publicas-privadas para lograrlo

    How to encrypt your email

    Lo del control de lectura no lo entendi, se supone que desde la pagina web de gmail podrias revisar el correo, no necesitas de una aplicacion

    saludos

    ResponderEliminar
  3. Hola leandro,
    Esta pregunta no tiene mucho que ver con tu post, pero si con el envio de mails ..

    Depronto conoces servidores SMTP gratuitos para enviar correos via telnet desde la consola de comandos ???
    Sería algo mas o menos como lo que mencionan en esta dirección:

    http://www.trydex.com/online/enviomail.htm

    Mil gracias !!!!

    ResponderEliminar
  4. hola Cristian

    en realidad con cualqueir smtp puede suar telnet, porque basicamente es enviar comento al protocolo

    si sabes como enviar los comando puede hacerlo con cualquier smtp

    smtp e-mail: utilizar gmail desde TELNET

    saludos

    ResponderEliminar
  5. Mil gracias leandro !!
    Como siempre,con la mejor voluntad para ayudar a los demás ;)

    ResponderEliminar
  6. hola, pido urgentemente ayuda, requiero hacer q desde una aplicacion en asp, al generar un reporte se envie automaticamente un ocrreo de aviso que se ha registrado un nuevo reporte, preferentemente debe mandarlo a un correo de outlook gracias por su ayuda

    ResponderEliminar
  7. hola maricela

    pero este automatismo en la generacion del reporte quien lo realiza? o sea una apgina aspx de por si no se eejcuta sola de forma desatentidfa como para generar el reporte

    ademas reporte confeccionado con que tool, Crystal, Reporting Service ,etc
    Estos por lo general exportan a pdf, por lo que podrias dejar el resultado en una carpeta

    despues el envio de mail es normal como se explica en este articulo, se puede enviar a una cuenta de outlook aunque este no tiene nada que ver para el envio, lo importante es saber que servicio de smtp se va a utilizar

    saludos

    ResponderEliminar
  8. hola. leandro me gustaria saber si tienes skype, quiero compartir contigo un proyecto y megustaria q me ayudaras porfavor, comunicamte conmigo en este correo earias2981@hotmail.com, gracias anticipadas...

    ResponderEliminar
  9. hola edward

    la verdad no uso skydrive, si necesitas podrias plantearme la consulta via mail
    pero de ser posible que sean alguna consulta puntual asi es mas simple de responder, digo que no sea pasar todo el codigo en un zip para validarlo porque puede ser complicado

    si es una consulta sobre una duda no hay problema

    saludos

    ResponderEliminar
  10. Hola q tal, puedo encriptar el *.exe.config de un servicio windows, pense que era igual que encriptar una seccion de un web.config pero no me funciona.

    ResponderEliminar
  11. hola ANDRES

    es que es igual, se usa el mismo comando de asp.net para trabajar con los app.config de una aplicacion desktop

    es mas en el articulo uso un app.config para encriptar la session, ya que el ejemplo no es web

    saludos

    ResponderEliminar
  12. es posible que no se pueda bajar el archivo ?

    ResponderEliminar
  13. es posible que no se pueda bajar el archivo ?

    ResponderEliminar
  14. hola estudiante

    el archivo esta publicado en skydrive, intente recien ingresar y me dejo hacerlo
    por lo que puede descargarlo

    saludos

    ResponderEliminar
  15. Hola Leandro, estoy migrando un sistema en vb 2003 - al vb 2010 y tengo un problema a la hora de levantar el sistema lo cual me sale este mensaje: "se ha producido un error al crear un controlador de seccion de configuracion para uipconfiguration: el comodin '##any' permite el elemento 'navigationGraph' y hace que el modelo de contenido sea ambiguo. un modelo de contenido debe estar constituido de forma que durante la validacion de una secuencia de elementos de informacion de elemento, la particula contenida directa, indirecta o implicitamente en el mismo y con la que se debe intentar validar cada elemento de secuencia se pueda determinar de forma exclusiva sin examinar el contenido o los atributos de dicho elemento, y sin ninguna informacion acerca de los elementos del resto de la secuencia"

    si serias amable de poder ayudarme. gracias

    ResponderEliminar
  16. hola Jorge

    respondi en la pregunta del otro articulo

    saludos

    ResponderEliminar
  17. Que tal Leandro, otra vez guiandome con tus tutoriales.

    En este caso tengo la situación con una aplicación Winforms en C#, la cuál utilizo Entity Framework y mi cadena de conexió se encuentra en app.conifg, para este caso que podría hacer para encriptar la cadena de conexión a BD? He leído acerca de encriptar sólo dicha sección, sin embargo EF al momento de establecer conexión no sabría como desencriptar dichos datos. Hay algún libro o post que me pueda servir para saber como resolver dicho problema??

    Saludos!

    ResponderEliminar
    Respuestas
    1. hola
      Porque dices que EF no podrias desencriptar la sessionde configuracion ?
      Consideraciones de seguridad (Entity Framework)
      How to: Secure Connection Strings When Using Data Source Controls
      Entiendo que no deberias tener problemas para encriptar la session de configuracion y que esta sea interpretada por EF
      saludos

      Eliminar
  18. Leandro replique exactamente el primer metodo para mandar mail. y no me anduvo. Me sale el siguiente error:
    Se produjo una excepción de tipo 'System.Net.Mail.SmtpException' en System.dll pero no se controló en el código del usuario

    Información adicional: El servidor SMTP requiere una conexión segura o el cliente no se autenticó. La respuesta del servidor fue: 5.5.1 Authentication Required.

    Que puede llegar a ser?

    ResponderEliminar
    Respuestas
    1. hola
      Cuando menciona la conexion seguro quiere decir que debes definir la propiedad EnableSSL = true
      Tambien valida que asignaste las credenciales de forma correcta.
      saludos

      Eliminar
  19. Hola , excelente tu post, lo que intento hacer es diferenciar con el SSL con el TLS al momento de enviar el correo electronico de que manera lo puedo realizar???? C#

    ResponderEliminar
  20. Que tal Leandro Como agrego una imagen antes del mensaje del correo en windowsform no he podido agregar.

    ResponderEliminar