Envío de correo electrónico por SMTP con autenticación
Por Willy Mejía
Publicado: Septiembre 2004
Actualizado: Febrero 2006
Este artículo muestra como podemos enviar correo electrónico por medio de servidores SMTP que requieren credenciales de autenticación.
- Envío de correo electrónico en .NET
- El Problema: El servicio SMTP requiere autenticación
- Estableciendo las Credenciales de Autenticación en CDO
- Utilizando CDO (cdosys.dll) para el envío de mensajes con autenticación
- Utilizando MailMessage.Fields para el envío de mensajes con autenticación
- Utilizando SmtpClient para el envío de mensajes con autenticación [.NET 2.0]
- Conclusión
–
Envío de correo electrónico en .NET
Para que una aplicación .NET pueda realizar envío de correo electrónico con el .NET Framework 1.x se emplea el espacio de nombres System.Web.Mail, que contiene las clases que permiten construir y enviar mensajes mediante el componente COM de mensajería conocido como CDO (Collaboration Data Objects) formalmente llamado «Microsoft CDO for Windows 2000 Library» que se incluye en Windows 2000 y posteriores. El mensaje de correo se entrega por medio del servicio de correo SMTP (Simple Mail Transport Protocol) ya sea con el servidor incluido con IIS o bien mediante algún otro servidor SMTP externo. Las clases de System.Web.Mail se pueden utilizar desde cualquier aplicación administrada ya sea Web (ASP.NET) o Windows.
Imports System.Web.Mail Module Module1 Sub Main() Dim oMsg As MailMessage = New MailMessage() oMsg.From = "sender@somewhere.com" oMsg.To = "recipient@somewhere.com" oMsg.Subject = "Send using Web Mail" oMsg.BodyFormat = MailFormat.Html oMsg.Body = "Hello World!" SmtpMail.SmtpServer = "smtp.domain.com" SmtpMail.Send(oMsg) End Sub End Module
Lo anterior lo podemos emplear también en el .NET Framework 2.0, no obstante éste introduce una serie clases mejoradas para la comunicación por red, incluyendo aquellas relacionadas con el envío de correo electrónico. Dichas clases se encuentran bajo el Namespace System.Net.Mail y ya no son un wrapper para CDO.
Imports System.Net.Mail Module Module1 Sub Main() Dim oMsg As MailMessage = New MailMessage() oMsg.From = "sender@somewhere.com" oMsg.To = "recipient@somewhere.com" oMsg.Subject = "Send using Web Mail" oMsg.IsBodyHtml = True oMsg.Body = "Hello World!" Dim oClient As SmtpClient = New SmtpClient("smtp.domain.com") oClient.Send(oMsg) End Sub End Module
En este caso MailMessage es una nueva clase que pertenece al Namespace System.Net.Mail, por lo que no es la misma clase del Namespace System.Web.Mail aun presente por compatibilidad. Se observa también que ahora se tiene que crear una instancia de la nueva clase SmtpClient para realizar el envío de correo electrónico.
El Problema: El servicio SMTP requiere autenticación
Con el fin de evitar el envío de correo electrónico con remitente falso y reducir el spam, algunos servidores de correo electrónico SMTP requieren que el usuario se autentifique antes de enviar un mensaje. Si bajo estas circunstancias se envía un correo electrónico sin las credenciales de autenticación se recibe un mensaje error.
Para resolver el problema del envío de correo electrónico mediante un servidor SMTP que requiere credenciales autenticación tenemos tres posibles soluciones:
- Para el .NET Framework 1.0 o posterior: Utilizar CDO (cdosys.dll) por medio de Interoperabilidad COM, y establecer las credenciales de autenticación mediante la colección Fields de CDO.
- Para el .NET Framework 1.1 o posterior: Emplear el nuevo miembro Fields de la clase MailMessage para establecer las credenciales de autenticación.
- Para el .NET Framework 2.0: Emplear las propiedades de la nueva clase SmtpClient para especificar las credenciales de autenticación.
Cuando el método SmtpMail.Send de System.Web.Mail no puede completar el envío del mensaje de correo electrónico, se dispara una excepción con un mensaje parecido a:
No se puede obtener acceso al objeto ‘CDO.Message’.
Lamentablemente dicho mensaje no brinda mayor información sobre la causa real del problema. Por ello debemos utilizar InnerException para conocer la excepción interna que dió origen a la excepción actual. Como en el ejemplo siguiente, donde EscribeLog es una función para escribir el Log en un archivo, una base de datos, o al EventLog:
Try System.Web.Mail.SmtpMail.Send(Correo) Catch ex As System.Exception EscribeLog("Excepción: " & ex.Message) Do Until (ex.InnerException Is Nothing) EscribeLog("Excepción interna: " & _ ex.InnerException.ToString()) ex = ex.InnerException Loop End Try
De este modo podemos determinar la causa por la qué no se pudo enviar el mensaje. Si la causa tiene que ver con la falta de autenticación entonces continue leyendo.
Estableciendo las Credenciales de Autenticación en CDO.
La configuración del componente CDO se establece a través de un conjunto de propiedades en forma de pares de nombre/valor llamados CDO Fields. Dichos Campos se implementan en una Colección de tipo ADODB.Fields llamada a su vez Fields. Esta implementación esta asociada al objeto Configuration de CDO quien a su vez esta asociado a los distintos objetos de CDO. Es entonces que debemos emplear el objeto Configuration para definir parámetros de configuración específicos al objeto Message de CDO a través de su colección Fields para así establecer los valores de los Campos necesarios para establecer las credenciales de autenticación.
Todos los campos que necesitamos establecer se encuentran bajo el Espacio de Nombres de CDO (no confundir con los Namespaces de .NET) «http://schemas.microsoft.com/cdo/configuration/» que se encuentran agrupadas en la Biblioteca de CDO bajo el Módulo CdoConfiguration, y que están representados por constantes que equivalen al nombre completo del Campo, esto es: Espacio de nombres + Nombre del campo. Así por ejemplo, la constante cdoSMTPAuthenticate equivale al Campo smtpauthenticate cuyo nombre completo es: «http://schemas.microsoft.com/cdo/configuration/smtpauthenticate«.
Mediante el campo smtpauthenticate podemos indicar el mecanismo de autenticación requerida para enviar los mensajes, los valores permitidos se listan en el Enumerado CdoProtocolsAuthentication, que alberga a tres constantes: cdoAnonymous (valor numérico 0), cdoBasic (valor numérico 1) y cdoNTLM (valor numérico 2).
Para que el campo smtpauthenticate sea tomado en cuenta debe establecerse a su vez el campo sendusing («http://schemas.microsoft.com/cdo/configuration/sendusing»), al valor de la constante cdoSendUsingPort (valor numérico 2) del Enumerado CdoSendUsing, este campo define si se debe enviar el mensaje utilizando el servicio local SMTP (si es que el host local tuviese el servicio SMTP instalado) o un servicio SMTP externo sobre la red de trabajo local, que es el que debemos establecer.
También debe establecerse el valor del campo smtpserver («http://schemas.microsoft.com/cdo/configuration/smtpserver») con la dirección IP específica o el nombre DNS del host que alberga el servicio SMTP, y en caso de ser necesario, debe establecerse el valor numérico del puerto del mismo en el campo smtpserverport («http://schemas.microsoft.com/cdo/configuration/smtpserverport»).
Por último y mas importante, para establecer las credenciales de autenticación requeridas para la conexión con el servicio SMTP, deben establecerse los valores de los campos sendusername («http://schemas.microsoft.com/cdo/configuration/sendusername») y sendpassword («http://schemas.microsoft.com/cdo/configuration/sendpassword») correspondientes a el nombre de usuario (login) y la contraseña de usuario. Estos dos campos junto con el campo smtpauthenticate son los indispensables para establecer las credenciales de autenticación para el servidor SMTP.
En resumen, los Campos CDO a emplear y cuyos valores debemos establecer son:
- smtpserver: Nombre del servidor SMTP.
- smtpserverport: Puerto del servidor SMTP (25).
- sendusing: Método de envío, establecido a cdoSendUsingPort (2) para el uso de la red de trabajo.
- smtpauthenticate: Mecanismo de autenticación hacia el servicio SMTP sobre la red de trabajo. Establecido a cdoBasic (1).
- sendusername: Nombre de usuario
- sendpassword: Contraseña
Utilizando CDO (cdosys.dll) para el envío de mensajes con autenticación.
Este primer método no emplea el Namespace System.Web.Mail, sino que emplea directamente la Biblioteca de tipos de CDO a través de Interoperabilidad COM. Para ello lo primero que tenemos que hacer es crear nuestro Ensamblado de Interoperabilidad. Desde su IDE puede hacerlo agregando la referencia COM de dicho componente, o bien, desde la línea de comandos lo obtenemos escribiendo:
>tlbimp cdosys.dll /out:Interop.CDO.dll
Podemos ahora elaborar un ejemplo con VB.NET, para ello creamos un Proyecto de Consola y agregamos la referencia de CDO, y creamos un archivo de nombre Module1.vb. Después, introducimos el código siguiente:
Option Explicit On Module Module1 Sub Main() Const ConfigNamespace As String = _ "http://schemas.microsoft.com/cdo/configuration/" Dim oMsg As New CDO.Message() Dim iConfig As New CDO.Configuration() Dim Flds As ADODB.Fields = iConfig.Fields With Flds .Item(ConfigNamespace & "smtpserver").Value = "smtp.mail.dominio" .Item(ConfigNamespace & "smtpserverport").Value = 25 .Item(ConfigNamespace & "sendusing").Value = _ CDO.CdoSendUsing.cdoSendUsingPort .Item(ConfigNamespace & "sendusername").Value = "usuario" .Item(ConfigNamespace & "sendpassword").Value = "contraseña" .Item(ConfigNamespace & "smtpauthenticate").Value = _ CDO.CdoProtocolsAuthentication.cdoBasic .Update() End With With oMsg .Configuration = iConfig .From = "usuario@dominio" .To = "alguien@dominio" .Subject = "Test con CDO" .TextBody = "Hola Mundo" .Send() End With oMsg = Nothing iConfig = Nothing End Sub End Module
Nota: Debe cambiar los valores de los campos y propiedades por aquellos que correspondan a su caso particular. Si el servidor SMTP además requiere una conexión SSL, se debe agregar el campo «smtpusessl» con valor establecido a «true».
Compilamos desde nuestro IDE, o bien, para compilar desde la línea de comandos:
>vbc Module1.vb /r:Interop.CDO.dll,adodb.dll
Nota: El ensamblado ADODB.DLL es el PIA (Primary Interop Assembly) de ADO (ActiveX Data Objects) que por lo general se encuentra en: %programfiles%\Microsoft.NET\Primary Interop Assemblies\ En caso contrario deberá crearse manualmente con tlbimp.
Esta es la única solución para el envío de correo electrónico con autenticación para la versión 1.0 del .NET Framework, mas puede funcionar también con las versiones 1.1 y 2.0.
Utilizando MailMessage.Fields para el envío de mensajes con autenticación.
En la documentación del .Net Framework versión 1.1 se observa que la clase System.Web.Mail.MailMessage tiene un nuevo miembro llamado Fields —mismo que hasta el día de hoy no esta del todo documentado— con el cual podemos establecer los valores de la colección Fields de la biblioteca CDO.
Para probarlo modificaremos el código anterior cambiando los objetos CDO por sus equivalentes en System.Web.Mail, empleando un System.Collections.IDictionary en lugar de ADODB.Fields, así como cambiando las nombres de las constantes CDO por sus valores numéricos correspondientes para evitar cualquier referencia a CDO, teniendo como resultado el siguiente código:
Option Explicit On Module Module1 Sub Main() Const ConfigNamespace As String = _ "http://schemas.microsoft.com/cdo/configuration/" Dim oMsg As New System.Web.Mail.MailMessage Dim Flds As System.Collections.IDictionary Flds = oMsg.Fields With Flds .Add(ConfigNamespace & "smtpserver", "smtp.mail.dominio") .Add(ConfigNamespace & "smtpserverport", 25) .Add(ConfigNamespace & "sendusing", 2) .Add(ConfigNamespace & "sendusername", "usuario") .Add(ConfigNamespace & "sendpassword", "contraseña") .Add(ConfigNamespace & "smtpauthenticate", 1) End With With oMsg .From = "usuario@dominio" .To = "alguien@dominio" .Subject = "Test con MailMessage.Fields" .Body = "Hola Mundo" End With System.Web.Mail.SmtpMail.Send(oMsg) End Sub End Module
Nota: Debe cambiar los valores de los campos y propiedades por aquellos que correspondan a su caso particular. Si el servidor SMTP además requiere una conexión SSL, se debe agregar el campo «smtpusessl» con valor establecido a «true».
Observe que dado a que el código se especificó el Host del servicio SMTP mediante el campo «smtpserver» ya no es necesario hacerlo mediante la propiedad SmtpServer. Por otro lado, si se emplea la propiedad SmtpServer se pueden omitir los campos «smtpserver», «smtpserverport» y «sendusing» y sólo necesitaríamos especificar los tres campos restantes: «sendusername», «sendpassword» y «smtpauthenticate».
De este modo podemos enviar mensajes con autenticación sin necesidad de emplear la Biblioteca COM de CDO mediante Interoperabilidad COM gracias a la propiedad Fields de MailMessage presente a partir de la versión 1.1 del .Net Framework. Por ende esta solución no funciona en la versión 1.0 pero sí en la versión 2.0 del .NET Framework.
Utilizando SmtpClient para el envío de mensajes con autenticación.
Dado que las nuevas clases bajo el Namespace System.Net.Mail del .NET Framework 2.0 ya no emplean CDO, las credenciales de autenticación ya no se especifican mediante Campos CDO de ningún tipo. En lugar de ello se utilizan o especifican las Credenciales de Red a través de la instancia de la clase SmtpClient creada para el envío de correo electrónico.
Si se desea autenticar con las credenciales del usuario actual debemos especificarlo utilizando la propiedad UseDefaultCredentials de SmtpClient y establecer su valor a True. Tal y como se muestra en la siguiente sentencia:
mySmtpClient.UseDefaultCredentials = True
Para especificar credenciales distintas deberá utilizarse la propiedad Credentials de SmtpClient que es de tipo ICredentialsByHost. Por lo que podemos asignar una instancia de la clase System.Net.NetworkCredential que albergará las credenciales a utilizar. Por ejemplo, para establecer las cadenas de usuario y contraseña (empleando Autenticación Básica) podemos utilizar la siguiente sentencia:
mySmtpClient.Credentials = _ New System.Net.NetworkCredential("usuario", "contraseña")
Ahora modifiquemos el ejemplo visto en los casos anteriores para utilizar las nuevas clases del .NET Framework 2.0.
Option Explicit On Module Module1 Sub Main() Dim oMsg As New System.Net.Mail.MailMessage With oMsg .From = "usuario@dominio" .To = "alguien@dominio" .Subject = "Test con SmtpClient" .Body = "Hola Mundo" End With Dim oClient As New System.Net.Mail.SmtpClient("smtp.mail.dominio") oClient.UseDefaultCredentials = False oClient.Credentials = _ New System.Net.NetworkCredential("usuario", "contraseña") oClient.Send(oMsg) End Sub End Module
Nota: Debe cambiar los valores de los constructores y propiedades por aquellos que correspondan a su caso particular.
Si el servidor SMTP además requiere una conexión SSL, se debe utilizar la propiedad EnableSsl del SmtpClient con valor establecido a «true».
oClient.EnableSsl = True
Tal y como se aprecia, el código necesario se ha simplificado y reducido considerablemente. Se puede reducir aun mas si se establece la configuración del envío de correo electrónico mediante el archivo de configuración del sitio web (web.config). Tal y como se muestra a continuación.
<configuration> <system.net> <mailSettings> <smtp deliveryMethod="Network"> <network host="smtp.mail.dominio" defaultCredentials="false" userName="usuario" password="contraseña" /> </smtp> </mailSettings> </system.net> </configuration>
Nota: La configuración SMTP también puede establecerse mediante la Herramienta Administración de sitios Web => ficha «Aplicación».
De manera que el código queda reducido a:
Option Explicit On Module Module1 Sub Main() Dim oMsg As New System.Net.Mail.MailMessage With oMsg .From = "usuario@dominio" .To = "alguien@dominio" .Subject = "Test con SmtpClient" .Body = "Hola Mundo" End With Dim oClient As New System.Net.Mail.SmtpClient() oClient.Send(oMsg) End Sub End Module
Utilizar el archivo de configuración permite que el código sea reutilizable y la configuración fácil de personalizar si las condiciones han cambiado. Por ejemplo, si en lugar de un servidor SMTP externo ahora se desea utilizar el servicio SMTP de IIS, sólo tendremos que cambiar la configuración como se muestra a continuación:
<configuration> <system.net> <mailSettings> <smtp deliveryMethod="PickupDirectoryFromIis" /> </mailSettings> </system.net> </configuration>
Aunque también es posible establecer el método de envío (deliveryMethod) y otros parámetros mediante código -tal y como lo hemos visto- es mejor establecerlo mediante el archivo de configuración.
La utilización de Autenticación Básica implica enviar los valores de userName y password al servidor sin encriptar. Cualquier persona que este monitoreando el tráfico de la red puede ver las credenciales y utilizarlas para conectarse al servidor. Por ello se recomienda utilizar mecanismos de autenticación más seguros, tales como Kerberos o NTLM. Dichos protocolos se utilizan si se emplea defaultCredentials con el valor establecido a true (aplicable con el .NET Framework 2.0), siempre y cuando el servidor los soporte.
Conclusión
En el presente artículo se han presentado tres formas, una para cada versión del .NET Framework, para el envío de correo electrónico mediante servidores SMTP que requieren autenticación. Las dos primeras consisten en manipular los Campos de Configuración de CDO: «smtpserver», «smtpserverport» y «sendusing» para establecer los valores necesarios, siendo la única diferencia que la primero utiliza CDO mediante Interoperabilidad COM, y la segundo emplea la propiedad Fields de System.Web.Mail.MailMessage presente a partir del .NET Framework 1.1. La última consiste en utilizar las propiedades de las nuevas clases del .NET Framework 2.0 agrupadas bajo el Namespace System.Net.Mail, así como utilizando los archivos de configuración.
hola que tal.. valiosa informacion… yo pude enviar correo pero noto que lo hace cuando el outlook esta abierto y pasa un tiempo y deja de mandar correo desde mi site… justo abro outlook y otra vez funciona, pasa un tiempo y deja de funcionar… estoy pensando que es autentificacion como dicen aqui por que parece ser que outlook abre la sesion de la cuenta osea la autentifica cosa que en asp no hago… me sirvio mucho esta explicacion… un salu2
Muy buena explicacion pero aun no lologro con mi server je jeje, saludos, pero eso si ke funciona!!
😉
Hola, muy completo el artículo y de mucha utiliadad.. aunque ya es antiguo, lástima un poco de más información referente al encriptado de la autenticación, sin embargo por sí solo ya es información muy valiosa. Saludos!
Como a que tipo de información te refieres???
El cifrado en este caso queda en manos del servicio de correo, que a lo sumo solo podrá ofrecer una conexión SSL, y es a lo cual me refiero en el artículo con:
>Si el servidor SMTP además requiere una conexión SSL,
>se debe utilizar la propiedad EnableSsl del SmtpClient
>con valor establecido a “true”.
>
>oClient.EnableSsl = True
…como en el caso de Google u otros de pago.
Ahora que sobre el cifrado de información de autenticación en aplicaciones web, es otra cosa, para lo cual puedes leer por ejemplo
También un poco viejo, pero sirve…
Saludos,
Willy Mejía
Estimado, buscando encontré esto. En referencia a la información, que la encontré muy profesional y bien descriptiva, ahora si es posible saber como puedo agregar al cuerpo del mensaje «Avances de Linea», y que no se vea todo en sola linea.
Es una cuestión de formato y manipulación de texto, más que del envío de correo en sí.
Dado que los avances de línea van en el cuerpo del mensaje, bastaría con colocar un retorno y nueva línea (\r\n) si el mensaje es de texto:
msg.Body = string.Format("Primera línea{0}{0}segunda…", "\r\n");
…o bien el tag correspondiente <br /> si es con HTML:
msg.Body = "Primera línea<br />segunda…";
Saludos.